Fix #5089 and don't auto-load Registries (#5093)

* Fix #5089 and made Registries instance based

* Instead of using instance based Registries, manually initialize them

* Address review

* Commit this too pls
This commit is contained in:
Tim203 2024-10-27 22:53:56 +01:00 committed by GitHub
parent 6331cb6d58
commit d6d19b02b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 626 additions and 424 deletions

View file

@ -37,7 +37,6 @@ 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 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;
@ -146,13 +145,8 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> 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<T> build() {
return build(!EnvironmentUtils.isUnitTesting);
return build(true);
}
/**

View file

@ -145,7 +145,6 @@ 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;
@ -1122,10 +1121,7 @@ public final class EntityDefinitions {
.identifier("minecraft:armor_stand") // Emulated
.build(false); // 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
}
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() {

View file

@ -32,7 +32,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
import java.util.Map;
/**
* A write-only wrapper for temporarily storing entity metadata that will be sent to Bedrock.
* A wrapper for temporarily storing entity metadata that will be sent to Bedrock.
*/
public final class GeyserDirtyMetadata {
private final Map<EntityDataType<?>, Object> metadata = new Object2ObjectLinkedOpenHashMap<>();
@ -53,6 +53,14 @@ public final class GeyserDirtyMetadata {
return !metadata.isEmpty();
}
/**
* Intended for testing purposes only
*/
public <T> T get(EntityDataType<T> entityData) {
//noinspection unchecked
return (T) metadata.get(entityData);
}
@Override
public String toString() {
return metadata.toString();

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
@ -36,6 +37,7 @@ import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.MathUtils;
@ -123,6 +125,12 @@ public class ArmorStandEntity extends LivingEntity {
this.position = position;
}
@Override
public void updateNametag(@Nullable Team team) {
// unlike all other LivingEntities, armor stands are not affected by team nametag visibility
super.updateNametag(team, true);
}
@Override
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
super.setDisplayName(entityMetadata);

View file

@ -43,7 +43,6 @@ import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataMap;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
@ -112,20 +111,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
this.texturesProperty = texturesProperty;
}
/**
* Do not use! For testing purposes only
*/
public PlayerEntity(GeyserSession session, long geyserId, UUID uuid, String username) {
super(session, -1, geyserId, uuid, EntityDefinitions.PLAYER, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0);
this.username = username;
this.nametag = username;
this.texturesProperty = null;
// clear initial metadata
dirtyMetadata.apply(new EntityDataMap());
setFlagsDirty(false);
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
@ -193,11 +178,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
if (session.getEntityCache().getPlayerEntity(uuid) == null)
return;
if (session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
session.getEntityCache().spawnEntity(this);
} else {
spawnEntity();
}
session.getEntityCache().spawnEntity(this);
}
@Override

View file

@ -62,6 +62,8 @@ import java.util.*;
* Holds all the common registries in Geyser.
*/
public final class Registries {
private static boolean initialized = false;
/**
* A registry holding all the providers.
* This has to be initialized first to allow extensions to access providers during other registry events.
@ -69,7 +71,7 @@ public final class Registries {
public static final SimpleMappedRegistry<Class<?>, ProviderSupplier> PROVIDERS = SimpleMappedRegistry.create(new IdentityHashMap<>(), ProviderRegistryLoader::new);
/**
* A registry holding a CompoundTag of the known entity identifiers.
* A registry holding a NbtMap of the known entity identifiers.
*/
public static final SimpleRegistry<NbtMap> BEDROCK_ENTITY_IDENTIFIERS = SimpleRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT);
@ -79,7 +81,7 @@ public final class Registries {
public static final PacketTranslatorRegistry<BedrockPacket> BEDROCK_PACKET_TRANSLATORS = PacketTranslatorRegistry.create();
/**
* A registry holding a CompoundTag of all the known biomes.
* A registry holding a NbtMap of all the known biomes.
*/
public static final SimpleRegistry<NbtMap> BIOMES_NBT = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT);
@ -118,6 +120,9 @@ public final class Registries {
*/
public static final ListRegistry<Item> JAVA_ITEMS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
/**
* A registry containing item identifiers.
*/
public static final SimpleMappedRegistry<String, Item> JAVA_ITEM_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
/**
@ -135,7 +140,7 @@ public final class Registries {
/**
* A registry holding all the potion mixes.
*/
public static final VersionedRegistry<Set<PotionMixData>> POTION_MIXES;
public static final VersionedRegistry<Set<PotionMixData>> POTION_MIXES = VersionedRegistry.create(PotionMixRegistryLoader::new);
/**
* A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value.
@ -163,15 +168,35 @@ public final class Registries {
public static final SimpleMappedRegistry<SoundTranslator, SoundInteractionTranslator<?>> SOUND_TRANSLATORS = SimpleMappedRegistry.create("org.geysermc.geyser.translator.sound.SoundTranslator", SoundTranslatorRegistryLoader::new);
public static void init() {
// no-op
}
if (initialized) return;
initialized = true;
PROVIDERS.load();
BEDROCK_ENTITY_IDENTIFIERS.load();
BEDROCK_PACKET_TRANSLATORS.load();
BIOMES_NBT.load();
BIOME_IDENTIFIERS.load();
BLOCK_ENTITIES.load();
ENTITY_DEFINITIONS.load();
BEDROCK_ENTITY_PROPERTIES.load();
JAVA_ENTITY_IDENTIFIERS.load();
JAVA_PACKET_TRANSLATORS.load();
JAVA_ITEMS.load();
JAVA_ITEM_IDENTIFIERS.load();
ITEMS.load();
PARTICLES.load();
// load potion mixes later
RECIPES.load();
RESOURCE_PACKS.load();
SOUNDS.load();
SOUND_LEVEL_EVENTS.load();
SOUND_TRANSLATORS.load();
static {
PacketRegistryPopulator.populate();
ItemRegistryPopulator.populate();
// Create registries that require other registries to load first
POTION_MIXES = VersionedRegistry.create(PotionMixRegistryLoader::new);
// potion mixes depend on other registries
POTION_MIXES.load();
// Remove unneeded client generation data from NbtMapBuilder
NbtMapBuilder biomesNbt = NbtMap.builder();

View file

@ -25,9 +25,9 @@
package org.geysermc.geyser.registry;
import org.geysermc.geyser.registry.loader.RegistryLoader;
import java.util.function.Consumer;
import org.geysermc.geyser.registry.loader.RegistryLoader;
import org.geysermc.geyser.registry.loader.RegistryLoaderHolder;
/**
* A wrapper around a value which is loaded based on the output from the provided
@ -63,7 +63,9 @@ import java.util.function.Consumer;
*
* @param <M> the value being held by the registry
*/
@SuppressWarnings("rawtypes")
public abstract class Registry<M> implements IRegistry<M> {
protected RegistryLoaderHolder loaderHolder;
protected M mappings;
/**
@ -76,7 +78,17 @@ public abstract class Registry<M> implements IRegistry<M> {
* @param <I> the input type
*/
protected <I> Registry(I input, RegistryLoader<I, M> registryLoader) {
this.mappings = registryLoader.load(input);
this.loaderHolder = new RegistryLoaderHolder<>(input, registryLoader);
}
public void load() {
// don't load twice
if (this.mappings != null) return;
var holder = this.loaderHolder;
this.loaderHolder = null;
//noinspection unchecked
this.mappings = (M) holder.registryLoader().load(holder.input());
}
/**
@ -111,4 +123,4 @@ public abstract class Registry<M> implements IRegistry<M> {
public void register(Consumer<M> consumer) {
consumer.accept(this.mappings);
}
}
}

View file

@ -125,4 +125,4 @@ public class PotionMixRegistryLoader implements RegistryLoader<Object, Int2Objec
return itemMapping;
}
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.registry.loader;
/**
* A holder of the constructor parameters to prevent them from automatically loading,
* and instead load them when the load method is called.
*/
public record RegistryLoaderHolder<I, M>(I input, RegistryLoader<I, M> registryLoader) {
}

View file

@ -90,6 +90,8 @@ public final class Team {
// Remove old team from this map, and from the set of players of the old team.
// Java 1.19.3 Mojmap: Scoreboard#addPlayerToTeam calls #removePlayerFromTeam
oldTeam.entities.remove(player);
// also remove the managed entity if there is one
removeManagedEntity(player);
}
return this;
});
@ -282,6 +284,15 @@ public final class Team {
}
}
/**
* Used internally to remove a managed entity without causing an update.
* This is fine because its only used when the entity is added to another team,
* which will fire the correct nametag updates etc.
*/
private void removeManagedEntity(String name) {
managedEntities.removeIf(entity -> name.equals(entity.teamIdentifier()));
}
private void refreshAllEntities() {
for (Entity entity : session().getEntityCache().getEntities().values()) {
entity.updateNametag(scoreboard.getTeamFor(entity.teamIdentifier()));

View file

@ -69,12 +69,13 @@ public class EntityCache {
public void spawnEntity(Entity entity) {
if (cacheEntity(entity)) {
entity.spawnEntity();
// start tracking newly spawned entities.
// This is however not called for players, that's done in addPlayerEntity
// start tracking newly spawned entities. Doing this before the actual entity spawn can result in combining
// the otherwise sent metadata packet (in the case of team visibility, which sets the NAME metadata to
// empty) with the entity spawn packet (which also includes metadata). Resulting in 1 less packet sent.
session.getWorldCache().getScoreboard().entityRegistered(entity);
entity.spawnEntity();
if (entity instanceof Tickable) {
// Start ticking it
tickableEntities.add((Tickable) entity);
@ -144,8 +145,6 @@ public class EntityCache {
// notify scoreboard for new entity
var scoreboard = session.getWorldCache().getScoreboard();
scoreboard.playerRegistered(entity);
// spawnPlayer's entityRegistered is not called for players
scoreboard.entityRegistered(entity);
}
public PlayerEntity getPlayerEntity(UUID uuid) {

View file

@ -25,7 +25,6 @@
package org.geysermc.geyser.translator.protocol.java.entity.player;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
@ -35,6 +34,7 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinManager;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntry;
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntryAction;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket;
@ -95,8 +95,6 @@ public class JavaPlayerInfoUpdateTranslator extends PacketTranslator<Clientbound
if (self) {
SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername()));
} else {
playerEntity.setValid(true);
}
}
}

View file

@ -25,17 +25,14 @@
package org.geysermc.geyser.translator.protocol.java.entity.spawn;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.FallingBlockData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ProjectileData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.WardenData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.*;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.FallingBlockEntity;
import org.geysermc.geyser.entity.type.FishingHookEntity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.PaintingEntity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
@ -43,6 +40,14 @@ import org.geysermc.geyser.skin.SkinManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EnvironmentUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.FallingBlockData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.ProjectileData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.WardenData;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket;
@Translator(packet = ClientboundAddEntityPacket.class)
public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEntityPacket> {
@ -83,10 +88,13 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
entity.setHeadYaw(headYaw);
entity.setMotion(motion);
}
session.getEntityCache().cacheEntity(entity);
entity.sendPlayer();
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
// only load skin if we're not in a test environment.
// Otherwise, it tries to load various resources
if (!EnvironmentUtils.IS_UNIT_TESTING) {
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
}
return;
}

View file

@ -25,13 +25,13 @@
package org.geysermc.geyser.translator.protocol.java.scoreboard;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket;
import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.WorldCache;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetDisplayObjectivePacket;
@Translator(packet = ClientboundSetDisplayObjectivePacket.class)
public class JavaSetDisplayObjectiveTranslator extends PacketTranslator<ClientboundSetDisplayObjectivePacket> {

View file

@ -297,7 +297,7 @@ public final class EntityUtils {
private static String translatedEntityName(@NonNull String namespace, @NonNull String name, @NonNull GeyserSession session) {
// MinecraftLocale would otherwise invoke getBootstrap (which doesn't exist) and create some folders,
// so use the default fallback value as used in Minecraft Java
if (EnvironmentUtils.isUnitTesting) {
if (EnvironmentUtils.IS_UNIT_TESTING) {
return "entity." + namespace + "." + name;
}
return MinecraftLocale.getLocaleString("entity." + namespace + "." + name, session.locale());

View file

@ -26,7 +26,7 @@
package org.geysermc.geyser.util;
public final class EnvironmentUtils {
public static final boolean isUnitTesting = isUnitTesting();
public static final boolean IS_UNIT_TESTING = isUnitTesting();
private EnvironmentUtils() {}

View file

@ -27,7 +27,7 @@ package org.geysermc.geyser.scoreboard.network;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockAndAddPlayerEntity;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
import net.kyori.adventure.text.Component;
@ -47,7 +47,7 @@ public class NameVisibilityScoreboardTest {
mockContextScoreboard(context -> {
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setPlayerTeamTranslator,
@ -64,12 +64,12 @@ public class NameVisibilityScoreboardTest {
new String[]{"player1"}
)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
}, context);
});
});
}
@ -78,7 +78,7 @@ public class NameVisibilityScoreboardTest {
mockContextScoreboard(context -> {
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setPlayerTeamTranslator,
@ -96,12 +96,12 @@ public class NameVisibilityScoreboardTest {
)
);
// only hidden if session player (Tim203) is in a team as well
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix");
return packet;
}, context);
});
assertNoNextPacket(context);
// create another team and add Tim203 to it
@ -121,12 +121,12 @@ public class NameVisibilityScoreboardTest {
)
);
// Tim203 is now in another team, so it should be hidden
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
}, context);
});
assertNoNextPacket(context);
// add Tim203 to same team as player1, score should be visible again
@ -134,12 +134,12 @@ public class NameVisibilityScoreboardTest {
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket("team1", TeamAction.ADD_PLAYER, new String[]{"Tim203"})
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix");
return packet;
}, context);
});
});
}
@ -148,7 +148,7 @@ public class NameVisibilityScoreboardTest {
mockContextScoreboard(context -> {
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setPlayerTeamTranslator,
@ -166,12 +166,12 @@ public class NameVisibilityScoreboardTest {
)
);
// Tim203 is not in a team (let alone the same team), so should be visible
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix");
return packet;
}, context);
});
assertNoNextPacket(context);
// Tim203 is now in the same team as player1, so should be hidden
@ -179,12 +179,12 @@ public class NameVisibilityScoreboardTest {
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket("team1", TeamAction.ADD_PLAYER, new String[]{"Tim203"})
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
}, context);
});
assertNoNextPacket(context);
// create another team and add Tim203 to there, score should be visible again
@ -203,12 +203,12 @@ public class NameVisibilityScoreboardTest {
new String[]{"Tim203"}
)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix");
return packet;
}, context);
});
});
}
@ -217,7 +217,7 @@ public class NameVisibilityScoreboardTest {
mockContextScoreboard(context -> {
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setPlayerTeamTranslator,
@ -234,12 +234,12 @@ public class NameVisibilityScoreboardTest {
new String[]{"player1"}
)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "§4prefix§r§4player1§r§4suffix");
return packet;
}, context);
});
// adding self to another team shouldn't make a difference
context.translate(

View file

@ -25,23 +25,58 @@
package org.geysermc.geyser.scoreboard.network;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import net.kyori.adventure.text.Component;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.geysermc.geyser.entity.type.living.monster.EnderDragonPartEntity;
import org.geysermc.geyser.session.cache.EntityCache;
import org.geysermc.geyser.translator.protocol.java.entity.JavaRemoveEntitiesTranslator;
import org.geysermc.geyser.translator.protocol.java.entity.JavaSetEntityDataTranslator;
import org.geysermc.geyser.translator.protocol.java.entity.player.JavaPlayerInfoUpdateTranslator;
import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddEntityTranslator;
import org.geysermc.geyser.translator.protocol.java.entity.spawn.JavaAddExperienceOrbTranslator;
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntry;
import org.geysermc.mcprotocollib.protocol.data.game.PlayerListEntryAction;
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.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundRemoveEntitiesPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddExperienceOrbPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket;
import org.junit.jupiter.api.Test;
import java.util.EnumSet;
import java.util.Optional;
import java.util.UUID;
/**
* Tests that don't fit in a larger system (e.g. sidebar objective) that were reported on GitHub
* Tests for issues reported on GitHub.
*/
public class ScoreboardIssueTests {
/**
@ -90,4 +125,137 @@ public class ScoreboardIssueTests {
});
});
}
/**
* Test for <a href="https://github.com/GeyserMC/Geyser/issues/5089">#5089</a>.
* It follows the reproduction steps with all the packets it sends along its way.
* Tested with the 2.0.0-SNAPSHOT version.
* Note that this exact issue is actually 2 issues:
* <ul>
* <li>
* An issue caused by remainders of code that was part of the initial PR that added support for players.
* The code is now more streamlined.
* </li>
* <li>Armor stands are excluded from team visibility checks (the only living entity)</li>
* </ul>
*/
@Test
void nameNotUpdating() {
mockContextScoreboard(context -> {
var playerInfoUpdateTranslator = new JavaPlayerInfoUpdateTranslator();
var setPlayerTeamTranslator = new JavaSetPlayerTeamTranslator();
var addEntityTranslator = new JavaAddEntityTranslator();
var setEntityDataTranslator = new JavaSetEntityDataTranslator();
// first command, create NPC
var npcUuid = UUID.fromString("b0eb01d7-52c9-4730-9fd3-2c03fcb00d6e");
context.translate(
playerInfoUpdateTranslator,
new ClientboundPlayerInfoUpdatePacket(
EnumSet.of(PlayerListEntryAction.ADD_PLAYER, PlayerListEntryAction.UPDATE_LISTED),
new PlayerListEntry[] {
new PlayerListEntry(npcUuid, new GameProfile(npcUuid, "1297"), false, 0, GameMode.SURVIVAL, null, null, 0, null, null)
}));
//todo we don't have to remove an entry that was never in the playerlist in the first place
assertNextPacket(context, () -> {
var packet = new PlayerListPacket();
packet.getEntries().add(new PlayerListPacket.Entry(npcUuid));
packet.setAction(PlayerListPacket.Action.REMOVE);
return packet;
});
assertNoNextPacket(context);
context.translate(
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket(
"npc_team_1297",
Component.empty(),
Component.empty(),
Component.empty(),
false,
false,
NameTagVisibility.NEVER,
CollisionRule.NEVER,
TeamColor.WHITE,
new String[0]
)
);
context.translate(
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket("npc_team_1297", TeamAction.ADD_PLAYER, new String[]{ "1297" }));
context.translate(addEntityTranslator, new ClientboundAddEntityPacket(1297, npcUuid, EntityType.PLAYER, 1, 2, 3, 4, 5, 6));
// then it updates the displayed skin parts, which isn't relevant for us
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(3, packet.getRuntimeEntityId());
assertEquals(npcUuid, packet.getUuid());
assertEquals("1297", packet.getUsername());
assertEquals((byte) 1, packet.getMetadata().get(EntityDataTypes.NAMETAG_ALWAYS_SHOW));
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
assertNoNextPacket(context);
// second command, create hologram
var hologramUuid = UUID.fromString("b1586291-5f68-44dc-847d-6c123c5b8cbf");
context.translate(
addEntityTranslator,
new ClientboundAddEntityPacket(1298, hologramUuid, EntityType.ARMOR_STAND, 6, 5, 4, 3, 2, 1));
assertNextPacketMatch(context, AddEntityPacket.class, packet -> {
assertEquals(4, packet.getRuntimeEntityId());
assertEquals("minecraft:armor_stand", packet.getIdentifier());
});
// metadata set: invisible, custom name, custom name visible
context.translate(setEntityDataTranslator, new ClientboundSetEntityDataPacket(1298, new EntityMetadata[]{
new ByteEntityMetadata(0, MetadataType.BYTE, (byte) 0x20),
new ObjectEntityMetadata<>(2, MetadataType.OPTIONAL_CHAT, Optional.of(Component.text("tesss"))),
new BooleanEntityMetadata(3, MetadataType.BOOLEAN, true)
}));
assertNextPacketMatch(context, SetEntityDataPacket.class, packet -> {
assertEquals(4, packet.getRuntimeEntityId());
var metadata = packet.getMetadata();
assertEquals(0.0f, metadata.get(EntityDataTypes.SCALE));
assertEquals("tesss", metadata.get(EntityDataTypes.NAME));
assertEquals((byte) 1, metadata.get(EntityDataTypes.NAMETAG_ALWAYS_SHOW));
});
// because the armor stand turned invisible and has a nametag (nametag is hidden when invisible)
assertNextPacketType(context, MoveEntityAbsolutePacket.class);
context.translate(
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket(
"npc_team_1298",
Component.empty(),
Component.empty(),
Component.empty(),
false,
false,
NameTagVisibility.NEVER,
CollisionRule.NEVER,
TeamColor.WHITE,
new String[0]
)
);
context.translate(
setPlayerTeamTranslator,
new ClientboundSetPlayerTeamPacket("npc_team_1298", TeamAction.ADD_PLAYER, new String[]{ hologramUuid.toString() }));
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.getMetadata().put(EntityDataTypes.NAME, "§f§r§ftesss§r§f");
packet.setRuntimeEntityId(4);
return packet;
});
});
}
}

