Fix powder snow and fish buckets (#2437)

This commit is contained in:
David Choo 2021-08-01 22:20:15 -04:00 committed by GitHub
parent 20b183ddda
commit 3eb73a5634
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 182 additions and 20 deletions

View file

@ -26,14 +26,27 @@
package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.google.common.collect.ImmutableList;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import it.unimi.dsi.fastutil.ints.IntList;
import org.geysermc.connector.entity.living.AbstractFishEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.List;
public class TropicalFishEntity extends AbstractFishEntity {
/**
* A list of variant numbers that are given special names
* The index of the variant in this list is used as part of the locale key
*/
private static final IntList PREDEFINED_VARIANTS = IntList.of(117506305, 117899265, 185008129, 117441793, 118161664, 65536, 50726144, 67764993, 234882305, 67110144, 117441025, 16778497, 101253888, 50660352, 918529, 235340288, 918273, 67108865, 917504, 459008, 67699456, 67371009);
private static final List<String> VARIANT_NAMES = ImmutableList.of("kob", "sunstreak", "snooper", "dasher", "brinely", "spotty", "flopper", "stripey", "glitter", "blockfish", "betty", "clayfish");
private static final List<String> COLOR_NAMES = ImmutableList.of("white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray", "light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black");
public TropicalFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@ -43,11 +56,48 @@ public class TropicalFishEntity extends AbstractFishEntity {
if (entityMetadata.getId() == 17) {
int varNumber = (int) entityMetadata.getValue();
metadata.put(EntityData.VARIANT, varNumber & 0xFF); // Shape 0-1
metadata.put(EntityData.MARK_VARIANT, (varNumber >> 8) & 0xFF); // Pattern 0-5
metadata.put(EntityData.COLOR, (byte) ((varNumber >> 16) & 0xFF)); // Base color 0-15
metadata.put(EntityData.COLOR_2, (byte) ((varNumber >> 24) & 0xFF)); // Pattern color 0-15
metadata.put(EntityData.VARIANT, getShape(varNumber)); // Shape 0-1
metadata.put(EntityData.MARK_VARIANT, getPattern(varNumber)); // Pattern 0-5
metadata.put(EntityData.COLOR, getBaseColor(varNumber)); // Base color 0-15
metadata.put(EntityData.COLOR_2, getPatternColor(varNumber)); // Pattern color 0-15
}
super.updateBedrockMetadata(entityMetadata, session);
}
public static int getShape(int variant) {
return Math.min(variant & 0xFF, 1);
}
public static int getPattern(int variant) {
return Math.min((variant >> 8) & 0xFF, 5);
}
public static byte getBaseColor(int variant) {
byte color = (byte) ((variant >> 16) & 0xFF);
if (!(0 <= color && color <= 15)) {
return 0;
}
return color;
}
public static byte getPatternColor(int variant) {
byte color = (byte) ((variant >> 24) & 0xFF);
if (!(0 <= color && color <= 15)) {
return 0;
}
return color;
}
public static String getVariantName(int variant) {
int id = 6 * getShape(variant) + getPattern(variant);
return VARIANT_NAMES.get(id);
}
public static String getColorName(byte colorId) {
return COLOR_NAMES.get(colorId);
}
public static int getPredefinedId(int variant) {
return PREDEFINED_VARIANTS.indexOf(variant);
}
}

View file

@ -38,7 +38,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
import com.nukkitx.protocol.bedrock.data.inventory.*;
import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
@ -149,7 +148,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
*/
// CraftBukkit+ check - see https://github.com/PaperMC/Paper/blob/458db6206daae76327a64f4e2a17b67a7e38b426/Spigot-Server-Patches/0532-Move-range-check-for-block-placing-up.patch
Vector3f playerPosition = session.getPlayerEntity().getPosition();
EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags();
// Adjust position for current eye height
switch (session.getPose()) {
@ -210,21 +208,26 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
if (session.getItemMappings().getBoatIds().contains(packet.getItemInHand().getId())) {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}
// Check actions, otherwise buckets may be activated when block inventories are accessed
else if (session.getItemMappings().getBucketIds().contains(packet.getItemInHand().getId())) {
} else if (session.getItemMappings().getBucketIds().contains(packet.getItemInHand().getId())) {
// Let the server decide if the bucket item should change, not the client, and revert the changes the client made
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(packet.getHotbarSlot());
slotPacket.setItem(packet.getItemInHand());
session.sendUpstreamPacket(slotPacket);
// Delay the interaction in case the client doesn't intend to actually use the bucket
// See BedrockActionTranslator.java
session.setBucketScheduledFuture(session.getConnector().getGeneralThreadPool().schedule(() -> {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}, 5, TimeUnit.MILLISECONDS));
// Don't send ClientPlayerUseItemPacket for powder snow buckets
if (packet.getItemInHand().getId() != session.getItemMappings().getStoredItems().powderSnowBucket().getBedrockId()) {
// Special check for crafting tables since clients don't send BLOCK_INTERACT when interacting
int blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition());
if (session.isSneaking() || blockState != BlockRegistries.JAVA_IDENTIFIERS.get("minecraft:crafting_table")) {
// Delay the interaction in case the client doesn't intend to actually use the bucket
// See BedrockActionTranslator.java
session.setBucketScheduledFuture(session.getConnector().getGeneralThreadPool().schedule(() -> {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}, 5, TimeUnit.MILLISECONDS));
}
}
}
}

