chore: more tests for components

This commit is contained in:
mworzala 2024-05-27 11:48:44 -04:00 committed by Matt Worzala
parent aa7c03aa12
commit 0fd91b827c
43 changed files with 737 additions and 89 deletions

View File

@ -4,7 +4,7 @@ metadata.format.version = "1.1"
# Important dependencies
data = "1.20.5-rv2"
adventure = "4.16.0"
adventure = "4.17.0"
hydrazine = "1.7.2"
jetbrainsAnnotations = "23.0.0"
slf4j = "2.0.7"

View File

@ -3,6 +3,7 @@ package net.minestom.server.color;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
@ -25,6 +26,8 @@ public record Color(int red, int green, int blue) implements RGBLike {
return new Color(buffer.read(NetworkBuffer.INT));
}
};
public static final BinaryTagSerializer<RGBLike> NBT_TYPE = BinaryTagSerializer.INT
.map(Color::new, color -> Color.fromRGBLike(color).asRGB());
public static @NotNull Color fromRGBLike(@NotNull RGBLike rgbLike) {
if (rgbLike instanceof Color color) return color;

View File

@ -1635,15 +1635,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
return true;
}
/**
* Gets if this player is in creative. Used for code readability.
*
* @return true if the player is in creative mode
*/
public boolean isCreative() {
return gameMode == GameMode.CREATIVE;
}
/**
* Changes the dimension of the player.
* Mostly unsafe since it requires sending chunks after.

View File

@ -450,8 +450,7 @@ public class AnvilLoader implements IChunkLoader {
// Save the block and biome palettes
final CompoundBinaryTag.Builder blockStates = CompoundBinaryTag.builder();
// Pre-copy because adventure does not -- https://github.com/KyoriPowered/adventure/issues/1070
blockStates.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, List.copyOf(blockPaletteEntries)));
blockStates.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, blockPaletteEntries));
if (blockPaletteEntries.size() > 1) {
// If there is only one entry we do not need to write the packed indices
var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(blockPaletteEntries.size()) / Math.log(2)));
@ -460,8 +459,7 @@ public class AnvilLoader implements IChunkLoader {
sectionData.put("block_states", blockStates.build());
final CompoundBinaryTag.Builder biomes = CompoundBinaryTag.builder();
// Pre-copy because adventure does not -- https://github.com/KyoriPowered/adventure/issues/1070
biomes.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, List.copyOf(biomePalette)));
biomes.put("palette", ListBinaryTag.listBinaryTag(BinaryTagTypes.STRING, biomePalette));
if (biomePalette.size() > 1) {
// If there is only one entry we do not need to write the packed indices
var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(biomePalette.size()) / Math.log(2)));

View File

@ -1,6 +1,7 @@
package net.minestom.server.inventory;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.item.EntityEquipEvent;

View File

@ -41,7 +41,7 @@ public final class ItemComponent {
public static final DataComponent<Tool> TOOL = DataComponent.register("tool", Tool.NETWORK_TYPE, Tool.NBT_TYPE);
public static final DataComponent<EnchantmentList> STORED_ENCHANTMENTS = DataComponent.register("stored_enchantments", EnchantmentList.NETWORK_TYPE, EnchantmentList.NBT_TYPE);
public static final DataComponent<DyedItemColor> DYED_COLOR = DataComponent.register("dyed_color", DyedItemColor.NETWORK_TYPE, DyedItemColor.NBT_TYPE);
public static final DataComponent<RGBLike> MAP_COLOR = DataComponent.register("map_color", Color.NETWORK_TYPE, BinaryTagSerializer.INT.map(Color::new, color -> Color.fromRGBLike(color).asRGB()));
public static final DataComponent<RGBLike> MAP_COLOR = DataComponent.register("map_color", Color.NETWORK_TYPE, Color.NBT_TYPE);
public static final DataComponent<Integer> MAP_ID = DataComponent.register("map_id", NetworkBuffer.VAR_INT, BinaryTagSerializer.INT);
public static final DataComponent<MapDecorations> MAP_DECORATIONS = DataComponent.register("map_decorations", null, MapDecorations.NBT_TYPE);
public static final DataComponent<MapPostProcessing> MAP_POST_PROCESSING = DataComponent.register("map_post_processing", MapPostProcessing.NETWORK_TYPE, null);

View File

@ -39,4 +39,6 @@ public record ArmorTrim(@NotNull DynamicRegistry.Key<TrimMaterial> material, @No
.putBoolean("show_in_tooltip", value.showInTooltip)
.build()
);
}

View File

@ -1,7 +1,6 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.ByteBinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.predicate.BlockPredicate;
@ -58,9 +57,7 @@ public record BlockPredicates(@NotNull List<BlockPredicate> predicates, boolean
// This default is fine in either case because the single predicate shouldnt have this key anyway.
// https://github.com/KyoriPowered/adventure/issues/1068
boolean showInTooltip = true;
if (compound.get("show_in_tooltip") instanceof ByteBinaryTag showInTooltipTag)
showInTooltip = showInTooltipTag.value() != 0;
boolean showInTooltip = compound.getBoolean("show_in_tooltip", true);
return new BlockPredicates(predicates, showInTooltip);
}

View File

@ -1,7 +1,6 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.ByteBinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.IntBinaryTag;
import net.kyori.adventure.util.RGBLike;
@ -39,8 +38,7 @@ public record DyedItemColor(@NotNull RGBLike color, boolean showInTooltip) {
public @NotNull DyedItemColor read(@NotNull BinaryTag tag) {
if (tag instanceof CompoundBinaryTag compoundTag) {
int color = compoundTag.getInt("color");
// https://github.com/KyoriPowered/adventure/issues/1068
boolean showInTooltip = !(compoundTag.get("show_in_tooltip") instanceof ByteBinaryTag showInTooltipTag) || showInTooltipTag.value() != 0;
boolean showInTooltip = compoundTag.getBoolean("show_in_tooltip", true);
return new DyedItemColor(new Color(color), showInTooltip);
} else if (tag instanceof IntBinaryTag intTag) {
return new DyedItemColor(new Color(intTag.intValue()), true);

View File

@ -1,7 +1,6 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.ByteBinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.item.enchant.Enchantment;
import net.minestom.server.network.NetworkBuffer;
@ -54,11 +53,8 @@ public record EnchantmentList(@NotNull Map<Enchantment, Integer> enchantments, b
}
// Doesnt matter which variant we chose, the default will work.
// https://github.com/KyoriPowered/adventure/issues/1068
boolean showInTooltip = true;
if (tag.get("show_in_tooltip") instanceof ByteBinaryTag byteTag) {
showInTooltip = byteTag.value() != 0;
}
boolean showInTooltip = tag.getBoolean("show_in_tooltip", true);
return new EnchantmentList(enchantments, showInTooltip);
},
value -> {
@ -78,6 +74,14 @@ public record EnchantmentList(@NotNull Map<Enchantment, Integer> enchantments, b
enchantments = Map.copyOf(enchantments);
}
public EnchantmentList(@NotNull Map<Enchantment, Integer> enchantments) {
this(enchantments, true);
}
public EnchantmentList(@NotNull Enchantment enchantment, int level) {
this(Map.of(enchantment, level), true);
}
public boolean has(@NotNull Enchantment enchantment) {
return enchantments.containsKey(enchantment);
}
@ -92,6 +96,12 @@ public record EnchantmentList(@NotNull Map<Enchantment, Integer> enchantments, b
return new EnchantmentList(newEnchantments, showInTooltip);
}
public @NotNull EnchantmentList remove(@NotNull Enchantment enchantment) {
Map<Enchantment, Integer> newEnchantments = new HashMap<>(enchantments);
newEnchantments.remove(enchantment);
return new EnchantmentList(newEnchantments, showInTooltip);
}
public @NotNull EnchantmentList withTooltip(boolean showInTooltip) {
return new EnchantmentList(enchantments, showInTooltip);
}

View File

@ -1,53 +1,50 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.play.data.WorldPos;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
public record LodestoneTracker(@NotNull String dimension, @NotNull Point blockPosition, boolean tracked) {
public record LodestoneTracker(@NotNull WorldPos target, boolean tracked) {
public static final NetworkBuffer.Type<LodestoneTracker> NETWORK_TYPE = new NetworkBuffer.Type<>() {
@Override
public void write(@NotNull NetworkBuffer buffer, @NotNull LodestoneTracker value) {
buffer.write(NetworkBuffer.STRING, value.dimension);
buffer.write(NetworkBuffer.BLOCK_POSITION, value.blockPosition);
buffer.write(WorldPos.NETWORK_TYPE, value.target);
buffer.write(NetworkBuffer.BOOLEAN, value.tracked);
}
@Override
public @NotNull LodestoneTracker read(@NotNull NetworkBuffer buffer) {
return new LodestoneTracker(
buffer.read(NetworkBuffer.STRING),
buffer.read(NetworkBuffer.BLOCK_POSITION),
buffer.read(NetworkBuffer.BOOLEAN)
buffer.read(WorldPos.NETWORK_TYPE),
buffer.read(NetworkBuffer.BOOLEAN)
);
}
};
public static final BinaryTagSerializer<LodestoneTracker> NBT_TYPE = new BinaryTagSerializer<>() {
@Override
public @NotNull BinaryTag write(@NotNull LodestoneTracker value) {
throw new UnsupportedOperationException("Not implemented"); //todo
}
public static final BinaryTagSerializer<LodestoneTracker> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> new LodestoneTracker(
WorldPos.NBT_TYPE.read(tag.get("target")),
tag.getBoolean("tracked")),
value -> CompoundBinaryTag.builder()
.put("target", WorldPos.NBT_TYPE.write(value.target))
.putBoolean("tracked", value.tracked)
.build()
);
@Override
public @NotNull LodestoneTracker read(@NotNull BinaryTag tag) {
throw new UnsupportedOperationException("Not implemented"); //todo
}
};
public @NotNull LodestoneTracker withDimension(@NotNull String dimension) {
return new LodestoneTracker(dimension, blockPosition, tracked);
public LodestoneTracker(@NotNull String dimension, @NotNull Point blockPosition, boolean tracked) {
this(new WorldPos(dimension, blockPosition), tracked);
}
public @NotNull LodestoneTracker withBlockPosition(@NotNull Point blockPosition) {
return new LodestoneTracker(dimension, blockPosition, tracked);
public @NotNull LodestoneTracker withTarget(@NotNull WorldPos target) {
return new LodestoneTracker(target, tracked);
}
public @NotNull LodestoneTracker withTracked(boolean tracked) {
return new LodestoneTracker(dimension, blockPosition, tracked);
return new LodestoneTracker(target, tracked);
}
}

View File

@ -10,14 +10,11 @@ import java.util.Map;
public record MapDecorations(@NotNull Map<String, Entry> decorations) {
public record Entry(@NotNull String type, double x, double z, float rotation) {
}
public static final BinaryTagSerializer<MapDecorations> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> {
Map<String, Entry> map = new HashMap<>(tag.size());
for (Map.Entry<String, ? extends BinaryTag> entry : tag) {
if (!(entry instanceof CompoundBinaryTag entryTag)) continue;
if (!(entry.getValue() instanceof CompoundBinaryTag entryTag)) continue;
map.put(entry.getKey(), new Entry(
entryTag.getString("type"),
entryTag.getDouble("x"),
@ -41,4 +38,27 @@ public record MapDecorations(@NotNull Map<String, Entry> decorations) {
return builder.build();
}
);
public MapDecorations {
decorations = Map.copyOf(decorations);
}
public @NotNull MapDecorations with(@NotNull String id, @NotNull String type, double x, double z, float rotation) {
return with(id, new Entry(type, x, z, rotation));
}
public @NotNull MapDecorations with(@NotNull String id, @NotNull Entry entry) {
Map<String, Entry> newDecorations = new HashMap<>(decorations);
newDecorations.put(id, entry);
return new MapDecorations(newDecorations);
}
public @NotNull MapDecorations remove(@NotNull String id) {
Map<String, Entry> newDecorations = new HashMap<>(decorations);
newDecorations.remove(id);
return new MapDecorations(newDecorations);
}
public record Entry(@NotNull String type, double x, double z, float rotation) {
}
}

View File

@ -23,6 +23,10 @@ public record PotDecorations(
this(getOrAir(list, 0), getOrAir(list, 1), getOrAir(list, 2), getOrAir(list, 3));
}
public PotDecorations(@NotNull Material material) {
this(material, material, material, material);
}
public @NotNull List<Material> asList() {
return List.of(back, left, right, front);
}

View File

@ -44,7 +44,25 @@ public record PotionContents(
public static final BinaryTagSerializer<PotionContents> NBT_TYPE = new BinaryTagSerializer<>() {
@Override
public @NotNull BinaryTag write(@NotNull PotionContents value) {
throw new UnsupportedOperationException();
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
if (value.potion != null) {
builder.put("potion", StringBinaryTag.stringBinaryTag(value.potion.name()));
}
if (value.customColor != null) {
builder.put("custom_color", Color.NBT_TYPE.write(value.customColor));
}
if (!value.customEffects.isEmpty()) {
ListBinaryTag.Builder<BinaryTag> effectsBuilder = ListBinaryTag.builder();
for (CustomPotionEffect effect : value.customEffects) {
effectsBuilder.add(CustomPotionEffect.NBT_TYPE.write(effect));
}
builder.put("custom_effects", effectsBuilder.build());
}
return builder.build();
}
@Override
@ -84,4 +102,21 @@ public record PotionContents(
public PotionContents {
customEffects = List.copyOf(customEffects);
}
public PotionContents(@NotNull PotionType potion) {
this(potion, null, List.of());
}
public PotionContents(@NotNull PotionType potion, @NotNull RGBLike customColor) {
this(potion, customColor, List.of());
}
public PotionContents(@NotNull List<CustomPotionEffect> customEffects) {
this(null, null, customEffects);
}
public PotionContents(@NotNull CustomPotionEffect customEffect) {
this(null, null, List.of(customEffect));
}
}

View File

@ -6,10 +6,12 @@ import net.minestom.server.potion.PotionEffect;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public record SuspiciousStewEffects(@NotNull List<Effect> effects) {
public static final int DEFAULT_DURATION = 160;
public static final SuspiciousStewEffects EMPTY = new SuspiciousStewEffects(List.of());
public static final NetworkBuffer.Type<SuspiciousStewEffects> NETWORK_TYPE = Effect.NETWORK_TYPE.list(Short.MAX_VALUE).map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
public static final BinaryTagSerializer<SuspiciousStewEffects> NBT_TYPE = Effect.NBT_TYPE.list().map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
@ -18,6 +20,16 @@ public record SuspiciousStewEffects(@NotNull List<Effect> effects) {
effects = List.copyOf(effects);
}
public SuspiciousStewEffects(@NotNull Effect effect) {
this(List.of(effect));
}
public @NotNull SuspiciousStewEffects with(@NotNull Effect effect) {
List<Effect> newEffects = new ArrayList<>(effects);
newEffects.add(effect);
return new SuspiciousStewEffects(newEffects);
}
public record Effect(@NotNull PotionEffect id, int durationTicks) {
public static final NetworkBuffer.Type<Effect> NETWORK_TYPE = new NetworkBuffer.Type<>() {

View File

@ -6,7 +6,7 @@ import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
public record Unbreakable(boolean showInTooltip) {
public static final Unbreakable DEFAULT = new Unbreakable(false);
public static final Unbreakable DEFAULT = new Unbreakable();
public static final NetworkBuffer.Type<Unbreakable> NETWORK_TYPE = new NetworkBuffer.Type<Unbreakable>() {
@Override
@ -20,8 +20,12 @@ public record Unbreakable(boolean showInTooltip) {
}
};
public Unbreakable() {
this(true);
}
public static final BinaryTagSerializer<Unbreakable> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> new Unbreakable(tag.getBoolean("showInTooltip", false)),
tag -> new Unbreakable(tag.getBoolean("showInTooltip", true)),
unbreakable -> CompoundBinaryTag.builder().putBoolean("showInTooltip", unbreakable.showInTooltip()).build()
);

View File

@ -1,5 +1,6 @@
package net.minestom.server.listener;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.PlayerStartFlyingEvent;
@ -9,7 +10,7 @@ import net.minestom.server.network.packet.client.play.ClientPlayerAbilitiesPacke
public class AbilitiesListener {
public static void listener(ClientPlayerAbilitiesPacket packet, Player player) {
final boolean canFly = player.isAllowFlying() || player.isCreative();
final boolean canFly = player.isAllowFlying() || player.getGameMode() == GameMode.CREATIVE;
if (canFly) {
final boolean isFlying = (packet.flags() & 0x2) > 0;

View File

@ -1,5 +1,6 @@
package net.minestom.server.listener;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
@ -10,7 +11,7 @@ import java.util.Objects;
public final class CreativeInventoryActionListener {
public static void listener(ClientCreativeInventoryActionPacket packet, Player player) {
if (!player.isCreative()) return;
if (player.getGameMode() != GameMode.CREATIVE) return;
short slot = packet.slot();
final ItemStack item = packet.item();
if (slot == -1) {

View File

@ -1,5 +1,6 @@
package net.minestom.server.listener;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.inventory.InventoryCloseEvent;

View File

@ -1,22 +1,51 @@
package net.minestom.server.network.packet.server.play.data;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.BLOCK_POSITION;
import static net.minestom.server.network.NetworkBuffer.STRING;
public record WorldPos(@NotNull String dimension, @NotNull Point position) implements NetworkBuffer.Writer {
public record WorldPos(@NotNull String dimension, @NotNull Point blockPosition) implements NetworkBuffer.Writer {
public static final NetworkBuffer.Type<WorldPos> NETWORK_TYPE = new NetworkBuffer.Type<WorldPos>() {
@Override
public void write(@NotNull NetworkBuffer buffer, WorldPos value) {
buffer.write(NetworkBuffer.STRING, value.dimension);
buffer.write(NetworkBuffer.BLOCK_POSITION, value.blockPosition);
}
@Override
public WorldPos read(@NotNull NetworkBuffer buffer) {
return new WorldPos(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.BLOCK_POSITION));
}
};
public static final BinaryTagSerializer<WorldPos> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
tag -> new WorldPos(tag.getString("dimension"), BinaryTagSerializer.BLOCK_POSITION.read(tag.get("pos"))),
pos -> CompoundBinaryTag.builder()
.putString("dimension", pos.dimension)
.put("pos", BinaryTagSerializer.BLOCK_POSITION.write(pos.blockPosition))
.build()
);
public WorldPos(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.read(BLOCK_POSITION));
}
public @NotNull WorldPos withDimension(@NotNull String dimension) {
return new WorldPos(dimension, blockPosition);
}
public @NotNull WorldPos withBlockPosition(@NotNull Point blockPosition) {
return new WorldPos(dimension, blockPosition);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, dimension);
writer.write(BLOCK_POSITION, position);
writer.write(BLOCK_POSITION, blockPosition);
}
}

View File

@ -12,7 +12,7 @@ import java.util.Collection;
public sealed interface PotionEffect extends StaticProtocolObject, PotionEffects permits PotionEffectImpl {
NetworkBuffer.Type<PotionEffect> NETWORK_TYPE = PotionEffectImpl.NETWORK_TYPE;
NetworkBuffer.Type<PotionEffect> NETWORK_TYPE = NetworkBuffer.VAR_INT.map(PotionEffectImpl::getId, PotionEffect::id);
@Contract(pure = true)
@NotNull Registry.PotionEffectEntry registry();

View File

@ -1,6 +1,5 @@
package net.minestom.server.potion;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
@ -10,8 +9,6 @@ record PotionEffectImpl(Registry.PotionEffectEntry registry) implements PotionEf
private static final Registry.Container<PotionEffect> CONTAINER = Registry.createStaticContainer(Registry.Resource.POTION_EFFECTS,
(namespace, properties) -> new PotionEffectImpl(Registry.potionEffect(namespace, properties)));
public static final NetworkBuffer.Type<PotionEffect> NETWORK_TYPE = NetworkBuffer.VAR_INT.map(PotionEffectImpl::getId, PotionEffect::id);
static PotionEffect get(@NotNull String namespace) {
return CONTAINER.get(namespace);
}

View File

@ -6,6 +6,8 @@ import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.item.ItemStack;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.ProtocolObject;
@ -228,6 +230,21 @@ public interface BinaryTagSerializer<T> {
}
};
BinaryTagSerializer<Point> BLOCK_POSITION = new BinaryTagSerializer<Point>() {
@Override
public @NotNull BinaryTag write(@NotNull Point value) {
return IntArrayBinaryTag.intArrayBinaryTag(value.blockX(), value.blockY(), value.blockZ());
}
@Override
public @NotNull Point read(@NotNull BinaryTag tag) {
if (!(tag instanceof IntArrayBinaryTag intArrayTag))
return Vec.ZERO;
int[] value = intArrayTag.value();
return new Vec(value[0], value[1], value[2]);
}
};
static <T extends ProtocolObject> @NotNull BinaryTagSerializer<DynamicRegistry.Key<T>> registryKey(@NotNull Function<Registries, DynamicRegistry<T>> registrySelector) {
//todo need to pass Registries as context here somehow.
return STRING.map(

View File

@ -191,7 +191,7 @@ public class PlayerIntegrationTest {
assertNotNull(player.getDeathLocation());
assertEquals(dimensionNamespace, player.getDeathLocation().dimension());
assertEquals(5, player.getDeathLocation().position().x());
assertEquals(5, player.getDeathLocation().blockPosition().x());
}
@Test

View File

@ -15,7 +15,6 @@ import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class AbstractItemComponentTest<T> {
@ -30,29 +29,25 @@ public abstract class AbstractItemComponentTest<T> {
@ParameterizedTest(name = "{0}")
@MethodSource("directReadWriteMethodSource")
public void directReadWriteNbt(String testName, @NotNull T entry) {
assumeTrue(component().isSerialized());
public void directReadWriteTest(String testName, @NotNull T entry) {
if (component().isSerialized()) {
var written1 = component().write(entry);
var written1 = component().write(entry);
var read = component().read(written1);
assertEquals(entry, read);
var read = component().read(written1);
assertEquals(entry, read);
var written2 = component().write(read);
assertEquals(written1, written2);
}
var written2 = component().write(read);
assertEquals(written1, written2);
}
if (component().isSynced()) {
var written1 = NetworkBuffer.makeArray(b -> component().write(b, entry));
@ParameterizedTest(name = "{0}")
@MethodSource("directReadWriteMethodSource")
public void directReadWriteNetwork(String testName, @NotNull T entry) {
assumeTrue(component().isSynced());
var read = component().read(new NetworkBuffer(ByteBuffer.wrap(written1)));
assertEquals(entry, read);
var written1 = NetworkBuffer.makeArray(b -> component().write(b, entry));
var read = component().read(new NetworkBuffer(ByteBuffer.wrap(written1)));
assertEquals(entry, read);
var written2 = NetworkBuffer.makeArray(b -> component().write(b, entry));
assertArrayEquals(written1, written2);
var written2 = NetworkBuffer.makeArray(b -> component().write(b, entry));
assertArrayEquals(written1, written2);
}
}
}

View File

@ -0,0 +1,31 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
public class BoolTest extends AbstractItemComponentTest<Boolean> {
// This is not a test, but it creates a compile error if the component type is changed away from boolean,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<Boolean>> SHARED_COMPONENTS = List.of(
ItemComponent.ENCHANTMENT_GLINT_OVERRIDE
);
@Override
protected @NotNull DataComponent<Boolean> component() {
return SHARED_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, Boolean>> directReadWriteEntries() {
return List.of(
entry("true", true),
entry("false", false)
);
}
}

View File

@ -0,0 +1,42 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.IntBinaryTag;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.color.Color;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ColorTest extends AbstractItemComponentTest<RGBLike> {
// This is not a test, but it creates a compile error if the component type is changed away from Integer,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<RGBLike>> SHARED_COMPONENTS = List.of(
ItemComponent.MAP_COLOR
);
@Override
protected @NotNull DataComponent<RGBLike> component() {
return SHARED_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, RGBLike>> directReadWriteEntries() {
return List.of(
entry("simple", new Color(0x123456))
);
}
@Test
void namedTextColor() {
var tag = ItemComponent.MAP_COLOR.write(NamedTextColor.YELLOW);
assertEquals(IntBinaryTag.intBinaryTag(16777045), tag);
}
}

View File

@ -0,0 +1,32 @@
package net.minestom.server.item.component;
import net.kyori.adventure.text.Component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class ComponentTest extends AbstractItemComponentTest<Component> {
// This is not a test, but it creates a compile error if the component type is changed away from Component,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<Component>> SHARED_COMPONENTS = List.of(
ItemComponent.CUSTOM_NAME,
ItemComponent.ITEM_NAME
);
@Override
protected @NotNull DataComponent<Component> component() {
return SHARED_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, Component>> directReadWriteEntries() {
// Component serialization is well tested elsewhere, this is just a sanity check really.
return List.of(
Map.entry("empty component", Component.empty()),
Map.entry("text component", Component.text("Hello, world!"))
);
}
}

View File

@ -11,9 +11,18 @@ import java.util.Map;
import static java.util.Map.entry;
public class CustomDataTest extends AbstractItemComponentTest<CustomData> {
// This is not a test, but it creates a compile error if the component type is changed away,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<CustomData>> SHARED_COMPONENTS = List.of(
ItemComponent.CUSTOM_DATA,
ItemComponent.ENTITY_DATA,
ItemComponent.BUCKET_ENTITY_DATA,
ItemComponent.BLOCK_ENTITY_DATA
);
@Override
protected @NotNull DataComponent<CustomData> component() {
return ItemComponent.CUSTOM_DATA;
return SHARED_COMPONENTS.getFirst();
}
@Override

View File

@ -1,13 +1,16 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.IntBinaryTag;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DyedItemColorTest extends AbstractItemComponentTest<DyedItemColor> {
@ -23,4 +26,10 @@ public class DyedItemColorTest extends AbstractItemComponentTest<DyedItemColor>
entry("no tooltip", new DyedItemColor(0xCAFEBB, false))
);
}
@Test
void alternativeSyntax() {
var value = ItemComponent.DYED_COLOR.read(IntBinaryTag.intBinaryTag(16777045));
assertEquals(new DyedItemColor(16777045), value);
}
}

View File

@ -0,0 +1,48 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.enchant.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EnchantmentListTest extends AbstractItemComponentTest<EnchantmentList> {
// This is not a test, but it creates a compile error if the component type is changed away from Unit,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<EnchantmentList>> SHARED_COMPONENTS = List.of(
ItemComponent.ENCHANTMENTS,
ItemComponent.STORED_ENCHANTMENTS
);
@Override
protected @NotNull DataComponent<EnchantmentList> component() {
return SHARED_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, EnchantmentList>> directReadWriteEntries() {
return List.of(
Map.entry("empty", EnchantmentList.EMPTY),
Map.entry("single entry", new EnchantmentList(Map.of(Enchantment.SHARPNESS, 1), true)),
Map.entry("multi entry", new EnchantmentList(Map.of(Enchantment.SHARPNESS, 1, Enchantment.PUNCH, 2), false))
);
}
@Test
void testShorthandNbtSyntax() throws Exception {
var tag = TagStringIOExt.readTag("""
{
"sharpness": 1,
"punch": 2,
}
""");
var value = component().read(tag);
assertEquals(new EnchantmentList(Map.of(Enchantment.SHARPNESS, 1, Enchantment.PUNCH, 2), true), value);
}
}

View File

@ -0,0 +1,36 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
public class IntTest extends AbstractItemComponentTest<Integer> {
// This is not a test, but it creates a compile error if the component type is changed away from Integer,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<Integer>> INT_COMPONENTS = List.of(
ItemComponent.MAX_STACK_SIZE,
ItemComponent.MAX_DAMAGE,
ItemComponent.DAMAGE,
ItemComponent.CUSTOM_MODEL_DATA,
ItemComponent.REPAIR_COST,
ItemComponent.MAP_ID,
ItemComponent.OMINOUS_BOTTLE_AMPLIFIER
);
@Override
protected @NotNull DataComponent<Integer> component() {
return INT_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, Integer>> directReadWriteEntries() {
return List.of(
entry("instance", 2)
);
}
}

View File

@ -0,0 +1,32 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.IntBinaryTag;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ItemRarityTest extends AbstractItemComponentTest<ItemRarity> {
@Override
protected @NotNull DataComponent<ItemRarity> component() {
return ItemComponent.RARITY;
}
@Override
protected @NotNull List<Map.Entry<String, ItemRarity>> directReadWriteEntries() {
return List.of(
Map.entry("common", ItemRarity.COMMON)
);
}
@Test
void testReadFromNbtInt() {
var value = ItemRarity.NBT_TYPE.read(IntBinaryTag.intBinaryTag(2));
assertEquals(ItemRarity.RARE, value);
}
}

View File

@ -0,0 +1,26 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class LodestoneTrackerTest extends AbstractItemComponentTest<LodestoneTracker> {
@Override
protected @NotNull DataComponent<LodestoneTracker> component() {
return ItemComponent.LODESTONE_TRACKER;
}
@Override
protected @NotNull List<Map.Entry<String, LodestoneTracker>> directReadWriteEntries() {
return List.of(
Map.entry("tracked", new LodestoneTracker("minecraft:overworld", Vec.ZERO, true)),
Map.entry("not tracked", new LodestoneTracker("minecraft:overworld", new Vec(1, 2, 3), false))
);
}
}

View File

@ -0,0 +1,29 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class MapDecorationsTest extends AbstractItemComponentTest<MapDecorations> {
@Override
protected @NotNull DataComponent<MapDecorations> component() {
return ItemComponent.MAP_DECORATIONS;
}
@Override
protected @NotNull List<Map.Entry<String, MapDecorations>> directReadWriteEntries() {
return List.of(
Map.entry("empty", new MapDecorations(Map.of())),
Map.entry("single", new MapDecorations(Map.of("id", new MapDecorations.Entry("type", 1.0, 2.0, 3)))),
Map.entry("multiple", new MapDecorations(Map.of(
"id1", new MapDecorations.Entry("type1", 1.0, 2.0, 3),
"id2", new MapDecorations.Entry("type2", 4.0, 5.0, 6)
)))
);
}
}

View File

@ -0,0 +1,23 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class MapPostProcessingTest extends AbstractItemComponentTest<MapPostProcessing> {
@Override
protected @NotNull DataComponent<MapPostProcessing> component() {
return ItemComponent.MAP_POST_PROCESSING;
}
@Override
protected @NotNull List<Map.Entry<String, MapPostProcessing>> directReadWriteEntries() {
return List.of(
Map.entry("lock", MapPostProcessing.LOCK),
Map.entry("scale", MapPostProcessing.SCALE)
);
}
}

View File

@ -0,0 +1,27 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class PotDecorationsTest extends AbstractItemComponentTest<PotDecorations> {
@Override
protected @NotNull DataComponent<PotDecorations> component() {
return ItemComponent.POT_DECORATIONS;
}
@Override
protected @NotNull List<Map.Entry<String, PotDecorations>> directReadWriteEntries() {
return List.of(
Map.entry("instance", PotDecorations.EMPTY),
Map.entry("one", new PotDecorations(Material.DIAMOND, PotDecorations.DEFAULT_ITEM, PotDecorations.DEFAULT_ITEM, PotDecorations.DEFAULT_ITEM)),
Map.entry("two", new PotDecorations(Material.DIAMOND, Material.DIAMOND, PotDecorations.DEFAULT_ITEM, PotDecorations.DEFAULT_ITEM)),
Map.entry("three", new PotDecorations(Material.DIAMOND, Material.DIAMOND, Material.DIAMOND, PotDecorations.DEFAULT_ITEM)),
Map.entry("four", new PotDecorations(Material.DIAMOND, Material.DIAMOND, Material.DIAMOND, Material.DIAMOND))
);
}
}

View File

@ -0,0 +1,48 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.minestom.server.color.Color;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.potion.CustomPotionEffect;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionType;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PotionContentsTest extends AbstractItemComponentTest<PotionContents> {
@Override
protected @NotNull DataComponent<PotionContents> component() {
return ItemComponent.POTION_CONTENTS;
}
@Override
protected @NotNull List<Map.Entry<String, PotionContents>> directReadWriteEntries() {
return List.of(
Map.entry("empty", PotionContents.EMPTY),
Map.entry("single effect", new PotionContents(PotionType.STRONG_SWIFTNESS)),
Map.entry("single effect, color", new PotionContents(PotionType.STRONG_SWIFTNESS, new Color(0x123456))),
Map.entry("custom effect", new PotionContents(new CustomPotionEffect(PotionEffect.INVISIBILITY, (byte) 2, 10, true, false, true))),
Map.entry("custom effect recursive", new PotionContents(new CustomPotionEffect(PotionEffect.INVISIBILITY, new CustomPotionEffect.Settings(
(byte) 2, 10, true, false, true, new CustomPotionEffect.Settings(
(byte) 2, 10, true, false, true, null))))),
Map.entry("custom effect", new PotionContents(List.of(
new CustomPotionEffect(PotionEffect.INVISIBILITY, (byte) 2, 10, true, false, true),
new CustomPotionEffect(PotionEffect.STRENGTH, (byte) 3, 10000, false, true, false)
)))
);
}
@Test
void alternativeNbtSyntax() {
var value = ItemComponent.POTION_CONTENTS.read(StringBinaryTag.stringBinaryTag("minecraft:strong_swiftness"));
var expected = new PotionContents(PotionType.STRONG_SWIFTNESS, null, List.of());
assertEquals(expected, value);
}
}

View File

@ -0,0 +1,24 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class SeededContainerLootTest extends AbstractItemComponentTest<SeededContainerLoot> {
@Override
protected @NotNull DataComponent<SeededContainerLoot> component() {
return ItemComponent.CONTAINER_LOOT;
}
@Override
protected @NotNull List<Map.Entry<String, SeededContainerLoot>> directReadWriteEntries() {
return List.of(
Map.entry("instance", new SeededContainerLoot("loot_table", 1234567890L))
);
}
}

View File

@ -0,0 +1,32 @@
package net.minestom.server.item.component;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
public class StringTest extends AbstractItemComponentTest<String> {
// This is not a test, but it creates a compile error if the component type is changed away,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<String>> SHARED_COMPONENTS = List.of(
ItemComponent.INSTRUMENT,
ItemComponent.NOTE_BLOCK_SOUND,
ItemComponent.LOCK
);
@Override
protected @NotNull DataComponent<String> component() {
return SHARED_COMPONENTS.getFirst();
}
@Override
protected @NotNull List<Map.Entry<String, String>> directReadWriteEntries() {
return List.of(
entry("instance", "hello, world")
);
}
}

View File

@ -0,0 +1,42 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.potion.PotionEffect;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class SuspiciousStewEffectsTest extends AbstractItemComponentTest<SuspiciousStewEffects> {
@Override
protected @NotNull DataComponent<SuspiciousStewEffects> component() {
return ItemComponent.SUSPICIOUS_STEW_EFFECTS;
}
@Override
protected @NotNull List<Map.Entry<String, SuspiciousStewEffects>> directReadWriteEntries() {
return List.of(
Map.entry("empty", SuspiciousStewEffects.EMPTY),
Map.entry("single", new SuspiciousStewEffects(new SuspiciousStewEffects.Effect(PotionEffect.ABSORPTION, 100))),
Map.entry("multi", new SuspiciousStewEffects(List.of(
new SuspiciousStewEffects.Effect(PotionEffect.ABSORPTION, 100),
new SuspiciousStewEffects.Effect(PotionEffect.STRENGTH, 2)
)))
);
}
@Test
void nbtReadDefaultDuration() throws Exception {
var value = ItemComponent.SUSPICIOUS_STEW_EFFECTS.read(TagStringIOExt.readTag("""
[{"id": "minecraft:strength"}]
"""));
var expected = new SuspiciousStewEffects(new SuspiciousStewEffects.Effect(PotionEffect.STRENGTH, 160));
assertEquals(expected, value);
}
}

View File

@ -0,0 +1,35 @@
package net.minestom.server.item.component;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class UnbreakableTest extends AbstractItemComponentTest<Unbreakable> {
@Override
protected @NotNull DataComponent<Unbreakable> component() {
return ItemComponent.UNBREAKABLE;
}
@Override
protected @NotNull List<Map.Entry<String, Unbreakable>> directReadWriteEntries() {
return List.of(
Map.entry("shown", new Unbreakable(true)),
Map.entry("not shown", new Unbreakable(false))
);
}
@Test
void testDefaultNbtValue() throws IOException {
var tag = TagStringIOExt.readTag("{}");
var value = ItemComponent.UNBREAKABLE.read(tag);
assertTrue(value.showInTooltip());
}
}

View File

@ -11,9 +11,19 @@ import java.util.Map;
import static java.util.Map.entry;
public class UnitTest extends AbstractItemComponentTest<Unit> {
// This is not a test, but it creates a compile error if the component type is changed away from Unit,
// as a reminder that tests should be added for that new component type.
private static final List<DataComponent<Unit>> UNIT_COMPONENTS = List.of(
ItemComponent.HIDE_ADDITIONAL_TOOLTIP,
ItemComponent.HIDE_TOOLTIP,
ItemComponent.CREATIVE_SLOT_LOCK,
ItemComponent.INTANGIBLE_PROJECTILE,
ItemComponent.FIRE_RESISTANT
);
@Override
protected @NotNull DataComponent<Unit> component() {
return ItemComponent.HIDE_TOOLTIP;
return UNIT_COMPONENTS.getFirst();
}
@Override