View file

@ -27,7 +27,7 @@ package org.geysermc.geyser.scoreboard.network.belowname;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockAndAddPlayerEntity;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
import net.kyori.adventure.text.Component;
@ -80,7 +80,7 @@ public class BasicBelownameScoreboardTests {
var setObjectiveTranslator = new JavaSetObjectiveTranslator();
var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setObjectiveTranslator,
@ -98,12 +98,12 @@ public class BasicBelownameScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "0 §r§9objective");
return packet;
}, context);
});
});
}
@ -113,7 +113,7 @@ public class BasicBelownameScoreboardTests {
var setObjectiveTranslator = new JavaSetObjectiveTranslator();
var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setObjectiveTranslator,
@ -131,24 +131,24 @@ public class BasicBelownameScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective");
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "");
return packet;
}, context);
});
});
}
@ -158,7 +158,7 @@ public class BasicBelownameScoreboardTests {
var setObjectiveTranslator = new JavaSetObjectiveTranslator();
var setDisplayObjectiveTranslator = new JavaSetDisplayObjectiveTranslator();
mockAndAddPlayerEntity(context, "player1", 2);
spawnPlayerSilently(context, "player1", 2);
context.translate(
setObjectiveTranslator,
@ -186,42 +186,42 @@ public class BasicBelownameScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective2")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective2");
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "objective1")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "");
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "0 §robjective1");
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.BELOW_NAME, "")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.SCORE, "");
return packet;
}, context);
});
});
}
}

