Skip to content

Commit 521df04

Browse files
committed
Fix experience orbs after the scoreboard rework
Fixes #5075
1 parent ef4acb1 commit 521df04

File tree

9 files changed

+250
-39
lines changed

9 files changed

+250
-39
lines changed

core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525

2626
package org.geysermc.geyser.entity;
2727

28-
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
29-
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
30-
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
3128
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
29+
import java.util.List;
30+
import java.util.Locale;
31+
import java.util.function.BiConsumer;
3232
import lombok.Setter;
3333
import lombok.experimental.Accessors;
3434
import org.geysermc.geyser.GeyserImpl;
@@ -37,10 +37,10 @@
3737
import org.geysermc.geyser.entity.type.Entity;
3838
import org.geysermc.geyser.registry.Registries;
3939
import org.geysermc.geyser.translator.entity.EntityMetadataTranslator;
40-
41-
import java.util.List;
42-
import java.util.Locale;
43-
import java.util.function.BiConsumer;
40+
import org.geysermc.geyser.util.EnvironmentUtils;
41+
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
42+
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
43+
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
4444

4545
/**
4646
* 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 Builder<T> addTranslator(EntityMetadataTranslator<T, ?, ?> translator) {
146146
return this;
147147
}
148148

149+
/**
150+
* Build the given entity. If a testing environment has been discovered the entity is not registered,
151+
* otherwise it is. This is to prevent all the registries from loading, which will fail (and should
152+
* not be loaded) while testing
153+
*/
149154
public EntityDefinition<T> build() {
150-
return build(true);
155+
return build(!EnvironmentUtils.isUnitTesting);
151156
}
152157

