From 521df04ed9ac866feeb7ac13404dc82da6d78136 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Wed, 9 Oct 2024 11:39:16 +0200 Subject: [PATCH] Fix experience orbs after the scoreboard rework Fixes #5075 --- .../geyser/entity/EntityDefinition.java | 21 +-- .../geyser/entity/EntityDefinitions.java | 126 ++++++++++++++++-- .../geysermc/geyser/entity/type/Entity.java | 5 +- .../entity/type/player/PlayerEntity.java | 15 +-- .../org/geysermc/geyser/util/EntityUtils.java | 7 +- .../geyser/util/EnvironmentUtils.java | 41 ++++++ .../network/TeamIdentifierTest.java | 61 +++++++++ .../scoreboard/network/util/AssertUtils.java | 8 ++ .../util/GeyserMockContextScoreboard.java | 5 +- 9 files changed, 250 insertions(+), 39 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java create mode 100644 core/src/test/java/org/geysermc/geyser/scoreboard/network/TeamIdentifierTest.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java index f9b65a545..ea3950bd4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java @@ -25,10 +25,10 @@ package org.geysermc.geyser.entity; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.BiConsumer; import lombok.Setter; import lombok.experimental.Accessors; import org.geysermc.geyser.GeyserImpl; @@ -37,10 +37,10 @@ import org.geysermc.geyser.entity.properties.GeyserEntityProperties; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.translator.entity.EntityMetadataTranslator; - -import java.util.List; -import java.util.Locale; -import java.util.function.BiConsumer; +import org.geysermc.geyser.util.EnvironmentUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; /** * Represents data for an entity. This includes properties such as height and width, as well as the list of entity @@ -146,8 +146,13 @@ public record EntityDefinition(EntityFactory factory, Entit return this; } + /** + * Build the given entity. If a testing environment has been discovered the entity is not registered, + * otherwise it is. This is to prevent all the registries from loading, which will fail (and should + * not be loaded) while testing + */ public EntityDefinition build() { - return build(true); + return build(!EnvironmentUtils.isUnitTesting); } /** diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 5932ecf41..39357eb60 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -25,34 +25,131 @@ package org.geysermc.geyser.entity; -import org.geysermc.geyser.entity.type.AbstractWindChargeEntity; -import org.geysermc.geyser.entity.factory.EntityFactory; -import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.geyser.entity.factory.EntityFactory; import org.geysermc.geyser.entity.properties.GeyserEntityProperties; -import org.geysermc.geyser.entity.type.*; -import org.geysermc.geyser.entity.type.living.*; -import org.geysermc.geyser.entity.type.living.animal.*; -import org.geysermc.geyser.entity.type.living.animal.horse.*; +import org.geysermc.geyser.entity.type.AbstractArrowEntity; +import org.geysermc.geyser.entity.type.AbstractWindChargeEntity; +import org.geysermc.geyser.entity.type.AreaEffectCloudEntity; +import org.geysermc.geyser.entity.type.ArrowEntity; +import org.geysermc.geyser.entity.type.BoatEntity; +import org.geysermc.geyser.entity.type.ChestBoatEntity; +import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity; +import org.geysermc.geyser.entity.type.DisplayBaseEntity; +import org.geysermc.geyser.entity.type.EnderCrystalEntity; +import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.EvokerFangsEntity; +import org.geysermc.geyser.entity.type.ExpOrbEntity; +import org.geysermc.geyser.entity.type.FallingBlockEntity; +import org.geysermc.geyser.entity.type.FireballEntity; +import org.geysermc.geyser.entity.type.FireworkEntity; +import org.geysermc.geyser.entity.type.FishingHookEntity; +import org.geysermc.geyser.entity.type.FurnaceMinecartEntity; +import org.geysermc.geyser.entity.type.InteractionEntity; +import org.geysermc.geyser.entity.type.ItemEntity; +import org.geysermc.geyser.entity.type.ItemFrameEntity; +import org.geysermc.geyser.entity.type.LeashKnotEntity; +import org.geysermc.geyser.entity.type.LightningEntity; +import org.geysermc.geyser.entity.type.LivingEntity; +import org.geysermc.geyser.entity.type.MinecartEntity; +import org.geysermc.geyser.entity.type.PaintingEntity; +import org.geysermc.geyser.entity.type.SpawnerMinecartEntity; +import org.geysermc.geyser.entity.type.TNTEntity; +import org.geysermc.geyser.entity.type.TextDisplayEntity; +import org.geysermc.geyser.entity.type.ThrowableEntity; +import org.geysermc.geyser.entity.type.ThrowableItemEntity; +import org.geysermc.geyser.entity.type.ThrownPotionEntity; +import org.geysermc.geyser.entity.type.TridentEntity; +import org.geysermc.geyser.entity.type.WitherSkullEntity; +import org.geysermc.geyser.entity.type.living.AbstractFishEntity; +import org.geysermc.geyser.entity.type.living.AgeableEntity; +import org.geysermc.geyser.entity.type.living.AllayEntity; +import org.geysermc.geyser.entity.type.living.ArmorStandEntity; +import org.geysermc.geyser.entity.type.living.BatEntity; +import org.geysermc.geyser.entity.type.living.DolphinEntity; +import org.geysermc.geyser.entity.type.living.GlowSquidEntity; +import org.geysermc.geyser.entity.type.living.IronGolemEntity; +import org.geysermc.geyser.entity.type.living.MagmaCubeEntity; +import org.geysermc.geyser.entity.type.living.MobEntity; +import org.geysermc.geyser.entity.type.living.SlimeEntity; +import org.geysermc.geyser.entity.type.living.SnowGolemEntity; +import org.geysermc.geyser.entity.type.living.SquidEntity; +import org.geysermc.geyser.entity.type.living.TadpoleEntity; +import org.geysermc.geyser.entity.type.living.animal.ArmadilloEntity; +import org.geysermc.geyser.entity.type.living.animal.AxolotlEntity; +import org.geysermc.geyser.entity.type.living.animal.BeeEntity; +import org.geysermc.geyser.entity.type.living.animal.ChickenEntity; +import org.geysermc.geyser.entity.type.living.animal.CowEntity; +import org.geysermc.geyser.entity.type.living.animal.FoxEntity; +import org.geysermc.geyser.entity.type.living.animal.FrogEntity; +import org.geysermc.geyser.entity.type.living.animal.GoatEntity; +import org.geysermc.geyser.entity.type.living.animal.HoglinEntity; +import org.geysermc.geyser.entity.type.living.animal.MooshroomEntity; +import org.geysermc.geyser.entity.type.living.animal.OcelotEntity; +import org.geysermc.geyser.entity.type.living.animal.PandaEntity; +import org.geysermc.geyser.entity.type.living.animal.PigEntity; +import org.geysermc.geyser.entity.type.living.animal.PolarBearEntity; +import org.geysermc.geyser.entity.type.living.animal.PufferFishEntity; +import org.geysermc.geyser.entity.type.living.animal.RabbitEntity; +import org.geysermc.geyser.entity.type.living.animal.SheepEntity; +import org.geysermc.geyser.entity.type.living.animal.SnifferEntity; +import org.geysermc.geyser.entity.type.living.animal.StriderEntity; +import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity; +import org.geysermc.geyser.entity.type.living.animal.TurtleEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.ChestedHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.HorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.SkeletonHorseEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.TraderLlamaEntity; +import org.geysermc.geyser.entity.type.living.animal.horse.ZombieHorseEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.CatEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.TameableEntity; import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity; import org.geysermc.geyser.entity.type.living.merchant.AbstractMerchantEntity; import org.geysermc.geyser.entity.type.living.merchant.VillagerEntity; -import org.geysermc.geyser.entity.type.living.monster.*; +import org.geysermc.geyser.entity.type.living.monster.AbstractSkeletonEntity; +import org.geysermc.geyser.entity.type.living.monster.BasePiglinEntity; +import org.geysermc.geyser.entity.type.living.monster.BlazeEntity; +import org.geysermc.geyser.entity.type.living.monster.BoggedEntity; +import org.geysermc.geyser.entity.type.living.monster.BreezeEntity; +import org.geysermc.geyser.entity.type.living.monster.CreeperEntity; +import org.geysermc.geyser.entity.type.living.monster.ElderGuardianEntity; +import org.geysermc.geyser.entity.type.living.monster.EnderDragonEntity; +import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity; +import org.geysermc.geyser.entity.type.living.monster.EndermanEntity; +import org.geysermc.geyser.entity.type.living.monster.GhastEntity; +import org.geysermc.geyser.entity.type.living.monster.GiantEntity; +import org.geysermc.geyser.entity.type.living.monster.GuardianEntity; +import org.geysermc.geyser.entity.type.living.monster.MonsterEntity; +import org.geysermc.geyser.entity.type.living.monster.PhantomEntity; +import org.geysermc.geyser.entity.type.living.monster.PiglinEntity; +import org.geysermc.geyser.entity.type.living.monster.ShulkerEntity; +import org.geysermc.geyser.entity.type.living.monster.SkeletonEntity; +import org.geysermc.geyser.entity.type.living.monster.SpiderEntity; +import org.geysermc.geyser.entity.type.living.monster.VexEntity; +import org.geysermc.geyser.entity.type.living.monster.WardenEntity; +import org.geysermc.geyser.entity.type.living.monster.WitherEntity; +import org.geysermc.geyser.entity.type.living.monster.ZoglinEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombieEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombieVillagerEntity; +import org.geysermc.geyser.entity.type.living.monster.ZombifiedPiglinEntity; import org.geysermc.geyser.entity.type.living.monster.raid.PillagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.RaidParticipantEntity; +import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.SpellcasterIllagerEntity; import org.geysermc.geyser.entity.type.living.monster.raid.VindicatorEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.EnvironmentUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; public final class EntityDefinitions { public static final EntityDefinition ALLAY; @@ -1025,7 +1122,10 @@ public final class EntityDefinitions { .identifier("minecraft:armor_stand") // Emulated .build(false); // Never sent over the network - Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network + // causes the registries to load + if (!EnvironmentUtils.isUnitTesting) { + Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network + } } public static void init() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 983455da8..a016916f0 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -427,7 +427,10 @@ public class Entity implements GeyserEntity { } public String teamIdentifier() { - return uuid.toString(); + // experience orbs are the only known entities that do not send an uuid (even though they do have one), + // but to be safe in the future it's done in the entity class itself instead of the entity specific one. + // All entities without an uuid cannot show up in the scoreboard! + return uuid != null ? uuid.toString() : null; } public void setDisplayName(EntityMetadata, ?> entityMetadata) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 85905d716..4e0de44ea 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -53,7 +53,6 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; -import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; @@ -66,7 +65,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; @Getter @Setter public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { @@ -118,18 +116,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { * Do not use! For testing purposes only */ public PlayerEntity(GeyserSession session, long geyserId, UUID uuid, String username) { - super( - session, - -1, - geyserId, - uuid, - EntityDefinition.builder(null).type(EntityType.PLAYER).build(false), - Vector3f.ZERO, - Vector3f.ZERO, - 0, - 0, - 0 - ); + super(session, -1, geyserId, uuid, EntityDefinitions.PLAYER, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); this.username = username; this.nametag = username; this.texturesProperty = null; diff --git a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java index b7f4e7d76..9e365ab67 100644 --- a/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/EntityUtils.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.util; +import java.util.Locale; import net.kyori.adventure.key.Key; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.GameType; @@ -46,8 +47,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import java.util.Locale; - public final class EntityUtils { /** * A constant array of the two hands that a player can interact with an entity. @@ -294,6 +293,10 @@ public final class EntityUtils { } private static String translatedEntityName(String namespace, String name, GeyserSession session) { + // MinecraftLocale would otherwise invoke getBootstrap (which doesn't exist) and create some folders + if (EnvironmentUtils.isUnitTesting) { + return "entity." + namespace + "." + name; + } return MinecraftLocale.getLocaleString("entity." + namespace + "." + name, session.locale()); } diff --git a/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java b/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java new file mode 100644 index 000000000..909398bf4 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/EnvironmentUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.util; + +public final class EnvironmentUtils { + public static final boolean isUnitTesting = isUnitTesting(); + + private EnvironmentUtils() {} + + private static boolean isUnitTesting() { + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + if (element.getClassName().startsWith("org.junit.")) { + return true; + } + } + return false; + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/TeamIdentifierTest.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/TeamIdentifierTest.java new file mode 100644 index 000000000..c7fa866e1 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/TeamIdentifierTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.scoreboard.network; + +import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType; +import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket; +import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket; +import org.geysermc.geyser.translator.protocol.java.entity.JavaRemoveEntitiesTranslator; +import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddExperienceOrbTranslator; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveEntitiesPacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddExperienceOrbPacket; +import org.junit.jupiter.api.Test; + +public class TeamIdentifierTest { + @Test + void entityWithoutUuid() { + // experience orbs are the only known entities without an uuid, see Entity#teamIdentifier for more info + mockContextScoreboard(context -> { + var addExperienceOrbTranslator = new JavaAddExperienceOrbTranslator(); + var removeEntitiesTranslator = new JavaRemoveEntitiesTranslator(); + + // Entity#teamIdentifier used to throw because it returned uuid.toString where uuid could be null. + // this would result in both EntityCache#spawnEntity and EntityCache#removeEntity throwing an exception, + // because the entity would be registered and deregistered to the scoreboard. + assertDoesNotThrow(() -> { + context.translate(addExperienceOrbTranslator, new ClientboundAddExperienceOrbPacket(2, 0, 0, 0, 1)); + context.translate(removeEntitiesTranslator, new ClientboundRemoveEntitiesPacket(new int[] { 2 })); + }); + + // we know that spawning and removing the entity should be fine + assertNextPacketType(context, AddEntityPacket.class); + assertNextPacketType(context, RemoveEntityPacket.class); + }); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java index 770131325..b15994533 100644 --- a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/AssertUtils.java @@ -42,6 +42,14 @@ public class AssertUtils { assertContextEquals(expected, context.nextPacket()); } + public static void assertNextPacketType(GeyserMockContext context, Class type) { + var actual = context.nextPacket(); + if (actual == null) { + Assertions.fail("Expected another packet! " + type); + } + Assertions.assertEquals(type, actual.getClass()); + } + public static void assertNoNextPacket(GeyserMockContext context) { Assertions.assertEquals( Collections.emptyList(), diff --git a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java index 36ceeb79b..bc76a1b70 100644 --- a/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java +++ b/core/src/test/java/org/geysermc/geyser/scoreboard/network/util/GeyserMockContextScoreboard.java @@ -34,8 +34,8 @@ import static org.mockito.Mockito.when; import java.util.UUID; import java.util.function.Consumer; -import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataMap; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; @@ -57,7 +57,10 @@ public class GeyserMockContextScoreboard { // GeyserSession has so many dependencies, it's easier to just mock it var session = context.mock(GeyserSession.class); + when(session.getGeyser()).thenReturn(context.mockOrSpy(GeyserImpl.class)); + when(session.locale()).thenReturn("en_US"); + doAnswer((Answer) invocation -> { context.addPacket(invocation.getArgument(0, BedrockPacket.class)); return null;