View file

@ -32,7 +32,6 @@ import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScore
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket;
@ -75,7 +74,7 @@ public class BasicPlayerlistScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -83,7 +82,7 @@ public class BasicPlayerlistScoreboardTests {
packet.setDisplaySlot("list");
packet.setSortOrder(1);
return packet;
}, context);
});
});
}
@ -98,7 +97,7 @@ public class BasicPlayerlistScoreboardTests {
new ClientboundSetObjectivePacket(
"objective",
ObjectiveAction.ADD,
Component.text("objective", Style.style(NamedTextColor.AQUA, TextDecoration.BOLD)),
Component.text("objective", NamedTextColor.AQUA, TextDecoration.BOLD),
ScoreType.INTEGER,
null
)
@ -109,7 +108,7 @@ public class BasicPlayerlistScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("§b§lobjective");
@ -117,7 +116,7 @@ public class BasicPlayerlistScoreboardTests {
packet.setDisplaySlot("list");
packet.setSortOrder(1);
return packet;
}, context);
});
});
}
@ -156,7 +155,7 @@ public class BasicPlayerlistScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective2")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective2");
@ -164,26 +163,26 @@ public class BasicPlayerlistScoreboardTests {
packet.setDisplaySlot("list");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
// session player name is Tim203
packet.setInfos(List.of(new ScoreInfo(1, "0", 2, ScoreInfo.ScorerType.PLAYER, 1)));
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective1")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new RemoveObjectivePacket();
packet.setObjectiveId("0");
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("2");
packet.setDisplayName("objective1");
@ -191,14 +190,14 @@ public class BasicPlayerlistScoreboardTests {
packet.setDisplaySlot("list");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
// session player name is Tim203
packet.setInfos(List.of(new ScoreInfo(3, "2", 1, ScoreInfo.ScorerType.PLAYER, 1)));
return packet;
}, context);
});
});
}
}