153158
/**

core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,131 @@
2525

2626
package org.geysermc.geyser.entity;
2727

28-
import org.geysermc.geyser.entity.type.AbstractWindChargeEntity;
29-
import org.geysermc.geyser.entity.factory.EntityFactory;
30-
import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity;
31-
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
32-
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
33-
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
34-
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
3528
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
3629
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
30+
import org.geysermc.geyser.entity.factory.EntityFactory;
3731
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
38-
import org.geysermc.geyser.entity.type.*;
39-
import org.geysermc.geyser.entity.type.living.*;
40-
import org.geysermc.geyser.entity.type.living.animal.*;
41-
import org.geysermc.geyser.entity.type.living.animal.horse.*;
32+
import org.geysermc.geyser.entity.type.AbstractArrowEntity;
33+
import org.geysermc.geyser.entity.type.AbstractWindChargeEntity;
34+
import org.geysermc.geyser.entity.type.AreaEffectCloudEntity;
35+
import org.geysermc.geyser.entity.type.ArrowEntity;
36+
import org.geysermc.geyser.entity.type.BoatEntity;
37+
import org.geysermc.geyser.entity.type.ChestBoatEntity;
38+
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
39+
import org.geysermc.geyser.entity.type.DisplayBaseEntity;
40+
import org.geysermc.geyser.entity.type.EnderCrystalEntity;
41+
import org.geysermc.geyser.entity.type.Entity;
42+
import org.geysermc.geyser.entity.type.EvokerFangsEntity;
43+
import org.geysermc.geyser.entity.type.ExpOrbEntity;
44+
import org.geysermc.geyser.entity.type.FallingBlockEntity;
45+
import org.geysermc.geyser.entity.type.FireballEntity;
46+
import org.geysermc.geyser.entity.type.FireworkEntity;
47+
import org.geysermc.geyser.entity.type.FishingHookEntity;
48+
import org.geysermc.geyser.entity.type.FurnaceMinecartEntity;
49+
import org.geysermc.geyser.entity.type.InteractionEntity;
50+
import org.geysermc.geyser.entity.type.ItemEntity;
51+
import org.geysermc.geyser.entity.type.ItemFrameEntity;
52+
import org.geysermc.geyser.entity.type.LeashKnotEntity;
53+
import org.geysermc.geyser.entity.type.LightningEntity;
54+
import org.geysermc.geyser.entity.type.LivingEntity;
55+
import org.geysermc.geyser.entity.type.MinecartEntity;
56+
import org.geysermc.geyser.entity.type.PaintingEntity;
57+
import org.geysermc.geyser.entity.type.SpawnerMinecartEntity;
58+
import org.geysermc.geyser.entity.type.TNTEntity;
59+
import org.geysermc.geyser.entity.type.TextDisplayEntity;
60+
import org.geysermc.geyser.entity.type.ThrowableEntity;
61+
import org.geysermc.geyser.entity.type.ThrowableItemEntity;
62+
import org.geysermc.geyser.entity.type.ThrownPotionEntity;
63+
import org.geysermc.geyser.entity.type.TridentEntity;
64+
import org.geysermc.geyser.entity.type.WitherSkullEntity;
65+
import org.geysermc.geyser.entity.type.living.AbstractFishEntity;
66+
import org.geysermc.geyser.entity.type.living.AgeableEntity;
67+
import org.geysermc.geyser.entity.type.living.AllayEntity;
68+
import org.geysermc.geyser.entity.type.living.ArmorStandEntity;
69+
import org.geysermc.geyser.entity.type.living.BatEntity;
70+
import org.geysermc.geyser.entity.type.living.DolphinEntity;
71+
import org.geysermc.geyser.entity.type.living.GlowSquidEntity;
72+
import org.geysermc.geyser.entity.type.living.IronGolemEntity;
73+
import org.geysermc.geyser.entity.type.living.MagmaCubeEntity;
74+
import org.geysermc.geyser.entity.type.living.MobEntity;
75+
import org.geysermc.geyser.entity.type.living.SlimeEntity;
76+
import org.geysermc.geyser.entity.type.living.SnowGolemEntity;
77+
import org.geysermc.geyser.entity.type.living.SquidEntity;
78+
import org.geysermc.geyser.entity.type.living.TadpoleEntity;
79+
import org.geysermc.geyser.entity.type.living.animal.ArmadilloEntity;
80+
import org.geysermc.geyser.entity.type.living.animal.AxolotlEntity;
81+
import org.geysermc.geyser.entity.type.living.animal.BeeEntity;
82+
import org.geysermc.geyser.entity.type.living.animal.ChickenEntity;
83+
import org.geysermc.geyser.entity.type.living.animal.CowEntity;
84+
import org.geysermc.geyser.entity.type.living.animal.FoxEntity;
85+
import org.geysermc.geyser.entity.type.living.animal.FrogEntity;
86+
import org.geysermc.geyser.entity.type.living.animal.GoatEntity;
87+
import org.geysermc.geyser.entity.type.living.animal.HoglinEntity;
88+
import org.geysermc.geyser.entity.type.living.animal.MooshroomEntity;
89+
import org.geysermc.geyser.entity.type.living.animal.OcelotEntity;
90+
import org.geysermc.geyser.entity.type.living.animal.PandaEntity;
91+
import org.geysermc.geyser.entity.type.living.animal.PigEntity;
92+
import org.geysermc.geyser.entity.type.living.animal.PolarBearEntity;
93+
import org.geysermc.geyser.entity.type.living.animal.PufferFishEntity;
94+
import org.geysermc.geyser.entity.type.living.animal.RabbitEntity;
95+
import org.geysermc.geyser.entity.type.living.animal.SheepEntity;
96+
import org.geysermc.geyser.entity.type.living.animal.SnifferEntity;
97+
import org.geysermc.geyser.entity.type.living.animal.StriderEntity;
98+
import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity;
99+
import org.geysermc.geyser.entity.type.living.animal.TurtleEntity;
100+
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
101+
import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity;
102+
import org.geysermc.geyser.entity.type.living.animal.horse.ChestedHorseEntity;
103+
import org.geysermc.geyser.entity.type.living.animal.horse.HorseEntity;
104+
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
105+
import org.geysermc.geyser.entity.type.living.animal.horse.SkeletonHorseEntity;
106+
import org.geysermc.geyser.entity.type.living.animal.horse.TraderLlamaEntity;
107+
import org.geysermc.geyser.entity.type.living.animal.horse.ZombieHorseEntity;
42108
import org.geysermc.geyser.entity.type.living.animal.tameable.CatEntity;
43109
import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity;
44110
import org.geysermc.geyser.entity.type.living.animal.tameable.TameableEntity;
45111
import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity;
46112
import org.geysermc.geyser.entity.type.living.merchant.AbstractMerchantEntity;
47113
import org.geysermc.geyser.entity.type.living.merchant.VillagerEntity;
48-
import org.geysermc.geyser.entity.type.living.monster.*;
114+
import org.geysermc.geyser.entity.type.living.monster.AbstractSkeletonEntity;
115+
import org.geysermc.geyser.entity.type.living.monster.BasePiglinEntity;
116+
import org.geysermc.geyser.entity.type.living.monster.BlazeEntity;
117+
import org.geysermc.geyser.entity.type.living.monster.BoggedEntity;
118+
import org.geysermc.geyser.entity.type.living.monster.BreezeEntity;
119+
import org.geysermc.geyser.entity.type.living.monster.CreeperEntity;
120+
import org.geysermc.geyser.entity.type.living.monster.ElderGuardianEntity;
121+
import org.geysermc.geyser.entity.type.living.monster.EnderDragonEntity;
122+
import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity;
123+
import org.geysermc.geyser.entity.type.living.monster.EndermanEntity;
124+
import org.geysermc.geyser.entity.type.living.monster.GhastEntity;
125+
import org.geysermc.geyser.entity.type.living.monster.GiantEntity;
126+
import org.geysermc.geyser.entity.type.living.monster.GuardianEntity;
127+
import org.geysermc.geyser.entity.type.living.monster.MonsterEntity;
128+
import org.geysermc.geyser.entity.type.living.monster.PhantomEntity;
129+
import org.geysermc.geyser.entity.type.living.monster.PiglinEntity;
130+
import org.geysermc.geyser.entity.type.living.monster.ShulkerEntity;
131+
import org.geysermc.geyser.entity.type.living.monster.SkeletonEntity;
132+
import org.geysermc.geyser.entity.type.living.monster.SpiderEntity;
133+
import org.geysermc.geyser.entity.type.living.monster.VexEntity;
134+
import org.geysermc.geyser.entity.type.living.monster.WardenEntity;
135+
import org.geysermc.geyser.entity.type.living.monster.WitherEntity;
136+
import org.geysermc.geyser.entity.type.living.monster.ZoglinEntity;
137+
import org.geysermc.geyser.entity.type.living.monster.ZombieEntity;
138+
import org.geysermc.geyser.entity.type.living.monster.ZombieVillagerEntity;
139+
import org.geysermc.geyser.entity.type.living.monster.ZombifiedPiglinEntity;
49140
import org.geysermc.geyser.entity.type.living.monster.raid.PillagerEntity;
50141
import org.geysermc.geyser.entity.type.living.monster.raid.RaidParticipantEntity;
142+
import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity;
51143
import org.geysermc.geyser.entity.type.living.monster.raid.SpellcasterIllagerEntity;
52144
import org.geysermc.geyser.entity.type.living.monster.raid.VindicatorEntity;
53145
import org.geysermc.geyser.entity.type.player.PlayerEntity;
54146
import org.geysermc.geyser.registry.Registries;
55147
import org.geysermc.geyser.translator.text.MessageTranslator;
148+
import org.geysermc.geyser.util.EnvironmentUtils;
149+
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
150+
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
151+
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
152+
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
56153

57154
public final class EntityDefinitions {
58155
public static final EntityDefinition<AllayEntity> ALLAY;
@@ -1025,7 +1122,10 @@ public final class EntityDefinitions {
10251122
.identifier("minecraft:armor_stand") // Emulated
10261123
.build(false); // Never sent over the network
10271124

1028-
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
1125+
// causes the registries to load
1126+
if (!EnvironmentUtils.isUnitTesting) {
1127+
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
1128+
}
10291129
}
10301130

10311131
public static void init() {

core/src/main/java/org/geysermc/geyser/entity/type/Entity.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,10 @@ protected short getMaxAir() {
427427
}
428428

429429
public String teamIdentifier() {
430-
return uuid.toString();
430+
// experience orbs are the only known entities that do not send an uuid (even though they do have one),
431+
// but to be safe in the future it's done in the entity class itself instead of the entity specific one.
432+
// All entities without an uuid cannot show up in the scoreboard!
433+
return uuid != null ? uuid.toString() : null;
431434
}
432435

433436
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {

core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
5454
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
5555
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
56-
import org.geysermc.geyser.entity.EntityDefinition;
5756
import org.geysermc.geyser.entity.EntityDefinitions;
5857
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
5958
import org.geysermc.geyser.entity.type.Entity;
@@ -66,7 +65,6 @@
6665
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
6766
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
6867
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
69-
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
7068

7169
@Getter @Setter
7270
public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
@@ -118,18 +116,7 @@ public PlayerEntity(GeyserSession session, int entityId, long geyserId, UUID uui
118116
* Do not use! For testing purposes only
119117
*/
120118
public PlayerEntity(GeyserSession session, long geyserId, UUID uuid, String username) {
121-
super(
122-
session,
123-
-1,
124-
geyserId,
125-
uuid,
126-
EntityDefinition.builder(null).type(EntityType.PLAYER).build(false),
127-
Vector3f.ZERO,
128-
Vector3f.ZERO,
129-
0,
130-
0,
131-
0
132-
);
119+
super(session, -1, geyserId, uuid, EntityDefinitions.PLAYER, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0);
133120
this.username = username;
134121
this.nametag = username;
135122
this.texturesProperty = null;

core/src/main/java/org/geysermc/geyser/util/EntityUtils.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
package org.geysermc.geyser.util;
2727

28+
import java.util.Locale;
2829
import net.kyori.adventure.key.Key;
2930
import org.cloudburstmc.math.vector.Vector3f;
3031
import org.cloudburstmc.protocol.bedrock.data.GameType;
@@ -46,8 +47,6 @@
4647
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
4748
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
4849

49-
import java.util.Locale;
50-
5150
public final class EntityUtils {
5251
/**
5352
* A constant array of the two hands that a player can interact with an entity.
@@ -294,6 +293,10 @@ public static GameType toBedrockGamemode(GameMode gamemode) {
294293
}
295294

296295
private static String translatedEntityName(String namespace, String name, GeyserSession session) {
296+
// MinecraftLocale would otherwise invoke getBootstrap (which doesn't exist) and create some folders
297+
if (EnvironmentUtils.isUnitTesting) {
298+
return "entity." + namespace + "." + name;
299+
}
297300
return MinecraftLocale.getLocaleString("entity." + namespace + "." + name, session.locale());
298301
}
299302

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2024 GeyserMC. http://geysermc.org
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* @author GeyserMC
23+
* @link https://github.com/GeyserMC/Geyser
24+
*/
25+
26+
package org.geysermc.geyser.util;
27+
28+
public final class EnvironmentUtils {
29+
public static final boolean isUnitTesting = isUnitTesting();
30+
31+
private EnvironmentUtils() {}
32+
33+
private static boolean isUnitTesting() {
34+
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
35+
if (element.getClassName().startsWith("org.junit.")) {
36+
return true;
37+
}
38+
}
39+
return false;
40+
}
41+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2024 GeyserMC. http://geysermc.org
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* @author GeyserMC
23+
* @link https://github.com/GeyserMC/Geyser
24+
*/
25+
26+
package org.geysermc.geyser.scoreboard.network;
27+
28+
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
29+
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
30+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
31+
32+
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
33+
import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket;
34+
import org.geysermc.geyser.translator.protocol.java.entity.JavaRemoveEntitiesTranslator;
35+
import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddExperienceOrbTranslator;
36+
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveEntitiesPacket;
37+
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddExperienceOrbPacket;
38+
import org.junit.jupiter.api.Test;
39+
40+
public class TeamIdentifierTest {
41+
@Test
42+
void entityWithoutUuid() {
43+
// experience orbs are the only known entities without an uuid, see Entity#teamIdentifier for more info
44+
mockContextScoreboard(context -> {
45+
var addExperienceOrbTranslator = new JavaAddExperienceOrbTranslator();
46+
var removeEntitiesTranslator = new JavaRemoveEntitiesTranslator();
47+
48+
// Entity#teamIdentifier used to throw because it returned uuid.toString where uuid could be null.
49+
// this would result in both EntityCache#spawnEntity and EntityCache#removeEntity throwing an exception,
50+
// because the entity would be registered and deregistered to the scoreboard.
51+
assertDoesNotThrow(() -> {
52+
context.translate(addExperienceOrbTranslator, new ClientboundAddExperienceOrbPacket(2, 0, 0, 0, 1));
53+
context.translate(removeEntitiesTranslator, new ClientboundRemoveEntitiesPacket(new int[] { 2 }));
54+
});
55+
56+
// we know that spawning and removing the entity should be fine
57+
assertNextPacketType(context, AddEntityPacket.class);
58+
assertNextPacketType(context, RemoveEntityPacket.class);
59+
});
60+
}
61+
}

0 commit comments

Comments
 (0)