View file

@ -46,6 +46,7 @@ public class StoredItemMappings {
private final ItemMapping fishingRod;
private final ItemMapping lodestoneCompass;
private final ItemMapping milkBucket;
private final ItemMapping powderSnowBucket;
private final ItemMapping egg;
private final ItemMapping shield;
private final ItemMapping wheat;
@ -60,6 +61,7 @@ public class StoredItemMappings {
this.fishingRod = load(itemMappings, "fishing_rod");
this.lodestoneCompass = load(itemMappings, "lodestone_compass");
this.milkBucket = load(itemMappings, "milk_bucket");
this.powderSnowBucket = load(itemMappings, "powder_snow_bucket");
this.egg = load(itemMappings, "egg");
this.shield = load(itemMappings, "shield");
this.wheat = load(itemMappings, "wheat");

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.geysermc.connector.entity.living.animal.TropicalFishEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.registry.type.ItemMapping;
import org.geysermc.connector.utils.LocaleUtils;
import java.util.ArrayList;
import java.util.List;
@ItemRemapper
public class TropicalFishBucketTranslator extends NbtItemStackTranslator {
private static final Style LORE_STYLE = Style.style(NamedTextColor.GRAY, TextDecoration.ITALIC);
@Override
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
// Prevent name from appearing as "Bucket of"
itemTag.put(new ByteTag("AppendCustomName", (byte) 1));
itemTag.put(new StringTag("CustomName", LocaleUtils.getLocaleString("entity.minecraft.tropical_fish", session.getLocale())));
// Add Java's client side lore tag
Tag bucketVariantTag = itemTag.get("BucketVariantTag");
if (bucketVariantTag instanceof IntTag) {
CompoundTag displayTag = itemTag.get("display");
if (displayTag == null) {
displayTag = new CompoundTag("display");
itemTag.put(displayTag);
}
List<Tag> lore = new ArrayList<>();
int varNumber = ((IntTag) bucketVariantTag).getValue();
int predefinedVariantId = TropicalFishEntity.getPredefinedId(varNumber);
if (predefinedVariantId != -1) {
Component tooltip = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE);
lore.add(0, new StringTag("", MessageTranslator.convertMessage(tooltip, session.getLocale())));
} else {
Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(varNumber), LORE_STYLE);
lore.add(0, new StringTag("", MessageTranslator.convertMessage(typeTooltip, session.getLocale())));
byte baseColor = TropicalFishEntity.getBaseColor(varNumber);
byte patternColor = TropicalFishEntity.getPatternColor(varNumber);
Component colorTooltip = Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(baseColor), LORE_STYLE);
if (baseColor != patternColor) {
colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE))
.append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor), LORE_STYLE));
}
lore.add(1, new StringTag("", MessageTranslator.convertMessage(colorTooltip, session.getLocale())));
}
ListTag loreTag = displayTag.get("Lore");
if (loreTag != null) {
lore.addAll(loreTag.getValue());
}
displayTag.put(new ListTag("Lore", lore));
}
}
@Override
public boolean acceptItem(ItemMapping mapping) {
return mapping.getJavaIdentifier().equals("minecraft:tropical_fish_bucket");
}
}

View file

@ -32,12 +32,14 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHandler;
import org.geysermc.connector.network.translators.sound.SoundHandler;
@SoundHandler(items = "bucket")
@SoundHandler(items = "bucket", ignoreSneakingWhileHolding = true)
public class BucketSoundInteractionHandler implements BlockSoundInteractionHandler {
@Override
public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
if (session.getBucketScheduledFuture() == null) return; // No bucket was really interacted with
if (session.getBucketScheduledFuture() == null) {
return; // No bucket was really interacted with
}
String handItemIdentifier = session.getPlayerInventory().getItemInHand().getMapping(session).getJavaIdentifier();
LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
soundEventPacket.setPosition(position);
@ -52,22 +54,31 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
soundEvent = SoundEvent.BUCKET_FILL_WATER;
} else if (identifier.contains("lava[")) {
soundEvent = SoundEvent.BUCKET_FILL_LAVA;
} else if (identifier.contains("powder_snow")) {
soundEvent = SoundEvent.BUCKET_FILL_POWDER_SNOW;
}
break;
case "minecraft:lava_bucket":
soundEvent = SoundEvent.BUCKET_EMPTY_LAVA;
break;
case "minecraft:fish_bucket":
case "minecraft:axolotl_bucket":
case "minecraft:cod_bucket":
case "minecraft:salmon_bucket":
case "minecraft:pufferfish_bucket":
case "minecraft:tropical_fish_bucket":
soundEvent = SoundEvent.BUCKET_EMPTY_FISH;
break;
case "minecraft:water_bucket":
soundEvent = SoundEvent.BUCKET_EMPTY_WATER;
break;
case "minecraft:powder_snow_bucket":
soundEvent = SoundEvent.BUCKET_EMPTY_POWDER_SNOW;
break;
}
if (soundEvent != null) {
soundEventPacket.setSound(soundEvent);
session.sendUpstreamPacket(soundEventPacket);
session.setBucketScheduledFuture(null);
}
session.setBucketScheduledFuture(null);
}
}

@ -1 +1 @@
Subproject commit f109d34a343da0ade6132661839b893859680d91
Subproject commit 2efdb453e4e76992d63824b5c8b551bebec67b71