diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java index 4d7d74045..47ca821b9 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java @@ -25,7 +25,6 @@ package org.geysermc.connector.command.defaults; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; @@ -33,15 +32,12 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.BlockUtils; public class OffhandCommand extends GeyserCommand { - private final GeyserConnector connector; - public OffhandCommand(GeyserConnector connector, String name, String description, String permission) { super(name, description, permission); - - this.connector = connector; } @Override @@ -50,7 +46,7 @@ public class OffhandCommand extends GeyserCommand { return; } - ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0), + ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, BlockFace.DOWN); session.sendDownstreamPacket(releaseItemPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java b/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java new file mode 100644 index 000000000..954e3d32a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java @@ -0,0 +1,53 @@ +/* + * 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.configuration; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; + +public enum EmoteOffhandWorkaroundOption { + NO_EMOTES, + EMOTES_AND_OFFHAND, + DISABLED; + + public static class Deserializer extends JsonDeserializer { + @Override + public EmoteOffhandWorkaroundOption deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + switch (value) { + case "no-emotes": + return NO_EMOTES; + case "emotes-and-offhand": + return EMOTES_AND_OFFHAND; + default: + return DISABLED; + } + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index b6af30fed..4ebc048fe 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -77,6 +77,8 @@ public interface GeyserConfiguration { boolean isShowCoordinates(); + EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); + String getDefaultLocale(); Path getFloodgateKeyPath(); @@ -138,6 +140,8 @@ public interface GeyserConfiguration { boolean isPasswordAuthentication(); boolean isUseProxyProtocol(); + + boolean isForwardHost(); } interface IUserAuthenticationInfo { diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java index f8b652e53..9a3024de7 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -28,6 +28,7 @@ package org.geysermc.connector.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.GeyserConnector; @@ -100,6 +101,10 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("show-coordinates") private boolean showCoordinates = true; + @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) + @JsonProperty("emote-offhand-workaround") + private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; + @JsonProperty("allow-third-party-ears") private boolean allowThirdPartyEars = false; @@ -197,6 +202,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("use-proxy-protocol") private boolean useProxyProtocol = false; + + @JsonProperty("forward-hostname") + private boolean forwardHost = false; } @Getter diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index 69d1c1c49..f381bd510 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -27,8 +27,12 @@ package org.geysermc.connector.dump; import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.steveice10.mc.protocol.MinecraftConstants; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteSource; +import com.google.common.io.Files; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.serializer.AsteriskSerializer; @@ -40,6 +44,7 @@ import org.geysermc.connector.utils.FileUtils; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.FloodgateConfigHolder; +import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -56,6 +61,7 @@ public class DumpInfo { private Properties gitInfo; private final GeyserConfiguration config; private final Object floodgateConfig; + private final HashInfo hashInfo; private final Object2IntMap userPlatforms; private final RamInfo ramInfo; private final BootstrapDumpInfo bootstrapInfo; @@ -71,6 +77,27 @@ public class DumpInfo { this.config = GeyserConnector.getInstance().getConfig(); this.floodgateConfig = FloodgateConfigHolder.getConfig(); + + String md5Hash = "unknown"; + String sha256Hash = "unknown"; + try { + // https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file + // https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java + File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + ByteSource byteSource = Files.asByteSource(file); + // Jenkins uses MD5 for its hash + //noinspection UnstableApiUsage + md5Hash = byteSource.hash(Hashing.md5()).toString(); + //noinspection UnstableApiUsage + sha256Hash = byteSource.hash(Hashing.sha256()).toString(); + } catch (Exception e) { + if (GeyserConnector.getInstance().getConfig().isDebugMode()) { + e.printStackTrace(); + } + } + + this.hashInfo = new HashInfo(md5Hash, sha256Hash); + this.ramInfo = new DumpInfo.RamInfo(); this.userPlatforms = new Object2IntOpenHashMap<>(); @@ -108,6 +135,13 @@ public class DumpInfo { } } + @AllArgsConstructor + @Getter + public static class HashInfo { + private final String md5Hash; + private final String sha256Hash; + } + @Getter public static class NetworkInfo { private final boolean dockerCheck; diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java index fc5bc722c..075690662 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java @@ -34,4 +34,14 @@ public class AnimalEntity extends AgeableEntity { public AnimalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } + + /** + * @param javaIdentifierStripped the stripped Java identifier of the item that is potential breeding food. For example, + * wheat. + * @return true if this is a valid item to breed with for this animal. + */ + public boolean canEat(String javaIdentifierStripped) { + // This is what it defaults to. OK. + return javaIdentifierStripped.equals("wheat"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index bdffbbcd5..d076b8a6f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -34,7 +35,15 @@ import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.Set; + public class BeeEntity extends AnimalEntity { + /** + * A list of all flowers. Used for feeding bees. + */ + private static final Set FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", + "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", + "sunflower", "lilac", "rose_bush", "peony"); public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -63,4 +72,9 @@ public class BeeEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return FLOWERS.contains(javaIdentifierStripped); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java new file mode 100644 index 000000000..66e8f9ac8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java @@ -0,0 +1,41 @@ +/* + * 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.entity.living.animal; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.type.EntityType; + +public class ChickenEntity extends AnimalEntity { + + public ChickenEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.contains("seeds"); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index 90514cf12..bfa376272 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -52,4 +52,9 @@ public class FoxEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("sweet_berries"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java index 1878648b4..b915c07de 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -47,4 +47,9 @@ public class HoglinEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("crimson_fungus"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 87320838a..7c7d74770 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -44,4 +44,9 @@ public class OcelotEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index eec07af50..9431d66a0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -79,6 +79,11 @@ public class PandaEntity extends AnimalEntity { super.updateBedrockMetadata(entityMetadata, session); } + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("bamboo"); + } + /** * Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up * when both main and hidden genes match diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index e747405b2..551e1c345 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -46,6 +46,11 @@ public class PigEntity extends AnimalEntity { super.updateBedrockMetadata(entityMetadata, session); } + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot"); + } + @Override protected float getDefaultMaxHealth() { return 10f; diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index db658dd8e..7b5d42f35 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -44,4 +44,9 @@ public class PolarBearEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return false; + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index 752a0d106..a789b48e4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -59,4 +59,9 @@ public class RabbitEntity extends AnimalEntity { metadata.put(EntityData.VARIANT, variant); } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot"); + } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index 9ea97eb18..2a99a5a03 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -85,4 +85,9 @@ public class StriderEntity extends AnimalEntity { super.updateBedrockMetadata(session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("warped_fungus"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java index 9456f4d28..89df815d7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -46,4 +46,9 @@ public class TurtleEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("seagrass"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index 1fe8d4362..d2f89e101 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -38,7 +39,15 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; +import java.util.Set; + public class AbstractHorseEntity extends AnimalEntity { + /** + * A list of all foods a horse/donkey can eat on Java Edition. + * Used to display interactive tag if needed. + */ + private static final Set DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", + "golden_carrot", "sugar", "apple", "wheat", "hay_block"); public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -101,4 +110,9 @@ public class AbstractHorseEntity extends AnimalEntity { updateBedrockAttributes(session); } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 48e321932..0b21c771e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -75,4 +75,9 @@ public class LlamaEntity extends ChestedHorseEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index 87d700259..7866d25cf 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -83,4 +83,9 @@ public class CatEntity extends TameableEntity { } } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index f9df03d6b..50ec9ed04 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -45,4 +45,9 @@ public class ParrotEntity extends TameableEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index 144c0fe25..91350ef54 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -26,13 +26,23 @@ package org.geysermc.connector.entity.living.animal.tameable; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.Set; + public class WolfEntity extends TameableEntity { + /** + * A list of all foods a wolf can eat on Java Edition. + * Used to display interactive tag or particles if needed. + */ + private static final Set WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken", + "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", + "cooked_rabbit"); private byte collarColor; @@ -75,4 +85,10 @@ public class WolfEntity extends TameableEntity { super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + // Cannot be a baby to eat these foods + return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java index cd07faf39..2d1b41765 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java @@ -42,7 +42,7 @@ public class AbstractSkeletonEntity extends MonsterEntity { if (entityMetadata.getId() == 14) { byte xd = (byte) entityMetadata.getValue(); // A bit of a loophole so the hands get raised - set the target ID to its own ID - metadata.put(EntityData.TARGET_EID, (xd == 4) ? geyserId : 0); + metadata.put(EntityData.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); } super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java index ad9f059ab..d43bd8fb7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java @@ -25,13 +25,43 @@ package org.geysermc.connector.entity.living.monster.raid; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; public class SpellcasterIllagerEntity extends AbstractIllagerEntity { + private static final int SUMMON_VEX_PARTICLE_COLOR = (179 << 16) | (179 << 8) | 204; + private static final int ATTACK_PARTICLE_COLOR = (102 << 16) | (77 << 8) | 89; + private static final int WOLOLO_PARTICLE_COLOR = (179 << 16) | (128 << 8) | 51; public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 16) { + int spellType = (int) (byte) entityMetadata.getValue(); + // Summon vex, attack, or wololo + metadata.getFlags().setFlag(EntityFlag.CASTING, spellType == 1 || spellType == 2 || spellType == 3); + int rgbData = 0; + // Set the spell color based on Java values + switch (spellType) { + case 1: + rgbData = SUMMON_VEX_PARTICLE_COLOR; + break; + case 2: + rgbData = ATTACK_PARTICLE_COLOR; + break; + case 3: + rgbData = WOLOLO_PARTICLE_COLOR; + break; + } + metadata.put(EntityData.EVOKER_SPELL_COLOR, rgbData); + } + super.updateBedrockMetadata(entityMetadata, session); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java new file mode 100644 index 000000000..8705a8030 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java @@ -0,0 +1,49 @@ +/* + * 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.entity.living.monster.raid; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class VindicatorEntity extends AbstractIllagerEntity { + + public VindicatorEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + // Allow the axe to be shown if necessary + if (entityMetadata.getId() == 14) { + byte xd = (byte) entityMetadata.getValue(); + metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 4) == 4); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index f38e56fd8..43658f63b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -36,10 +36,7 @@ import org.geysermc.connector.entity.living.animal.tameable.WolfEntity; import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; import org.geysermc.connector.entity.living.merchant.VillagerEntity; import org.geysermc.connector.entity.living.monster.*; -import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity; -import org.geysermc.connector.entity.living.monster.raid.PillagerEntity; -import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity; -import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity; +import org.geysermc.connector.entity.living.monster.raid.*; import org.geysermc.connector.entity.player.PlayerEntity; import java.util.ArrayList; @@ -48,7 +45,7 @@ import java.util.List; @Getter public enum EntityType { - CHICKEN(AnimalEntity.class, 10, 0.7f, 0.4f), + CHICKEN(ChickenEntity.class, 10, 0.7f, 0.4f), COW(AnimalEntity.class, 11, 1.4f, 0.9f), PIG(PigEntity.class, 12, 0.9f), SHEEP(SheepEntity.class, 13, 1.3f, 0.9f), @@ -97,7 +94,7 @@ public enum EntityType { SHULKER(ShulkerEntity.class, 54, 1f, 1f), ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f), AGENT(Entity.class, 56, 0f), - VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), + VINDICATOR(VindicatorEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), PILLAGER(PillagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f), WANDERING_TRADER(AbstractMerchantEntity.class, 118, 1.8f, 0.6f, 0.6f, 1.62f), PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f), @@ -188,7 +185,7 @@ public enum EntityType { static { List allJavaIdentifiers = new ArrayList<>(); - for (EntityType type : values()) { + for (EntityType type : VALUES) { if (type == AGENT || type == BALLOON || type == CHALKBOARD || type == NPC || type == TRIPOD_CAMERA || type == ENDER_DRAGON_PART) { continue; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index f1ce484aa..2ee0bb770 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -77,6 +77,7 @@ import lombok.Setter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; +import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Tickable; import org.geysermc.connector.entity.attribute.Attribute; @@ -427,9 +428,7 @@ public class GeyserSession implements CommandSender { @Setter private boolean waitingForStatistics = false; - @Setter - private List selectedEmotes = new ArrayList<>(); - private final Set emotes = new HashSet<>(); + private final Set emotes; /** * The thread that will run every 50 milliseconds - one Minecraft tick. @@ -464,9 +463,14 @@ public class GeyserSession implements CommandSender { this.spawned = false; this.loggedIn = false; - // Make a copy to prevent ConcurrentModificationException - final List tmpPlayers = new ArrayList<>(connector.getPlayers()); - tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes())); + if (connector.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) { + this.emotes = new HashSet<>(); + // Make a copy to prevent ConcurrentModificationException + final List tmpPlayers = new ArrayList<>(connector.getPlayers()); + tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes())); + } else { + this.emotes = null; + } bedrockServerSession.addDisconnectHandler(disconnectReason -> { InetAddress address = bedrockServerSession.getRealAddress().getAddress(); @@ -668,37 +672,50 @@ public class GeyserSession implements CommandSender { @Override public void packetSending(PacketSendingEvent event) { //todo move this somewhere else - if (event.getPacket() instanceof HandshakePacket && floodgate) { - byte[] encryptedData; + if (event.getPacket() instanceof HandshakePacket) { + String addressSuffix; + if (floodgate) { + byte[] encryptedData; - try { - FloodgateSkinUploader skinUploader = connector.getSkinUploader(); - FloodgateCipher cipher = connector.getCipher(); + try { + FloodgateSkinUploader skinUploader = connector.getSkinUploader(); + FloodgateCipher cipher = connector.getCipher(); - encryptedData = cipher.encryptFromString(BedrockData.of( - clientData.getGameVersion(), - authData.getName(), - authData.getXboxUUID(), - clientData.getDeviceOs().ordinal(), - clientData.getLanguageCode(), - clientData.getUiProfile().ordinal(), - clientData.getCurrentInputMode().ordinal(), - upstream.getAddress().getAddress().getHostAddress(), - skinUploader.getId(), - skinUploader.getVerifyCode() - ).toString()); - } catch (Exception e) { - connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); - disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.floodgate.encryption_fail", getClientData().getLanguageCode())); - return; + encryptedData = cipher.encryptFromString(BedrockData.of( + clientData.getGameVersion(), + authData.getName(), + authData.getXboxUUID(), + clientData.getDeviceOs().ordinal(), + clientData.getLanguageCode(), + clientData.getUiProfile().ordinal(), + clientData.getCurrentInputMode().ordinal(), + upstream.getAddress().getAddress().getHostAddress(), + skinUploader.getId(), + skinUploader.getVerifyCode() + ).toString()); + } catch (Exception e) { + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); + disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.floodgate.encryption_fail", getClientData().getLanguageCode())); + return; + } + + addressSuffix = '\0' + new String(encryptedData, StandardCharsets.UTF_8); + } else { + addressSuffix = ""; } - String finalDataString = new String(encryptedData, StandardCharsets.UTF_8); - HandshakePacket handshakePacket = event.getPacket(); + + String address; + if (connector.getConfig().getRemote().isForwardHost()) { + address = clientData.getServerAddress().split(":")[0]; + } else { + address = handshakePacket.getHostname(); + } + event.setPacket(new HandshakePacket( handshakePacket.getProtocolVersion(), - handshakePacket.getHostname() + '\0' + finalDataString, + address + addressSuffix, handshakePacket.getPort(), handshakePacket.getIntent() )); @@ -825,7 +842,7 @@ public class GeyserSession implements CommandSender { /** * Called every 50 milliseconds - one Minecraft tick. */ - public void tick() { + protected void tick() { // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) { // Recalculate in case something else changed position @@ -997,7 +1014,6 @@ public class GeyserSession implements CommandSender { startGamePacket.setLightningLevel(0); startGamePacket.setMultiplayerGame(true); startGamePacket.setBroadcastingToLan(true); - startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", connector.getConfig().isShowCoordinates())); startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setCommandsEnabled(!connector.getConfig().isXboxAchievementsEnabled()); @@ -1189,13 +1205,14 @@ public class GeyserSession implements CommandSender { /** * Update the cached value for the reduced debug info gamerule. - * This also toggles the coordinates display + * If enabled, also hides the player's coordinates. * * @param value The new value for reducedDebugInfo */ public void setReducedDebugInfo(boolean value) { - worldCache.setShowCoordinates(!value); reducedDebugInfo = value; + // Set the showCoordinates data. This is done because updateShowCoordinates() uses this gamerule as a variable. + getWorldCache().updateShowCoordinates(); } /** @@ -1281,7 +1298,6 @@ public class GeyserSession implements CommandSender { } public void refreshEmotes(List emotes) { - this.selectedEmotes = emotes; this.emotes.addAll(emotes); for (GeyserSession player : connector.getPlayers()) { List pieces = new ArrayList<>(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 0cbfffbaf..84678c211 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network.session.cache; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import lombok.Getter; import lombok.Setter; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; @@ -38,7 +39,13 @@ public class WorldCache { private final GeyserSession session; @Setter private Difficulty difficulty = Difficulty.EASY; - private boolean showCoordinates = true; + + /** + * True if the client prefers being shown their coordinates, regardless if they're being shown or not. + * This will be true everytime the client joins the server because neither the client nor server store the preference permanently. + */ + @Setter + private boolean prefersShowCoordinates = true; private Scoreboard scoreboard; private final ScoreboardUpdater scoreboardUpdater; @@ -66,12 +73,15 @@ public class WorldCache { } /** - * Tell the client to hide or show the coordinates + * Tell the client to hide or show the coordinates. * - * @param value True to show, false to hide + * If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply:
+ *
+ * {@link GeyserSession#reducedDebugInfo} is enabled + * {@link GeyserConfiguration#isShowCoordinates()} is disabled */ - public void setShowCoordinates(boolean value) { - showCoordinates = value; - session.sendGameRule("showcoordinates", value); + public void updateShowCoordinates() { + boolean allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates(); + session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java index 7e2238f33..2519aa447 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.nukkitx.protocol.bedrock.packet.EmoteListPacket; +import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -35,6 +36,10 @@ public class BedrockEmoteListTranslator extends PacketTranslator { @Override public void translate(EmotePacket packet, GeyserSession session) { + if (session.getConnector().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { + // Activate the workaround - we should trigger the offhand now + ClientPlayerActionPacket swapHandsPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, + BlockFace.DOWN); + session.sendDownstreamPacket(swapHandsPacket); + + if (session.getConnector().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { + return; + } + } + long javaId = session.getPlayerEntity().getEntityId(); for (GeyserSession otherSession : session.getConnector().getPlayers()) { if (otherSession != session) { if (otherSession.isClosed()) continue; Entity otherEntity = otherSession.getEntityCache().getEntityByJavaId(javaId); if (otherEntity == null) continue; - packet.setRuntimeEntityId(otherEntity.getGeyserId()); - otherSession.sendUpstreamPacket(packet); + EmotePacket otherEmotePacket = new EmotePacket(); + otherEmotePacket.setEmoteId(packet.getEmoteId()); + otherEmotePacket.setRuntimeEntityId(otherEntity.getGeyserId()); + otherSession.sendUpstreamPacket(otherEmotePacket); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java index 6e03c7df3..49b582cef 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/EnchantingInventoryTranslator.java @@ -72,7 +72,7 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla // The Bedrock index might need changed, so let's look it up and see. int bedrockIndex = value; if (bedrockIndex != -1) { - Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + JavaEnchantment.values()[bedrockIndex].name().toLowerCase()); + Enchantment enchantment = Enchantment.getByJavaIdentifier("minecraft:" + Enchantment.JavaEnchantment.of(bedrockIndex).name().toLowerCase()); if (enchantment != null) { // Convert the Java enchantment index to Bedrock's bedrockIndex = enchantment.ordinal(); @@ -170,48 +170,4 @@ public class EnchantingInventoryTranslator extends AbstractBlockInventoryTransla public Inventory createInventory(String name, int windowId, WindowType windowType, PlayerInventory playerInventory) { return new EnchantingContainer(name, windowId, this.size, windowType, playerInventory); } - - /** - * Enchantments classified by their Java index - */ - public enum JavaEnchantment { - PROTECTION, - FIRE_PROTECTION, - FEATHER_FALLING, - BLAST_PROTECTION, - PROJECTILE_PROTECTION, - RESPIRATION, - AQUA_AFFINITY, - THORNS, - DEPTH_STRIDER, - FROST_WALKER, - BINDING_CURSE, - SOUL_SPEED, - SHARPNESS, - SMITE, - BANE_OF_ARTHROPODS, - KNOCKBACK, - FIRE_ASPECT, - LOOTING, - SWEEPING, - EFFICIENCY, - SILK_TOUCH, - UNBREAKING, - FORTUNE, - POWER, - PUNCH, - FLAME, - INFINITY, - LUCK_OF_THE_SEA, - LURE, - LOYALTY, - IMPALING, - RIPTIDE, - CHANNELING, - MULTISHOT, - QUICK_CHARGE, - PIERCING, - MENDING, - VANISHING_CURSE - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java index a3b4b6c31..14b918a4f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Enchantment.java @@ -69,17 +69,7 @@ public enum Enchantment { QUICK_CHARGE, SOUL_SPEED; - /** - * A list of all enchantment Java identifiers for use with command suggestions. - */ - public static final String[] ALL_JAVA_IDENTIFIERS; - - static { - ALL_JAVA_IDENTIFIERS = new String[values().length]; - for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) { - ALL_JAVA_IDENTIFIERS[i] = values()[i].javaIdentifier; - } - } + private static final Enchantment[] VALUES = values(); private final String javaIdentifier; @@ -88,7 +78,7 @@ public enum Enchantment { } public static Enchantment getByJavaIdentifier(String javaIdentifier) { - for (Enchantment enchantment : Enchantment.values()) { + for (Enchantment enchantment : VALUES) { if (enchantment.javaIdentifier.equals(javaIdentifier) || enchantment.name().toLowerCase(Locale.ENGLISH).equalsIgnoreCase(javaIdentifier)) { return enchantment; } @@ -97,9 +87,71 @@ public enum Enchantment { } public static Enchantment getByBedrockId(int bedrockId) { - if (bedrockId >= 0 && bedrockId < Enchantment.values().length) { - return Enchantment.values()[bedrockId]; + if (bedrockId >= 0 && bedrockId < VALUES.length) { + return VALUES[bedrockId]; } return null; } + + /** + * Enchantments classified by their Java index + */ + public enum JavaEnchantment { + PROTECTION, + FIRE_PROTECTION, + FEATHER_FALLING, + BLAST_PROTECTION, + PROJECTILE_PROTECTION, + RESPIRATION, + AQUA_AFFINITY, + THORNS, + DEPTH_STRIDER, + FROST_WALKER, + BINDING_CURSE, + SOUL_SPEED, + SHARPNESS, + SMITE, + BANE_OF_ARTHROPODS, + KNOCKBACK, + FIRE_ASPECT, + LOOTING, + SWEEPING, + EFFICIENCY, + SILK_TOUCH, + UNBREAKING, + FORTUNE, + POWER, + PUNCH, + FLAME, + INFINITY, + LUCK_OF_THE_SEA, + LURE, + LOYALTY, + IMPALING, + RIPTIDE, + CHANNELING, + MULTISHOT, + QUICK_CHARGE, + PIERCING, + MENDING, + VANISHING_CURSE; + + private static final JavaEnchantment[] VALUES = JavaEnchantment.values(); + + public static JavaEnchantment of(int index) { + return VALUES[index]; + } + + /** + * A list of all enchantment Java identifiers for use with command suggestions. + */ + public static final String[] ALL_JAVA_IDENTIFIERS; + + static { + ALL_JAVA_IDENTIFIERS = new String[VALUES.length]; + for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) { + ALL_JAVA_IDENTIFIERS[i] = "minecraft:" + VALUES[i].name().toLowerCase(Locale.ENGLISH); + } + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 7550bc818..10c14074f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -408,6 +408,13 @@ public class ItemRegistry { "", bedrockBlockId, stackSize); } + } else if (entry.getKey().equals("minecraft:spectral_arrow") || entry.getKey().equals("minecraft:knowledge_book")) { + // These items don't exist on Java, so set up a container that indicates they should have custom names + itemEntry = new TranslatableItemEntry( + entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, + entry.getValue().get("bedrock_data").intValue(), + bedrockBlockId, + stackSize); } else { itemEntry = new ItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, @@ -471,7 +478,6 @@ public class ItemRegistry { } itemNames.add("minecraft:furnace_minecart"); - itemNames.add("minecraft:spectral_arrow"); if (lodestoneCompassId == 0) { throw new RuntimeException("Lodestone compass not found in item palette!"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 2cd1aad94..78bfd07ce 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -40,6 +40,7 @@ import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; +import org.geysermc.connector.utils.LocaleUtils; import org.reflections.Reflections; import java.util.*; @@ -137,8 +138,6 @@ public abstract class ItemTranslator { nbt.put(new IntTag("map", 0)); } - ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); - if (nbt != null) { for (NbtItemStackTranslator translator : NBT_TRANSLATORS) { if (translator.acceptItem(bedrockItem)) { @@ -147,7 +146,9 @@ public abstract class ItemTranslator { } } - translateDisplayProperties(session, nbt); + nbt = translateDisplayProperties(session, nbt, bedrockItem); + + ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); ItemData.Builder builder; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); @@ -393,8 +394,20 @@ public abstract class ItemTranslator { * Translates the display name of the item * @param session the Bedrock client's session * @param tag the tag to translate + * @param itemEntry the item entry, in case it requires translation + * + * @return the new tag to use, should the current one be null */ - public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) { + public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry) { + return translateDisplayProperties(session, tag, itemEntry, 'f'); + } + + /** + * @param translationColor if this item is not available on Java, the color that the new name should be. + * Normally, this should just be white, but for shulker boxes this should be gray. + */ + public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry, char translationColor) { + boolean hasCustomName = false; if (tag != null) { CompoundTag display = tag.get("display"); if (display != null && display.contains("Name")) { @@ -405,11 +418,32 @@ public abstract class ItemTranslator { // Add the new name tag display.put(new StringTag("Name", name)); + // Indicate that a custom name is present + hasCustomName = true; // Add to the new root tag tag.put(display); } } + + if (!hasCustomName && itemEntry instanceof TranslatableItemEntry) { + // No custom name, but we need to localize the item's name + if (tag == null) { + tag = new CompoundTag(""); + } + CompoundTag display = tag.get("display"); + if (display == null) { + display = new CompoundTag("display"); + // Add to the new root tag + tag.put(display); + } + + String translationKey = ((TranslatableItemEntry) itemEntry).getTranslationString(); + // Reset formatting since Bedrock defaults to italics + display.put(new StringTag("Name", "§r§" + translationColor + LocaleUtils.getLocaleString(translationKey, session.getLocale()))); + } + + return tag; } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java index 4e710c550..57f8c756c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java @@ -74,6 +74,8 @@ public enum Potion { SLOW_FALLING(40), LONG_SLOW_FALLING(41); + public static final Potion[] VALUES = values(); + private final String javaIdentifier; private final short bedrockId; @@ -83,7 +85,7 @@ public enum Potion { } public static Potion getByJavaIdentifier(String javaIdentifier) { - for (Potion potion : Potion.values()) { + for (Potion potion : VALUES) { if (potion.javaIdentifier.equals(javaIdentifier)) { return potion; } @@ -92,7 +94,7 @@ public enum Potion { } public static Potion getByBedrockId(int bedrockId) { - for (Potion potion : Potion.values()) { + for (Potion potion : VALUES) { if (potion.bedrockId == bedrockId) { return potion; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java index 16cbc54ac..30be7da23 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java @@ -80,7 +80,7 @@ public class PotionMixRegistry { // Add all types of potions as inputs ItemEntry fillerIngredient = ingredients.get(0); for (ItemEntry input : inputs) { - for (Potion potion : Potion.values()) { + for (Potion potion : Potion.VALUES) { potionMixes.add(new PotionMixData( input.getBedrockId(), potion.getBedrockId(), fillerIngredient.getBedrockId(), fillerIngredient.getBedrockData(), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java index 5aac09790..312de593c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java @@ -77,6 +77,8 @@ public enum TippedArrowPotion { SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING), LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING); + private static final TippedArrowPotion[] VALUES = values(); + private final String javaIdentifier; private final short bedrockId; /** @@ -92,7 +94,7 @@ public enum TippedArrowPotion { } public static TippedArrowPotion getByJavaIdentifier(String javaIdentifier) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.javaIdentifier.equals(javaIdentifier)) { return potion; } @@ -101,7 +103,7 @@ public enum TippedArrowPotion { } public static TippedArrowPotion getByBedrockId(int bedrockId) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.bedrockId == bedrockId) { return potion; } @@ -114,7 +116,7 @@ public enum TippedArrowPotion { * @return the tipped arrow potion that most closely resembles that color. */ public static TippedArrowPotion getByJavaColor(int color) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.javaColor == color) { return potion; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java new file mode 100644 index 000000000..b4ea92a0e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java @@ -0,0 +1,41 @@ +/* + * 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; + +import lombok.Getter; + +/** + * Used when an item should have a custom name applied, if there already isn't one. + */ +public class TranslatableItemEntry extends ItemEntry { + @Getter + private final String translationString; + + public TranslatableItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, int bedrockBlockId, int stackSize) { + super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, bedrockBlockId, stackSize); + this.translationString = "item." + javaIdentifier.replace(":", "."); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index 5ddaa9975..197e119fc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -28,10 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.network.translators.item.*; @ItemRemapper public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { @@ -54,11 +51,13 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { boxItemTag.put(new StringTag("Name", boxItemEntry.getBedrockIdentifier())); boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue())); - if (itemData.contains("tag")) { - // Only the display name is what we have interest in, so just translate that if relevant - CompoundTag displayTag = itemData.get("tag"); - ItemTranslator.translateDisplayProperties(session, displayTag); - boxItemTag.put(displayTag); + // Only the display name is what we have interest in, so just translate that if relevant + CompoundTag displayTag = itemData.get("tag"); + if (displayTag == null && boxItemEntry instanceof TranslatableItemEntry) { + displayTag = new CompoundTag("tag"); + } + if (displayTag != null) { + boxItemTag.put(ItemTranslator.translateDisplayProperties(session, displayTag, boxItemEntry, '7')); } itemsList.add(boxItemTag); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java index 942bc7fdc..e8e6ae6b7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java @@ -249,7 +249,7 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator { + private static final List SKIN_PART_VALUES = Arrays.asList(SkinPart.values()); + @Override public void translate(ServerJoinGamePacket packet, GeyserSession session) { PlayerEntity entity = session.getPlayerEntity(); @@ -86,12 +88,13 @@ public class JavaJoinGameTranslator extends PacketTranslator("doimmediaterespawn", !packet.isEnableRespawnScreen())); session.sendUpstreamPacket(gamerulePacket); + session.setReducedDebugInfo(packet.isReducedDebugInfo()); + session.setRenderDistance(packet.getViewDistance()); // We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc String locale = session.getLocale(); - List skinParts = Arrays.asList(SkinPart.values()); - ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, HandPreference.RIGHT_HAND); + ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, SKIN_PART_VALUES, HandPreference.RIGHT_HAND); session.sendDownstreamPacket(clientSettingsPacket); session.sendDownstreamPacket(new ClientPluginMessagePacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData())); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java index 59ea29925..e33ef7bf1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java @@ -30,12 +30,14 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.LivingEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -47,9 +49,11 @@ public class JavaEntityStatusTranslator extends PacketTranslator> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) { continue; @@ -66,7 +67,9 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) { continue; @@ -67,7 +68,10 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler> 1, true); - } - public int getFullBlock(int index) { return this.palette.getInt(this.bitArray.get(index)); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java index 0b59efff3..ff7ad11cf 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java @@ -25,12 +25,12 @@ package org.geysermc.connector.utils; -import com.google.common.collect.ImmutableSet; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import lombok.Getter; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.living.animal.horse.HorseEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -40,20 +40,6 @@ import java.util.EnumSet; import java.util.Set; public class InteractiveTagManager { - /** - * A list of all foods a horse/donkey can eat on Java Edition. - * Used to display interactive tag if needed. - */ - private static final Set DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", - "golden_carrot", "sugar", "apple", "wheat", "hay_block"); - - /** - * A list of all flowers. Used for feeding bees. - */ - private static final Set FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", - "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", - "sunflower", "lilac", "rose_bush", "peony"); - /** * All entity types that can be leashed on Java Edition */ @@ -66,14 +52,6 @@ public class InteractiveTagManager { private static final Set SADDLEABLE_WHEN_TAMED_MOB_TYPES = EnumSet.of(EntityType.DONKEY, EntityType.HORSE, EntityType.ZOMBIE_HORSE, EntityType.MULE); - /** - * A list of all foods a wolf can eat on Java Edition. - * Used to display interactive tag if needed. - */ - private static final Set WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken", - "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", - "cooked_rabbit"); - /** * Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride") * @@ -85,9 +63,8 @@ public class InteractiveTagManager { ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); - // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen - // TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food InteractiveTag interactiveTag = InteractiveTag.NONE; + if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { // Unleash the entity interactiveTag = InteractiveTag.REMOVE_LEASH; @@ -105,31 +82,24 @@ public class InteractiveTagManager { // Holding a leash and the mob is leashable for sure // (Plugins can change this behavior so that's something to look into in the far far future) interactiveTag = InteractiveTag.LEASH; + } else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(javaIdentifierStripped)) { + // This animal can be fed + interactiveTag = InteractiveTag.FEED; } else { switch (interactEntity.getEntityType()) { - case BEE: - if (FLOWERS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; + case BOAT: + if (interactEntity.getPassengers().size() < 2) { + interactiveTag = InteractiveTag.BOARD_BOAT; } break; - case BOAT: - interactiveTag = InteractiveTag.BOARD_BOAT; - break; case CAT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; break; } break; - case CHICKEN: - if (javaIdentifierStripped.contains("seeds")) { - interactiveTag = InteractiveTag.FEED; - } - break; case MOOSHROOM: // Shear the mooshroom if (javaIdentifierStripped.equals("shears")) { @@ -143,9 +113,7 @@ public class InteractiveTagManager { } // Fall down to COW as this works on mooshrooms case COW: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (javaIdentifierStripped.equals("bucket")) { + if (javaIdentifierStripped.equals("bucket")) { // Milk the cow interactiveTag = InteractiveTag.MILK; } @@ -175,69 +143,28 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.OPEN_CONTAINER; break; } - // have another switch statement as, while these share mount attributes they don't share food - switch (interactEntity.getEntityType()) { - case LLAMA: - case TRADER_LLAMA: - if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) { - interactiveTag = InteractiveTag.FEED; - break; - } - case DONKEY: - case HORSE: - // Undead can't eat - if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; - break; - } - } if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { // Can't ride a baby if (tamed) { interactiveTag = InteractiveTag.RIDE_HORSE; - } else if (itemEntry.equals(ItemEntry.AIR)) { + } else if (itemEntry.getJavaId() == 0) { // Can't hide an untamed entity without having your hand empty interactiveTag = InteractiveTag.MOUNT; } } break; - case FOX: - if (javaIdentifierStripped.equals("sweet_berries")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case HOGLIN: - if (javaIdentifierStripped.equals("crimson_fungus")) { - interactiveTag = InteractiveTag.FEED; - } - break; case MINECART: - interactiveTag = InteractiveTag.RIDE_MINECART; + if (interactEntity.getPassengers().isEmpty()) { + interactiveTag = InteractiveTag.RIDE_MINECART; + } break; case MINECART_CHEST: case MINECART_COMMAND_BLOCK: case MINECART_HOPPER: interactiveTag = InteractiveTag.OPEN_CONTAINER; break; - case OCELOT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PANDA: - if (javaIdentifierStripped.equals("bamboo")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PARROT: - if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) { - interactiveTag = InteractiveTag.FEED; - } - break; case PIG: - if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.MOUNT; } break; @@ -246,15 +173,8 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.BARTER; } break; - case RABBIT: - if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) { - interactiveTag = InteractiveTag.FEED; - } - break; case SHEEP: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { + if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { if (javaIdentifierStripped.equals("shears")) { // Shear the sheep interactiveTag = InteractiveTag.SHEAR; @@ -265,17 +185,10 @@ public class InteractiveTagManager { } break; case STRIDER: - if (javaIdentifierStripped.equals("warped_fungus")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.RIDE_STRIDER; } break; - case TURTLE: - if (javaIdentifierStripped.equals("seagrass")) { - interactiveTag = InteractiveTag.FEED; - } - break; case VILLAGER: if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby @@ -289,10 +202,6 @@ public class InteractiveTagManager { if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { // Bone and untamed - can tame interactiveTag = InteractiveTag.TAME; - } else if (WOLF_FOODS.contains(javaIdentifierStripped)) { - // Compatible food in hand - feed - // Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this - interactiveTag = InteractiveTag.FEED; } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index 65e27e5ed..fa6e8c024 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -35,7 +35,6 @@ import org.geysermc.cumulus.component.DropdownComponent; import org.geysermc.cumulus.response.CustomFormResponse; public class SettingsUtils { - /** * Build a settings form for the given session and store it for later * @@ -48,9 +47,13 @@ public class SettingsUtils { CustomForm.Builder builder = CustomForm.builder() .translator(LanguageUtils::getPlayerLocaleString, language) .title("geyser.settings.title.main") - .iconPath("textures/ui/settings_glyph_color_2x.png") - .label("geyser.settings.title.client") - .toggle("geyser.settings.option.coordinates"); + .iconPath("textures/ui/settings_glyph_color_2x.png"); + + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + builder.label("geyser.settings.title.client") + .toggle("geyser.settings.option.coordinates", session.getWorldCache().isPrefersShowCoordinates()); + } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { @@ -94,7 +97,13 @@ public class SettingsUtils { return; } - session.getWorldCache().setShowCoordinates(response.next()); + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + response.skip(); // Client settings title + + session.getWorldCache().setPrefersShowCoordinates(response.next()); + session.getWorldCache().updateShowCoordinates(); + } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { GameMode gameMode = GameMode.values()[(int) response.next()]; diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 3f23712f3..0ac9b82f7 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -53,6 +53,9 @@ remote: # 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config. # IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT! use-proxy-protocol: false + # Forward the hostname that the Bedrock client used to connect over to the Java server + # This is designed to be used for forced hosts on proxies + forward-hostname: false # Floodgate uses encryption to ensure use from authorised sources. # This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) @@ -121,6 +124,13 @@ show-cooldown: title # Controls if coordinates are shown to players. show-coordinates: true +# 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 +# no-emotes - emotes will NOT be sent to other Bedrock clients and offhand will be swapped. This effectively disables all emotes from being seen. +# emotes-and-offhand - emotes will be sent to Bedrock clients and offhand will be swapped +emote-offhand-workaround: "disabled" + # The default locale if we dont have the one the client requested. Uncomment to not use the default system language. # default-locale: en_us