feat: partial anvil writing, update tests (still many broken)

This commit is contained in:
mworzala 2024-04-23 09:46:54 -04:00
parent 4d4a073559
commit 8ff9ab99f7
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
40 changed files with 678 additions and 987 deletions

View File

@ -46,6 +46,7 @@ public final class MinecraftServer {
public static final String VERSION_NAME = "1.20.5-pre1";
public static final int PROTOCOL_VERSION = 1073742009;
public static final int DATA_VERSION = 3829;
// Threads
public static final String THREAD_NAME_BENCHMARK = "Ms-Benchmark";

View File

@ -7,7 +7,6 @@ import net.minestom.server.entity.metadata.animal.*;
import net.minestom.server.entity.metadata.animal.tameable.CatMeta;
import net.minestom.server.entity.metadata.animal.tameable.ParrotMeta;
import net.minestom.server.entity.metadata.animal.tameable.WolfMeta;
import net.minestom.server.entity.metadata.projectile.*;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.entity.metadata.display.ItemDisplayMeta;
import net.minestom.server.entity.metadata.display.TextDisplayMeta;
@ -20,11 +19,13 @@ import net.minestom.server.entity.metadata.item.*;
import net.minestom.server.entity.metadata.minecart.*;
import net.minestom.server.entity.metadata.monster.*;
import net.minestom.server.entity.metadata.monster.raider.*;
import net.minestom.server.entity.metadata.monster.skeleton.BoggedMeta;
import net.minestom.server.entity.metadata.monster.skeleton.SkeletonMeta;
import net.minestom.server.entity.metadata.monster.skeleton.StrayMeta;
import net.minestom.server.entity.metadata.monster.skeleton.WitherSkeletonMeta;
import net.minestom.server.entity.metadata.monster.zombie.*;
import net.minestom.server.entity.metadata.other.*;
import net.minestom.server.entity.metadata.projectile.*;
import net.minestom.server.entity.metadata.villager.VillagerMeta;
import net.minestom.server.entity.metadata.villager.WanderingTraderMeta;
import net.minestom.server.entity.metadata.water.AxolotlMeta;
@ -39,8 +40,6 @@ import java.util.Collection;
import java.util.Map;
import java.util.function.BiFunction;
import static java.util.Map.entry;
record EntityTypeImpl(Registry.EntityEntry registry) implements EntityType {
private static final Registry.Container<EntityType> CONTAINER = Registry.createStaticContainer(Registry.Resource.ENTITIES,
(namespace, properties) -> new EntityTypeImpl(Registry.entity(namespace, properties)));
@ -73,132 +72,136 @@ record EntityTypeImpl(Registry.EntityEntry registry) implements EntityType {
private static Map<String, BiFunction<Entity, Metadata, EntityMeta>> createMetaMap() {
return Map.<String, BiFunction<Entity, Metadata, EntityMeta>>ofEntries(
entry("minecraft:allay", AllayMeta::new),
entry("minecraft:area_effect_cloud", AreaEffectCloudMeta::new),
entry("minecraft:armor_stand", ArmorStandMeta::new),
entry("minecraft:arrow", ArrowMeta::new),
entry("minecraft:axolotl", AxolotlMeta::new),
entry("minecraft:bat", BatMeta::new),
entry("minecraft:bee", BeeMeta::new),
entry("minecraft:blaze", BlazeMeta::new),
entry("minecraft:block_display", BlockDisplayMeta::new),
entry("minecraft:boat", BoatMeta::new),
entry("minecraft:breeze", BreezeMeta::new),
entry("minecraft:chest_boat", BoatMeta::new),
entry("minecraft:camel", CamelMeta::new),
entry("minecraft:cat", CatMeta::new),
entry("minecraft:cave_spider", CaveSpiderMeta::new),
entry("minecraft:chicken", ChickenMeta::new),
entry("minecraft:cod", CodMeta::new),
entry("minecraft:cow", CowMeta::new),
entry("minecraft:creeper", CreeperMeta::new),
entry("minecraft:dolphin", DolphinMeta::new),
entry("minecraft:donkey", DonkeyMeta::new),
entry("minecraft:dragon_fireball", DragonFireballMeta::new),
entry("minecraft:drowned", DrownedMeta::new),
entry("minecraft:elder_guardian", ElderGuardianMeta::new),
entry("minecraft:end_crystal", EndCrystalMeta::new),
entry("minecraft:ender_dragon", EnderDragonMeta::new),
entry("minecraft:enderman", EndermanMeta::new),
entry("minecraft:endermite", EndermiteMeta::new),
entry("minecraft:evoker", EvokerMeta::new),
entry("minecraft:evoker_fangs", EvokerFangsMeta::new),
entry("minecraft:experience_orb", ExperienceOrbMeta::new),
entry("minecraft:eye_of_ender", EyeOfEnderMeta::new),
entry("minecraft:falling_block", FallingBlockMeta::new),
entry("minecraft:firework_rocket", FireworkRocketMeta::new),
entry("minecraft:fox", FoxMeta::new),
entry("minecraft:frog", FrogMeta::new),
entry("minecraft:ghast", GhastMeta::new),
entry("minecraft:giant", GiantMeta::new),
entry("minecraft:glow_item_frame", GlowItemFrameMeta::new),
entry("minecraft:glow_squid", GlowSquidMeta::new),
entry("minecraft:goat", GoatMeta::new),
entry("minecraft:guardian", GuardianMeta::new),
entry("minecraft:hoglin", HoglinMeta::new),
entry("minecraft:horse", HorseMeta::new),
entry("minecraft:husk", HuskMeta::new),
entry("minecraft:illusioner", IllusionerMeta::new),
entry("minecraft:interaction", InteractionMeta::new),
entry("minecraft:iron_golem", IronGolemMeta::new),
entry("minecraft:item", ItemEntityMeta::new),
entry("minecraft:item_display", ItemDisplayMeta::new),
entry("minecraft:item_frame", ItemFrameMeta::new),
entry("minecraft:fireball", FireballMeta::new),
entry("minecraft:leash_knot", LeashKnotMeta::new),
entry("minecraft:lightning_bolt", LightningBoltMeta::new),
entry("minecraft:llama", LlamaMeta::new),
entry("minecraft:llama_spit", LlamaSpitMeta::new),
entry("minecraft:magma_cube", MagmaCubeMeta::new),
entry("minecraft:marker", MarkerMeta::new),
entry("minecraft:minecart", MinecartMeta::new),
entry("minecraft:chest_minecart", ChestMinecartMeta::new),
entry("minecraft:command_block_minecart", CommandBlockMinecartMeta::new),
entry("minecraft:furnace_minecart", FurnaceMinecartMeta::new),
entry("minecraft:hopper_minecart", HopperMinecartMeta::new),
entry("minecraft:spawner_minecart", SpawnerMinecartMeta::new),
entry("minecraft:text_display", TextDisplayMeta::new),
entry("minecraft:tnt_minecart", TntMinecartMeta::new),
entry("minecraft:mule", MuleMeta::new),
entry("minecraft:mooshroom", MooshroomMeta::new),
entry("minecraft:ocelot", OcelotMeta::new),
entry("minecraft:painting", PaintingMeta::new),
entry("minecraft:panda", PandaMeta::new),
entry("minecraft:parrot", ParrotMeta::new),
entry("minecraft:phantom", PhantomMeta::new),
entry("minecraft:pig", PigMeta::new),
entry("minecraft:piglin", PiglinMeta::new),
entry("minecraft:piglin_brute", PiglinBruteMeta::new),
entry("minecraft:pillager", PillagerMeta::new),
entry("minecraft:polar_bear", PolarBearMeta::new),
entry("minecraft:tnt", PrimedTntMeta::new),
entry("minecraft:pufferfish", PufferfishMeta::new),
entry("minecraft:rabbit", RabbitMeta::new),
entry("minecraft:ravager", RavagerMeta::new),
entry("minecraft:salmon", SalmonMeta::new),
entry("minecraft:sheep", SheepMeta::new),
entry("minecraft:shulker", ShulkerMeta::new),
entry("minecraft:shulker_bullet", ShulkerBulletMeta::new),
entry("minecraft:silverfish", SilverfishMeta::new),
entry("minecraft:skeleton", SkeletonMeta::new),
entry("minecraft:skeleton_horse", SkeletonHorseMeta::new),
entry("minecraft:slime", SlimeMeta::new),
entry("minecraft:small_fireball", SmallFireballMeta::new),
entry("minecraft:sniffer", SnifferMeta::new),
entry("minecraft:snow_golem", SnowGolemMeta::new),
entry("minecraft:snowball", SnowballMeta::new),
entry("minecraft:spectral_arrow", SpectralArrowMeta::new),
entry("minecraft:spider", SpiderMeta::new),
entry("minecraft:squid", SquidMeta::new),
entry("minecraft:stray", StrayMeta::new),
entry("minecraft:strider", StriderMeta::new),
entry("minecraft:tadpole", TadpoleMeta::new),
entry("minecraft:egg", ThrownEggMeta::new),
entry("minecraft:ender_pearl", ThrownEnderPearlMeta::new),
entry("minecraft:experience_bottle", ThrownExperienceBottleMeta::new),
entry("minecraft:potion", ThrownPotionMeta::new),
entry("minecraft:trident", ThrownTridentMeta::new),
entry("minecraft:trader_llama", TraderLlamaMeta::new),
entry("minecraft:tropical_fish", TropicalFishMeta::new),
entry("minecraft:turtle", TurtleMeta::new),
entry("minecraft:vex", VexMeta::new),
entry("minecraft:villager", VillagerMeta::new),
entry("minecraft:vindicator", VindicatorMeta::new),
entry("minecraft:wandering_trader", WanderingTraderMeta::new),
entry("minecraft:warden", WardenMeta::new),
entry("minecraft:wind_charge", WindChargeMeta::new),
entry("minecraft:witch", WitchMeta::new),
entry("minecraft:wither", WitherMeta::new),
entry("minecraft:wither_skeleton", WitherSkeletonMeta::new),
entry("minecraft:wither_skull", WitherSkullMeta::new),
entry("minecraft:wolf", WolfMeta::new),
entry("minecraft:zoglin", ZoglinMeta::new),
entry("minecraft:zombie", ZombieMeta::new),
entry("minecraft:zombie_horse", ZombieHorseMeta::new),
entry("minecraft:zombie_villager", ZombieVillagerMeta::new),
entry("minecraft:zombified_piglin", ZombifiedPiglinMeta::new),
entry("minecraft:player", PlayerMeta::new),
entry("minecraft:fishing_bobber", FishingHookMeta::new)
Map.entry("minecraft:allay", AllayMeta::new),
Map.entry("minecraft:area_effect_cloud", AreaEffectCloudMeta::new),
Map.entry("minecraft:armadillo", ArmadilloMeta::new),
Map.entry("minecraft:armor_stand", ArmorStandMeta::new),
Map.entry("minecraft:arrow", ArrowMeta::new),
Map.entry("minecraft:axolotl", AxolotlMeta::new),
Map.entry("minecraft:bat", BatMeta::new),
Map.entry("minecraft:bee", BeeMeta::new),
Map.entry("minecraft:blaze", BlazeMeta::new),
Map.entry("minecraft:block_display", BlockDisplayMeta::new),
Map.entry("minecraft:boat", BoatMeta::new),
Map.entry("minecraft:bogged", BoggedMeta::new),
Map.entry("minecraft:breeze", BreezeMeta::new),
Map.entry("minecraft:breeze_wind_charge", BreezeWindChargeMeta::new),
Map.entry("minecraft:chest_boat", BoatMeta::new),
Map.entry("minecraft:camel", CamelMeta::new),
Map.entry("minecraft:cat", CatMeta::new),
Map.entry("minecraft:cave_spider", CaveSpiderMeta::new),
Map.entry("minecraft:chicken", ChickenMeta::new),
Map.entry("minecraft:cod", CodMeta::new),
Map.entry("minecraft:cow", CowMeta::new),
Map.entry("minecraft:creeper", CreeperMeta::new),
Map.entry("minecraft:dolphin", DolphinMeta::new),
Map.entry("minecraft:donkey", DonkeyMeta::new),
Map.entry("minecraft:dragon_fireball", DragonFireballMeta::new),
Map.entry("minecraft:drowned", DrownedMeta::new),
Map.entry("minecraft:elder_guardian", ElderGuardianMeta::new),
Map.entry("minecraft:end_crystal", EndCrystalMeta::new),
Map.entry("minecraft:ender_dragon", EnderDragonMeta::new),
Map.entry("minecraft:enderman", EndermanMeta::new),
Map.entry("minecraft:endermite", EndermiteMeta::new),
Map.entry("minecraft:evoker", EvokerMeta::new),
Map.entry("minecraft:evoker_fangs", EvokerFangsMeta::new),
Map.entry("minecraft:experience_orb", ExperienceOrbMeta::new),
Map.entry("minecraft:eye_of_ender", EyeOfEnderMeta::new),
Map.entry("minecraft:falling_block", FallingBlockMeta::new),
Map.entry("minecraft:firework_rocket", FireworkRocketMeta::new),
Map.entry("minecraft:fox", FoxMeta::new),
Map.entry("minecraft:frog", FrogMeta::new),
Map.entry("minecraft:ghast", GhastMeta::new),
Map.entry("minecraft:giant", GiantMeta::new),
Map.entry("minecraft:glow_item_frame", GlowItemFrameMeta::new),
Map.entry("minecraft:glow_squid", GlowSquidMeta::new),
Map.entry("minecraft:goat", GoatMeta::new),
Map.entry("minecraft:guardian", GuardianMeta::new),
Map.entry("minecraft:hoglin", HoglinMeta::new),
Map.entry("minecraft:horse", HorseMeta::new),
Map.entry("minecraft:husk", HuskMeta::new),
Map.entry("minecraft:illusioner", IllusionerMeta::new),
Map.entry("minecraft:interaction", InteractionMeta::new),
Map.entry("minecraft:iron_golem", IronGolemMeta::new),
Map.entry("minecraft:item", ItemEntityMeta::new),
Map.entry("minecraft:item_display", ItemDisplayMeta::new),
Map.entry("minecraft:item_frame", ItemFrameMeta::new),
Map.entry("minecraft:fireball", FireballMeta::new),
Map.entry("minecraft:leash_knot", LeashKnotMeta::new),
Map.entry("minecraft:lightning_bolt", LightningBoltMeta::new),
Map.entry("minecraft:llama", LlamaMeta::new),
Map.entry("minecraft:llama_spit", LlamaSpitMeta::new),
Map.entry("minecraft:magma_cube", MagmaCubeMeta::new),
Map.entry("minecraft:marker", MarkerMeta::new),
Map.entry("minecraft:minecart", MinecartMeta::new),
Map.entry("minecraft:chest_minecart", ChestMinecartMeta::new),
Map.entry("minecraft:command_block_minecart", CommandBlockMinecartMeta::new),
Map.entry("minecraft:furnace_minecart", FurnaceMinecartMeta::new),
Map.entry("minecraft:hopper_minecart", HopperMinecartMeta::new),
Map.entry("minecraft:spawner_minecart", SpawnerMinecartMeta::new),
Map.entry("minecraft:text_display", TextDisplayMeta::new),
Map.entry("minecraft:tnt_minecart", TntMinecartMeta::new),
Map.entry("minecraft:mule", MuleMeta::new),
Map.entry("minecraft:mooshroom", MooshroomMeta::new),
Map.entry("minecraft:ocelot", OcelotMeta::new),
Map.entry("minecraft:ominous_item_spawner", OminousItemSpawnerMeta::new),
Map.entry("minecraft:painting", PaintingMeta::new),
Map.entry("minecraft:panda", PandaMeta::new),
Map.entry("minecraft:parrot", ParrotMeta::new),
Map.entry("minecraft:phantom", PhantomMeta::new),
Map.entry("minecraft:pig", PigMeta::new),
Map.entry("minecraft:piglin", PiglinMeta::new),
Map.entry("minecraft:piglin_brute", PiglinBruteMeta::new),
Map.entry("minecraft:pillager", PillagerMeta::new),
Map.entry("minecraft:polar_bear", PolarBearMeta::new),
Map.entry("minecraft:tnt", PrimedTntMeta::new),
Map.entry("minecraft:pufferfish", PufferfishMeta::new),
Map.entry("minecraft:rabbit", RabbitMeta::new),
Map.entry("minecraft:ravager", RavagerMeta::new),
Map.entry("minecraft:salmon", SalmonMeta::new),
Map.entry("minecraft:sheep", SheepMeta::new),
Map.entry("minecraft:shulker", ShulkerMeta::new),
Map.entry("minecraft:shulker_bullet", ShulkerBulletMeta::new),
Map.entry("minecraft:silverfish", SilverfishMeta::new),
Map.entry("minecraft:skeleton", SkeletonMeta::new),
Map.entry("minecraft:skeleton_horse", SkeletonHorseMeta::new),
Map.entry("minecraft:slime", SlimeMeta::new),
Map.entry("minecraft:small_fireball", SmallFireballMeta::new),
Map.entry("minecraft:sniffer", SnifferMeta::new),
Map.entry("minecraft:snow_golem", SnowGolemMeta::new),
Map.entry("minecraft:snowball", SnowballMeta::new),
Map.entry("minecraft:spectral_arrow", SpectralArrowMeta::new),
Map.entry("minecraft:spider", SpiderMeta::new),
Map.entry("minecraft:squid", SquidMeta::new),
Map.entry("minecraft:stray", StrayMeta::new),
Map.entry("minecraft:strider", StriderMeta::new),
Map.entry("minecraft:tadpole", TadpoleMeta::new),
Map.entry("minecraft:egg", ThrownEggMeta::new),
Map.entry("minecraft:ender_pearl", ThrownEnderPearlMeta::new),
Map.entry("minecraft:experience_bottle", ThrownExperienceBottleMeta::new),
Map.entry("minecraft:potion", ThrownPotionMeta::new),
Map.entry("minecraft:trident", ThrownTridentMeta::new),
Map.entry("minecraft:trader_llama", TraderLlamaMeta::new),
Map.entry("minecraft:tropical_fish", TropicalFishMeta::new),
Map.entry("minecraft:turtle", TurtleMeta::new),
Map.entry("minecraft:vex", VexMeta::new),
Map.entry("minecraft:villager", VillagerMeta::new),
Map.entry("minecraft:vindicator", VindicatorMeta::new),
Map.entry("minecraft:wandering_trader", WanderingTraderMeta::new),
Map.entry("minecraft:warden", WardenMeta::new),
Map.entry("minecraft:wind_charge", WindChargeMeta::new),
Map.entry("minecraft:witch", WitchMeta::new),
Map.entry("minecraft:wither", WitherMeta::new),
Map.entry("minecraft:wither_skeleton", WitherSkeletonMeta::new),
Map.entry("minecraft:wither_skull", WitherSkullMeta::new),
Map.entry("minecraft:wolf", WolfMeta::new),
Map.entry("minecraft:zoglin", ZoglinMeta::new),
Map.entry("minecraft:zombie", ZombieMeta::new),
Map.entry("minecraft:zombie_horse", ZombieHorseMeta::new),
Map.entry("minecraft:zombie_villager", ZombieVillagerMeta::new),
Map.entry("minecraft:zombified_piglin", ZombifiedPiglinMeta::new),
Map.entry("minecraft:player", PlayerMeta::new),
Map.entry("minecraft:fishing_bobber", FishingHookMeta::new)
);
}
}

View File

@ -0,0 +1,40 @@
package net.minestom.server.entity.metadata.projectile;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.entity.metadata.ObjectDataProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BreezeWindChargeMeta extends EntityMeta implements ObjectDataProvider, ProjectileMeta {
public static final byte OFFSET = EntityMeta.MAX_OFFSET;
public static final byte MAX_OFFSET = OFFSET + 0;
private Entity shooter;
public BreezeWindChargeMeta(@NotNull Entity entity, @NotNull Metadata metadata) {
super(entity, metadata);
}
@Override
@Nullable
public Entity getShooter() {
return shooter;
}
@Override
public void setShooter(@Nullable Entity shooter) {
this.shooter = shooter;
}
@Override
public int getObjectData() {
return this.shooter == null ? 0 : this.shooter.getEntityId();
}
@Override
public boolean requiresVelocityPacketAtSpawn() {
return true;
}
}

View File

@ -95,10 +95,10 @@ public class AnvilLoader implements IChunkLoader {
}
private @NotNull CompletableFuture<@Nullable Chunk> loadMCA(Instance instance, int chunkX, int chunkZ) throws IOException {
final RegionFile mcaFile = getMCAFile(instance, chunkX, chunkZ);
final RegionFile mcaFile = getMCAFile(chunkX, chunkZ);
if (mcaFile == null)
return CompletableFuture.completedFuture(null);
final CompoundBinaryTag chunkData = mcaFile.getChunk(chunkX, chunkZ);
final CompoundBinaryTag chunkData = mcaFile.readChunkData(chunkX, chunkZ);
if (chunkData == null)
return CompletableFuture.completedFuture(null);
@ -142,7 +142,7 @@ public class AnvilLoader implements IChunkLoader {
return CompletableFuture.completedFuture(chunk);
}
private @Nullable RegionFile getMCAFile(Instance instance, int chunkX, int chunkZ) {
private @Nullable RegionFile getMCAFile(int chunkX, int chunkZ) {
final int regionX = ChunkUtils.toRegionCoordinate(chunkX);
final int regionZ = ChunkUtils.toRegionCoordinate(chunkZ);
return alreadyLoaded.computeIfAbsent(RegionFile.getFileName(regionX, regionZ), n -> {
@ -154,7 +154,7 @@ public class AnvilLoader implements IChunkLoader {
try {
Set<IntIntImmutablePair> previousVersion = perRegionLoadedChunks.put(new IntIntImmutablePair(regionX, regionZ), new HashSet<>());
assert previousVersion == null : "The AnvilLoader cache should not already have data for this region.";
return new RegionFile(regionPath, regionX, regionZ, instance.getDimensionType());
return new RegionFile(regionPath);
} catch (IOException e) {
MinecraftServer.getExceptionManager().handleException(e);
return null;
@ -322,49 +322,91 @@ public class AnvilLoader implements IChunkLoader {
@Override
public @NotNull CompletableFuture<Void> saveChunk(@NotNull Chunk chunk) {
// final int chunkX = chunk.getChunkX();
// final int chunkZ = chunk.getChunkZ();
// RegionFile mcaFile;
// synchronized (alreadyLoaded) {
// mcaFile = getMCAFile(chunk.instance, chunkX, chunkZ);
// if (mcaFile == null) {
// final int regionX = CoordinatesKt.chunkToRegion(chunkX);
// final int regionZ = CoordinatesKt.chunkToRegion(chunkZ);
// final String n = RegionFile.Companion.createFileName(regionX, regionZ);
// File regionFile = new File(regionPath.toFile(), n);
// try {
// if (!regionFile.exists()) {
// if (!regionFile.getParentFile().exists()) {
// regionFile.getParentFile().mkdirs();
// }
// regionFile.createNewFile();
// }
// mcaFile = new RegionFile(new RandomAccessFile(regionFile, "rw"), regionX, regionZ);
// alreadyLoaded.put(n, mcaFile);
// } catch (AnvilException | IOException e) {
// LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e);
// MinecraftServer.getExceptionManager().handleException(e);
// return AsyncUtils.VOID_FUTURE;
// }
// }
// }
// ChunkWriter writer = new ChunkWriter(SupportedVersion.Companion.getLatest());
// save(chunk, writer);
// try {
// LOGGER.debug("Attempt saving at {} {}", chunk.getChunkX(), chunk.getChunkZ());
// mcaFile.writeColumnData(writer.toNBT(), chunk.getChunkX(), chunk.getChunkZ());
// } catch (IOException e) {
// LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e);
// MinecraftServer.getExceptionManager().handleException(e);
// return AsyncUtils.VOID_FUTURE;
// }
final int chunkX = chunk.getChunkX();
final int chunkZ = chunk.getChunkZ();
// Find the region file or create an empty one if missing
RegionFile mcaFile = getMCAFile(chunkX, chunkZ);
if (mcaFile == null) {
final int regionX = ChunkUtils.toRegionCoordinate(chunkX);
final int regionZ = ChunkUtils.toRegionCoordinate(chunkZ);
final String regionFileName = RegionFile.getFileName(regionX, regionZ);
try {
Path regionFile = regionPath.resolve(regionFileName);
if (!Files.exists(regionFile)) {
Files.createDirectories(regionFile.getParent());
Files.createFile(regionFile);
}
mcaFile = new RegionFile(regionFile);
alreadyLoaded.put(regionFileName, mcaFile);
} catch (IOException e) {
LOGGER.error("Failed to create region file for " + chunkX + ", " + chunkZ, e);
MinecraftServer.getExceptionManager().handleException(e);
return AsyncUtils.VOID_FUTURE;
}
}
try {
final CompoundBinaryTag.Builder chunkData = CompoundBinaryTag.builder();
chunkData.putInt("DataVersion", MinecraftServer.DATA_VERSION);
chunkData.putInt("xPos", chunkX);
chunkData.putInt("zPos", chunkZ);
chunkData.putInt("yPos", chunk.getMinSection());
chunkData.putString("status", "minecraft:full");
chunkData.putLong("LastUpdate", chunk.getInstance().getWorldAge());
saveSectionData(chunk, chunkData);
LOGGER.debug("Attempt saving at {} {}", chunk.getChunkX(), chunk.getChunkZ());
mcaFile.writeChunkData(chunkX, chunkZ, chunkData.build());
} catch (IOException e) {
LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e);
MinecraftServer.getExceptionManager().handleException(e);
}
return AsyncUtils.VOID_FUTURE;
}
// private BlockState getBlockState(final Block block) {
// return blockStateId2ObjectCacheTLS.get().computeIfAbsent(block.stateId(), _unused -> new BlockState(block.name(), block.properties()));
// }
private void saveSectionData(@NotNull Chunk chunk, @NotNull CompoundBinaryTag.Builder chunkData) {
final int minY = chunk.getMinSection() * Chunk.CHUNK_SECTION_SIZE;
final int maxY = chunk.getMaxSection() * Chunk.CHUNK_SECTION_SIZE - 1;
for (int sectionY = chunk.getMinSection(); sectionY < chunk.getMaxSection(); sectionY++) {
final Section section = chunk.getSection(sectionY);
final CompoundBinaryTag.Builder sectionData = CompoundBinaryTag.builder();
sectionData.putInt("Y", sectionY);
// Lighting
byte[] skyLight = section.skyLight().array();
if (skyLight != null && skyLight.length > 0)
sectionData.putByteArray("SkyLight", skyLight);
byte[] blockLight = section.blockLight().array();
if (blockLight != null && blockLight.length > 0)
sectionData.putByteArray("BlockLight", blockLight);
// Build block & biome palettes
//todo
// int[] blockStates = new int[Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE];
// int[] biomes = new int[64];
//
// for (int localY = 0; localY < Chunk.CHUNK_SECTION_SIZE; localY++) {
// for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
// for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
//
// }
// }
// }
throw new UnsupportedOperationException("Not implemented");
}
}
// private void save(Chunk chunk, ChunkWriter chunkWriter) {
// final int minY = chunk.getMinSection() * Chunk.CHUNK_SECTION_SIZE;
// final int maxY = chunk.getMaxSection() * Chunk.CHUNK_SECTION_SIZE - 1;
@ -376,14 +418,7 @@ public class AnvilLoader implements IChunkLoader {
// int[] palettedBiomes = new int[ChunkSection.Companion.getBiomeArraySize()];
// int[] palettedBlockStates = new int[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
// for (int sectionY = chunk.getMinSection(); sectionY < chunk.getMaxSection(); sectionY++) {
// ChunkSectionWriter sectionWriter = new ChunkSectionWriter(SupportedVersion.Companion.getLatest(), (byte) sectionY);
//
// Section section = chunk.getSection(sectionY);
// sectionWriter.setSkyLights(section.skyLight().array());
// sectionWriter.setBlockLights(section.blockLight().array());
//
// BiomePalette biomePalette = new BiomePalette();
// BlockPalette blockPalette = new BlockPalette();
// for (int sectionLocalY = 0; sectionLocalY < Chunk.CHUNK_SECTION_SIZE; sectionLocalY++) {
// for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
// for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
@ -437,38 +472,41 @@ public class AnvilLoader implements IChunkLoader {
// chunkWriter.setSectionsData(NBT.List(NBTType.TAG_Compound, sectionData));
// chunkWriter.setBlockEntityData(NBT.List(NBTType.TAG_Compound, blockEntities));
// }
//
// /**
// * Unload a given chunk. Also unloads a region when no chunk from that region is loaded.
// *
// * @param chunk the chunk to unload
// */
// @Override
// public void unloadChunk(Chunk chunk) {
// final int regionX = CoordinatesKt.chunkToRegion(chunk.chunkX);
// final int regionZ = CoordinatesKt.chunkToRegion(chunk.chunkZ);
//
// final IntIntImmutablePair regionKey = new IntIntImmutablePair(regionX, regionZ);
// synchronized (perRegionLoadedChunks) {
// Set<IntIntImmutablePair> chunks = perRegionLoadedChunks.get(regionKey);
// if (chunks != null) { // if null, trying to unload a chunk from a region that was not created by the AnvilLoader
// // don't check return value, trying to unload a chunk not created by the AnvilLoader is valid
// chunks.remove(new IntIntImmutablePair(chunk.chunkX, chunk.chunkZ));
//
// if (chunks.isEmpty()) {
// perRegionLoadedChunks.remove(regionKey);
// RegionFile regionFile = alreadyLoaded.remove(RegionFile.Companion.createFileName(regionX, regionZ));
// if (regionFile != null) {
// try {
// regionFile.close();
// } catch (IOException e) {
// MinecraftServer.getExceptionManager().handleException(e);
// }
// }
// }
// }
// }
// }
/**
* Unload a given chunk. Also unloads a region when no chunk from that region is loaded.
*
* @param chunk the chunk to unload
*/
@Override
public void unloadChunk(Chunk chunk) {
final int regionX = ChunkUtils.toRegionCoordinate(chunk.getChunkX());
final int regionZ = ChunkUtils.toRegionCoordinate(chunk.getChunkZ());
final IntIntImmutablePair regionKey = new IntIntImmutablePair(regionX, regionZ);
perRegionLoadedChunksLock.lock();
try {
Set<IntIntImmutablePair> chunks = perRegionLoadedChunks.get(regionKey);
if (chunks != null) { // if null, trying to unload a chunk from a region that was not created by the AnvilLoader
// don't check return value, trying to unload a chunk not created by the AnvilLoader is valid
chunks.remove(new IntIntImmutablePair(chunk.getChunkX(), chunk.getChunkZ()));
if (chunks.isEmpty()) {
perRegionLoadedChunks.remove(regionKey);
RegionFile regionFile = alreadyLoaded.remove(RegionFile.getFileName(regionX, regionZ));
if (regionFile != null) {
try {
regionFile.close();
} catch (IOException e) {
MinecraftServer.getExceptionManager().handleException(e);
}
}
}
}
} finally {
perRegionLoadedChunksLock.unlock();
}
}
@Override
public boolean supportsParallelLoading() {

View File

@ -5,14 +5,16 @@ import it.unimi.dsi.fastutil.booleans.BooleanList;
import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.world.DimensionType;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
/**
@ -27,8 +29,12 @@ final class RegionFile implements AutoCloseable {
private static final int SECTOR_SIZE = 4096;
private static final int SECTOR_1MB = 1024 * 1024 / SECTOR_SIZE;
private static final int HEADER_LENGTH = MAX_ENTRY_COUNT * 2 * 4; // 2 4-byte fields per entry
private static final int CHUNK_HEADER_LENGTH = 4 + 1; // Length + Compression type (todo non constant to support custom compression)
private static final int COMPRESSION_ZLIB = 2;
private static final BinaryTagIO.Reader TAG_READER = BinaryTagIO.unlimitedReader();
private static final BinaryTagIO.Writer TAG_WRITER = BinaryTagIO.writer();
public static @NotNull String getFileName(int regionX, int regionZ) {
return "r." + regionX + "." + regionZ + ".mca";
@ -41,7 +47,7 @@ final class RegionFile implements AutoCloseable {
private final int[] timestamps = new int[MAX_ENTRY_COUNT];
private final BooleanList freeSectors = new BooleanArrayList(2);
public RegionFile(@NotNull Path path, int regionX, int regionZ, @NotNull DimensionType dimensionType) throws IOException {
public RegionFile(@NotNull Path path) throws IOException {
this.file = new RandomAccessFile(path.toFile(), "rw");
readHeader();
@ -56,7 +62,7 @@ final class RegionFile implements AutoCloseable {
}
}
public @Nullable CompoundBinaryTag getChunk(int chunkX, int chunkZ) throws IOException {
public @Nullable CompoundBinaryTag readChunkData(int chunkX, int chunkZ) throws IOException {
lock.lock();
try {
if (!hasChunkData(chunkX, chunkZ)) return null;
@ -67,7 +73,7 @@ final class RegionFile implements AutoCloseable {
int compressionType = file.readByte();
BinaryTagIO.Compression compression = switch (compressionType) {
case 1 -> BinaryTagIO.Compression.GZIP;
case 2 -> BinaryTagIO.Compression.ZLIB;
case COMPRESSION_ZLIB -> BinaryTagIO.Compression.ZLIB;
case 3 -> BinaryTagIO.Compression.NONE;
default -> throw new IOException("Unsupported compression type: " + compressionType);
};
@ -83,12 +89,56 @@ final class RegionFile implements AutoCloseable {
}
}
public void writeChunkData(int chunkX, int chunkZ, @NotNull CompoundBinaryTag data) throws IOException {
// Write the data (compressed)
ByteArrayOutputStream out = new ByteArrayOutputStream();
TAG_WRITER.writeNamed(Map.entry("", data), out, BinaryTagIO.Compression.ZLIB);
byte[] dataBytes = out.toByteArray();
int chunkLength = CHUNK_HEADER_LENGTH + dataBytes.length;
int sectorCount = (int) Math.ceil(chunkLength / (double) SECTOR_SIZE);
Check.stateCondition(sectorCount >= SECTOR_1MB, "Chunk data is too large to fit in a region file");
lock.lock();
try {
// We don't attempt to reuse the current allocation, just write it to a new position and free the old one.
int oldLocation = getChunkIndex(chunkX, chunkZ);
// Find a new location
int firstSector = findFreeSectors(sectorCount);
if (firstSector == -1) {
firstSector = allocSectors(sectorCount);
}
int newLocation = (firstSector << 8) | sectorCount;
// Mark the sectors as used & free the old sectors
markLocation(oldLocation, true);
markLocation(newLocation, false);
// Write the chunk data
file.seek((long) firstSector * SECTOR_SIZE);
file.writeInt(chunkLength);
file.writeByte(COMPRESSION_ZLIB);
file.write(dataBytes);
// Update the header and write it
locations[oldLocation] = newLocation;
timestamps[oldLocation] = (int) (System.currentTimeMillis() / 1000);
writeHeader();
} finally {
lock.unlock();
}
}
@Override
public void close() throws Exception {
public void close() throws IOException {
file.close();
}
private int getChunkIndex(int chunkX, int chunkZ) {
return (ChunkUtils.toRegionLocal(chunkZ) << 5) | ChunkUtils.toRegionLocal(chunkX);
}
private void readHeader() throws IOException {
file.seek(0);
if (file.length() < HEADER_LENGTH) {
@ -105,13 +155,8 @@ final class RegionFile implements AutoCloseable {
file.seek(0);
for (int i = 0; i < MAX_ENTRY_COUNT; i++) {
int location = locations[i] = file.readInt();
int offset = location >> 8;
int length = location & 0xFF;
if (location != 0 && offset + length <= freeSectors.size()) {
for (int sectorIndex = 0; sectorIndex < length; sectorIndex++) {
freeSectors.set(sectorIndex + offset, false);
}
if (location != 0) {
markLocation(location, false);
}
}
@ -121,7 +166,49 @@ final class RegionFile implements AutoCloseable {
}
}
private int getChunkIndex(int chunkX, int chunkZ) {
return (ChunkUtils.toRegionLocal(chunkZ) << 5) | ChunkUtils.toRegionLocal(chunkX);
private void writeHeader() throws IOException {
file.seek(0);
for (int location : locations) {
file.writeInt(location);
}
for (int timestamp : timestamps) {
file.writeInt(timestamp);
}
}
private int findFreeSectors(int length) {
for (int start = 0; start < freeSectors.size() - length; start++) {
boolean found = true;
for (int i = 0; i < length; i++) {
if (!freeSectors.getBoolean(start++)) {
found = false;
break;
}
}
if (found) return start;
}
return -1;
}
private int allocSectors(int count) throws IOException {
var eof = file.length();
file.seek(eof);
byte[] emptySector = new byte[SECTOR_SIZE];
for (int i = 0; i < count; i++) {
freeSectors.add(true);
file.write(emptySector);
}
return (int) (eof / SECTOR_SIZE);
}
private void markLocation(int location, boolean free) {
int sectorCount = locations[location] & 0xFF;
int sectorStart = locations[location] >> 8;
Check.stateCondition(sectorStart + sectorCount > freeSectors.size(), "Invalid sector count");
for (int i = sectorStart; i < sectorStart + sectorCount; i++) {
freeSectors.set(i, free);
}
}
}

View File

@ -50,6 +50,10 @@ public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> propertie
}
);
public static @NotNull PropertiesPredicate exact(@NotNull String key, @NotNull String value) {
return new PropertiesPredicate(Map.of(key, new ValuePredicate.Exact(value)));
}
public PropertiesPredicate {
properties = Map.copyOf(properties);
}

View File

@ -12,6 +12,7 @@ import net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import static net.minestom.server.item.ItemComponentImpl.declare;
@ -95,4 +96,8 @@ public sealed interface ItemComponent<T> extends StaticProtocolObject permits It
static @Nullable ItemComponent<?> fromId(int id) {
return ItemComponentImpl.IDS.get(id);
}
static @NotNull Collection<ItemComponent<?>> values() {
return ItemComponentImpl.NAMESPACES.values();
}
}

View File

@ -120,11 +120,14 @@ record ItemStackImpl(Material material, int amount, ItemComponentPatch component
}
private static @NotNull CompoundBinaryTag toCompound(@NotNull ItemStack itemStack) {
return CompoundBinaryTag.builder()
.putString("id", itemStack.material().name())
.putInt("count", itemStack.amount())
.put("components", ItemComponentPatch.NBT_TYPE.write(((ItemStackImpl) itemStack).components))
.build();
CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder();
tag.putString("id", itemStack.material().name());
tag.putInt("count", itemStack.amount());
CompoundBinaryTag components = (CompoundBinaryTag) ItemComponentPatch.NBT_TYPE.write(((ItemStackImpl) itemStack).components);
if (components.size() > 0) tag.put("components", components);
return tag.build();
}
static final class Builder implements ItemStack.Builder {

View File

@ -66,6 +66,10 @@ public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @
pages = List.copyOf(pages);
}
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author) {
this(pages, title, author, 0, true);
}
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author, int generation, boolean resolved) {
this(pages.stream().map(page -> new FilteredText<>(page, null)).toList(), new FilteredText<>(title, null), author, generation, resolved);
}

View File

@ -1,10 +1,15 @@
package net.minestom.server.command;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.IntArrayBinaryTag;
import net.kyori.adventure.nbt.IntBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
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 net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentEnum;
import net.minestom.server.command.builder.arguments.ArgumentType;
@ -21,7 +26,6 @@ import net.minestom.server.utils.location.RelativeVec;
import net.minestom.server.utils.math.FloatRange;
import net.minestom.server.utils.math.IntRange;
import net.minestom.server.utils.time.TimeUnit;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.Test;
import java.time.Duration;
@ -32,11 +36,15 @@ import static org.junit.jupiter.api.Assertions.*;
public class ArgumentTypeTest {
static {
MinecraftServer.init();
}
@Test
public void testArgumentEnchantment() {
var arg = ArgumentType.Enchantment("enchantment");
assertInvalidArg(arg, "minecraft:invalid_enchantment");
assertArg(arg, Enchantment.SWEEPING, Enchantment.SWEEPING.name());
assertArg(arg, Enchantment.SWEEPING_EDGE, Enchantment.SWEEPING_EDGE.name());
assertArg(arg, Enchantment.MENDING, Enchantment.MENDING.name());
}
@ -169,11 +177,10 @@ public class ArgumentTypeTest {
@Test
public void testArgumentNbtCompoundTag() {
var arg = ArgumentType.NbtCompound("nbt_compound");
assertArg(arg, NBT.Compound(mut -> mut.put("long_array", NBT.LongArray(12, 49, 119))), "{\"long_array\":[L;12L,49L,119L]}");
assertArg(arg, NBT.Compound(mut -> mut.put("nested", NBT.Compound(mut2 ->
mut2.put("complex", NBT.IntArray(124, 999, 33256))
))
), "{\"nested\": {\"complex\": [I;124,999,33256]}}");
assertArg(arg, CompoundBinaryTag.builder().putLongArray("long_array", new long[]{12, 49, 119}).build(),
"{\"long_array\":[L;12L,49L,119L]}");
assertArg(arg, CompoundBinaryTag.builder().put("nested", CompoundBinaryTag.builder().putIntArray("complex", new int[]{124, 999, 33256}).build()).build(),
"{\"nested\": {\"complex\": [I;124,999,33256]}}");
assertInvalidArg(arg, "string");
assertInvalidArg(arg, "\"string\"");
@ -184,11 +191,12 @@ public class ArgumentTypeTest {
@Test
public void testArgumentNbtTag() {
var arg = ArgumentType.NBT("nbt");
assertArg(arg, NBT.String("string"), "string");
assertArg(arg, NBT.String("string"), "\"string\"");
assertArg(arg, NBT.Int(44), "44");
assertArg(arg, NBT.IntArray(11, 49, 33), "[I;11,49,33]");
assertArg(arg, NBT.Compound(mut -> mut.put("long_array", NBT.LongArray(12, 49, 119))), "{\"long_array\":[L;12L,49L,119L]}");
assertArg(arg, StringBinaryTag.stringBinaryTag("string"), "string");
assertArg(arg, StringBinaryTag.stringBinaryTag("string"), "\"string\"");
assertArg(arg, IntBinaryTag.intBinaryTag(44), "44");
assertArg(arg, IntArrayBinaryTag.intArrayBinaryTag(11, 49, 33), "[I;11,49,33]");
assertArg(arg, CompoundBinaryTag.builder().putLongArray("long_array", new long[]{12, 49, 119}).build(),
"{\"long_array\":[L;12L,49L,119L]}");
assertInvalidArg(arg, "\"unbalanced string");
assertInvalidArg(arg, "dd}");

View File

@ -2,19 +2,20 @@ package net.minestom.server.command;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.permission.Permission;
import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
public class CommandSenderTest {
@ -23,7 +24,7 @@ public class CommandSenderTest {
CommandSender sender = new SenderTest();
Permission permission = new Permission("permission.test", new NBTCompound());
Permission permission = new Permission("permission.test", CompoundBinaryTag.empty());
assertEquals(sender.getAllPermissions(), Set.of());

View File

@ -42,11 +42,14 @@ public class EntityMetaIntegrationTest {
assertEquals(2, packets.size());
validMetaDataPackets(packets, player.getEntityId(), entry -> {
final Object content = entry.value();
switch (entry.type()) {
case Metadata.TYPE_BYTE -> assertEquals((byte) 34, content);
case Metadata.TYPE_BOOLEAN -> assertTrue((boolean) content);
case Metadata.TYPE_POSE -> assertEquals(Entity.Pose.SNEAKING, content);
default -> Assertions.fail("Invalid MetaData entry");
if (entry.type() == Metadata.TYPE_BYTE) {
assertEquals((byte) 34, content);
} else if (entry.type() == Metadata.TYPE_BOOLEAN) {
assertTrue((boolean) content);
} else if (entry.type() == Metadata.TYPE_POSE) {
assertEquals(Entity.Pose.SNEAKING, content);
} else {
Assertions.fail("Invalid MetaData entry");
}
});
@ -58,11 +61,14 @@ public class EntityMetaIntegrationTest {
packets = incomingPackets.collect();
validMetaDataPackets(packets, player.getEntityId(), entry -> {
final Object content = entry.value();
switch (entry.type()) {
case Metadata.TYPE_BYTE -> assertTrue(content.equals((byte) 2) || content.equals((byte) 0));
case Metadata.TYPE_BOOLEAN -> assertFalse((boolean) content);
case Metadata.TYPE_POSE -> assertEquals(Entity.Pose.STANDING, content);
default -> Assertions.fail("Invalid MetaData entry");
if (entry.type() == Metadata.TYPE_BYTE) {
assertTrue(content.equals((byte) 2) || content.equals((byte) 0));
} else if (entry.type() == Metadata.TYPE_BOOLEAN) {
assertFalse((boolean) content);
} else if (entry.type() == Metadata.TYPE_POSE) {
assertEquals(Entity.Pose.STANDING, content);
} else {
Assertions.fail("Invalid MetaData entry");
}
});
// 4 changes, for two viewers

View File

@ -5,8 +5,13 @@ import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.instance.block.predicate.BlockPredicate;
import net.minestom.server.instance.block.predicate.BlockTypeFilter;
import net.minestom.server.instance.block.predicate.PropertiesPredicate;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.item.component.BlockPredicates;
import net.minestom.server.network.packet.client.play.ClientPlayerBlockPlacementPacket;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
@ -23,7 +28,7 @@ public class PlayerBlockPlacementIntegrationTest {
@ParameterizedTest
@MethodSource("placeBlockFromAdventureModeParams")
public void placeBlockFromAdventureMode(Block baseBlock, Block canPlaceOn, Env env) {
public void placeBlockFromAdventureMode(Block baseBlock, BlockPredicates canPlaceOn, Env env) {
var instance = env.createFlatInstance();
var connection = env.createConnection();
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
@ -31,7 +36,7 @@ public class PlayerBlockPlacementIntegrationTest {
instance.setBlock(2, 41, 0, baseBlock);
player.setGameMode(GameMode.ADVENTURE);
player.setItemInMainHand(ItemStack.builder(Material.WHITE_WOOL).meta(m -> m.canPlaceOn(canPlaceOn)).build());
player.setItemInMainHand(ItemStack.builder(Material.WHITE_WOOL).set(ItemComponent.CAN_PLACE_ON, canPlaceOn).build());
var packet = new ClientPlayerBlockPlacementPacket(
Player.Hand.MAIN, new Pos(2, 41, 0), BlockFace.WEST,
@ -42,16 +47,16 @@ public class PlayerBlockPlacementIntegrationTest {
player.interpretPacketQueue();
var placedBlock = instance.getBlock(1, 41, 0);
assertEquals("minecraft:white_wool", placedBlock.name());
}
private static Stream<Arguments> placeBlockFromAdventureModeParams() {
return Stream.of(
Arguments.of(Block.ACACIA_STAIRS.withProperty("facing", "south"), Block.ACACIA_STAIRS),
Arguments.of(Block.ACACIA_STAIRS, Block.ACACIA_STAIRS.withProperty("facing", "south")),
Arguments.of(Block.ACACIA_STAIRS.withProperty("facing", "south"), Block.ACACIA_STAIRS.withProperty("facing", "south")),
Arguments.of(Block.AMETHYST_BLOCK, Block.AMETHYST_BLOCK));
Arguments.of(Block.ACACIA_STAIRS.withProperty("facing", "south"), new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.ACACIA_STAIRS)))),
Arguments.of(Block.ACACIA_STAIRS, new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.ACACIA_STAIRS), PropertiesPredicate.exact("facing", "south"), null))),
Arguments.of(Block.ACACIA_STAIRS.withProperty("facing", "south"), new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.ACACIA_STAIRS), PropertiesPredicate.exact("facing", "south"), null))),
Arguments.of(Block.AMETHYST_BLOCK, new BlockPredicates(new BlockPredicate(new BlockTypeFilter.Blocks(Block.AMETHYST_BLOCK))))
);
}
}

View File

@ -1,18 +1,16 @@
package net.minestom.server.instance;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.tag.Tag;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.block.BlockUtils;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
@ -23,9 +21,9 @@ public class BlockClientNbtTest {
public void basic() {
assertNull(BlockUtils.extractClientNbt(Block.STONE));
assertNull(BlockUtils.extractClientNbt(Block.GRASS_BLOCK));
assertEquals(NBTCompound.EMPTY, BlockUtils.extractClientNbt(Block.CHEST));
assertEquals(CompoundBinaryTag.empty(), BlockUtils.extractClientNbt(Block.CHEST));
var nbt = NBT.Compound(Map.of("test", NBT.String("test")));
var nbt = CompoundBinaryTag.builder().putString("test", "test").build();
assertEquals(nbt, BlockUtils.extractClientNbt(Block.CHEST.withNbt(nbt)));
}
@ -43,11 +41,11 @@ public class BlockClientNbtTest {
}
};
var nbt = NBT.Compound(Map.of("test", NBT.String("test")));
var nbt = CompoundBinaryTag.builder().putString("test", "test").build();
assertNull(BlockUtils.extractClientNbt(Block.STONE.withNbt(nbt).withHandler(handler)));
assertEquals(nbt, BlockUtils.extractClientNbt(Block.CHEST.withNbt(nbt).withHandler(handler)));
assertEquals(nbt, BlockUtils.extractClientNbt(Block.CHEST
.withNbt(NBT.Compound(Map.of("test", NBT.String("test"), "test2", NBT.String("test"))))
.withNbt(CompoundBinaryTag.builder().putString("test", "test").putString("test2", "test2").build())
.withHandler(handler)));
}
}

View File

@ -1,11 +1,10 @@
package net.minestom.server.instance;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.block.Block;
import net.minestom.server.tag.Tag;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.Map;
@ -21,7 +20,7 @@ public class BlockTest {
assertFalse(block.hasNbt());
assertNull(block.nbt());
var nbt = new NBTCompound(Map.of("key", NBT.Int(5)));
var nbt = CompoundBinaryTag.builder().putInt("key", 5).build();
block = block.withNbt(nbt);
assertTrue(block.hasNbt());
assertEquals(block.nbt(), nbt);
@ -61,7 +60,7 @@ public class BlockTest {
@Test
public void testEquality() {
var nbt = new NBTCompound(Map.of("key", NBT.Int(5)));
var nbt = CompoundBinaryTag.builder().putInt("key", 5).build();
Block b1 = Block.CHEST;
Block b2 = Block.CHEST;
assertEquals(b1.withNbt(nbt), b2.withNbt(nbt));

View File

@ -1,14 +1,13 @@
package net.minestom.server.instance;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.instance.block.Block;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import net.minestom.server.instance.block.Block;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -21,7 +20,7 @@ public class GeneratorIntegrationTest {
@ValueSource(booleans = {false, true})
public void loader(boolean data, Env env) {
var manager = env.process().instance();
var block = data ? Block.STONE.withNbt(NBT.Compound(Map.of("key", NBT.String("value")))) : Block.STONE;
var block = data ? Block.STONE.withNbt(CompoundBinaryTag.builder().putString("key", "value").build()) : Block.STONE;
var instance = manager.createInstanceContainer();
instance.setGenerator(unit -> unit.modifier().fill(block));
instance.loadChunk(0, 0).join();

View File

@ -1,7 +1,7 @@
package net.minestom.server.instance;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.block.Block;
@ -10,12 +10,11 @@ import net.minestom.server.network.packet.server.play.BlockChangePacket;
import net.minestom.server.network.packet.server.play.BlockEntityDataPacket;
import net.minestom.server.tag.Tag;
import net.minestom.server.utils.NamespaceID;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import org.junit.jupiter.api.Test;
import java.io.StringReader;
import java.util.Collection;
import java.util.List;
@ -72,10 +71,10 @@ public class InstanceBlockPacketIntegrationTest {
assertEquals(Block.AIR, instance.getBlock(blockPoint));
final Block block;
final NBTCompound data;
final CompoundBinaryTag data;
try {
data = (NBTCompound) new SNBTParser(new StringReader("{\"GlowingText\":0B,\"Color\":\"black\",\"Text1\":\"{\\\"text\\\":\\\"wawsd\\\"}\"," +
"\"Text2\":\"{\\\"text\\\":\\\"\\\"}\",\"Text3\":\"{\\\"text\\\":\\\"\\\"}\",\"Text4\":\"{\\\"text\\\":\\\"\\\"}\"}")).parse();
data = (CompoundBinaryTag) TagStringIOExt.readTag("{\"GlowingText\":0B,\"Color\":\"black\",\"Text1\":\"{\\\"text\\\":\\\"wawsd\\\"}\"," +
"\"Text2\":\"{\\\"text\\\":\\\"\\\"}\",\"Text3\":\"{\\\"text\\\":\\\"\\\"}\",\"Text4\":\"{\\\"text\\\":\\\"\\\"}\"}");
block = Block.OAK_SIGN.withHandler(signHandler).withNbt(data);
} catch (Exception ex) {
throw new RuntimeException(ex);

View File

@ -6,23 +6,17 @@ import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.LightingChunk;
import net.minestom.server.instance.Section;
import net.minestom.server.instance.anvil.AnvilLoader;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.palette.Palette;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.jglrxavpok.hephaistos.mca.AnvilException;
import org.jglrxavpok.hephaistos.mca.BlockState;
import org.jglrxavpok.hephaistos.mca.ChunkSection;
import org.jglrxavpok.hephaistos.mca.RegionFile;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -32,7 +26,7 @@ public class LightParityIntegrationTest {
private static final int REGION_SIZE = 3;
@Test
public void test(Env env) throws URISyntaxException, IOException, AnvilException {
public void test(Env env) throws URISyntaxException, IOException {
Map<Vec, SectionEntry> sections = retrieveSections();
// Generate our own light
@ -130,45 +124,48 @@ public class LightParityIntegrationTest {
record SectionEntry(Palette blocks, byte[] sky, byte[] block) {
}
private static Map<Vec, SectionEntry> retrieveSections() throws IOException, URISyntaxException, AnvilException {
URL defaultImage = LightParityIntegrationTest.class.getResource("/net/minestom/server/instance/lighting/region/r.0.0.mca");
assert defaultImage != null;
File imageFile = new File(defaultImage.toURI());
var regionFile = new RegionFile(new RandomAccessFile(imageFile, "rw"),
0, 0, -64, 384);
private static Map<Vec, SectionEntry> retrieveSections() throws IOException, URISyntaxException {
Map<Vec, SectionEntry> sections = new HashMap<>();
// Read from anvil
for (int x = 1; x < REGION_SIZE - 1; x++) {
for (int z = 1; z < REGION_SIZE - 1; z++) {
var chunk = regionFile.getChunk(x, z);
if (chunk == null) continue;
for (int yLevel = chunk.getMinY(); yLevel <= chunk.getMaxY(); yLevel += 16) {
var section = chunk.getSection((byte) (yLevel/16));
var palette = loadBlocks(section);
var sky = section.getSkyLights();
var block = section.getBlockLights();
sections.put(new Vec(x, section.getY(), z), new SectionEntry(palette, sky, block));
}
}
}
return sections;
//todo
// URL defaultImage = LightParityIntegrationTest.class.getResource("/net/minestom/server/instance/lighting/region/r.0.0.mca");
// assert defaultImage != null;
// File imageFile = new File(defaultImage.toURI());
// var regionFile = new RegionFile(new RandomAccessFile(imageFile, "rw"),
// 0, 0, -64, 384);
//
// Map<Vec, SectionEntry> sections = new HashMap<>();
// // Read from anvil
// for (int x = 1; x < REGION_SIZE - 1; x++) {
// for (int z = 1; z < REGION_SIZE - 1; z++) {
// var chunk = regionFile.getChunk(x, z);
// if (chunk == null) continue;
//
// for (int yLevel = chunk.getMinY(); yLevel <= chunk.getMaxY(); yLevel += 16) {
// var section = chunk.getSection((byte) (yLevel/16));
// var palette = loadBlocks(section);
// var sky = section.getSkyLights();
// var block = section.getBlockLights();
// sections.put(new Vec(x, section.getY(), z), new SectionEntry(palette, sky, block));
// }
// }
// }
// return sections;
throw new UnsupportedOperationException("todo");
}
private static Palette loadBlocks(ChunkSection section) throws AnvilException {
var palette = Palette.blocks();
for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
final BlockState blockState = section.get(x, y, z);
String blockName = blockState.getName();
Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName), blockName)
.withProperties(blockState.getProperties());
palette.set(x, y, z, block.stateId());
}
}
}
return palette;
}
// private static Palette loadBlocks(ChunkSection section) throws AnvilException {
// var palette = Palette.blocks();
// for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
// for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
// for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
// final BlockState blockState = section.get(x, y, z);
// String blockName = blockState.getName();
// Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName), blockName)
// .withProperties(blockState.getProperties());
// palette.set(x, y, z, block.stateId());
// }
// }
// }
// return palette;
// }
}

View File

@ -15,7 +15,7 @@ public class ItemAirTest {
assertEquals(emptyItem, ItemStack.AIR, "AIR item can be compared to empty item");
assertSame(emptyItem, ItemStack.AIR, "AIR item identity can be compared to empty item");
assertSame(ItemStack.AIR, ItemStack.fromNBT(Material.DIAMOND, null, 0));
assertSame(ItemStack.AIR, ItemStack.of(Material.DIAMOND, 0));
assertSame(ItemStack.AIR, ItemStack.builder(Material.DIAMOND).amount(0).build());
}
}

View File

@ -1,63 +0,0 @@
package net.minestom.server.item;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.entity.attribute.AttributeOperation;
import net.minestom.server.item.attribute.AttributeSlot;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.tag.TagHandler;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.UUID;
import static net.minestom.testing.TestUtils.assertEqualsSNBT;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ItemAttributeTest {
@Test
public void attribute() {
var attributes = List.of(new ItemAttribute(
new UUID(0, 0), "generic.attack_damage", Attribute.ATTACK_DAMAGE,
AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND));
var item = ItemStack.builder(Material.STICK)
.meta(builder -> builder.attributes(attributes))
.build();
assertEquals(attributes, item.meta().getAttributes());
}
@Test
public void attributeReader() {
var attributes = List.of(new ItemAttribute(
new UUID(0, 0), "generic.attack_damage", Attribute.ATTACK_DAMAGE,
AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND));
TagHandler handler = TagHandler.newHandler();
handler.setTag(ItemTags.ATTRIBUTES, attributes);
var item = ItemStack.fromNBT(Material.STICK, handler.asCompound());
assertEquals(attributes, item.meta().getAttributes());
}
@Test
public void attributeNbt() {
var item = ItemStack.builder(Material.STICK)
.meta(builder -> builder.attributes(
List.of(new ItemAttribute(
new UUID(0, 0), "generic.attack_damage", Attribute.ATTACK_DAMAGE,
AttributeOperation.ADDITION, 2, AttributeSlot.MAINHAND))))
.build();
assertEqualsSNBT("""
{"AttributeModifiers":[
{
"Amount":2.0D,
"UUID":[I;0,0,0,0],
"Slot":"mainhand",
"Operation":0,
"AttributeName":"generic.attack_damage",
"Name":"generic.attack_damage"
}
]}
""", item.meta().toNBT());
}
}

View File

@ -1,68 +0,0 @@
package net.minestom.server.item;
import net.minestom.server.instance.block.Block;
import org.junit.jupiter.api.Test;
import static net.minestom.testing.TestUtils.assertEqualsSNBT;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ItemBlockTest {
@Test
public void canPlace() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canPlaceOn(Block.STONE))
.build();
assertTrue(item.meta().getCanPlaceOn().contains(Block.STONE.name()));
assertTrue(item.meta().canPlaceOn(Block.STONE));
}
@Test
public void canPlaceNbt() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canPlaceOn(Block.STONE))
.build();
assertEqualsSNBT("""
{"CanPlaceOn":["minecraft:stone"]}
""", item.meta().toNBT());
}
@Test
public void canPlaceMismatchProperties() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canPlaceOn(Block.SANDSTONE_STAIRS.withProperty("facing", "south")))
.build();
assertTrue(item.meta().getCanPlaceOn().contains(Block.SANDSTONE_STAIRS.name()));
assertTrue(item.meta().getCanPlaceOn().contains(Block.SANDSTONE_STAIRS.withProperty("facing", "south").name()));
assertTrue(item.meta().canPlaceOn(Block.SANDSTONE_STAIRS.withProperty("facing", "south")));
}
@Test
public void canDestroy() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canDestroy(Block.STONE))
.build();
assertTrue(item.meta().getCanDestroy().contains(Block.STONE.name()));
assertTrue(item.meta().canDestroy(Block.STONE));
}
@Test
public void canDestroyNbt() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canDestroy(Block.STONE))
.build();
assertEqualsSNBT("""
{"CanDestroy":["minecraft:stone"]}
""", item.meta().toNBT());
}
@Test
public void canDestroyMismatchProperties() {
var item = ItemStack.builder(Material.STONE)
.meta(builder -> builder.canDestroy(Block.SANDSTONE_STAIRS.withProperty("facing", "south")))
.build();
assertTrue(item.meta().getCanDestroy().contains(Block.SANDSTONE_STAIRS.name()));
assertTrue(item.meta().getCanDestroy().contains(Block.SANDSTONE_STAIRS.withProperty("facing", "south").name()));
assertTrue(item.meta().canDestroy(Block.SANDSTONE_STAIRS.withProperty("facing", "south")));
}
}

View File

@ -1,55 +0,0 @@
package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jglrxavpok.hephaistos.nbt.NBTString;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
public class ItemDisplayTest {
@Test
public void lore() {
var item = ItemStack.of(Material.DIAMOND_SWORD);
assertEquals(List.of(), item.getLore());
assertNull(item.meta().toNBT().get("display"));
{
var lore = List.of(Component.text("Hello"));
item = item.withLore(lore);
assertEquals(lore, item.getLore());
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
assertNotNull(loreNbt);
assertEquals(1, loreNbt.getSize());
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
}
{
var lore = List.of(Component.text("Hello"), Component.text("World"));
item = item.withLore(lore);
assertEquals(lore, item.getLore());
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
assertNotNull(loreNbt);
assertEquals(2, loreNbt.getSize());
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
}
{
var lore = Stream.of("string test").map(Component::text).toList();
item = item.withLore(lore);
assertEquals(lore, item.getLore());
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
assertNotNull(loreNbt);
assertEquals(1, loreNbt.getSize());
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
}
// Ensure that lore can be properly removed without residual (display compound)
item = item.withLore(List.of());
assertNull(item.meta().toNBT().get("display"));
}
}

View File

@ -1,40 +0,0 @@
package net.minestom.server.item;
import net.minestom.server.item.enchant.Enchantment;
import org.junit.jupiter.api.Test;
import java.util.Map;
public class ItemEnchantTest {
@Test
public void enchant() {
var item = ItemStack.of(Material.DIAMOND_SWORD);
var enchantments = item.meta().getEnchantmentMap();
assertTrue(enchantments.isEmpty(), "items do not have enchantments by default");
item = item.withMeta(meta -> meta.enchantment(Enchantment.EFFICIENCY, (short) 10));
enchantments = item.meta().getEnchantmentMap();
assertEquals(enchantments.size(), 1);
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
item = item.withMeta(meta -> meta.enchantment(Enchantment.INFINITY, (short) 5));
enchantments = item.meta().getEnchantmentMap();
assertEquals(enchantments.size(), 2);
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
assertEquals(enchantments.get(Enchantment.INFINITY), (short) 5);
item = item.withMeta(meta -> meta.enchantments(Map.of()));
enchantments = item.meta().getEnchantmentMap();
assertTrue(enchantments.isEmpty());
// Ensure that enchantments can still be modified after being emptied
item = item.withMeta(meta -> meta.enchantment(Enchantment.EFFICIENCY, (short) 10));
enchantments = item.meta().getEnchantmentMap();
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
item = item.withMeta(ItemMeta.Builder::clearEnchantment);
enchantments = item.meta().getEnchantmentMap();
assertTrue(enchantments.isEmpty());
}
}

View File

@ -1,57 +0,0 @@
package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.item.metadata.BundleMeta;
import net.minestom.server.item.metadata.PlayerHeadMeta;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.Test;
import java.util.Map;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class ItemMetaTest {
@Test
public void defaultMeta() {
var item = ItemStack.builder(Material.BUNDLE).build();
assertNotNull(item.meta());
}
@Test
public void fromNBT() {
var compound = NBT.Compound(Map.of("value", NBT.Int(5)));
var item = ItemStack.builder(Material.BUNDLE).meta(compound).build();
assertEquals(compound, item.meta().toNBT());
}
@Test
public void bundle() {
var item = ItemStack.builder(Material.BUNDLE)
.meta(BundleMeta.class, bundleMetaBuilder -> {
bundleMetaBuilder.addItem(ItemStack.of(Material.DIAMOND, 5));
bundleMetaBuilder.addItem(ItemStack.of(Material.RABBIT_FOOT, 5));
})
.build();
assertEquals(2, item.meta(BundleMeta.class).getItems().size());
}
@Test
public void buildView() {
var uuid = UUID.randomUUID();
var skin = new PlayerSkin("xx", "yy");
var meta = new PlayerHeadMeta.Builder()
.skullOwner(uuid)
.playerSkin(skin)
.build();
var item = ItemStack.builder(Material.PLAYER_HEAD)
.meta(meta)
.displayName(Component.text("Name"))
.build();
PlayerHeadMeta view = item.meta(PlayerHeadMeta.class);
assertEquals(uuid, view.getSkullOwner());
assertEquals(skin, view.getPlayerSkin());
}
}

View File

@ -1,20 +0,0 @@
package net.minestom.server.item;
import net.minestom.server.item.metadata.BundleMeta;
import net.minestom.server.tag.TagHandler;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
public class ItemMetaViewTest {
@Test
public void viewType() {
assertEquals(BundleMeta.Builder.class, ItemMetaViewImpl.viewType(BundleMeta.class));
}
@Test
public void construct() {
assertInstanceOf(BundleMeta.Builder.class, ItemMetaViewImpl.constructBuilder(BundleMeta.class, TagHandler.newHandler()));
}
}

View File

@ -1,209 +0,0 @@
package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.item.metadata.WrittenBookMeta;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import org.junit.jupiter.api.Test;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.fail;
public class ItemMetaWrittenBookTest {
@Test
public void testPages() {
ItemStack book = ItemStack.of(Material.WRITTEN_BOOK);
Component pageA = Component.text("Page A");
Component pageB = Component.text("Page B").append(Component.newline()).append(Component.text("Page B"));
Component pageC = Component.text("Page C");
List<Component> originalPages = List.of(pageA, pageB, pageC);
book = book.withMeta(WrittenBookMeta.class, meta -> meta.pages(List.of(pageA, pageB, pageC)));
WrittenBookMeta meta = book.meta(WrittenBookMeta.class);
assertEquals(originalPages, meta.getPages(), "written book output meta must equal original input meta");
}
@Test
public void testStyleComponents() {
List<TextColor> colors = List.of(NamedTextColor.BLUE, NamedTextColor.WHITE, NamedTextColor.DARK_BLUE);
List<TextDecoration> decorations = List.of(TextDecoration.UNDERLINED, TextDecoration.STRIKETHROUGH, TextDecoration.ITALIC);
List<ClickEvent> clicks = List.of(
ClickEvent.runCommand("/test"),
ClickEvent.openUrl("https://minestom.net"),
ClickEvent.changePage(2),
ClickEvent.openFile("/test"),
ClickEvent.copyToClipboard("clipboard text"),
ClickEvent.suggestCommand("/test"));
List<HoverEvent<?>> hovers = List.of(
HoverEvent.showText(Component.text("Hover text")),
ItemStack.of(Material.STONE).asHoverEvent(),
HoverEvent.showText(Component.text("Hover text")),
HoverEvent.showText(Component.text("Hover text")));
for (TextColor color : colors) {
for (TextDecoration decoration : decorations) {
for (ClickEvent click : clicks) {
for (HoverEvent<?> hover : hovers) {
Component pageA = Component.text("Page A").style(style -> {
style.color(color);
style.decoration(decoration, true);
style.clickEvent(click);
style.hoverEvent(hover);
});
Component pageB = Component.text("test");
List<Component> originalPages = List.of(pageA, pageB);
ItemStack book = ItemStack.of(Material.WRITTEN_BOOK)
.withMeta(WrittenBookMeta.class, meta -> meta.pages(pageA, pageB));
WrittenBookMeta meta = book.meta(WrittenBookMeta.class);
assertEquals(originalPages, meta.getPages(), "written book output meta must equal original input meta");
}
}
}
}
}
@Test
public void buildFromVanillSNBT() {
String vanillaSNBT = """
{
pages:['[
"",
{"text":"\\\\n\\\\ntest\\\\n\\\\n"},
{"text":"TESTSETSET","clickEvent":{"action":"run_command","value":"hi"}},
{"text":"\\\\n\\\\n"},
{"text":"COLORS","color":"dark_red"},
{"text":"\\\\n\\\\n","color":"reset"},
{"text":"EVERYTHING","bold":true,"italic":true,"strikethrough":true,"underlined":true,"obfuscated":true,
"color":"green","clickEvent":{"action":"run_command","value":"EVERYTHING"},
"hoverEvent":{"action":"show_text","contents":"Test"}}
]'],
title:"Minestom Book",
author:"https://minestom.net/",
display:{Lore:["Minestom."]}
}
""";
SNBTParser parser = new SNBTParser(new StringReader(vanillaSNBT));
ItemStack book = ItemStack.of(Material.WRITTEN_BOOK).withMeta(WrittenBookMeta.class, meta -> {
List<Component> pageA = new ArrayList<>();
pageA.add(Component.newline());
pageA.add(Component.newline());
pageA.add(Component.text("test"));
pageA.add(Component.newline());
pageA.add(Component.newline());
pageA.add(Component.text("TESTSETSET").clickEvent(ClickEvent.runCommand("hi")));
pageA.add(Component.newline());
pageA.add(Component.newline());
pageA.add(Component.text("COLORS").color(NamedTextColor.DARK_RED));
pageA.add(Component.newline());
pageA.add(Component.newline());
pageA.add(Component.text("EVERYTHING").style(style -> {
style.decoration(TextDecoration.BOLD, true);
style.decoration(TextDecoration.ITALIC, true);
style.decoration(TextDecoration.UNDERLINED, true);
style.decoration(TextDecoration.STRIKETHROUGH, true);
style.decoration(TextDecoration.OBFUSCATED, true);
style.color(NamedTextColor.GREEN);
style.clickEvent(ClickEvent.runCommand("EVERYTHING"));
style.hoverEvent(HoverEvent.showText(Component.text("Test")));
}));
Component firstPage = pageA.stream().reduce(Component::append).orElseGet(Component::empty);
meta.pages(firstPage.compact());
meta.title(Component.text("Minestom Book"));
meta.author(Component.text("https://minestom.net/"));
meta.lore(Component.text("Minestom."));
});
try {
NBTCompound nbt = (NBTCompound) parser.parse();
ItemStack vanillaBook = ItemStack.fromNBT(Material.WRITTEN_BOOK, nbt);
var pagesA = vanillaBook.meta(WrittenBookMeta.class).getPages();
var pagesB = book.meta(WrittenBookMeta.class).getPages();
// Compact the components to ensure they are the same format.
pagesA = pagesA.stream().map(Component::compact).toList();
pagesB = pagesB.stream().map(Component::compact).toList();
assertEquals(pagesA, pagesB, "written book output meta must equal original input meta");
} catch (Throwable e) {
fail(e);
}
}
// TODO: Compare to vanilla snbt. This depends on a modern serializer that defaults to legacy in some properties.
// @Test
// public void compareToVanilla() {
// String vanillaSNBT = """
//{
// pages:['[
// "",
// {"text":"\\\\n\\\\ntest\\\\n\\\\n"},
// {"text":"TESTSETSET","clickEvent":{"action":"run_command","value":"hi"}},
// {"text":"\\\\n\\\\n"},
// {"text":"COLORS","color":"dark_red"},
// {"text":"\\\\n\\\\n","color":"reset"},
// {"text":"EVERYTHING","bold":true,"italic":true,"strikethrough":true,"underlined":true,"obfuscated":true,
// "color":"green","clickEvent":{"action":"run_command","value":"EVERYTHING"},
// "hoverEvent":{"action":"show_text","contents":"Test"}}
// ]'],
// title:"Minestom Book",
// author:"https://minestom.net/",
// display:{Lore:["Minestom."]}
//}
// """;
//
// ItemStack book = ItemStack.of(Material.WRITTEN_BOOK).withMeta(WrittenBookMeta.class, meta -> {
// List<Component> pageA = new ArrayList<>();
//
// pageA.add(Component.newline());
// pageA.add(Component.newline());
// pageA.add(Component.text("test"));
// pageA.add(Component.newline());
// pageA.add(Component.newline());
// pageA.add(Component.text("TESTSETSET").clickEvent(ClickEvent.runCommand("hi")));
// pageA.add(Component.newline());
// pageA.add(Component.newline());
// pageA.add(Component.text("COLORS").color(NamedTextColor.DARK_RED));
// pageA.add(Component.newline());
// pageA.add(Component.newline());
// pageA.add(Component.text("EVERYTHING").style(style -> {
// style.decoration(TextDecoration.BOLD, true);
// style.decoration(TextDecoration.ITALIC, true);
// style.decoration(TextDecoration.UNDERLINED, true);
// style.decoration(TextDecoration.STRIKETHROUGH, true);
// style.decoration(TextDecoration.OBFUSCATED, true);
// style.color(NamedTextColor.GREEN);
// style.clickEvent(ClickEvent.runCommand("EVERYTHING"));
// style.hoverEvent(HoverEvent.showText(Component.text("Test")));
// }));
//
// Component firstPage = pageA.stream().reduce(Component::append).orElseGet(() -> Component.empty());
//
// meta.pages(firstPage.compact());
// meta.title(Component.text("Minestom Book"));
// meta.author(Component.text("https://minestom.net/"));
// meta.lore(Component.text("Minestom."));
// });
//
// TestUtils.assertEqualsSNBT(vanillaSNBT, (NBTCompound) book.toItemNBT().get("tag"));
// }
}

View File

@ -3,10 +3,11 @@ package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.component.EnchantmentList;
import net.minestom.server.item.enchant.Enchantment;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@ -18,10 +19,20 @@ public class ItemTest {
var item = ItemStack.of(Material.DIAMOND_SWORD);
assertEquals(item.material(), Material.DIAMOND_SWORD, "Material must be the same");
assertEquals(item.amount(), 1, "Default item amount must be 1");
assertNull(item.getDisplayName(), "Default item display name must be null");
assertTrue(item.getLore().isEmpty(), "Default item lore must be empty");
// Should have the exact same components as the material prototype
var prototype = Material.DIAMOND_SWORD.registry().prototype();
for (ItemComponent<?> component : ItemComponent.values()) {
var proto = prototype.get(component);
if (proto == null) {
assertFalse(item.has(component), "Item should not have component " + component);
} else {
assertEquals(proto, item.get(component), "Item should have the same component as the prototype");
}
}
ItemStack finalItem = item;
assertThrows(Exception.class, () -> finalItem.getLore().add(Component.text("Hey!")), "Lore list cannot be modified directly");
assertThrows(UnsupportedOperationException.class, () -> finalItem.get(ItemComponent.LORE).add(Component.text("Hey!")), "Lore list cannot be modified directly");
item = item.withAmount(5);
assertEquals(item.amount(), 5, "Items with different amount should not be equals");
@ -33,10 +44,20 @@ public class ItemTest {
var item = ItemStack.builder(Material.DIAMOND_SWORD).build();
assertEquals(item.material(), Material.DIAMOND_SWORD, "Material must be the same");
assertEquals(item.amount(), 1, "Default item amount must be 1");
assertNull(item.getDisplayName(), "Default item display name must be null");
assertTrue(item.getLore().isEmpty(), "Default item lore must be empty");
// Should have the exact same components as the material prototype
var prototype = Material.DIAMOND_SWORD.registry().prototype();
for (ItemComponent<?> component : ItemComponent.values()) {
var proto = prototype.get(component);
if (proto == null) {
assertFalse(item.has(component), "Item should not have component " + component);
} else {
assertEquals(proto, item.get(component), "Item should have the same component as the prototype");
}
}
ItemStack finalItem = item;
assertThrows(Exception.class, () -> finalItem.getLore().add(Component.text("Hey!")), "Lore list cannot be modified directly");
assertThrows(UnsupportedOperationException.class, () -> finalItem.get(ItemComponent.LORE).add(Component.text("Hey!")), "Lore list cannot be modified directly");
item = item.withAmount(5);
assertEquals(item.amount(), 5, "Items with different amount should not be equals");
@ -52,17 +73,7 @@ public class ItemTest {
assertTrue(item1.isSimilar(item2));
assertTrue(item1.withAmount(5).isSimilar(item2.withAmount(2)));
assertFalse(item1.isSimilar(item2.withDisplayName(Component.text("Hey!"))));
}
@Test
public void testItemNbt() {
var itemNbt = createItem().toItemNBT();
assertEquals(itemNbt.getString("id"), createItem().material().name(), "id string should be the material name");
assertEquals(itemNbt.getByte("Count"), (byte) createItem().amount(), "Count byte should be the item amount");
var metaNbt = itemNbt.getCompound("tag");
var metaNbt2 = createItem().meta().toNBT();
assertEquals(metaNbt, metaNbt2, "tag compound should be equal to the meta nbt");
assertFalse(item1.isSimilar(item2.with(ItemComponent.CUSTOM_NAME, Component.text("Hey!"))));
}
@Test
@ -71,33 +82,31 @@ public class ItemTest {
var item = ItemStack.fromItemNBT(itemNbt);
assertEquals(createItem(), item, "Items must be equal if created from the same item nbt");
assertEquals(itemNbt, item.toItemNBT(), "Item nbt must be equal back");
var metaNbt = createItem().meta().toNBT();
item = ItemStack.fromNBT(createItem().material(), metaNbt, createItem().amount());
assertEquals(createItem(), item, "Items must be equal if created from the same meta nbt");
}
@Test
public void testBuilderReuse() {
var builder = ItemStack.builder(Material.DIAMOND);
var item1 = builder.build();
var item2 = builder.displayName(Component.text("Name")).build();
assertNull(item1.getDisplayName());
assertNotNull(item2.getDisplayName());
var item2 = builder.set(ItemComponent.CUSTOM_NAME, Component.text("Name")).build();
assertNull(item1.get(ItemComponent.CUSTOM_NAME));
assertNotNull(item2.get(ItemComponent.CUSTOM_NAME));
assertNotEquals(item1, item2, "Item builder should be reusable");
}
@Test
public void materialUpdate() {
var nbt = NBT.Compound(Map.of("key", NBT.String("value")));
var item1 = ItemStack.fromNBT(Material.DIAMOND, nbt, 5);
var item1 = ItemStack.builder(Material.DIAMOND)
.amount(5).set(ItemComponent.CUSTOM_NAME, Component.text("Name"))
.build();
var item2 = item1.withMaterial(Material.GOLD_INGOT);
assertEquals(Material.DIAMOND, item1.material());
assertEquals(Material.GOLD_INGOT, item2.material());
assertEquals(nbt, item1.meta().toNBT());
assertEquals(nbt, item2.meta().toNBT());
var nbt1 = item1.toItemNBT().remove("id");
var nbt2 = item2.toItemNBT().remove("id");
assertEquals(nbt1, nbt2);
assertEquals(5, item1.amount());
assertEquals(5, item2.amount());
@ -121,11 +130,9 @@ public class ItemTest {
static ItemStack createItem() {
return ItemStack.builder(Material.STONE)
.displayName(Component.text("Display name!", NamedTextColor.GREEN))
.lore(Component.text("Line 1"), Component.text("Line 2"))
.meta(metaBuilder ->
metaBuilder.enchantment(Enchantment.EFFICIENCY, (short) 10)
.hideFlag(ItemHideFlag.HIDE_ENCHANTS))
.set(ItemComponent.CUSTOM_NAME, Component.text("Display name!", NamedTextColor.GREEN))
.set(ItemComponent.LORE, List.of(Component.text("Line 1"), Component.text("Line 2")))
.set(ItemComponent.ENCHANTMENTS, new EnchantmentList(Map.of(Enchantment.EFFICIENCY, 10), false))
.build();
}
}

View File

@ -1,6 +1,8 @@
package net.minestom.server.network;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
@ -12,6 +14,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static net.kyori.adventure.nbt.IntBinaryTag.intBinaryTag;
import static net.minestom.server.network.NetworkBuffer.*;
import static org.junit.jupiter.api.Assertions.*;
@ -237,8 +240,8 @@ public class NetworkBufferTest {
@Test
public void nbt() {
assertBufferType(NetworkBuffer.NBT, org.jglrxavpok.hephaistos.nbt.NBT.Int(5));
assertBufferType(NetworkBuffer.NBT, org.jglrxavpok.hephaistos.nbt.NBT.Compound(Map.of("key", org.jglrxavpok.hephaistos.nbt.NBT.Int(5))));
assertBufferType(NetworkBuffer.NBT, intBinaryTag(5));
assertBufferType(NetworkBuffer.NBT, CompoundBinaryTag.from(Map.of("key", intBinaryTag(5))));
}
@Test
@ -254,9 +257,9 @@ public class NetworkBufferTest {
@Test
public void item() {
assertBufferType(ITEM, ItemStack.AIR);
assertBufferType(ITEM, ItemStack.of(Material.STONE, 1));
assertBufferType(ITEM, ItemStack.of(Material.DIAMOND_AXE, 1).withMeta(builder -> builder.damage(1)));
assertBufferType(ItemStack.NETWORK_TYPE, ItemStack.AIR);
assertBufferType(ItemStack.NETWORK_TYPE, ItemStack.of(Material.STONE, 1));
assertBufferType(ItemStack.NETWORK_TYPE, ItemStack.of(Material.DIAMOND_AXE, 1).with(ItemComponent.DAMAGE, 1));
}
@Test

View File

@ -2,6 +2,7 @@ package net.minestom.server.network;
import com.google.gson.JsonObject;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EquipmentSlot;
@ -22,7 +23,6 @@ import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.packet.server.play.DeclareRecipesPacket.Ingredient;
import net.minestom.server.network.packet.server.status.ResponsePacket;
import net.minestom.server.recipe.RecipeCategory;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -65,7 +65,7 @@ public class PacketWriteReadTest {
SERVER_PACKETS.add(new BlockActionPacket(VEC, (byte) 5, (byte) 5, 5));
SERVER_PACKETS.add(new BlockBreakAnimationPacket(5, VEC, (byte) 5));
SERVER_PACKETS.add(new BlockChangePacket(VEC, 0));
SERVER_PACKETS.add(new BlockEntityDataPacket(VEC, 5, NBT.Compound(Map.of("key", NBT.String("value")))));
SERVER_PACKETS.add(new BlockEntityDataPacket(VEC, 5, CompoundBinaryTag.builder().putString("key", "value").build()));
SERVER_PACKETS.add(new BossBarPacket(UUID.randomUUID(), new BossBarPacket.AddAction(COMPONENT, 5f, BossBar.Color.BLUE, BossBar.Overlay.PROGRESS, (byte) 2)));
SERVER_PACKETS.add(new BossBarPacket(UUID.randomUUID(), new BossBarPacket.RemoveAction()));
SERVER_PACKETS.add(new BossBarPacket(UUID.randomUUID(), new BossBarPacket.UpdateHealthAction(5f)));
@ -157,7 +157,7 @@ public class PacketWriteReadTest {
@BeforeAll
public static void setupClient() {
CLIENT_PACKETS.add(new ClientHandshakePacket(755, "localhost", 25565, 2));
CLIENT_PACKETS.add(new ClientHandshakePacket(755, "localhost", 25565, ClientHandshakePacket.Intent.LOGIN));
}
@Test

View File

@ -41,14 +41,14 @@ public class ParticleDataTest {
@Test
public void testParticleValid() {
var particle = Particle.AMBIENT_ENTITY_EFFECT;
var particle = Particle.ENTITY_EFFECT;
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertDoesNotThrow(() -> packet.write(new NetworkBuffer()));
}
@Test
public void testParticleData() {
var particle = Particle.AMBIENT_ENTITY_EFFECT;
var particle = Particle.ENTITY_EFFECT;
ParticlePacket packet = new ParticlePacket(particle, true, 0, 0, 0, 0, 0, 0, 0, 0);
assertDoesNotThrow(() -> packet.write(new NetworkBuffer()));
}

View File

@ -1,8 +1,8 @@
package net.minestom.server.permission;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -35,10 +35,10 @@ public class TestPermissions {
};
permission1 = new Permission("perm.name",
NBT.Compound(nbt -> {
nbt.setString("name", "Minestom");
nbt.setInt("amount", 5);
})
CompoundBinaryTag.builder()
.putString("name", "Minestom")
.putInt("amount", 5)
.build()
);
permission2 = new Permission("perm.name2");

View File

@ -103,7 +103,7 @@ public class TagItemTest {
{
"item": {
"id":"minecraft:diamond",
"Count":1B
"count":1
}
}
""", handler.asCompound());

View File

@ -1,12 +1,10 @@
package net.minestom.server.tag;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import net.kyori.adventure.nbt.*;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -16,49 +14,49 @@ public class TagNbtSeparatorTest {
@Test
public void primitives() {
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Byte("key"), (byte) 1),
"key", NBT.Byte(1));
"key", ByteBinaryTag.byteBinaryTag((byte) 1));
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Short("key"), (short) 1),
"key", NBT.Short(1));
"key", ShortBinaryTag.shortBinaryTag((short) 1));
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Integer("key"), 1),
"key", NBT.Int(1));
"key", IntBinaryTag.intBinaryTag(1));
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Long("key"), 1L),
"key", NBT.Long(1));
"key", LongBinaryTag.longBinaryTag(1));
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Float("key"), 1f),
"key", NBT.Float(1));
"key", FloatBinaryTag.floatBinaryTag(1));
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Double("key"), 1d),
"key", NBT.Double(1));
"key", DoubleBinaryTag.doubleBinaryTag(1));
}
@Test
public void compound() {
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Byte("key").path("path"), (byte) 1),
"path", NBT.Compound(Map.of("key", NBT.Byte(1))));
"path", CompoundBinaryTag.builder().putByte("key", (byte) 1).build());
}
@Test
public void compoundMultiple() {
assertSeparation(Set.of(new TagNbtSeparator.Entry<>(Tag.Byte("key").path("path"), (byte) 1),
new TagNbtSeparator.Entry<>(Tag.Integer("key2").path("path"), 2)),
"path", NBT.Compound(Map.of("key", NBT.Byte(1), "key2", NBT.Int(2))));
"path", CompoundBinaryTag.builder().putByte("key", (byte) 1).putInt("key2", 2).build());
}
@Test
public void list() {
assertSeparation(new TagNbtSeparator.Entry<>(Tag.Integer("key").list(), List.of(1)),
"key", NBT.List(NBTType.TAG_Int, NBT.Int(1)));
"key", ListBinaryTag.listBinaryTag(BinaryTagTypes.INT, List.of(IntBinaryTag.intBinaryTag(1))));
}
void assertSeparation(Set<TagNbtSeparator.Entry<?>> expected, String key, NBT nbt) {
void assertSeparation(Set<TagNbtSeparator.Entry<?>> expected, String key, BinaryTag nbt) {
assertEquals(expected, retrieve(key, nbt));
}
void assertSeparation(TagNbtSeparator.Entry<?> expected, String key, NBT nbt) {
void assertSeparation(TagNbtSeparator.Entry<?> expected, String key, BinaryTag nbt) {
var entries = retrieve(key, nbt);
assertEquals(1, entries.size());
assertEquals(expected, entries.iterator().next());
}
Set<TagNbtSeparator.Entry<?>> retrieve(String key, NBT nbt) {
Set<TagNbtSeparator.Entry<?>> retrieve(String key, BinaryTag nbt) {
Set<TagNbtSeparator.Entry<?>> entries = new HashSet<>();
TagNbtSeparator.separate(key, nbt, entries::add);
return Set.copyOf(entries);

View File

@ -1,13 +1,17 @@
package net.minestom.server.tag;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTInt;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.IntBinaryTag;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static net.kyori.adventure.nbt.IntArrayBinaryTag.intArrayBinaryTag;
import static net.kyori.adventure.nbt.IntBinaryTag.intBinaryTag;
import static net.kyori.adventure.nbt.ListBinaryTag.listBinaryTag;
import static net.minestom.testing.TestUtils.assertEqualsSNBT;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
@ -21,7 +25,7 @@ public class TagNbtTest {
public void list() {
var handler = TagHandler.newHandler();
var tag = Tag.NBT("nbt").list();
List<NBT> list = List.of(NBT.Int(1), NBT.Int(2), NBT.Int(3));
List<BinaryTag> list = List.of(intBinaryTag(1), intBinaryTag(2), intBinaryTag(3));
handler.setTag(tag, list);
assertEquals(list, handler.getTag(tag));
assertEqualsSNBT("""
@ -37,7 +41,7 @@ public class TagNbtTest {
@Test
public void map() {
var handler = TagHandler.newHandler();
var tag = Tag.NBT("nbt").map(nbt -> ((NBTInt) nbt).getValue(), NBT::Int);
var tag = Tag.NBT("nbt").map(nbt -> ((IntBinaryTag) nbt).value(), IntBinaryTag::intBinaryTag);
handler.setTag(tag, 5);
assertEquals(5, handler.getTag(tag));
assertEqualsSNBT("""
@ -52,7 +56,7 @@ public class TagNbtTest {
@Test
public void fromCompoundModify() {
var compound = NBT.Compound(Map.of("key", NBT.Int(5)));
var compound = CompoundBinaryTag.builder().putInt("key", 5).build();
var handler = TagHandler.fromCompound(compound);
assertEquals(compound, handler.asCompound());
assertEqualsSNBT("""
@ -72,7 +76,7 @@ public class TagNbtTest {
@Test
public void fromCompoundModifyPath() {
var compound = NBT.Compound(Map.of("path", NBT.Compound(Map.of("key", NBT.Int(5)))));
var compound = CompoundBinaryTag.builder().put("path", CompoundBinaryTag.builder().putInt("key", 5).build()).build();
var handler = TagHandler.fromCompound(compound);
var tag = Tag.Integer("key").path("path");
@ -89,8 +93,8 @@ public class TagNbtTest {
@Test
public void fromCompoundModifyDoublePath() {
var compound = NBT.Compound(Map.of("path", NBT.Compound(Map.of("path2",
NBT.Compound(Map.of("key", NBT.Int(5)))))));
var compound = CompoundBinaryTag.builder().put("path", CompoundBinaryTag.builder()
.put("path2", CompoundBinaryTag.builder().putInt("key", 5).build()).build()).build();
var handler = TagHandler.fromCompound(compound);
var tag = Tag.Integer("key").path("path", "path2");
@ -110,8 +114,8 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("path1");
var nbt1 = NBT.Compound(Map.of("key", NBT.Int(5)));
var nbt2 = NBT.Compound(Map.of("other-key", NBT.Int(5)));
var nbt1 = CompoundBinaryTag.from(Map.of("key", intBinaryTag(5)));
var nbt2 = CompoundBinaryTag.from(Map.of("other-key", intBinaryTag(5)));
handler.setTag(nbtTag, nbt1);
assertEquals(nbt1, handler.getTag(nbtTag));
@ -124,7 +128,7 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("path1");
var nbt = NBT.Compound(Map.of("key", NBT.Int(5)));
var nbt = CompoundBinaryTag.from(Map.of("key", intBinaryTag(5)));
handler.setTag(nbtTag, nbt);
assertEquals(nbt, handler.getTag(nbtTag));
@ -137,7 +141,7 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("compound").path("path");
var nbt = NBT.Compound(Map.of("key", NBT.Int(5)));
var nbt = CompoundBinaryTag.from(Map.of("key", intBinaryTag(5)));
handler.setTag(nbtTag, nbt);
assertEquals(nbt, handler.getTag(nbtTag));
@ -150,7 +154,7 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("path1");
var nbt = NBT.Compound(Map.of("path2", NBT.Compound(Map.of("key", NBT.Int(5)))));
var nbt = CompoundBinaryTag.from(Map.of("path2", CompoundBinaryTag.from(Map.of("key", intBinaryTag(5)))));
handler.setTag(nbtTag, nbt);
assertEquals(nbt, handler.getTag(nbtTag));
@ -163,21 +167,21 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("path1");
var nbt = NBT.Compound(Map.of("key", NBT.Int(5)));
var nbt = CompoundBinaryTag.from(Map.of("key", intBinaryTag(5)));
handler.setTag(nbtTag, nbt);
assertEquals(nbt, handler.getTag(nbtTag));
var path = Tag.Integer("key").path("path1");
handler.setTag(path, 10);
assertEquals(10, handler.getTag(path));
assertEquals(NBT.Compound(Map.of("key", NBT.Int(10))), handler.getTag(nbtTag));
assertEquals(CompoundBinaryTag.from(Map.of("key", intBinaryTag(10))), handler.getTag(nbtTag));
}
@Test
public void rawList() {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("list");
var list = NBT.List(NBTType.TAG_Int, NBT.Int(1));
var list = listBinaryTag(BinaryTagTypes.INT, List.of(intBinaryTag(1)));
handler.setTag(nbtTag, list);
assertEquals(list, handler.getTag(nbtTag));
}
@ -187,7 +191,7 @@ public class TagNbtTest {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("list");
var listTag = Tag.Integer("list").list();
var list = NBT.List(NBTType.TAG_Int, NBT.Int(1));
var list = listBinaryTag(BinaryTagTypes.INT, List.of(intBinaryTag(1)));
handler.setTag(nbtTag, list);
assertEquals(list, handler.getTag(nbtTag));
@ -199,7 +203,7 @@ public class TagNbtTest {
public void rawArray() {
var handler = TagHandler.newHandler();
var nbtTag = Tag.NBT("array");
var array = NBT.IntArray(1, 2, 3);
var array = intArrayBinaryTag(1, 2, 3);
handler.setTag(nbtTag, array);
assertEquals(array, handler.getTag(nbtTag));
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.item.ItemStack;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.List;
@ -27,21 +27,23 @@ public class TagRecordTest {
@Test
public void fromNBT() {
var vecCompound = NBT.Compound(Map.of(
"x", NBT.Double(1),
"y", NBT.Double(2),
"z", NBT.Double(3)));
var handler = TagHandler.fromCompound(NBT.Compound(Map.of("vec", vecCompound)));
var vecCompound = CompoundBinaryTag.builder()
.putDouble("x", 1)
.putDouble("y", 2)
.putDouble("z", 3)
.build();
var handler = TagHandler.fromCompound(CompoundBinaryTag.from(Map.of("vec", vecCompound)));
var tag = Tag.Structure("vec", Vec.class);
assertEquals(new Vec(1, 2, 3), handler.getTag(tag));
}
@Test
public void fromNBTView() {
var handler = TagHandler.fromCompound(NBT.Compound(Map.of(
"x", NBT.Double(1),
"y", NBT.Double(2),
"z", NBT.Double(3))));
var handler = TagHandler.fromCompound(CompoundBinaryTag.builder()
.putDouble("x", 1)
.putDouble("y", 2)
.putDouble("z", 3)
.build());
var tag = Tag.View(Vec.class);
assertEquals(new Vec(1, 2, 3), handler.getTag(tag));
}
@ -75,9 +77,9 @@ public class TagRecordTest {
@Test
public void nbtSerializer() {
record CompoundRecord(NBTCompound compound) {
record CompoundRecord(CompoundBinaryTag compound) {
}
var test = new CompoundRecord(NBT.Compound(Map.of("key", NBT.String("value"))));
var test = new CompoundRecord(CompoundBinaryTag.from(Map.of("key", StringBinaryTag.stringBinaryTag("value"))));
var handler = TagHandler.newHandler();
var serializer = TagRecord.serializer(CompoundRecord.class);
serializer.write(handler, test);

View File

@ -1,7 +1,7 @@
package net.minestom.server.tag;
import net.minestom.server.item.firework.FireworkEffect;
import net.minestom.server.item.firework.FireworkEffectType;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.item.component.FireworkExplosion;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -10,8 +10,8 @@ import java.util.List;
public class TagSerializerTest {
@Test
public void fromCompound(){
var serializer = TagSerializer.fromCompound(FireworkEffect::fromCompound, FireworkEffect::asCompound);
var effect = new FireworkEffect(false, false, FireworkEffectType.BURST, List.of(), List.of());
var serializer = TagSerializer.fromCompound(FireworkExplosion.NBT_TYPE::read, explosion -> (CompoundBinaryTag) FireworkExplosion.NBT_TYPE.write(explosion));
var effect = new FireworkExplosion(FireworkExplosion.Shape.BURST, List.of(), List.of(), false, false);
TagHandler handler = TagHandler.newHandler();
serializer.write(handler, effect);
Assertions.assertEquals(effect, serializer.read(handler));

View File

@ -1,71 +1,68 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
public class TagTest {
@Test
public void intGet() {
var mutable = new MutableNBTCompound().setInt("key", 5);
var mutable = CompoundBinaryTag.builder().putInt("key", 5);
var tag = Tag.Integer("key");
var handler = TagHandler.fromCompound(new MutableNBTCompound());
var handler = TagHandler.fromCompound(CompoundBinaryTag.empty());
handler.setTag(tag, 5);
assertEquals(5, handler.getTag(tag));
assertEquals(mutable.toCompound(), handler.asCompound(), "NBT is not the same");
assertEquals(mutable.build(), handler.asCompound(), "NBT is not the same");
// Removal
handler.setTag(tag, null);
assertEquals(new NBTCompound(), handler.asCompound(), "Tag must be removed when set to null");
assertEquals(CompoundBinaryTag.empty(), handler.asCompound(), "Tag must be removed when set to null");
}
@Test
public void intNull() {
var handler = TagHandler.fromCompound(new MutableNBTCompound().set("key", NBT.Int(5)));
var handler = TagHandler.fromCompound(CompoundBinaryTag.builder().putInt("key", 5).build());
// Removal
var tag = Tag.Integer("key");
handler.setTag(tag, null);
assertFalse(handler.hasTag(tag));
assertEquals(NBTCompound.EMPTY, handler.asCompound(), "Tag must be removed when set to null");
assertEquals(CompoundBinaryTag.empty(), handler.asCompound(), "Tag must be removed when set to null");
}
@Test
public void intRemove() {
var handler = TagHandler.fromCompound(new MutableNBTCompound().set("key", NBT.Int(5)));
var handler = TagHandler.fromCompound(CompoundBinaryTag.builder().putInt("key", 5).build());
// Removal
var tag = Tag.Integer("key");
handler.removeTag(tag);
assertFalse(handler.hasTag(tag));
assertEquals(NBTCompound.EMPTY, handler.asCompound(), "Tag must be removed when set to null");
assertEquals(CompoundBinaryTag.empty(), handler.asCompound(), "Tag must be removed when set to null");
}
@Test
public void snbt() {
var mutable = new MutableNBTCompound().setInt("key", 5);
var reader = TagHandler.fromCompound(mutable);
assertEquals(reader.asCompound().toSNBT(), mutable.toCompound().toSNBT(), "SNBT is not the same");
var compound = CompoundBinaryTag.builder().putInt("key", 5).build();
var reader = TagHandler.fromCompound(compound);
assertEquals(TagStringIOExt.writeTag(reader.asCompound()), TagStringIOExt.writeTag(compound), "SNBT is not the same");
}
@Test
public void fromNbt() {
var mutable = new MutableNBTCompound().setInt("key", 5);
var handler = TagHandler.fromCompound(mutable);
var compound = CompoundBinaryTag.builder().putInt("key", 5).build();
var handler = TagHandler.fromCompound(compound);
assertEquals(5, handler.getTag(Tag.Integer("key")));
assertEquals(mutable.toCompound(), handler.asCompound(), "NBT is not the same");
assertEquals(compound, handler.asCompound(), "NBT is not the same");
}
@Test
public void fromNbtCache() {
// Ensure that TagHandler#asCompound reuse the same compound used for construction
var compound = NBT.Compound(Map.of("key", NBT.Int(5)));
var compound = CompoundBinaryTag.builder().putInt("key", 5).build();
var handler = TagHandler.fromCompound(compound);
assertSame(compound, handler.asCompound(), "NBT is not the same");
}
@ -122,9 +119,10 @@ public class TagTest {
@Test
public void nbtResizing() {
var handler = TagHandler.fromCompound(NBT.Compound(Map.of(
"tag1", NBT.Int(5),
"tag2", NBT.Int(1))));
var handler = TagHandler.fromCompound(CompoundBinaryTag.builder()
.putInt("tag1", 5)
.putInt("tag2", 1)
.build());
assertEquals(5, handler.getTag(Tag.Integer("tag1")));
assertEquals(1, handler.getTag(Tag.Integer("tag2")));

View File

@ -1,7 +1,6 @@
package net.minestom.server.tag;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTIntArray;
import net.kyori.adventure.nbt.IntArrayBinaryTag;
import org.junit.jupiter.api.Test;
import java.util.UUID;
@ -40,16 +39,15 @@ public class TagUuidTest {
var handler = TagHandler.newHandler();
handler.setTag(tag, UUID.fromString("9ab8ca63-3d7b-43ba-b805-a20a352dae9c"));
var nbt = handler.asCompound();
NBTIntArray array = (NBTIntArray) nbt.get("uuid");
assertArrayEquals(new int[]{-1699165597, 1031488442, -1207590390, 892186268},
array.getValue().copyArray());
IntArrayBinaryTag array = (IntArrayBinaryTag) nbt.get("uuid");
assertArrayEquals(new int[]{-1699165597, 1031488442, -1207590390, 892186268}, array.value());
}
@Test
public void fromNbt() {
var tag = Tag.UUID("uuid");
var handler = TagHandler.newHandler();
handler.setTag(Tag.NBT("uuid"), NBT.IntArray(-1699165597, 1031488442, -1207590390, 892186268));
handler.setTag(Tag.NBT("uuid"), IntArrayBinaryTag.intArrayBinaryTag(-1699165597, 1031488442, -1207590390, 892186268));
assertEquals(UUID.fromString("9ab8ca63-3d7b-43ba-b805-a20a352dae9c"), handler.getTag(tag));
}
}

View File

@ -1,13 +1,10 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static net.minestom.testing.TestUtils.assertEqualsSNBT;
import static org.junit.jupiter.api.Assertions.*;
@ -150,7 +147,7 @@ public class TagViewTest {
public void compoundSerializer() {
var tag = Tag.View(TagSerializer.COMPOUND);
var handler = TagHandler.newHandler();
handler.setTag(tag, NBT.Compound(Map.of("value", NBT.String("hello"))));
handler.setTag(tag, CompoundBinaryTag.builder().putString("value", "hello").build());
assertEqualsSNBT("""
{
"value":"hello"
@ -164,7 +161,7 @@ public class TagViewTest {
}
""", handler.asCompound());
handler.setTag(tag, NBTCompound.EMPTY);
handler.setTag(tag, CompoundBinaryTag.empty());
assertEqualsSNBT("{}", handler.asCompound());
handler.setTag(tag, null);

View File

@ -1,10 +1,9 @@
package net.minestom.testing;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIOExt;
import java.io.StringReader;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Set;
@ -33,11 +32,11 @@ public final class TestUtils {
assertEquals(Set.copyOf(expected), Set.copyOf(actual));
}
public static void assertEqualsSNBT(String snbt, NBTCompound compound) {
public static void assertEqualsSNBT(String snbt, CompoundBinaryTag compound) {
try {
final var converted = (NBTCompound) new SNBTParser(new StringReader(snbt)).parse();
final var converted = (CompoundBinaryTag) TagStringIOExt.readTag(snbt);
assertEquals(converted, compound);
} catch (NBTException e) {
} catch (IOException e) {
fail(e);
}
}