View file

@ -26,21 +26,22 @@
package org.geysermc.geyser.scoreboard.network.server;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockAndAddPlayerEntity;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayer;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetDisplayObjectivePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetScorePacket;
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetDisplayObjectiveTranslator;
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetObjectiveTranslator;
@ -88,7 +89,7 @@ public class CubecraftScoreboardTest {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "sidebar")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("sidebar");
@ -96,7 +97,7 @@ public class CubecraftScoreboardTest {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
});
// Now they're going to create a bunch of teams and add players to those teams in a very inefficient way.
@ -191,27 +192,23 @@ public class CubecraftScoreboardTest {
ObjectiveAction.UPDATE,
Component.empty()
.append(Component.text(
"CubeCraft", Style.style(NamedTextColor.WHITE, TextDecoration.BOLD))),
"CubeCraft", NamedTextColor.WHITE, TextDecoration.BOLD)),
ScoreType.INTEGER,
null));
assertNextPacket(
() -> {
var packet = new RemoveObjectivePacket();
packet.setObjectiveId("0");
return packet;
},
context);
assertNextPacket(
() -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("§f§lCubeCraft");
packet.setCriteria("dummy");
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new RemoveObjectivePacket();
packet.setObjectiveId("0");
return packet;
});
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("§f§lCubeCraft");
packet.setCriteria("dummy");
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
});
context.translate(
setTeamTranslator,
@ -234,7 +231,7 @@ public class CubecraftScoreboardTest {
new ClientboundSetPlayerTeamPacket(
"SB_l-0",
Component.text("SB_l-0"),
Component.empty().append(Component.text("", Style.style(NamedTextColor.BLACK))),
Component.empty().append(Component.text("", NamedTextColor.BLACK)),
Component.empty(),
true,
true,
@ -244,14 +241,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§0", "sidebar", 10));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 10, "§r§0§0§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 10, "§r§0§0§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -287,14 +282,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§1", "sidebar", 9));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(2, "0", 9, "§bUser: §r§fTim203§r§0§1§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(2, "0", 9, "§bUser: §r§fTim203§r§0§1§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -330,14 +323,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§2", "sidebar", 8));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(3, "0", 8, "§bRank: §r§f\uE1AB §r§0§2§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(3, "0", 8, "§bRank: §r§f\uE1AB §r§0§2§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -370,14 +361,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§3", "sidebar", 7));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(4, "0", 7, "§r§0§3§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(4, "0", 7, "§r§0§3§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -410,14 +399,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§4", "sidebar", 6));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(5, "0", 6, "§r§0§4§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(5, "0", 6, "§r§0§4§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -450,14 +437,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§5", "sidebar", 5));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(6, "0", 5, "§r§0§5§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(6, "0", 5, "§r§0§5§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -493,14 +478,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§6", "sidebar", 4));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(7, "0", 4, "§bLobby: §r§fEU #10§r§0§6§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(7, "0", 4, "§bLobby: §r§fEU #10§r§0§6§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -536,14 +519,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§7", "sidebar", 3));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(8, "0", 3, "§bPlayers: §r§f783§r§0§7§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(8, "0", 3, "§bPlayers: §r§f783§r§0§7§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -576,14 +557,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§8", "sidebar", 2));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(9, "0", 2, "§r§0§8§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(9, "0", 2, "§r§0§8§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -616,14 +595,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§9", "sidebar", 1));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(10, "0", 1, "§824/09/24 (g2208)§r§0§9§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(10, "0", 1, "§824/09/24 (g2208)§r§0§9§r")));
return packet;
});
context.translate(
setTeamTranslator,
@ -656,14 +633,12 @@ public class CubecraftScoreboardTest {
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("§0§a", "sidebar", 0));
assertNextPacket(
() -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(11, "0", 0, "§6play.cubecraft.net§r§0§a§r")));
return packet;
},
context);
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(11, "0", 0, "§6play.cubecraft.net§r§0§a§r")));
return packet;
});
// after this we get a ClientboundPlayerInfoUpdatePacket with the action UPDATE_DISPLAY_NAME,
// but that one is only shown in the tablist so we don't have to handle that.
@ -672,85 +647,53 @@ public class CubecraftScoreboardTest {
// CubeCraft seems to use two armor stands per player: 1 for the rank badge and 1 for the player name.
// So the only thing we have to verify is that the nametag is hidden
mockAndAddPlayerEntity(context, "A_Player", 2);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "A_Player", 2);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(2, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "B_Player", 3);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(3);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "B_Player", 3);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(3, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "E_Player", 4);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(4);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "E_Player", 4);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(4, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "H_Player", 5);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(5);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "H_Player", 5);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(5, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "J_Player", 6);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(6);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "J_Player", 6);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(6, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "K_Player", 7);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(7);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "K_Player", 7);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(7, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "L_Player", 8);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(8);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "L_Player", 8);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(8, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
mockAndAddPlayerEntity(context, "O_Player", 9);
assertNextPacket(
() -> {
var packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(9);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
},
context);
spawnPlayer(context, "O_Player", 9);
assertNextPacketMatch(context, AddPlayerPacket.class, packet -> {
assertEquals(9, packet.getRuntimeEntityId());
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});
});
}
}

View file

@ -32,7 +32,6 @@ import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScore
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.cloudburstmc.protocol.bedrock.data.ScoreInfo;
import org.cloudburstmc.protocol.bedrock.packet.RemoveObjectivePacket;
@ -75,7 +74,7 @@ public class BasicSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -83,17 +82,17 @@ public class BasicSidebarScoreboardTests {
packet.setDisplaySlot("list");
packet.setSortOrder(1);
return packet;
}, context);
});
context.translate(
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.PLAYER_LIST, "")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new RemoveObjectivePacket();
packet.setObjectiveId("0");
return packet;
}, context);
});
});
}
@ -108,7 +107,7 @@ public class BasicSidebarScoreboardTests {
new ClientboundSetObjectivePacket(
"objective",
ObjectiveAction.ADD,
Component.text("objective", Style.style(NamedTextColor.AQUA, TextDecoration.BOLD)),
Component.text("objective", NamedTextColor.AQUA, TextDecoration.BOLD),
ScoreType.INTEGER,
null
)
@ -119,7 +118,7 @@ public class BasicSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("§b§lobjective");
@ -127,7 +126,7 @@ public class BasicSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
});
});
}
@ -170,7 +169,7 @@ public class BasicSidebarScoreboardTests {
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective2")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective2");
@ -178,13 +177,13 @@ public class BasicSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "Tim203")));
return packet;
}, context);
});
assertNoNextPacket(context);
@ -193,12 +192,12 @@ public class BasicSidebarScoreboardTests {
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective1")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new RemoveObjectivePacket();
packet.setObjectiveId("0");
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("2");
packet.setDisplayName("objective1");
@ -206,13 +205,13 @@ public class BasicSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(3, "2", 1, "Tim203")));
return packet;
}, context);
});
});
}
}

View file

@ -98,7 +98,7 @@ public class OrderAndLimitSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -106,8 +106,8 @@ public class OrderAndLimitSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -128,7 +128,7 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(15, "0", 3, "c")
));
return packet;
}, context);
});
assertNoNextPacket(context);
// remove a score
@ -136,43 +136,43 @@ public class OrderAndLimitSidebarScoreboardTests {
resetScoreTranslator,
new ClientboundResetScorePacket("m", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(5, "0", 13, "m")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b")));
return packet;
}, context);
});
// add a score
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("aa", "objective", 13)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(17, "0", 13, "aa")));
return packet;
}, context);
});
// add score with same score value (after)
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("ga", "objective", 9)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -180,8 +180,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(9, "0", 9, "§0§rg")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -189,14 +189,14 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 9, "§1§rga")
));
return packet;
}, context);
});
// add another score with same score value (before all)
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("ag", "objective", 9)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -205,8 +205,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 9, "§2§rga")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -215,14 +215,14 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 9, "§2§rga")
));
return packet;
}, context);
});
// remove score with same value
context.translate(
resetScoreTranslator,
new ClientboundResetScorePacket("g", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -230,8 +230,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 9, "§1§rga")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -239,14 +239,14 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(20, "0", 4, "e")
));
return packet;
}, context);
});
// remove the other score with the same value
context.translate(
resetScoreTranslator,
new ClientboundResetScorePacket("ga", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -254,8 +254,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(19, "0", 9, "ag")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -263,7 +263,7 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(21, "0", 3, "c")
));
return packet;
}, context);
});
});
}
@ -327,7 +327,7 @@ public class OrderAndLimitSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -335,8 +335,8 @@ public class OrderAndLimitSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -357,7 +357,7 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(15, "0", 3, "c")
));
return packet;
}, context);
});
assertNoNextPacket(context);
// remove a score
@ -365,36 +365,36 @@ public class OrderAndLimitSidebarScoreboardTests {
resetScoreTranslator,
new ClientboundResetScorePacket("m", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(5, "0", 13, "m")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b")));
return packet;
}, context);
});
// add a score
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("aa", "objective", 13)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(16, "0", 2, "b")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(17, "0", 13, "aa")));
return packet;
}, context);
});
// add some teams for the upcoming score adds
context.translate(
@ -435,7 +435,7 @@ public class OrderAndLimitSidebarScoreboardTests {
setScoreTranslator,
new ClientboundSetScorePacket("oa", "objective", 11)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -443,8 +443,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(7, "0", 11, "§0§r§4prefix§r§4o§r§4suffix")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -452,7 +452,7 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix")
));
return packet;
}, context);
});
// add a score that on Java should be before 'o', but would be after on Bedrock without manual order
// due to the team color
@ -460,7 +460,7 @@ public class OrderAndLimitSidebarScoreboardTests {
setScoreTranslator,
new ClientboundSetScorePacket("ao", "objective", 11)
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -469,8 +469,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 11, "§2§r§3prefix§r§3oa§r§3suffix")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -479,14 +479,14 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 11, "§2§r§3prefix§r§3oa§r§3suffix")
));
return packet;
}, context);
});
// remove original 'o' score
context.translate(
resetScoreTranslator,
new ClientboundResetScorePacket("o", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -494,8 +494,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(18, "0", 11, "§1§r§3prefix§r§3oa§r§3suffix")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -503,14 +503,14 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(20, "0", 4, "e")
));
return packet;
}, context);
});
// remove the other score with the same value as 'o'
context.translate(
resetScoreTranslator,
new ClientboundResetScorePacket("oa", "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(
@ -518,8 +518,8 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(19, "0", 11, "§5prefix§r§5ao§r§5suffix")
));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(
@ -527,7 +527,7 @@ public class OrderAndLimitSidebarScoreboardTests {
new ScoreInfo(21, "0", 3, "c")
));
return packet;
}, context);
});
});
}
}

View file

@ -69,7 +69,7 @@ public class VanillaSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -77,16 +77,16 @@ public class VanillaSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 1));
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner")));
return packet;
}, context);
});
});
}
@ -114,7 +114,7 @@ public class VanillaSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -122,22 +122,22 @@ public class VanillaSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner")));
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(setScoreTranslator, new ClientboundSetScorePacket("owner", "objective", 2));
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "owner")));
return packet;
}, context);
});
});
}
@ -166,7 +166,7 @@ public class VanillaSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -174,31 +174,31 @@ public class VanillaSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner")));
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("owner", "objective", 1).withDisplay(Component.text("hi"))
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "hi")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "hi")));
return packet;
}, context);
});
});
}
@ -227,7 +227,7 @@ public class VanillaSidebarScoreboardTests {
setDisplayObjectiveTranslator,
new ClientboundSetDisplayObjectivePacket(ScoreboardPosition.SIDEBAR, "objective")
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetDisplayObjectivePacket();
packet.setObjectiveId("0");
packet.setDisplayName("objective");
@ -235,31 +235,31 @@ public class VanillaSidebarScoreboardTests {
packet.setDisplaySlot("sidebar");
packet.setSortOrder(1);
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 1, "owner")));
return packet;
}, context);
});
assertNoNextPacket(context);
context.translate(
setScoreTranslator,
new ClientboundSetScorePacket("owner", "objective", 2).withDisplay(Component.text("hi"))
);
assertNextPacket(() -> {
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.REMOVE);
packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "hi")));
return packet;
}, context);
assertNextPacket(() -> {
});
assertNextPacket(context, () -> {
var packet = new SetScorePacket();
packet.setAction(SetScorePacket.Action.SET);
packet.setInfos(List.of(new ScoreInfo(1, "0", 2, "hi")));
return packet;
}, context);
});
});
}
}

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.scoreboard.network.util;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.junit.jupiter.api.Assertions;
@ -38,7 +39,7 @@ public class AssertUtils {
Assertions.assertEquals(expected.get(), actual);
}
public static void assertNextPacket(Supplier<BedrockPacket> expected, GeyserMockContext context) {
public static void assertNextPacket(GeyserMockContext context, Supplier<BedrockPacket> expected) {
assertContextEquals(expected, context.nextPacket());
}
@ -50,6 +51,16 @@ public class AssertUtils {
Assertions.assertEquals(type, actual.getClass());
}
public static <T extends BedrockPacket> void assertNextPacketMatch(GeyserMockContext context, Class<T> type, Consumer<T> matcher) {
var actual = context.nextPacket();
if (actual == null) {
Assertions.fail("Expected another packet!");
}
Assertions.assertEquals(type, actual.getClass(), "Expected packet to be an instance of " + type);
//noinspection unchecked verified in the line above me
matcher.accept((T) actual);
}
public static void assertNoNextPacket(GeyserMockContext context) {
Assertions.assertEquals(
Collections.emptyList(),

View file

@ -35,16 +35,15 @@ import java.util.function.Consumer;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
public class GeyserMockContext {
private final List<Object> mocksAndSpies = new ArrayList<>();
private final List<Object> storedObjects = new ArrayList<>();
private final List<BedrockPacket> packets = Collections.synchronizedList(new ArrayList<>());
private MockedStatic<GeyserImpl> geyserImplMock;
public static void mockContext(Consumer<GeyserMockContext> geyserContext) {
var context = new GeyserMockContext();
@ -59,9 +58,15 @@ public class GeyserMockContext {
var logger = context.storeObject(new EmptyGeyserLogger());
when(geyserImpl.getLogger()).thenReturn(logger);
try (var mocked = mockStatic(GeyserImpl.class)) {
mocked.when(GeyserImpl::getInstance).thenReturn(geyserImpl);
context.geyserImplMock = mocked;
try (var geyserImplMock = mockStatic(GeyserImpl.class)) {
geyserImplMock.when(GeyserImpl::getInstance).thenReturn(geyserImpl);
// Since Geyser isn't actually loaded, the Registries#init will not be called.
// This means that we manually load the registries we want to use
Registries.ENTITY_DEFINITIONS.load();
Registries.JAVA_ENTITY_IDENTIFIERS.load();
Registries.BEDROCK_ENTITY_PROPERTIES.load();
geyserContext.accept(context);
}
}
@ -136,8 +141,4 @@ public class GeyserMockContext {
public <T> void translate(PacketTranslator<T> translator, T packet) {
translator.translate(session(), packet);
}
public MockedStatic<GeyserImpl> geyserImplMock() {
return geyserImplMock;
}
}

View file

@ -25,6 +25,7 @@
package org.geysermc.geyser.scoreboard.network.util;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContext.mockContext;
import static org.mockito.ArgumentMatchers.any;
@ -34,6 +35,8 @@ import static org.mockito.Mockito.when;
import java.util.UUID;
import java.util.function.Consumer;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
@ -82,15 +85,20 @@ public class GeyserMockContextScoreboard {
when(worldCache.increaseAndGetScoreboardPacketsPerSecond()).thenReturn(0);
}
public static PlayerEntity mockAndAddPlayerEntity(GeyserMockContext context, String username, long geyserId) {
var playerEntity = spy(new PlayerEntity(context.session(), geyserId, UUID.randomUUID(), username));
// fake the player being spawned
when(playerEntity.isValid()).thenReturn(true);
public static PlayerEntity spawnPlayerSilently(GeyserMockContext context, String username, long geyserId) {
var player = spawnPlayer(context, username, geyserId);
assertNextPacketType(context, AddPlayerPacket.class);
return player;
}
public static PlayerEntity spawnPlayer(GeyserMockContext context, String username, long geyserId) {
var playerEntity = spy(new PlayerEntity(context.session(), (int) geyserId, geyserId, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, username, null));
var entityCache = context.mockOrSpy(EntityCache.class);
entityCache.addPlayerEntity(playerEntity);
// called when the player spawns
entityCache.cacheEntity(playerEntity);
entityCache.spawnEntity(playerEntity);
return playerEntity;
}
}