From 6b3f670cfb1050a94a43f174cd31a4ee3fd56080 Mon Sep 17 00:00:00 2001 From: mworzala Date: Sun, 28 Jan 2024 18:12:09 -0500 Subject: [PATCH 1/5] fix: add missing default metadata values --- .../net/minestom/server/entity/Metadata.java | 2 ++ .../minestom/server/entity/MetadataImpl.java | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Metadata.java b/src/main/java/net/minestom/server/entity/Metadata.java index 54f2f8dacda..f71041a7a7f 100644 --- a/src/main/java/net/minestom/server/entity/Metadata.java +++ b/src/main/java/net/minestom/server/entity/Metadata.java @@ -155,6 +155,8 @@ public static Entry Quaternion(float @NotNull[] value) { public static final byte TYPE_VECTOR3 = 26; public static final byte TYPE_QUATERNION = 27; + // Impl Note: Adding an entry here requires that a default value entry is added in MetadataImpl.EMPTY_VALUES + private static final VarHandle NOTIFIED_CHANGES; static { diff --git a/src/main/java/net/minestom/server/entity/MetadataImpl.java b/src/main/java/net/minestom/server/entity/MetadataImpl.java index 7401369d520..c2777eee2bb 100644 --- a/src/main/java/net/minestom/server/entity/MetadataImpl.java +++ b/src/main/java/net/minestom/server/entity/MetadataImpl.java @@ -2,6 +2,10 @@ import net.kyori.adventure.text.Component; import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.metadata.animal.FrogMeta; +import net.minestom.server.entity.metadata.animal.SnifferMeta; +import net.minestom.server.entity.metadata.animal.tameable.CatMeta; +import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.utils.Direction; @@ -10,10 +14,6 @@ import org.jetbrains.annotations.UnknownNullability; import org.jglrxavpok.hephaistos.nbt.NBTEnd; -import static net.minestom.server.entity.Metadata.Boolean; -import static net.minestom.server.entity.Metadata.Byte; -import static net.minestom.server.entity.Metadata.Float; -import static net.minestom.server.entity.Metadata.String; import static net.minestom.server.entity.Metadata.*; import static net.minestom.server.network.NetworkBuffer.VAR_INT; @@ -23,6 +23,7 @@ final class MetadataImpl { static { EMPTY_VALUES.set(TYPE_BYTE, Byte((byte) 0)); EMPTY_VALUES.set(TYPE_VARINT, VarInt(0)); + EMPTY_VALUES.set(TYPE_LONG, Long(0L)); EMPTY_VALUES.set(TYPE_FLOAT, Float(0f)); EMPTY_VALUES.set(TYPE_STRING, String("")); EMPTY_VALUES.set(TYPE_CHAT, Chat(Component.empty())); @@ -34,12 +35,20 @@ final class MetadataImpl { EMPTY_VALUES.set(TYPE_OPTPOSITION, OptPosition(null)); EMPTY_VALUES.set(TYPE_DIRECTION, Direction(Direction.DOWN)); EMPTY_VALUES.set(TYPE_OPTUUID, OptUUID(null)); + EMPTY_VALUES.set(TYPE_BLOCKSTATE, BlockState(Block.AIR.id())); EMPTY_VALUES.set(TYPE_OPTBLOCKSTATE, OptBlockState(null)); EMPTY_VALUES.set(TYPE_NBT, NBT(NBTEnd.INSTANCE)); //EMPTY_VALUES.set(TYPE_PARTICLE -> throw new UnsupportedOperationException(); EMPTY_VALUES.set(TYPE_VILLAGERDATA, VillagerData(0, 0, 0)); EMPTY_VALUES.set(TYPE_OPTVARINT, OptVarInt(null)); EMPTY_VALUES.set(TYPE_POSE, Pose(Entity.Pose.STANDING)); + EMPTY_VALUES.set(TYPE_CAT_VARIANT, CatVariant(CatMeta.Variant.TABBY)); + EMPTY_VALUES.set(TYPE_FROG_VARIANT, FrogVariant(FrogMeta.Variant.TEMPERATE)); + // OptGlobalPos + // PaintingVariant + EMPTY_VALUES.set(TYPE_SNIFFER_STATE, SnifferState(SnifferMeta.State.IDLING)); + EMPTY_VALUES.set(TYPE_VECTOR3, Vector3(Vec.ZERO)); + EMPTY_VALUES.set(TYPE_QUATERNION, Quaternion(new float[]{0, 0, 0, 0})); EMPTY_VALUES.trim(); } From 002dcc2745437c45805de92237d131ed404c4879 Mon Sep 17 00:00:00 2001 From: Ryan Jones <10679776+wordandahalf@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:24:26 -0500 Subject: [PATCH 2/5] More safely handle offline authentication servers. (#124) --- .../server/listener/preplay/LoginListener.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/minestom/server/listener/preplay/LoginListener.java b/src/main/java/net/minestom/server/listener/preplay/LoginListener.java index c14c02beb3e..d05c6569e8c 100644 --- a/src/main/java/net/minestom/server/listener/preplay/LoginListener.java +++ b/src/main/java/net/minestom/server/listener/preplay/LoginListener.java @@ -123,8 +123,13 @@ public static void loginEncryptionResponseListener(@NotNull ClientEncryptionResp final HttpClient client = HttpClient.newHttpClient(); final HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build(); client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).whenComplete((response, throwable) -> { - if (throwable != null) { - MinecraftServer.getExceptionManager().handleException(throwable); + final boolean ok = throwable == null && response.statusCode() == 200 && response.body() != null && !response.body().isEmpty(); + + if (!ok) { + if (throwable != null) { + MinecraftServer.getExceptionManager().handleException(throwable); + } + if (socketConnection.getPlayer() != null) { socketConnection.getPlayer().kick(Component.text("Failed to contact Mojang's Session Servers (Are they down?)")); } else { @@ -134,15 +139,6 @@ public static void loginEncryptionResponseListener(@NotNull ClientEncryptionResp } try { final JsonObject gameProfile = GSON.fromJson(response.body(), JsonObject.class); - if (gameProfile == null) { - // Invalid response - if (socketConnection.getPlayer() != null) { - socketConnection.getPlayer().kick(Component.text("Failed to get data from Mojang's Session Servers (Are they down?)")); - } else { - socketConnection.disconnect(); - } - return; - } socketConnection.setEncryptionKey(getSecretKey(packet.sharedSecret())); UUID profileUUID = java.util.UUID.fromString(gameProfile.get("id").getAsString() .replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); From 9fc4137995aacebf1bf54c24ea224d2d337f183d Mon Sep 17 00:00:00 2001 From: mworzala Date: Mon, 29 Jan 2024 14:53:15 -0500 Subject: [PATCH 3/5] fix: Player#switchEntityType throws an exception --- .../src/main/java/net/minestom/demo/Main.java | 1 + .../minestom/demo/commands/SetEntityType.java | 29 +++++++++++++++++++ .../net/minestom/server/entity/Player.java | 23 ++++++++++++--- .../listener/PlayerDiggingListener.java | 6 ++-- 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 demo/src/main/java/net/minestom/demo/commands/SetEntityType.java diff --git a/demo/src/main/java/net/minestom/demo/Main.java b/demo/src/main/java/net/minestom/demo/Main.java index 255bc525bf7..cebf98a4a00 100644 --- a/demo/src/main/java/net/minestom/demo/Main.java +++ b/demo/src/main/java/net/minestom/demo/Main.java @@ -62,6 +62,7 @@ public static void main(String[] args) { commandManager.register(new TestCommand2()); commandManager.register(new ConfigCommand()); commandManager.register(new SidebarCommand()); + commandManager.register(new SetEntityType()); commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED))); diff --git a/demo/src/main/java/net/minestom/demo/commands/SetEntityType.java b/demo/src/main/java/net/minestom/demo/commands/SetEntityType.java new file mode 100644 index 00000000000..365db971fd9 --- /dev/null +++ b/demo/src/main/java/net/minestom/demo/commands/SetEntityType.java @@ -0,0 +1,29 @@ +package net.minestom.demo.commands; + +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.CommandContext; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType; +import net.minestom.server.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class SetEntityType extends Command { + private final ArgumentEntityType entityTypeArg = ArgumentType.EntityType("type"); + + public SetEntityType() { + super("setentitytype"); + + addSyntax(this::execute, entityTypeArg); + } + + private void execute(@NotNull CommandSender sender, @NotNull CommandContext context) { + if (!(sender instanceof Player player)) { + return; + } + + var entityType = context.get(entityTypeArg); + player.switchEntityType(entityType); + player.sendMessage("set entity type to " + entityType); + } +} diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index c440f3d1797..ed8cd36d6ab 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -820,6 +820,9 @@ protected void updatePose() { Pose oldPose = getPose(); Pose newPose; + // If they are not a player, do nothing + if (!getEntityType().equals(EntityType.PLAYER)) return; + // Figure out their expected state var meta = Objects.requireNonNull(getLivingEntityMeta()); if (meta.isFlyingWithElytra()) { @@ -1015,27 +1018,39 @@ public void setHealth(float health) { sendPacket(new UpdateHealthPacket(health, food, foodSaturation)); } - @Override - public @NotNull PlayerMeta getEntityMeta() { + /** + * Gets the entity meta for the player. + * + *

Note that this method will throw an exception if the player's entity type has + * been changed with {@link #switchEntityType(EntityType)}. It is wise to check + * {@link #getEntityType()} first.

+ */ + public @NotNull PlayerMeta getPlayerMeta() { return (PlayerMeta) super.getEntityMeta(); } /** * Gets the player additional hearts. * + *

Note that this function is uncallable if the player has their entity type switched + * with {@link #switchEntityType(EntityType)}.

+ * * @return the player additional hearts */ public float getAdditionalHearts() { - return getEntityMeta().getAdditionalHearts(); + return getPlayerMeta().getAdditionalHearts(); } /** * Changes the amount of additional hearts shown. * + *

Note that this function is uncallable if the player has their entity type switched + * with {@link #switchEntityType(EntityType)}.

+ * * @param additionalHearts the count of additional hearts */ public void setAdditionalHearts(float additionalHearts) { - getEntityMeta().setAdditionalHearts(additionalHearts); + getPlayerMeta().setAdditionalHearts(additionalHearts); } /** diff --git a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java index 47414120ff3..9c3beec2c31 100644 --- a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java +++ b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java @@ -4,7 +4,7 @@ import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; -import net.minestom.server.entity.metadata.PlayerMeta; +import net.minestom.server.entity.metadata.LivingEntityMeta; import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.item.ItemUpdateStateEvent; import net.minestom.server.event.player.PlayerCancelDiggingEvent; @@ -145,8 +145,8 @@ private static void dropSingle(Player player) { } private static void updateItemState(Player player) { - PlayerMeta meta = player.getEntityMeta(); - if (!meta.isHandActive()) return; + LivingEntityMeta meta = player.getLivingEntityMeta(); + if (meta == null || !meta.isHandActive()) return; Player.Hand hand = meta.getActiveHand(); player.refreshEating(null); From da46d076994d4346f23be2947b029717fcb7ecde Mon Sep 17 00:00:00 2001 From: mworzala Date: Mon, 29 Jan 2024 15:59:44 -0500 Subject: [PATCH 4/5] fix: still update pose for players who had their entity type switched --- src/main/java/net/minestom/server/entity/Entity.java | 2 +- src/main/java/net/minestom/server/entity/Player.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index db957dbf7a6..4eeb6d3a018 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -1237,7 +1237,7 @@ protected void updatePose() { setPose(Pose.FALL_FLYING); } else if (entityMeta.isSwimming()) { setPose(Pose.SWIMMING); - } else if (this instanceof LivingEntity && ((LivingEntityMeta) entityMeta).isInRiptideSpinAttack()) { + } else if (entityMeta instanceof LivingEntityMeta livingMeta && livingMeta.isInRiptideSpinAttack()) { setPose(Pose.SPIN_ATTACK); } else if (entityMeta.isSneaking()) { setPose(Pose.SNEAKING); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index ed8cd36d6ab..08bd990fdb4 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -33,6 +33,7 @@ import net.minestom.server.effects.Effects; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.fakeplayer.FakePlayer; +import net.minestom.server.entity.metadata.LivingEntityMeta; import net.minestom.server.entity.metadata.PlayerMeta; import net.minestom.server.entity.vehicle.PlayerVehicleInformation; import net.minestom.server.event.EventDispatcher; @@ -820,18 +821,15 @@ protected void updatePose() { Pose oldPose = getPose(); Pose newPose; - // If they are not a player, do nothing - if (!getEntityType().equals(EntityType.PLAYER)) return; - // Figure out their expected state - var meta = Objects.requireNonNull(getLivingEntityMeta()); + var meta = getEntityMeta(); if (meta.isFlyingWithElytra()) { newPose = Pose.FALL_FLYING; } else if (false) { // When should they be sleeping? We don't have any in-bed state... newPose = Pose.SLEEPING; } else if (meta.isSwimming()) { newPose = Pose.SWIMMING; - } else if (meta.isInRiptideSpinAttack()) { + } else if (meta instanceof LivingEntityMeta livingMeta && livingMeta.isInRiptideSpinAttack()) { newPose = Pose.SPIN_ATTACK; } else if (isSneaking() && !isFlying()) { newPose = Pose.SNEAKING; From 9d6752c86f8fe5111b7b584f34810580aaa664f5 Mon Sep 17 00:00:00 2001 From: mworzala Date: Tue, 30 Jan 2024 07:35:12 -0500 Subject: [PATCH 5/5] fix: shaped recipe protocol reorder --- .../src/main/java/net/minestom/demo/Main.java | 24 +++++++++++++++++++ .../server/play/DeclareRecipesPacket.java | 16 ++++++------- .../minestom/server/recipe/RecipeManager.java | 4 ++-- .../server/network/PacketWriteReadTest.java | 8 +++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/demo/src/main/java/net/minestom/demo/Main.java b/demo/src/main/java/net/minestom/demo/Main.java index cebf98a4a00..4567434ce78 100644 --- a/demo/src/main/java/net/minestom/demo/Main.java +++ b/demo/src/main/java/net/minestom/demo/Main.java @@ -10,15 +10,23 @@ import net.minestom.demo.commands.*; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; +import net.minestom.server.entity.Player; import net.minestom.server.event.server.ServerListPingEvent; import net.minestom.server.extras.lan.OpenToLAN; import net.minestom.server.extras.lan.OpenToLANConfig; import net.minestom.server.instance.block.BlockManager; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.network.packet.server.play.DeclareRecipesPacket; import net.minestom.server.ping.ResponseData; +import net.minestom.server.recipe.RecipeCategory; +import net.minestom.server.recipe.ShapedRecipe; import net.minestom.server.utils.identity.NamedAndIdentified; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.NotNull; import java.time.Duration; +import java.util.List; public class Main { @@ -104,6 +112,22 @@ public static void main(String[] args) { //responseData.setPlayersHidden(true); }); + var ironBlockRecipe = new ShapedRecipe( + "minestom:test", 2, 2, "", + RecipeCategory.Crafting.MISC, + List.of( + new DeclareRecipesPacket.Ingredient(List.of(ItemStack.of(Material.IRON_INGOT))), + new DeclareRecipesPacket.Ingredient(List.of(ItemStack.of(Material.IRON_INGOT))), + new DeclareRecipesPacket.Ingredient(List.of(ItemStack.of(Material.IRON_INGOT))), + new DeclareRecipesPacket.Ingredient(List.of(ItemStack.of(Material.IRON_INGOT))) + ), ItemStack.of(Material.IRON_BLOCK), true) { + @Override + public boolean shouldShow(@NotNull Player player) { + return true; + } + }; + MinecraftServer.getRecipeManager().addRecipe(ironBlockRecipe); + PlayerInit.init(); // VelocityProxy.enable("abcdef"); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java index 14d46a793a9..072d764ea4d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/DeclareRecipesPacket.java @@ -89,16 +89,16 @@ public void write(@NotNull NetworkBuffer writer) { } } - public record DeclaredShapedCraftingRecipe(@NotNull String recipeId, int width, int height, + public record DeclaredShapedCraftingRecipe(@NotNull String recipeId, @NotNull String group, @NotNull RecipeCategory.Crafting category, - @NotNull List ingredients, + int width, int height, @NotNull List ingredients, @NotNull ItemStack result, boolean showNotification) implements DeclaredRecipe { public DeclaredShapedCraftingRecipe { ingredients = List.copyOf(ingredients); } private DeclaredShapedCraftingRecipe(DeclaredShapedCraftingRecipe packet) { - this(packet.recipeId, packet.width, packet.height, packet.group, packet.category, packet.ingredients, packet.result, packet.showNotification); + this(packet.recipeId, packet.group, packet.category, packet.width, packet.height, packet.ingredients, packet.result, packet.showNotification); } public DeclaredShapedCraftingRecipe(@NotNull NetworkBuffer reader) { @@ -108,25 +108,25 @@ public DeclaredShapedCraftingRecipe(@NotNull NetworkBuffer reader) { private static DeclaredShapedCraftingRecipe read(@NotNull NetworkBuffer reader) { String recipeId = reader.read(STRING); - int width = reader.read(VAR_INT); - int height = reader.read(VAR_INT); String group = reader.read(STRING); RecipeCategory.Crafting category = reader.readEnum(RecipeCategory.Crafting.class); + int width = reader.read(VAR_INT); + int height = reader.read(VAR_INT); List ingredients = new ArrayList<>(); for (int slot = 0; slot < width * height; slot++) { ingredients.add(new Ingredient(reader)); } ItemStack result = reader.read(ITEM); boolean showNotification = reader.read(BOOLEAN); - return new DeclaredShapedCraftingRecipe(recipeId, width, height, group, category, ingredients, result, showNotification); + return new DeclaredShapedCraftingRecipe(recipeId, group, category, width, height, ingredients, result, showNotification); } @Override public void write(@NotNull NetworkBuffer writer) { - writer.write(VAR_INT, width); - writer.write(VAR_INT, height); writer.write(STRING, group); writer.writeEnum(RecipeCategory.Crafting.class, category); + writer.write(VAR_INT, width); + writer.write(VAR_INT, height); for (Ingredient ingredient : ingredients) { ingredient.write(writer); } diff --git a/src/main/java/net/minestom/server/recipe/RecipeManager.java b/src/main/java/net/minestom/server/recipe/RecipeManager.java index ba23a35235e..3f6db289851 100644 --- a/src/main/java/net/minestom/server/recipe/RecipeManager.java +++ b/src/main/java/net/minestom/server/recipe/RecipeManager.java @@ -59,10 +59,10 @@ private void refreshRecipesPacket() { recipesCache.add( new DeclareRecipesPacket.DeclaredShapedCraftingRecipe( shapedRecipe.getRecipeId(), - shapedRecipe.getWidth(), - shapedRecipe.getHeight(), shapedRecipe.getGroup(), shapedRecipe.getCategory(), + shapedRecipe.getWidth(), + shapedRecipe.getHeight(), shapedRecipe.getIngredients(), shapedRecipe.getResult(), shapedRecipe.getShowNotification())); diff --git a/src/test/java/net/minestom/server/network/PacketWriteReadTest.java b/src/test/java/net/minestom/server/network/PacketWriteReadTest.java index 58131cb3913..e3a972b3612 100644 --- a/src/test/java/net/minestom/server/network/PacketWriteReadTest.java +++ b/src/test/java/net/minestom/server/network/PacketWriteReadTest.java @@ -14,16 +14,14 @@ import net.minestom.server.network.packet.client.handshake.ClientHandshakePacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.common.DisconnectPacket; -import net.minestom.server.network.packet.server.status.ResponsePacket; import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; import net.minestom.server.network.packet.server.login.LoginSuccessPacket; import net.minestom.server.network.packet.server.login.SetCompressionPacket; import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.packet.server.play.DeclareRecipesPacket.Ingredient; import net.minestom.server.network.packet.server.status.PongPacket; +import net.minestom.server.network.packet.server.status.ResponsePacket; import net.minestom.server.recipe.RecipeCategory; -import net.minestom.server.utils.crypto.KeyUtils; -import org.apache.commons.net.util.Base64; import org.jglrxavpok.hephaistos.nbt.NBT; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -92,10 +90,10 @@ public static void setupServer() { ), new DeclareRecipesPacket.DeclaredShapedCraftingRecipe( "minecraft:torch", - 1, - 2, "", RecipeCategory.Crafting.MISC, + 1, + 2, List.of(new Ingredient(List.of(ItemStack.of(Material.COAL))), new Ingredient(List.of(ItemStack.of(Material.STICK)))), ItemStack.of(Material.TORCH),