Skip to content

Commit

Permalink
Fix teleport packets; implement new inventory packets
Browse files Browse the repository at this point in the history
  • Loading branch information
Camotoy committed Oct 25, 2024
1 parent 67dbd86 commit 2025a2d
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 46 deletions.
14 changes: 8 additions & 6 deletions core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@

package org.geysermc.geyser.entity.type;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -67,6 +61,13 @@
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

@Getter
@Setter
public class Entity implements GeyserEntity {
Expand All @@ -89,6 +90,7 @@ public class Entity implements GeyserEntity {

/**
* x = Yaw, y = Pitch, z = HeadYaw
* Java: Y = Yaw, X = Pitch
*/
protected float yaw;
protected float pitch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ public ItemMapping getMapping(GeyserSession session) {
}

public Item asItem() {
if (isEmpty()) {
return Items.AIR;
}
if (item == null) {
return (item = Registries.JAVA_ITEMS.get().get(javaId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ private static void handle(GeyserSession session, SessionPlayerEntity entity, Pl
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
session.sendUpstreamPacket(attributesPacket);
}
case JUMP -> entity.setOnGround(false); // Increase block break time while jumping
case MISSED_SWING -> {
// Java edition sends a cooldown when hitting air.
// Normally handled by BedrockLevelSoundEventTranslator, but there is no sound on Java for this.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ public void translate(GeyserSession session, ClientboundEntityPositionSyncPacket
clientVehicle.getVehicleComponent().moveAbsolute(pos.getX(), pos.getY(), pos.getZ());
}

entity.teleport(pos.toFloat(), packet.getXRot(), packet.getYRot(), packet.isOnGround());
entity.teleport(pos.toFloat(), packet.getYRot(), packet.getXRot(), packet.isOnGround());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ public void translate(GeyserSession session, ClientboundPlayerPositionPacket pac
// TODO this behavior seems outdated (1.21.2).
// The server sends an absolute teleport everytime the player is respawned
entity.setPosition(pos.toFloat());
entity.setYaw(packet.getXRot());
entity.setPitch(packet.getYRot());
entity.setHeadYaw(packet.getXRot());
entity.setYaw(packet.getYRot());
entity.setPitch(packet.getXRot());
entity.setHeadYaw(packet.getYRot());

RespawnPacket respawnPacket = new RespawnPacket();
respawnPacket.setRuntimeEntityId(0); // Bedrock server behavior
Expand All @@ -78,7 +78,7 @@ public void translate(GeyserSession session, ClientboundPlayerPositionPacket pac
session.sendUpstreamPacket(movePlayerPacket);
session.setSpawned(true);
// Make sure the player moves away from (0, 32767, 0) before accepting movement packets
session.setUnconfirmedTeleport(new TeleportCache(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getYRot(), packet.getXRot(), packet.getId())); // TODO
session.setUnconfirmedTeleport(new TeleportCache(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getXRot(), packet.getYRot(), packet.getId())); // TODO

acceptTeleport(session, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getYRot(), packet.getXRot(), packet.getId());

Expand Down Expand Up @@ -107,8 +107,8 @@ public void translate(GeyserSession session, ClientboundPlayerPositionPacket pac
double newZ = pos.getZ() +
(packet.getRelatives().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);

float newPitch = packet.getYRot() + (packet.getRelatives().contains(PositionElement.Y_ROT) ? entity.getPitch() : 0);
float newYaw = packet.getXRot() + (packet.getRelatives().contains(PositionElement.X_ROT) ? entity.getYaw() : 0);
float newPitch = packet.getXRot() + (packet.getRelatives().contains(PositionElement.X_ROT) ? entity.getPitch() : 0);
float newYaw = packet.getYRot() + (packet.getRelatives().contains(PositionElement.Y_ROT) ? entity.getYaw() : 0);

int id = packet.getId();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,8 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound

@Override
public void translate(GeyserSession session, ClientboundContainerSetSlotPacket packet) {
if (packet.getContainerId() == 255) { //cursor //TODO new packet
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
session.getPlayerInventory().setCursor(newItem, session);
InventoryUtils.updateCursor(session);
return;
}

//TODO: support window id -2, should update player inventory
//TODO: ^ I think this is outdated.
Inventory inventory = InventoryUtils.getInventory(session, packet.getContainerId());
if (inventory == null) {
return;
Expand All @@ -89,8 +83,9 @@ public void translate(GeyserSession session, ClientboundContainerSetSlotPacket p
updateCraftingGrid(session, slot, packet.getItem(), inventory, translator);

GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
if (packet.getContainerId() == 0 && !(translator instanceof PlayerInventoryTranslator)) { //TODO new packet
if (packet.getContainerId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
// In rare cases, the window ID can still be 0 but Java treats it as valid
// This behavior still exists as of Java Edition 1.21.2, despite the new packet
session.getPlayerInventory().setItem(slot, newItem, session);
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), slot);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2024 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.java.inventory;

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.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundSetCursorItemPacket;

@Translator(packet = ClientboundSetCursorItemPacket.class)
public class JavaSetCursorItemTranslator extends PacketTranslator<ClientboundSetCursorItemPacket> {

@Override
public void translate(GeyserSession session, ClientboundSetCursorItemPacket packet) {
GeyserItemStack newItem = GeyserItemStack.from(packet.getContents());
session.getPlayerInventory().setCursor(newItem, session);
InventoryUtils.updateCursor(session);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2024 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.java.inventory;

import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundSetPlayerInventoryPacket;

@Translator(packet = ClientboundSetPlayerInventoryPacket.class)
public class JavaSetPlayerInventoryTranslator extends PacketTranslator<ClientboundSetPlayerInventoryPacket> {

@Override
public void translate(GeyserSession session, ClientboundSetPlayerInventoryPacket packet) {
int slot = packet.getSlot();
if (slot >= 0 && slot <= 8) {
// As of 1.21.3 - can be replicated in vanilla server survival by picking an item in-world in your inventory not in your hotbar.
slot = session.getPlayerInventory().getOffsetForHotbar(slot);
}

if (slot >= session.getPlayerInventory().getSize()) {
GeyserLogger logger = session.getGeyser().getLogger();
logger.warning("ClientboundSetPlayerInventoryPacket sent to " + session.bedrockUsername()
+ " that exceeds inventory size!");
if (logger.isDebug()) {
logger.debug(packet.toString());
logger.debug(session.getPlayerInventory().toString());
}
return;
}

session.getPlayerInventory().setItem(slot, GeyserItemStack.from(packet.getContents()), session);
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), slot);
}
}
56 changes: 32 additions & 24 deletions core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
Expand Down Expand Up @@ -305,14 +304,7 @@ public static void findOrCreateItem(GeyserSession session, ItemStack itemStack)

// If we still have not found the item, and we're in creative, ask for the item from the server.
if (session.getGameMode() == GameMode.CREATIVE) {
int slot = findEmptyHotbarSlot(inventory);

ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) slot,
itemStack);
if ((slot - 36) != inventory.getHeldItemSlot()) {
setHotbarItem(session, slot);
}
session.sendDownstreamGamePacket(actionPacket);
setPickedItem(session, inventory, GeyserItemStack.from(itemStack));
}
}

Expand Down Expand Up @@ -372,24 +364,15 @@ public static void findOrCreateItem(GeyserSession session, Item item) {
return;
}

// If we still have not found the item, and we're in creative, ask for the item from the server.
// If we still have not found the item, and we're in creative, set the item ourselves.
if (session.getGameMode() == GameMode.CREATIVE) {
int slot = findEmptyHotbarSlot(inventory);

ItemMapping mapping = session.getItemMappings().getMapping(item);
ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short)slot,
new ItemStack(mapping.getJavaItem().javaId()));
if ((slot - 36) != inventory.getHeldItemSlot()) {
setHotbarItem(session, slot);
}
session.sendDownstreamGamePacket(actionPacket);
GeyserItemStack itemStack = item.newItemStack(1, null);
setPickedItem(session, inventory, itemStack);
}
}

/**
* @return the first empty slot found in this inventory, or else the player's currently held slot.
*/
private static int findEmptyHotbarSlot(PlayerInventory inventory) {
private static void setPickedItem(GeyserSession session, PlayerInventory inventory, GeyserItemStack itemStack) {
// Try to find an empty hotbar slot.
int slot = inventory.getHeldItemSlot() + 36;
if (!inventory.getItemInHand().isEmpty()) { // Otherwise we should just use the current slot
for (int i = 36; i < 45; i++) {
Expand All @@ -399,7 +382,32 @@ private static int findEmptyHotbarSlot(PlayerInventory inventory) {
}
}
}
return slot;
GeyserItemStack existingItem = inventory.getItem(slot);
if (!existingItem.isEmpty()) {
// Try to move the item to another slot.
for (int i = 9; i < 36; i++) {
if (inventory.getItem(i).isEmpty()) {
inventory.setItem(i, existingItem, session);
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, inventory, i);

ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) i,
existingItem.getItemStack());
session.sendDownstreamGamePacket(actionPacket);
break;
}
}
}

// As of 1.21.3 - the client does this on its own end and the server doesn't send a slot response back.
inventory.setItem(slot, itemStack, session);
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, inventory, slot);

ServerboundSetCreativeModeSlotPacket actionPacket = new ServerboundSetCreativeModeSlotPacket((short) slot,
itemStack.getItemStack());
if ((slot - 36) != inventory.getHeldItemSlot()) {
setHotbarItem(session, slot);
}
session.sendDownstreamGamePacket(actionPacket);
}

/**
Expand Down

0 comments on commit 2025a2d

Please sign in to comment.