mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' into tick-rate-handling
This commit is contained in:
commit
d20b18a5c0
9 changed files with 138 additions and 12 deletions
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.platform.mod.mixin.server;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(BlockItem.class)
|
||||
public class BlockPlaceMixin {
|
||||
|
||||
@Inject(method = "place", locals = LocalCapture.CAPTURE_FAILSOFT, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/core/BlockPos;Lnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V"))
|
||||
private void geyser$hijackPlaySound(BlockPlaceContext blockPlaceContext, CallbackInfoReturnable<InteractionResult> cir, BlockPlaceContext blockPlaceContext2, BlockState blockState, BlockPos blockPos, Level level, Player player, ItemStack itemStack, BlockState blockState2, SoundType soundType) {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeyserSession session = GeyserImpl.getInstance().connectionByUuid(player.getUUID());
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3f position = Vector3f.from(
|
||||
blockPos.getX(),
|
||||
blockPos.getY(),
|
||||
blockPos.getZ()
|
||||
);
|
||||
|
||||
LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket();
|
||||
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
|
||||
placeBlockSoundPacket.setPosition(position);
|
||||
placeBlockSoundPacket.setBabySound(false);
|
||||
placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(Block.BLOCK_STATE_REGISTRY.getId(blockState2)));
|
||||
placeBlockSoundPacket.setIdentifier(":");
|
||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
"package": "org.geysermc.geyser.platform.mod.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"server.BlockPlaceMixin",
|
||||
"server.ServerConnectionListenerMixin"
|
||||
],
|
||||
"server": [
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
|
@ -73,6 +74,10 @@ public class PlayerInventory extends Inventory {
|
|||
return items[36 + heldItemSlot];
|
||||
}
|
||||
|
||||
public boolean eitherHandMatchesItem(@NonNull Item item) {
|
||||
return getItemInHand().asItem() == item || getItemInHand(Hand.OFF_HAND).asItem() == item;
|
||||
}
|
||||
|
||||
public void setItemInHand(@NonNull GeyserItemStack item) {
|
||||
if (36 + heldItemSlot > this.size) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public class StoredItemMappings {
|
|||
private final ItemMapping milkBucket;
|
||||
private final ItemMapping powderSnowBucket;
|
||||
private final ItemMapping shield;
|
||||
private final ItemMapping totem;
|
||||
private final ItemMapping upgradeTemplate;
|
||||
private final ItemMapping wheat;
|
||||
private final ItemMapping writableBook;
|
||||
|
|
@ -66,6 +67,7 @@ public class StoredItemMappings {
|
|||
this.milkBucket = load(itemMappings, Items.MILK_BUCKET);
|
||||
this.powderSnowBucket = load(itemMappings, Items.POWDER_SNOW_BUCKET);
|
||||
this.shield = load(itemMappings, Items.SHIELD);
|
||||
this.totem = load(itemMappings, Items.TOTEM_OF_UNDYING);
|
||||
this.upgradeTemplate = load(itemMappings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE);
|
||||
this.wheat = load(itemMappings, Items.WHEAT);
|
||||
this.writableBook = load(itemMappings, Items.WRITABLE_BOOK);
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
private boolean closingInventory;
|
||||
|
||||
@Setter
|
||||
private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
|
||||
private @NonNull InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
|
||||
|
||||
/**
|
||||
* Use {@link #getNextItemNetId()} instead for consistency
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
|||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||
|
|
@ -42,11 +44,15 @@ import org.geysermc.geyser.entity.type.FishingHookEntity;
|
|||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.ArmadilloEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.WardenEntity;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
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.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundEntityEventPacket;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Translator(packet = ClientboundEntityEventPacket.class)
|
||||
|
|
@ -154,6 +160,16 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
|
|||
entityEventPacket.setType(EntityEventType.WITCH_HAT_MAGIC); //TODO: CHECK
|
||||
break;
|
||||
case TOTEM_OF_UNDYING_MAKE_SOUND:
|
||||
// Bedrock will not play the spinning animation without the item in the hand o.o
|
||||
// Fixes https://github.com/GeyserMC/Geyser/issues/2446
|
||||
boolean totemItemWorkaround = !session.getPlayerInventory().eitherHandMatchesItem(Items.TOTEM_OF_UNDYING);
|
||||
if (totemItemWorkaround) {
|
||||
InventoryContentPacket offhandPacket = new InventoryContentPacket();
|
||||
offhandPacket.setContainerId(ContainerId.OFFHAND);
|
||||
offhandPacket.setContents(Collections.singletonList(InventoryUtils.getTotemOfUndying().apply(session.getUpstream().getProtocolVersion())));
|
||||
session.sendUpstreamPacket(offhandPacket);
|
||||
}
|
||||
|
||||
entityEventPacket.setType(EntityEventType.CONSUME_TOTEM);
|
||||
|
||||
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||
|
|
@ -162,7 +178,16 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
|
|||
playSoundPacket.setVolume(1.0F);
|
||||
playSoundPacket.setPitch(1.0F + (ThreadLocalRandom.current().nextFloat() * 0.1F) - 0.05F);
|
||||
session.sendUpstreamPacket(playSoundPacket);
|
||||
break;
|
||||
|
||||
// Sent here early to ensure we have the totem in our hand
|
||||
session.sendUpstreamPacket(entityEventPacket);
|
||||
|
||||
if (totemItemWorkaround) {
|
||||
// Reset the item again
|
||||
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), 45);
|
||||
}
|
||||
|
||||
return;
|
||||
case SHEEP_GRAZE_OR_TNT_CART_EXPLODE:
|
||||
if (entity.getDefinition() == EntityDefinitions.SHEEP) {
|
||||
entityEventPacket.setType(EntityEventType.EAT_GRASS);
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public class JavaOpenBookTranslator extends PacketTranslator<ClientboundOpenBook
|
|||
}
|
||||
|
||||
InventoryTranslator translator = InventoryTranslator.inventoryTranslator(ContainerType.LECTERN);
|
||||
Objects.requireNonNull(translator, "could not find lectern inventory translator!");
|
||||
session.setInventoryTranslator(translator);
|
||||
|
||||
// Should never be null
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ package org.geysermc.geyser.translator.protocol.java.level;
|
|||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
|
|
@ -43,24 +43,27 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
|||
@Override
|
||||
public void translate(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
||||
Vector3i pos = packet.getEntry().getPosition();
|
||||
boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
|
||||
!session.getErosionHandler().isActive() && session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
// Platforms where Geyser has direct server access don't allow us to detect actual block changes,
|
||||
// hence why those platforms deal with sounds for block placements differently
|
||||
boolean updatePlacement = !worldManager.hasOwnChunkCache() &&
|
||||
!session.getErosionHandler().isActive() && worldManager.getBlockAt(session, pos) != packet.getEntry().getBlock();
|
||||
session.getWorldCache().updateServerCorrectBlockState(pos, packet.getEntry().getBlock());
|
||||
if (updatePlacement) {
|
||||
this.checkPlace(session, packet);
|
||||
this.checkPlaceSound(session, packet);
|
||||
}
|
||||
this.checkInteract(session, packet);
|
||||
}
|
||||
|
||||
private boolean checkPlace(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
||||
private void checkPlaceSound(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
||||
Vector3i lastPlacePos = session.getLastBlockPlacePosition();
|
||||
if (lastPlacePos == null) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if ((lastPlacePos.getX() != packet.getEntry().getPosition().getX()
|
||||
|| lastPlacePos.getY() != packet.getEntry().getPosition().getY()
|
||||
|| lastPlacePos.getZ() != packet.getEntry().getPosition().getZ())) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to check if the identifier is the same, else a packet with the sound of what the
|
||||
|
|
@ -74,7 +77,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
|||
if (!contains) {
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not sent from the server, so we need to send it this way
|
||||
|
|
@ -87,7 +90,6 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
|||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkInteract(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
|
@ -91,7 +92,7 @@ public class InventoryUtils {
|
|||
|
||||
public static void displayInventory(GeyserSession session, Inventory inventory) {
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null && translator.prepareInventory(session, inventory)) {
|
||||
if (translator.prepareInventory(session, inventory)) {
|
||||
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
||||
session.scheduleInEventLoop(() -> {
|
||||
Inventory openInv = session.getOpenInventory();
|
||||
|
|
@ -110,7 +111,11 @@ public class InventoryUtils {
|
|||
inventory.setDisplayed(true);
|
||||
}
|
||||
} else {
|
||||
// Can occur if we e.g. did not find a spot to put a fake container in
|
||||
ServerboundContainerClosePacket closePacket = new ServerboundContainerClosePacket(inventory.getJavaId());
|
||||
session.sendDownstreamGamePacket(closePacket);
|
||||
session.setOpenInventory(null);
|
||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,6 +253,12 @@ public class InventoryUtils {
|
|||
.count(1).build();
|
||||
}
|
||||
|
||||
public static IntFunction<ItemData> getTotemOfUndying() {
|
||||
return protocolVersion -> ItemData.builder()
|
||||
.definition(Registries.ITEMS.forVersion(protocolVersion).getStoredItems().totem().getBedrockDefinition())
|
||||
.count(1).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #findOrCreateItem(GeyserSession, String)}. This is for finding a specified {@link ItemStack}.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue