feat: initial conversion to adventure nbt. no tests, no anvil

This commit is contained in:
mworzala 2024-03-17 16:07:29 -04:00
parent 54212ebc97
commit 687968eea0
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
65 changed files with 1195 additions and 1230 deletions

View File

@ -72,10 +72,7 @@ dependencies {
api(libs.jetbrainsAnnotations)
api(libs.bundles.adventure)
api(libs.hydrazine)
api(libs.bundles.kotlin)
api(libs.bundles.hephaistos)
implementation(libs.minestomData)
implementation(libs.dependencyGetter)
// Performance/data structures
implementation(libs.caffeine)

View File

@ -1,5 +1,9 @@
package net.minestom.demo.block;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
@ -10,10 +14,6 @@ import net.minestom.server.tag.TagWritable;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.ArrayList;
import java.util.Collection;
@ -22,16 +22,17 @@ import java.util.List;
public class CampfireHandler implements BlockHandler {
public static final Tag<List<ItemStack>> ITEMS = Tag.View(new TagSerializer<>() {
private final Tag<NBT> internal = Tag.NBT("Items");
private final Tag<BinaryTag> internal = Tag.NBT("Items");
@Override
public @Nullable List<ItemStack> read(@NotNull TagReadable reader) {
NBTList<NBTCompound> item = (NBTList<NBTCompound>) reader.getTag(internal);
ListBinaryTag item = (ListBinaryTag) reader.getTag(internal);
if (item == null)
return null;
List<ItemStack> result = new ArrayList<>();
item.forEach(nbtCompound -> {
int amount = nbtCompound.getAsByte("Count");
item.forEach(childTag -> {
CompoundBinaryTag nbtCompound = (CompoundBinaryTag) childTag;
int amount = nbtCompound.getByte("Count");
String id = nbtCompound.getString("id");
Material material = Material.fromNamespaceId(id);
result.add(ItemStack.of(material, amount));
@ -45,14 +46,14 @@ public class CampfireHandler implements BlockHandler {
writer.removeTag(internal);
return;
}
writer.setTag(internal, NBT.List(
NBTType.TAG_Compound,
writer.setTag(internal, ListBinaryTag.listBinaryTag(
BinaryTagTypes.COMPOUND,
value.stream()
.map(item -> NBT.Compound(nbt -> {
nbt.setByte("Count", (byte) item.amount());
nbt.setByte("Slot", (byte) 1);
nbt.setString("id", item.material().name());
}))
.map(item -> (BinaryTag) CompoundBinaryTag.builder()
.putByte("Count", (byte) item.amount())
.putByte("Slot", (byte) 1)
.putString("id", item.material().name())
.build())
.toList()
));
}

View File

@ -4,7 +4,7 @@ metadata.format.version = "1.1"
# Important dependencies
data = "1.20.4-rv4"
adventure = "4.15.0"
adventure = "4.16.0"
kotlin = "1.7.22"
dependencyGetter = "v1.0.1"
hydrazine = "1.7.2"
@ -41,22 +41,16 @@ nexuspublish = "1.3.0"
# Important Dependencies
# Adventure
adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" }
adventure-nbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
adventure-serializer-gson = { group = "net.kyori", name = "adventure-text-serializer-gson", version.ref = "adventure" }
adventure-serializer-legacy = { group = "net.kyori", name = "adventure-text-serializer-legacy", version.ref = "adventure" }
adventure-serializer-plain = { group = "net.kyori", name = "adventure-text-serializer-plain", version.ref = "adventure" }
adventure-text-logger-slf4j = { group = "net.kyori", name = "adventure-text-logger-slf4j", version.ref = "adventure" }
# Kotlin
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
# Miscellaneous
hydrazine = { group = "com.github.MadMartian", name = "hydrazine-path-finding", version.ref = "hydrazine" }
minestomData = { group = "net.minestom", name = "data", version.ref = "data" }
dependencyGetter = { group = "com.github.Minestom", name = "DependencyGetter", version.ref = "dependencyGetter" }
jetbrainsAnnotations = { group = "org.jetbrains", name = "annotations", version.ref = "jetbrainsAnnotations" }
hephaistos-common = { group = "io.github.jglrxavpok.hephaistos", name = "common", version.ref = "hephaistos" }
hephaistos-gson = { group = "io.github.jglrxavpok.hephaistos", name = "gson", version.ref = "hephaistos" }
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j"}
# Performance / Data Structures
@ -86,11 +80,9 @@ logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.
[bundles]
kotlin = ["kotlin-stdlib-jdk8", "kotlin-reflect"]
flare = ["flare", "flare-fastutil"]
adventure = ["adventure-api", "adventure-serializer-gson", "adventure-serializer-legacy", "adventure-serializer-plain", "adventure-text-logger-slf4j"]
adventure = ["adventure-api", "adventure-nbt", "adventure-serializer-gson", "adventure-serializer-legacy", "adventure-serializer-plain", "adventure-text-logger-slf4j"]
junit = ["junit-api", "junit-engine", "junit-params", "junit-suite-api", "junit-suite-engine"]
hephaistos = ["hephaistos-common", "hephaistos-gson"]
logback = ["logback-core", "logback-classic"]
[plugins]

View File

@ -1,21 +1,18 @@
package net.minestom.server.adventure;
import java.io.StringReader;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.util.Codec;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import java.util.function.BiFunction;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
/**
* Adventure related constants, etc.
*/
@ -23,8 +20,8 @@ public final class MinestomAdventure {
/**
* A codec to convert between strings and NBT.
*/
public static final Codec<NBT, String, NBTException, RuntimeException> NBT_CODEC
= Codec.codec(encoded -> new SNBTParser(new StringReader(encoded)).parse(), NBT::toSNBT);
public static final Codec<CompoundBinaryTag, String, IOException, IOException> NBT_CODEC
= Codec.codec(TagStringIO.get()::asCompound, TagStringIO.get()::asString);
/**
* If components should be automatically translated in outgoing packets.

View File

@ -1,6 +1,7 @@
package net.minestom.server.adventure.provider;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
@ -9,14 +10,10 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.kyori.adventure.util.Codec;
import net.minestom.server.adventure.MinestomAdventure;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
final class NBTLegacyHoverEventSerializer implements LegacyHoverEventSerializer {
static final NBTLegacyHoverEventSerializer INSTANCE = new NBTLegacyHoverEventSerializer();
@ -30,76 +27,46 @@ final class NBTLegacyHoverEventSerializer implements LegacyHoverEventSerializer
@Override
public HoverEvent.@NotNull ShowItem deserializeShowItem(@NotNull Component input) throws IOException {
final String raw = PlainTextComponentSerializer.plainText().serialize(input);
try {
// attempt the parse
final NBT nbt = MinestomAdventure.NBT_CODEC.decode(raw);
if (!(nbt instanceof NBTCompound contents)) throw new IOException("contents were not a compound");
final NBTCompound tag = contents.getCompound(ITEM_TAG);
// attempt the parse
final CompoundBinaryTag contents = MinestomAdventure.NBT_CODEC.decode(raw);
final CompoundBinaryTag tag = contents.getCompound(ITEM_TAG);
// create the event
return HoverEvent.ShowItem.showItem(
Key.key(Objects.requireNonNullElse(contents.getString(ITEM_TYPE), "")),
Objects.requireNonNullElse(contents.getByte(ITEM_COUNT), (byte) 1),
tag == null ? null : BinaryTagHolder.encode(tag, MinestomAdventure.NBT_CODEC)
);
} catch (final NBTException e) {
throw new IOException(e);
}
// create the event
return HoverEvent.ShowItem.showItem(
Key.key(contents.getString(ITEM_TYPE, "")),
contents.getByte(ITEM_COUNT, (byte) 1),
tag.size() == 0 ? null : BinaryTagHolder.encode(tag, MinestomAdventure.NBT_CODEC)
);
}
@Override
public HoverEvent.@NotNull ShowEntity deserializeShowEntity(@NotNull Component input, Codec.Decoder<Component, String, ? extends RuntimeException> componentDecoder) throws IOException {
final String raw = PlainTextComponentSerializer.plainText().serialize(input);
try {
final NBT nbt = MinestomAdventure.NBT_CODEC.decode(raw);
if (!(nbt instanceof NBTCompound contents)) throw new IOException("contents were not a compound");
return HoverEvent.ShowEntity.showEntity(
Key.key(Objects.requireNonNullElse(contents.getString(ENTITY_TYPE), "")),
UUID.fromString(Objects.requireNonNullElse(contents.getString(ENTITY_ID), "")),
componentDecoder.decode(Objects.requireNonNullElse(contents.getString(ENTITY_NAME), ""))
);
} catch (NBTException e) {
throw new IOException(e);
}
final CompoundBinaryTag contents = MinestomAdventure.NBT_CODEC.decode(raw);
return HoverEvent.ShowEntity.showEntity(
Key.key(contents.getString(ENTITY_TYPE, "")),
UUID.fromString(Objects.requireNonNullElse(contents.getString(ENTITY_ID), "")),
componentDecoder.decode(Objects.requireNonNullElse(contents.getString(ENTITY_NAME), ""))
);
}
@Override
public @NotNull Component serializeShowItem(HoverEvent.@NotNull ShowItem input) throws IOException {
AtomicReference<NBTException> exception = new AtomicReference<>(null);
final NBTCompound tag = NBT.Compound(t -> {
t.setString(ITEM_TYPE, input.item().asString());
t.setByte(ITEM_COUNT, (byte) input.count());
final BinaryTagHolder nbt = input.nbt();
if (nbt != null) {
try {
t.set(ITEM_TAG, nbt.get(MinestomAdventure.NBT_CODEC));
} catch (NBTException e) {
exception.set(e);
}
}
});
if (exception.get() != null) {
throw new IOException(exception.get());
}
return Component.text(MinestomAdventure.NBT_CODEC.encode(tag));
CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder();
tag.putString(ITEM_TYPE, input.item().asString());
tag.putByte(ITEM_COUNT, (byte) input.count());
final BinaryTagHolder nbt = input.nbt();
if (nbt != null) tag.put(ITEM_TAG, nbt.get(MinestomAdventure.NBT_CODEC));
return Component.text(MinestomAdventure.NBT_CODEC.encode(tag.build()));
}
@Override
public @NotNull Component serializeShowEntity(HoverEvent.@NotNull ShowEntity input, Codec.Encoder<Component, String, ? extends RuntimeException> componentEncoder) {
final NBTCompound tag = NBT.Compound(t -> {
t.setString(ENTITY_ID, input.id().toString());
t.setString(ENTITY_TYPE, input.type().asString());
final Component name = input.name();
if (name != null) {
t.setString(ENTITY_NAME, componentEncoder.encode(name));
}
});
return Component.text(MinestomAdventure.NBT_CODEC.encode(tag));
public @NotNull Component serializeShowEntity(HoverEvent.@NotNull ShowEntity input, Codec.Encoder<Component, String, ? extends RuntimeException> componentEncoder) throws IOException {
CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder();
tag.putString(ENTITY_ID, input.id().toString());
tag.putString(ENTITY_TYPE, input.type().asString());
final Component name = input.name();
if (name != null) tag.putString(ENTITY_NAME, componentEncoder.encode(name));
return Component.text(MinestomAdventure.NBT_CODEC.encode(tag.build()));
}
}

View File

@ -1,11 +1,11 @@
package net.minestom.server.adventure.serializer.nbt;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
public interface NbtComponentSerializer extends ComponentSerializer<Component, Component, NBT> {
public interface NbtComponentSerializer extends ComponentSerializer<Component, Component, BinaryTag> {
static @NotNull NbtComponentSerializer nbt() {
return NbtComponentSerializerImpl.INSTANCE;
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.adventure.serializer.nbt;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.*;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.*;
import net.kyori.adventure.text.event.ClickEvent;
@ -12,14 +13,10 @@ import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.utils.validate.Check;
import org.intellij.lang.annotations.Subst;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
//todo write tests for me!!
@ -27,19 +24,19 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
static final NbtComponentSerializer INSTANCE = new NbtComponentSerializerImpl();
@Override
public @NotNull Component deserialize(@NotNull NBT input) {
public @NotNull Component deserialize(@NotNull BinaryTag input) {
return deserializeAnyComponent(input);
}
@Override
public @NotNull NBT serialize(@NotNull Component component) {
public @NotNull BinaryTag serialize(@NotNull Component component) {
return serializeComponent(component);
}
// DESERIALIZATION
private @NotNull Component deserializeAnyComponent(@NotNull NBT nbt) {
if (nbt instanceof NBTCompound compound) {
private @NotNull Component deserializeAnyComponent(@NotNull BinaryTag nbt) {
if (nbt instanceof CompoundBinaryTag compound) {
return deserializeComponent(compound);
} else {
//todo raw string + list
@ -47,12 +44,12 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
}
}
private @NotNull Component deserializeComponent(@NotNull NBTCompound compound) {
private @NotNull Component deserializeComponent(@NotNull CompoundBinaryTag compound) {
ComponentBuilder<?, ?> builder;
var type = compound.getString("type");
if (type != null) {
var type = compound.get("type");
if (type instanceof StringBinaryTag sType) {
// If type is specified, use that
builder = switch (type) {
builder = switch (sType.value()) {
case "text" -> deserializeTextComponent(compound);
case "translatable" -> deserializeTranslatableComponent(compound);
case "score" -> deserializeScoreComponent(compound);
@ -63,26 +60,25 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
};
} else {
// Try to infer the type from the fields present.
if (compound.containsKey("text")) {
Set<String> keys = compound.keySet();
if (keys.contains("text")) {
builder = deserializeTextComponent(compound);
} else if (compound.containsKey("translate")) {
} else if (keys.contains("translate")) {
builder = deserializeTranslatableComponent(compound);
} else if (compound.containsKey("score")) {
} else if (keys.contains("score")) {
builder = deserializeScoreComponent(compound);
} else if (compound.containsKey("selector")) {
} else if (keys.contains("selector")) {
builder = deserializeSelectorComponent(compound);
} else if (compound.containsKey("keybind")) {
} else if (keys.contains("keybind")) {
builder = deserializeKeybindComponent(compound);
} else if (compound.containsKey("nbt")) {
} else if (keys.contains("nbt")) {
builder = deserializeNbtComponent(compound);
} else throw new UnsupportedOperationException("Unable to infer component type");
}
// Children
var extra = compound.getList("extra");
Check.argCondition(extra != null && !extra.getSubtagType().equals(NBTType.TAG_Compound),
"Extra field must be a list of compounds");
if (extra != null) {
var extra = compound.getList("extra", BinaryTagTypes.COMPOUND);
if (extra.size() > 0) {
var list = new ArrayList<ComponentLike>();
for (var child : extra) list.add(deserializeAnyComponent(child));
builder.append(list);
@ -91,7 +87,7 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
// Formatting
var style = Style.style();
var color = compound.getString("color");
if (color != null) {
if (!color.isEmpty()) {
var hexColor = TextColor.fromHexString(color);
if (hexColor != null) {
style.color(hexColor);
@ -105,57 +101,60 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
}
}
@Subst("minecraft:default") var font = compound.getString("font");
if (font != null) style.font(Key.key(font));
var bold = compound.getByte("bold");
if (bold != null) style.decoration(TextDecoration.BOLD, bold == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
var italic = compound.getByte("italic");
if (italic != null) style.decoration(TextDecoration.ITALIC, italic == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
var underlined = compound.getByte("underlined");
if (underlined != null) style.decoration(TextDecoration.UNDERLINED, underlined == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
var strikethrough = compound.getByte("strikethrough");
if (strikethrough != null) style.decoration(TextDecoration.STRIKETHROUGH, strikethrough == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
var obfuscated = compound.getByte("obfuscated");
if (obfuscated != null) style.decoration(TextDecoration.OBFUSCATED, obfuscated == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
if (!font.isEmpty()) style.font(Key.key(font));
BinaryTag bold = compound.get("bold");
if (bold instanceof ByteBinaryTag b)
style.decoration(TextDecoration.BOLD, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
BinaryTag italic = compound.get("italic");
if (italic instanceof ByteBinaryTag b)
style.decoration(TextDecoration.ITALIC, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
BinaryTag underlined = compound.get("underlined");
if (underlined instanceof ByteBinaryTag b)
style.decoration(TextDecoration.UNDERLINED, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
BinaryTag strikethrough = compound.get("strikethrough");
if (strikethrough instanceof ByteBinaryTag b)
style.decoration(TextDecoration.STRIKETHROUGH, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
BinaryTag obfuscated = compound.get("obfuscated");
if (obfuscated instanceof ByteBinaryTag b)
style.decoration(TextDecoration.OBFUSCATED, b.value() == 1 ? TextDecoration.State.TRUE : TextDecoration.State.FALSE);
builder.style(style.build());
// Interactivity
var insertion = compound.getString("insertion");
if (insertion != null) builder.insertion(insertion);
if (!insertion.isEmpty()) builder.insertion(insertion);
var clickEvent = compound.getCompound("clickEvent");
if (clickEvent != null) builder.clickEvent(deserializeClickEvent(clickEvent));
if (clickEvent.size() > 0) builder.clickEvent(deserializeClickEvent(clickEvent));
var hoverEvent = compound.getCompound("hoverEvent");
if (hoverEvent != null) builder.hoverEvent(deserializeHoverEvent(hoverEvent));
if (hoverEvent.size() > 0) builder.hoverEvent(deserializeHoverEvent(hoverEvent));
return builder.build();
}
private @NotNull ComponentBuilder<?, ?> deserializeTextComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeTextComponent(@NotNull CompoundBinaryTag compound) {
var text = compound.getString("text");
Check.notNull(text, "Text component must have a text field");
return Component.text().content(text);
}
private @NotNull ComponentBuilder<?, ?> deserializeTranslatableComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeTranslatableComponent(@NotNull CompoundBinaryTag compound) {
var key = compound.getString("translate");
Check.notNull(key, "Translatable component must have a translate field");
var builder = Component.translatable().key(key);
var fallback = compound.getString("fallback");
if (fallback != null) builder.fallback(fallback);
var fallback = compound.get("fallback");
if (fallback instanceof StringBinaryTag s) builder.fallback(s.value());
NBTList<NBTCompound> args = compound.getList("with");
Check.argCondition(args != null && !args.getSubtagType().equals(NBTType.TAG_Compound),
"Translatable component with field must be a list of compounds");
if (args != null) {
ListBinaryTag args = compound.getList("with", BinaryTagTypes.COMPOUND);
if (args.size() > 0) {
var list = new ArrayList<ComponentLike>();
for (var arg : args) list.add(deserializeComponent(arg));
for (var arg : args) list.add(deserializeComponent((CompoundBinaryTag) arg));
builder.arguments(list);
}
return builder;
}
private @NotNull ComponentBuilder<?, ?> deserializeScoreComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeScoreComponent(@NotNull CompoundBinaryTag compound) {
var scoreCompound = compound.getCompound("score");
Check.notNull(scoreCompound, "Score component must have a score field");
var name = scoreCompound.getString("name");
@ -165,14 +164,14 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
var builder = Component.score().name(name).objective(objective);
var value = scoreCompound.getString("value");
if (value != null)
if (!value.isEmpty())
//noinspection deprecation
builder.value(value);
return builder;
}
private @NotNull ComponentBuilder<?, ?> deserializeSelectorComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeSelectorComponent(@NotNull CompoundBinaryTag compound) {
var selector = compound.getString("selector");
Check.notNull(selector, "Selector component must have a selector field");
var builder = Component.selector().pattern(selector);
@ -183,17 +182,17 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
return builder;
}
private @NotNull ComponentBuilder<?, ?> deserializeKeybindComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeKeybindComponent(@NotNull CompoundBinaryTag compound) {
var keybind = compound.getString("keybind");
Check.notNull(keybind, "Keybind component must have a keybind field");
return Component.keybind().keybind(keybind);
}
private @NotNull ComponentBuilder<?, ?> deserializeNbtComponent(@NotNull NBTCompound compound) {
private @NotNull ComponentBuilder<?, ?> deserializeNbtComponent(@NotNull CompoundBinaryTag compound) {
throw new UnsupportedOperationException("NBTComponent is not implemented yet");
}
private @NotNull ClickEvent deserializeClickEvent(@NotNull NBTCompound compound) {
private @NotNull ClickEvent deserializeClickEvent(@NotNull CompoundBinaryTag compound) {
var actionName = compound.getString("action");
Check.notNull(actionName, "Click event must have an action field");
var action = ClickEvent.Action.NAMES.value(actionName);
@ -203,7 +202,7 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
return ClickEvent.clickEvent(action, value);
}
private @NotNull HoverEvent<?> deserializeHoverEvent(@NotNull NBTCompound compound) {
private @NotNull HoverEvent<?> deserializeHoverEvent(@NotNull CompoundBinaryTag compound) {
var actionName = compound.getString("action");
Check.notNull(actionName, "Hover event must have an action field");
var contents = compound.getCompound("contents");
@ -216,13 +215,12 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
@Subst("minecraft:stick") var id = contents.getString("id");
Check.notNull(id, "Show item hover event must have an id field");
var count = contents.getInt("count");
var countInt = count == null ? 1 : count;
var tag = contents.getString("tag");
var binaryTag = tag == null ? null : BinaryTagHolder.binaryTagHolder(tag);
return HoverEvent.showItem(Key.key(id), countInt, binaryTag);
var binaryTag = tag.isEmpty() ? null : BinaryTagHolder.binaryTagHolder(tag);
return HoverEvent.showItem(Key.key(id), count, binaryTag);
} else if (action == HoverEvent.Action.SHOW_ENTITY) {
var name = contents.getCompound("name");
var nameComponent = name == null ? null : deserializeComponent(name);
var nameComponent = name.size() == 0 ? null : deserializeComponent(name);
@Subst("minecraft:pig") var type = contents.getString("type");
Check.notNull(type, "Show entity hover event must have a type field");
var id = contents.getString("id");
@ -235,36 +233,36 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
// SERIALIZATION
private @NotNull NBT serializeComponent(@NotNull Component component) {
MutableNBTCompound compound = new MutableNBTCompound();
private @NotNull CompoundBinaryTag serializeComponent(@NotNull Component component) {
CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder();
// Base component types
if (component instanceof TextComponent text) {
compound.setString("type", "text");
compound.setString("text", text.content());
compound.putString("type", "text");
compound.putString("text", text.content());
} else if (component instanceof TranslatableComponent translatable) {
compound.setString("type", "translatable");
compound.setString("translate", translatable.key());
compound.putString("type", "translatable");
compound.putString("translate", translatable.key());
var fallback = translatable.fallback();
if (fallback != null) compound.setString("fallback", fallback);
if (fallback != null) compound.putString("fallback", fallback);
var args = translatable.arguments();
if (!args.isEmpty()) compound.set("with", serializeTranslationArgs(args));
if (!args.isEmpty()) compound.put("with", serializeTranslationArgs(args));
} else if (component instanceof ScoreComponent score) {
compound.setString("type", "score");
var scoreCompound = new MutableNBTCompound();
scoreCompound.setString("name", score.name());
scoreCompound.setString("objective", score.objective());
compound.putString("type", "score");
CompoundBinaryTag.Builder scoreCompound = CompoundBinaryTag.builder();
scoreCompound.putString("name", score.name());
scoreCompound.putString("objective", score.objective());
@SuppressWarnings("deprecation") var value = score.value();
if (value != null) scoreCompound.setString("value", value);
compound.set("score", scoreCompound.toCompound());
if (value != null) scoreCompound.putString("value", value);
compound.put("score", scoreCompound.build());
} else if (component instanceof SelectorComponent selector) {
compound.setString("type", "selector");
compound.setString("selector", selector.pattern());
compound.putString("type", "selector");
compound.putString("selector", selector.pattern());
var separator = selector.separator();
if (separator != null) compound.set("separator", serializeComponent(separator));
if (separator != null) compound.put("separator", serializeComponent(separator));
} else if (component instanceof KeybindComponent keybind) {
compound.setString("type", "keybind");
compound.setString("keybind", keybind.keybind());
compound.putString("type", "keybind");
compound.putString("keybind", keybind.keybind());
} else if (component instanceof NBTComponent<?, ?> nbt) {
//todo
throw new UnsupportedOperationException("NBTComponent is not implemented yet");
@ -274,10 +272,10 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
// Children
if (!component.children().isEmpty()) {
var children = new ArrayList<NBT>();
ListBinaryTag.Builder<CompoundBinaryTag> children = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (var child : component.children())
children.add(serializeComponent(child));
compound.set("extra", new NBTList<>(NBTType.TAG_Compound, children));
compound.put("extra", children.build());
}
// Formatting
@ -285,94 +283,89 @@ final class NbtComponentSerializerImpl implements NbtComponentSerializer {
var color = style.color();
if (color != null) {
if (color instanceof NamedTextColor named) {
compound.setString("color", named.toString());
compound.putString("color", named.toString());
} else {
compound.setString("color", color.asHexString());
compound.putString("color", color.asHexString());
}
}
var font = style.font();
if (font != null)
compound.setString("font", font.toString());
compound.putString("font", font.toString());
var bold = style.decoration(TextDecoration.BOLD);
if (bold != TextDecoration.State.NOT_SET)
setBool(compound, "bold", bold == TextDecoration.State.TRUE);
compound.putBoolean("bold", bold == TextDecoration.State.TRUE);
var italic = style.decoration(TextDecoration.ITALIC);
if (italic != TextDecoration.State.NOT_SET)
setBool(compound, "italic", italic == TextDecoration.State.TRUE);
compound.putBoolean("italic", italic == TextDecoration.State.TRUE);
var underlined = style.decoration(TextDecoration.UNDERLINED);
if (underlined != TextDecoration.State.NOT_SET)
setBool(compound, "underlined", underlined == TextDecoration.State.TRUE);
compound.putBoolean("underlined", underlined == TextDecoration.State.TRUE);
var strikethrough = style.decoration(TextDecoration.STRIKETHROUGH);
if (strikethrough != TextDecoration.State.NOT_SET)
setBool(compound, "strikethrough", strikethrough == TextDecoration.State.TRUE);
compound.putBoolean("strikethrough", strikethrough == TextDecoration.State.TRUE);
var obfuscated = style.decoration(TextDecoration.OBFUSCATED);
if (obfuscated != TextDecoration.State.NOT_SET)
setBool(compound, "obfuscated", obfuscated == TextDecoration.State.TRUE);
compound.putBoolean("obfuscated", obfuscated == TextDecoration.State.TRUE);
// Interactivity
var insertion = component.insertion();
if (insertion != null) compound.setString("insertion", insertion);
if (insertion != null) compound.putString("insertion", insertion);
var clickEvent = component.clickEvent();
if (clickEvent != null) compound.set("clickEvent", serializeClickEvent(clickEvent));
if (clickEvent != null) compound.put("clickEvent", serializeClickEvent(clickEvent));
var hoverEvent = component.hoverEvent();
if (hoverEvent != null) compound.set("hoverEvent", serializeHoverEvent(hoverEvent));
if (hoverEvent != null) compound.put("hoverEvent", serializeHoverEvent(hoverEvent));
return compound.toCompound();
return compound.build();
}
private @NotNull NBT serializeTranslationArgs(@NotNull Collection<TranslationArgument> args) {
var list = new ArrayList<NBT>();
private @NotNull BinaryTag serializeTranslationArgs(@NotNull Collection<TranslationArgument> args) {
ListBinaryTag.Builder<CompoundBinaryTag> argList = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (var arg : args)
list.add(serializeComponent(arg.asComponent()));
return new NBTList<>(NBTType.TAG_Compound, list);
argList.add(serializeComponent(arg.asComponent()));
return argList.build();
}
private @NotNull NBT serializeClickEvent(@NotNull ClickEvent event) {
var compound = new MutableNBTCompound();
compound.setString("action", event.action().toString());
compound.setString("value", event.value());
return compound.toCompound();
private @NotNull BinaryTag serializeClickEvent(@NotNull ClickEvent event) {
return CompoundBinaryTag.builder()
.putString("action", event.action().toString())
.putString("value", event.value())
.build();
}
@SuppressWarnings("unchecked")
private @NotNull NBT serializeHoverEvent(@NotNull HoverEvent<?> event) {
var compound = new MutableNBTCompound();
private @NotNull BinaryTag serializeHoverEvent(@NotNull HoverEvent<?> event) {
CompoundBinaryTag.Builder compound = CompoundBinaryTag.builder();
//todo surely there is a better way to do this?
compound.setString("action", event.action().toString());
compound.putString("action", event.action().toString());
if (event.action() == HoverEvent.Action.SHOW_TEXT) {
var value = ((HoverEvent<Component>) event).value();
compound.set("contents", serializeComponent(value));
compound.put("contents", serializeComponent(value));
} else if (event.action() == HoverEvent.Action.SHOW_ITEM) {
var value = ((HoverEvent<HoverEvent.ShowItem>) event).value();
var itemCompound = new MutableNBTCompound();
itemCompound.setString("id", value.item().asString());
if (value.count() != 1) itemCompound.setInt("count", value.count());
CompoundBinaryTag.Builder itemCompound = CompoundBinaryTag.builder();
itemCompound.putString("id", value.item().asString());
if (value.count() != 1) itemCompound.putInt("count", value.count());
var tag = value.nbt();
if (tag != null) itemCompound.setString("tag", tag.string());
if (tag != null) itemCompound.putString("tag", tag.string());
compound.set("contents", itemCompound.toCompound());
compound.put("contents", itemCompound.build());
} else if (event.action() == HoverEvent.Action.SHOW_ENTITY) {
var value = ((HoverEvent<HoverEvent.ShowEntity>) event).value();
var entityCompound = new MutableNBTCompound();
CompoundBinaryTag.Builder entityCompound = CompoundBinaryTag.builder();
var name = value.name();
if (name != null) entityCompound.set("name", serializeComponent(name));
entityCompound.setString("type", value.type().asString());
entityCompound.setString("id", value.id().toString());
if (name != null) entityCompound.put("name", serializeComponent(name));
entityCompound.putString("type", value.type().asString());
entityCompound.putString("id", value.id().toString());
compound.set("contents", entityCompound.toCompound());
compound.put("contents", entityCompound.build());
} else {
throw new UnsupportedOperationException("Unknown hover event action: " + event.action());
}
return compound.toCompound();
return compound.build();
}
private void setBool(@NotNull MutableNBTCompound compound, @NotNull String key, boolean value) {
compound.setByte(key, value ? (byte) 1 : 0);
}
}

View File

@ -1,16 +1,15 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import java.io.StringReader;
import java.io.IOException;
/**
* Argument which can be used to retrieve an {@link ItemStack} from its material and with NBT data.
@ -65,10 +64,10 @@ public class ArgumentItemStack extends Argument<ItemStack> {
final String sNBT = input.substring(nbtIndex).replace("\\\"", "\"");
NBTCompound compound;
CompoundBinaryTag compound;
try {
compound = (NBTCompound) new SNBTParser(new StringReader(sNBT)).parse();
} catch (NBTException e) {
compound = TagStringIO.get().asCompound(sNBT);
} catch (IOException e) {
throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT);
}

View File

@ -1,22 +1,21 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import java.io.StringReader;
import java.io.IOException;
/**
* Argument used to retrieve a {@link NBTCompound} if you need key-value data.
* Argument used to retrieve a {@link CompoundBinaryTag} if you need key-value data.
* <p>
* Example: {display:{Name:"{\"text\":\"Sword of Power\"}"}}
*/
public class ArgumentNbtCompoundTag extends Argument<NBTCompound> {
public class ArgumentNbtCompoundTag extends Argument<CompoundBinaryTag> {
public static final int INVALID_NBT = 1;
@ -26,15 +25,15 @@ public class ArgumentNbtCompoundTag extends Argument<NBTCompound> {
@NotNull
@Override
public NBTCompound parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException {
public CompoundBinaryTag parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException {
try {
NBT nbt = new SNBTParser(new StringReader(input)).parse();
BinaryTag nbt = TagStringIO.get().asCompound(input);
if (!(nbt instanceof NBTCompound))
if (!(nbt instanceof CompoundBinaryTag compound))
throw new ArgumentSyntaxException("NBTCompound is invalid", input, INVALID_NBT);
return (NBTCompound) nbt;
} catch (NBTException e) {
return compound;
} catch (IOException e) {
throw new ArgumentSyntaxException("NBTCompound is invalid", input, INVALID_NBT);
}
}

View File

@ -1,23 +1,22 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import java.io.StringReader;
import java.io.IOException;
/**
* Argument used to retrieve a {@link NBT} based object, can be any kind of tag like
* {@link org.jglrxavpok.hephaistos.nbt.NBTCompound}, {@link org.jglrxavpok.hephaistos.nbt.NBTList},
* {@link org.jglrxavpok.hephaistos.nbt.NBTInt}, etc...
* Argument used to retrieve a {@link BinaryTag} based object, can be any kind of tag like
* {@link net.kyori.adventure.nbt.CompoundBinaryTag}, {@link net.kyori.adventure.nbt.ListBinaryTag},
* {@link net.kyori.adventure.nbt.IntBinaryTag}, etc...
* <p>
* Example: {display:{Name:"{\"text\":\"Sword of Power\"}"}} or [{display:{Name:"{\"text\":\"Sword of Power\"}"}}]
*/
public class ArgumentNbtTag extends Argument<NBT> {
public class ArgumentNbtTag extends Argument<BinaryTag> {
public static final int INVALID_NBT = 1;
@ -27,10 +26,10 @@ public class ArgumentNbtTag extends Argument<NBT> {
@NotNull
@Override
public NBT parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException {
public BinaryTag parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException {
try {
return new SNBTParser(new StringReader(input)).parse();
} catch (NBTException e) {
return TagStringIO.get().asCompound(input);
} catch (IOException e) {
throw new ArgumentSyntaxException("Invalid NBT", input, INVALID_NBT);
}
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.metadata.animal.FrogMeta;
@ -15,7 +16,6 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBT;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
@ -89,7 +89,7 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_OPTBLOCKSTATE, value, NetworkBuffer.OPT_BLOCK_STATE);
}
public static Entry<NBT> NBT(@NotNull NBT nbt) {
public static Entry<BinaryTag> NBT(@NotNull BinaryTag nbt) {
return new MetadataImpl.EntryImpl<>(TYPE_NBT, nbt, NetworkBuffer.NBT);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.entity;
import net.kyori.adventure.nbt.EndBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.metadata.animal.FrogMeta;
@ -13,7 +14,6 @@ import net.minestom.server.utils.Direction;
import net.minestom.server.utils.collection.ObjectArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBTEnd;
import static net.minestom.server.entity.Metadata.*;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
@ -38,7 +38,7 @@ final class MetadataImpl {
EMPTY_VALUES.set(TYPE_OPTUUID, OptUUID(null));
EMPTY_VALUES.set(TYPE_BLOCKSTATE, BlockState(Block.AIR.id()));
EMPTY_VALUES.set(TYPE_OPTBLOCKSTATE, OptBlockState(null));
EMPTY_VALUES.set(TYPE_NBT, NBT(NBTEnd.INSTANCE));
EMPTY_VALUES.set(TYPE_NBT, NBT(EndBinaryTag.endBinaryTag()));
//EMPTY_VALUES.set(TYPE_PARTICLE -> throw new UnsupportedOperationException();
EMPTY_VALUES.set(TYPE_VILLAGERDATA, VillagerData(0, 0, 0));
EMPTY_VALUES.set(TYPE_OPTVARINT, OptVarInt(null));

View File

@ -1,12 +1,12 @@
package net.minestom.server.entity.damage;
import net.minestom.server.registry.StaticProtocolObject;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
@ -36,7 +36,7 @@ public sealed interface DamageType extends StaticProtocolObject, DamageTypes per
return registry().scaling();
}
NBTCompound asNBT();
CompoundBinaryTag asNBT();
static @NotNull Collection<@NotNull DamageType> values() {
return DamageTypeImpl.values();
@ -54,7 +54,7 @@ public sealed interface DamageType extends StaticProtocolObject, DamageTypes per
return DamageTypeImpl.getId(id);
}
static NBTCompound getNBT() {
static CompoundBinaryTag getNBT() {
return DamageTypeImpl.getNBT();
}
}

View File

@ -1,14 +1,12 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements DamageType {
@ -33,12 +31,12 @@ record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements Dama
}
@Override
public NBTCompound asNBT() {
var elem = new HashMap<String, NBT>();
elem.put("exhaustion", NBT.Float(registry.exhaustion()));
elem.put("message_id", NBT.String(registry.messageId()));
elem.put("scaling", NBT.String(registry.scaling()));
return NBT.Compound(elem);
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
.putFloat("exhaustion", registry.exhaustion())
.putString("message_id", registry.messageId())
.putString("scaling", registry.scaling())
.build();
}
static Collection<DamageType> values() {
@ -55,22 +53,23 @@ record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements Dama
return id;
}
private static NBTCompound lazyNbt = null;
private static CompoundBinaryTag lazyNbt = null;
static NBTCompound getNBT() {
static CompoundBinaryTag getNBT() {
if (lazyNbt == null) {
var damageTypes = values().stream()
.map((damageType) -> NBT.Compound(Map.of(
"id", NBT.Int(damageType.id()),
"name", NBT.String(damageType.name()),
"element", damageType.asNBT()
)))
.toList();
var entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (var damageType : values()) {
entries.add(CompoundBinaryTag.builder()
.putInt("id", damageType.id())
.putString("name", damageType.name())
.put("element", damageType.asNBT())
.build());
}
lazyNbt = NBT.Compound(Map.of(
"type", NBT.String("minecraft:damage_type"),
"value", NBT.List(NBTType.TAG_Compound, damageTypes)
));
lazyNbt = CompoundBinaryTag.builder()
.putString("type", "minecraft:damage_type")
.put("value", entries.build())
.build();
}
return lazyNbt;
}

View File

@ -1,12 +1,11 @@
package net.minestom.server.entity.metadata;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Metadata;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import java.util.Map;
public class PlayerMeta extends LivingEntityMeta {
public static final byte OFFSET = LivingEntityMeta.MAX_OFFSET;
@ -109,23 +108,23 @@ public class PlayerMeta extends LivingEntityMeta {
}
@Nullable
public NBT getLeftShoulderEntityData() {
public BinaryTag getLeftShoulderEntityData() {
return super.metadata.getIndex(OFFSET + 4, null);
}
public void setLeftShoulderEntityData(@Nullable NBT value) {
if (value == null) value = NBT.Compound(Map.of());
public void setLeftShoulderEntityData(@Nullable BinaryTag value) {
if (value == null) value = CompoundBinaryTag.empty();
super.metadata.setIndex(OFFSET + 4, Metadata.NBT(value));
}
@Nullable
public NBT getRightShoulderEntityData() {
public BinaryTag getRightShoulderEntityData() {
return super.metadata.getIndex(OFFSET + 5, null);
}
public void setRightShoulderEntityData(@Nullable NBT value) {
if (value == null) value = NBT.Compound(Map.of());
public void setRightShoulderEntityData(@Nullable BinaryTag value) {
if (value == null) value = CompoundBinaryTag.empty();
super.metadata.setIndex(OFFSET + 5, Metadata.NBT(value));
}

View File

@ -1,34 +1,17 @@
package net.minestom.server.instance;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.world.biomes.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.mca.*;
import org.jglrxavpok.hephaistos.mca.readers.ChunkReader;
import org.jglrxavpok.hephaistos.mca.readers.ChunkSectionReader;
import org.jglrxavpok.hephaistos.mca.readers.SectionBiomeInformation;
import org.jglrxavpok.hephaistos.mca.writer.ChunkSectionWriter;
import org.jglrxavpok.hephaistos.mca.writer.ChunkWriter;
import org.jglrxavpok.hephaistos.nbt.*;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@ -36,7 +19,7 @@ public class AnvilLoader implements IChunkLoader {
private final static Logger LOGGER = LoggerFactory.getLogger(AnvilLoader.class);
private final static Biome PLAINS = MinecraftServer.getBiomeManager().getByName(NamespaceID.from("minecraft:plains"));
private final Map<String, RegionFile> alreadyLoaded = new ConcurrentHashMap<>();
// private final Map<String, RegionFile> alreadyLoaded = new ConcurrentHashMap<>();
private final Path path;
private final Path levelPath;
private final Path regionPath;
@ -50,7 +33,7 @@ public class AnvilLoader implements IChunkLoader {
private final RegionCache perRegionLoadedChunks = new RegionCache();
// thread local to avoid contention issues with locks
private final ThreadLocal<Int2ObjectMap<BlockState>> blockStateId2ObjectCacheTLS = ThreadLocal.withInitial(Int2ObjectArrayMap::new);
// private final ThreadLocal<Int2ObjectMap<BlockState>> blockStateId2ObjectCacheTLS = ThreadLocal.withInitial(Int2ObjectArrayMap::new);
public AnvilLoader(@NotNull Path path) {
this.path = path;
@ -64,409 +47,409 @@ public class AnvilLoader implements IChunkLoader {
@Override
public void loadInstance(@NotNull Instance instance) {
if (!Files.exists(levelPath)) {
return;
}
try (var reader = new NBTReader(Files.newInputStream(levelPath))) {
final NBTCompound tag = (NBTCompound) reader.read();
Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING);
instance.tagHandler().updateContent(tag);
} catch (IOException | NBTException e) {
MinecraftServer.getExceptionManager().handleException(e);
}
// if (!Files.exists(levelPath)) {
// return;
// }
// try (var reader = new NBTReader(Files.newInputStream(levelPath))) {
// final NBTCompound tag = (NBTCompound) reader.read();
// Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING);
// instance.tagHandler().updateContent(tag);
// } catch (IOException | NBTException e) {
// MinecraftServer.getExceptionManager().handleException(e);
// }
}
@Override
public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
if (!Files.exists(path)) {
// No world folder
return CompletableFuture.completedFuture(null);
}
try {
return loadMCA(instance, chunkX, chunkZ);
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
// if (!Files.exists(path)) {
// // No world folder
// return CompletableFuture.completedFuture(null);
// }
// try {
// return loadMCA(instance, chunkX, chunkZ);
// } catch (Exception e) {
// MinecraftServer.getExceptionManager().handleException(e);
// }
return CompletableFuture.completedFuture(null);
}
private @NotNull CompletableFuture<@Nullable Chunk> loadMCA(Instance instance, int chunkX, int chunkZ) throws IOException, AnvilException {
final RegionFile mcaFile = getMCAFile(instance, chunkX, chunkZ);
if (mcaFile == null)
return CompletableFuture.completedFuture(null);
final NBTCompound chunkData = mcaFile.getChunkData(chunkX, chunkZ);
if (chunkData == null)
return CompletableFuture.completedFuture(null);
final ChunkReader chunkReader = new ChunkReader(chunkData);
Chunk chunk = instance.getChunkSupplier().createChunk(instance, chunkX, chunkZ);
synchronized (chunk) {
var yRange = chunkReader.getYRange();
if (yRange.getStart() < instance.getDimensionType().getMinY()) {
throw new AnvilException(
String.format("Trying to load chunk with minY = %d, but instance dimension type (%s) has a minY of %d",
yRange.getStart(),
instance.getDimensionType().getName().asString(),
instance.getDimensionType().getMinY()
));
}
if (yRange.getEndInclusive() > instance.getDimensionType().getMaxY()) {
throw new AnvilException(
String.format("Trying to load chunk with maxY = %d, but instance dimension type (%s) has a maxY of %d",
yRange.getEndInclusive(),
instance.getDimensionType().getName().asString(),
instance.getDimensionType().getMaxY()
));
}
// TODO: Parallelize block, block entities and biome loading
// Blocks + Biomes
loadSections(chunk, chunkReader);
// Block entities
loadBlockEntities(chunk, chunkReader);
}
synchronized (perRegionLoadedChunks) {
int regionX = CoordinatesKt.chunkToRegion(chunkX);
int regionZ = CoordinatesKt.chunkToRegion(chunkZ);
var chunks = perRegionLoadedChunks.computeIfAbsent(new IntIntImmutablePair(regionX, regionZ), r -> new HashSet<>()); // region cache may have been removed on another thread due to unloadChunk
chunks.add(new IntIntImmutablePair(chunkX, chunkZ));
}
return CompletableFuture.completedFuture(chunk);
}
private @Nullable RegionFile getMCAFile(Instance instance, int chunkX, int chunkZ) {
final int regionX = CoordinatesKt.chunkToRegion(chunkX);
final int regionZ = CoordinatesKt.chunkToRegion(chunkZ);
return alreadyLoaded.computeIfAbsent(RegionFile.Companion.createFileName(regionX, regionZ), n -> {
try {
final Path regionPath = this.regionPath.resolve(n);
if (!Files.exists(regionPath)) {
return null;
}
synchronized (perRegionLoadedChunks) {
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(new RandomAccessFile(regionPath.toFile(), "rw"), regionX, regionZ, instance.getDimensionType().getMinY(), instance.getDimensionType().getMaxY() - 1);
} catch (IOException | AnvilException e) {
MinecraftServer.getExceptionManager().handleException(e);
return null;
}
});
}
private void loadSections(Chunk chunk, ChunkReader chunkReader) {
final HashMap<String, Biome> biomeCache = new HashMap<>();
for (NBTCompound sectionNBT : chunkReader.getSections()) {
ChunkSectionReader sectionReader = new ChunkSectionReader(chunkReader.getMinecraftVersion(), sectionNBT);
if (sectionReader.isSectionEmpty()) continue;
final int sectionY = sectionReader.getY();
final int yOffset = Chunk.CHUNK_SECTION_SIZE * sectionY;
Section section = chunk.getSection(sectionY);
if (sectionReader.getSkyLight() != null) {
section.setSkyLight(sectionReader.getSkyLight().copyArray());
}
if (sectionReader.getBlockLight() != null) {
section.setBlockLight(sectionReader.getBlockLight().copyArray());
}
// Biomes
if (chunkReader.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) {
SectionBiomeInformation sectionBiomeInformation = chunkReader.readSectionBiomes(sectionReader);
if (sectionBiomeInformation != null && sectionBiomeInformation.hasBiomeInformation()) {
if (sectionBiomeInformation.isFilledWithSingleBiome()) {
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x;
int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z;
int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y;
String biomeName = sectionBiomeInformation.getBaseBiome();
Biome biome = biomeCache.computeIfAbsent(biomeName, n ->
Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS));
chunk.setBiome(finalX, finalY, finalZ, biome);
}
}
}
} else {
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x;
int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z;
int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y;
int index = x / 4 + (z / 4) * 4 + (y / 4) * 16;
String biomeName = sectionBiomeInformation.getBiomes()[index];
Biome biome = biomeCache.computeIfAbsent(biomeName, n ->
Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS));
chunk.setBiome(finalX, finalY, finalZ, biome);
}
}
}
}
}
}
// Blocks
final NBTList<NBTCompound> blockPalette = sectionReader.getBlockPalette();
if (blockPalette != null) {
final int[] blockStateIndices = sectionReader.getUncompressedBlockStateIDs();
Block[] convertedPalette = new Block[blockPalette.getSize()];
for (int i = 0; i < convertedPalette.length; i++) {
final NBTCompound paletteEntry = blockPalette.get(i);
String blockName = Objects.requireNonNull(paletteEntry.getString("Name"));
if (blockName.equals("minecraft:air")) {
convertedPalette[i] = Block.AIR;
} else {
if (blockName.equals("minecraft:grass")) {
blockName = "minecraft:short_grass";
}
Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName));
// Properties
final Map<String, String> properties = new HashMap<>();
NBTCompound propertiesNBT = paletteEntry.getCompound("Properties");
if (propertiesNBT != null) {
for (var property : propertiesNBT) {
if (property.getValue().getID() != NBTType.TAG_String) {
LOGGER.warn("Fail to parse block state properties {}, expected a TAG_String for {}, but contents were {}",
propertiesNBT,
property.getKey(),
property.getValue().toSNBT());
} else {
properties.put(property.getKey(), ((NBTString) property.getValue()).getValue());
}
}
}
if (!properties.isEmpty()) block = block.withProperties(properties);
// Handler
final BlockHandler handler = MinecraftServer.getBlockManager().getHandler(block.name());
if (handler != null) block = block.withHandler(handler);
convertedPalette[i] = block;
}
}
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
try {
final int blockIndex = y * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE + z * Chunk.CHUNK_SECTION_SIZE + x;
final int paletteIndex = blockStateIndices[blockIndex];
final Block block = convertedPalette[paletteIndex];
chunk.setBlock(x, y + yOffset, z, block);
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
}
}
}
}
}
}
private void loadBlockEntities(Chunk loadedChunk, ChunkReader chunkReader) {
for (NBTCompound te : chunkReader.getBlockEntities()) {
final var x = te.getInt("x");
final var y = te.getInt("y");
final var z = te.getInt("z");
if (x == null || y == null || z == null) {
LOGGER.warn("Tile entity has failed to load due to invalid coordinate");
continue;
}
Block block = loadedChunk.getBlock(x, y, z);
final String tileEntityID = te.getString("id");
if (tileEntityID != null) {
final BlockHandler handler = MinecraftServer.getBlockManager().getHandlerOrDummy(tileEntityID);
block = block.withHandler(handler);
}
// Remove anvil tags
MutableNBTCompound mutableCopy = te.toMutableCompound();
mutableCopy.remove("id");
mutableCopy.remove("x");
mutableCopy.remove("y");
mutableCopy.remove("z");
mutableCopy.remove("keepPacked");
// Place block
final var finalBlock = mutableCopy.getSize() > 0 ?
block.withNbt(mutableCopy.toCompound()) : block;
loadedChunk.setBlock(x, y, z, finalBlock);
}
}
//
// private @NotNull CompletableFuture<@Nullable Chunk> loadMCA(Instance instance, int chunkX, int chunkZ) throws IOException, AnvilException {
// final RegionFile mcaFile = getMCAFile(instance, chunkX, chunkZ);
// if (mcaFile == null)
// return CompletableFuture.completedFuture(null);
// final NBTCompound chunkData = mcaFile.getChunkData(chunkX, chunkZ);
// if (chunkData == null)
// return CompletableFuture.completedFuture(null);
//
// final ChunkReader chunkReader = new ChunkReader(chunkData);
//
// Chunk chunk = instance.getChunkSupplier().createChunk(instance, chunkX, chunkZ);
// synchronized (chunk) {
// var yRange = chunkReader.getYRange();
// if (yRange.getStart() < instance.getDimensionType().getMinY()) {
// throw new AnvilException(
// String.format("Trying to load chunk with minY = %d, but instance dimension type (%s) has a minY of %d",
// yRange.getStart(),
// instance.getDimensionType().getName().asString(),
// instance.getDimensionType().getMinY()
// ));
// }
// if (yRange.getEndInclusive() > instance.getDimensionType().getMaxY()) {
// throw new AnvilException(
// String.format("Trying to load chunk with maxY = %d, but instance dimension type (%s) has a maxY of %d",
// yRange.getEndInclusive(),
// instance.getDimensionType().getName().asString(),
// instance.getDimensionType().getMaxY()
// ));
// }
//
// // TODO: Parallelize block, block entities and biome loading
// // Blocks + Biomes
// loadSections(chunk, chunkReader);
//
// // Block entities
// loadBlockEntities(chunk, chunkReader);
// }
// synchronized (perRegionLoadedChunks) {
// int regionX = CoordinatesKt.chunkToRegion(chunkX);
// int regionZ = CoordinatesKt.chunkToRegion(chunkZ);
// var chunks = perRegionLoadedChunks.computeIfAbsent(new IntIntImmutablePair(regionX, regionZ), r -> new HashSet<>()); // region cache may have been removed on another thread due to unloadChunk
// chunks.add(new IntIntImmutablePair(chunkX, chunkZ));
// }
// return CompletableFuture.completedFuture(chunk);
// }
//
// private @Nullable RegionFile getMCAFile(Instance instance, int chunkX, int chunkZ) {
// final int regionX = CoordinatesKt.chunkToRegion(chunkX);
// final int regionZ = CoordinatesKt.chunkToRegion(chunkZ);
// return alreadyLoaded.computeIfAbsent(RegionFile.Companion.createFileName(regionX, regionZ), n -> {
// try {
// final Path regionPath = this.regionPath.resolve(n);
// if (!Files.exists(regionPath)) {
// return null;
// }
// synchronized (perRegionLoadedChunks) {
// 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(new RandomAccessFile(regionPath.toFile(), "rw"), regionX, regionZ, instance.getDimensionType().getMinY(), instance.getDimensionType().getMaxY() - 1);
// } catch (IOException | AnvilException e) {
// MinecraftServer.getExceptionManager().handleException(e);
// return null;
// }
// });
// }
//
// private void loadSections(Chunk chunk, ChunkReader chunkReader) {
// final HashMap<String, Biome> biomeCache = new HashMap<>();
// for (NBTCompound sectionNBT : chunkReader.getSections()) {
// ChunkSectionReader sectionReader = new ChunkSectionReader(chunkReader.getMinecraftVersion(), sectionNBT);
//
// if (sectionReader.isSectionEmpty()) continue;
// final int sectionY = sectionReader.getY();
// final int yOffset = Chunk.CHUNK_SECTION_SIZE * sectionY;
//
// Section section = chunk.getSection(sectionY);
//
// if (sectionReader.getSkyLight() != null) {
// section.setSkyLight(sectionReader.getSkyLight().copyArray());
// }
// if (sectionReader.getBlockLight() != null) {
// section.setBlockLight(sectionReader.getBlockLight().copyArray());
// }
//
// // Biomes
// if (chunkReader.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) {
// SectionBiomeInformation sectionBiomeInformation = chunkReader.readSectionBiomes(sectionReader);
//
// if (sectionBiomeInformation != null && sectionBiomeInformation.hasBiomeInformation()) {
// if (sectionBiomeInformation.isFilledWithSingleBiome()) {
// for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
// for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
// for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
// int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x;
// int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z;
// int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y;
// String biomeName = sectionBiomeInformation.getBaseBiome();
// Biome biome = biomeCache.computeIfAbsent(biomeName, n ->
// Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS));
// chunk.setBiome(finalX, finalY, finalZ, biome);
// }
// }
// }
// } else {
// for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
// for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
// for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
// int finalX = chunk.chunkX * Chunk.CHUNK_SIZE_X + x;
// int finalZ = chunk.chunkZ * Chunk.CHUNK_SIZE_Z + z;
// int finalY = sectionY * Chunk.CHUNK_SECTION_SIZE + y;
//
// int index = x / 4 + (z / 4) * 4 + (y / 4) * 16;
// String biomeName = sectionBiomeInformation.getBiomes()[index];
// Biome biome = biomeCache.computeIfAbsent(biomeName, n ->
// Objects.requireNonNullElse(MinecraftServer.getBiomeManager().getByName(NamespaceID.from(n)), PLAINS));
// chunk.setBiome(finalX, finalY, finalZ, biome);
// }
// }
// }
// }
// }
// }
//
// // Blocks
// final NBTList<NBTCompound> blockPalette = sectionReader.getBlockPalette();
// if (blockPalette != null) {
// final int[] blockStateIndices = sectionReader.getUncompressedBlockStateIDs();
// Block[] convertedPalette = new Block[blockPalette.getSize()];
// for (int i = 0; i < convertedPalette.length; i++) {
// final NBTCompound paletteEntry = blockPalette.get(i);
// String blockName = Objects.requireNonNull(paletteEntry.getString("Name"));
// if (blockName.equals("minecraft:air")) {
// convertedPalette[i] = Block.AIR;
// } else {
// if (blockName.equals("minecraft:grass")) {
// blockName = "minecraft:short_grass";
// }
// Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName));
// // Properties
// final Map<String, String> properties = new HashMap<>();
// NBTCompound propertiesNBT = paletteEntry.getCompound("Properties");
// if (propertiesNBT != null) {
// for (var property : propertiesNBT) {
// if (property.getValue().getID() != NBTType.TAG_String) {
// LOGGER.warn("Fail to parse block state properties {}, expected a TAG_String for {}, but contents were {}",
// propertiesNBT,
// property.getKey(),
// property.getValue().toSNBT());
// } else {
// properties.put(property.getKey(), ((NBTString) property.getValue()).getValue());
// }
// }
// }
//
// if (!properties.isEmpty()) block = block.withProperties(properties);
// // Handler
// final BlockHandler handler = MinecraftServer.getBlockManager().getHandler(block.name());
// if (handler != null) block = block.withHandler(handler);
//
// convertedPalette[i] = block;
// }
// }
//
// for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
// for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
// for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
// try {
// final int blockIndex = y * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE + z * Chunk.CHUNK_SECTION_SIZE + x;
// final int paletteIndex = blockStateIndices[blockIndex];
// final Block block = convertedPalette[paletteIndex];
//
// chunk.setBlock(x, y + yOffset, z, block);
// } catch (Exception e) {
// MinecraftServer.getExceptionManager().handleException(e);
// }
// }
// }
// }
// }
// }
// }
//
// private void loadBlockEntities(Chunk loadedChunk, ChunkReader chunkReader) {
// for (NBTCompound te : chunkReader.getBlockEntities()) {
// final var x = te.getInt("x");
// final var y = te.getInt("y");
// final var z = te.getInt("z");
// if (x == null || y == null || z == null) {
// LOGGER.warn("Tile entity has failed to load due to invalid coordinate");
// continue;
// }
// Block block = loadedChunk.getBlock(x, y, z);
//
// final String tileEntityID = te.getString("id");
// if (tileEntityID != null) {
// final BlockHandler handler = MinecraftServer.getBlockManager().getHandlerOrDummy(tileEntityID);
// block = block.withHandler(handler);
// }
// // Remove anvil tags
// MutableNBTCompound mutableCopy = te.toMutableCompound();
// mutableCopy.remove("id");
// mutableCopy.remove("x");
// mutableCopy.remove("y");
// mutableCopy.remove("z");
// mutableCopy.remove("keepPacked");
// // Place block
// final var finalBlock = mutableCopy.getSize() > 0 ?
// block.withNbt(mutableCopy.toCompound()) : block;
// loadedChunk.setBlock(x, y, z, finalBlock);
// }
// }
//
@Override
public @NotNull CompletableFuture<Void> saveInstance(@NotNull Instance instance) {
final NBTCompound nbt = instance.tagHandler().asCompound();
if (nbt.isEmpty()) {
// Instance has no data
return AsyncUtils.VOID_FUTURE;
}
try (NBTWriter writer = new NBTWriter(Files.newOutputStream(levelPath))) {
writer.writeNamed("", nbt);
} catch (IOException e) {
e.printStackTrace();
}
// final NBTCompound nbt = instance.tagHandler().asCompound();
// if (nbt.isEmpty()) {
// // Instance has no data
// return AsyncUtils.VOID_FUTURE;
// }
// try (NBTWriter writer = new NBTWriter(Files.newOutputStream(levelPath))) {
// writer.writeNamed("", nbt);
// } catch (IOException e) {
// e.printStackTrace();
// }
return AsyncUtils.VOID_FUTURE;
}
@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();
// 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;
// }
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 save(Chunk chunk, ChunkWriter chunkWriter) {
final int minY = chunk.getMinSection() * Chunk.CHUNK_SECTION_SIZE;
final int maxY = chunk.getMaxSection() * Chunk.CHUNK_SECTION_SIZE - 1;
chunkWriter.setYPos(minY);
List<NBTCompound> blockEntities = new ArrayList<>();
chunkWriter.setStatus(ChunkColumn.GenerationStatus.Full);
List<NBTCompound> sectionData = new ArrayList<>((maxY - minY + 1) / Chunk.CHUNK_SECTION_SIZE);
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++) {
final int y = sectionLocalY + sectionY * Chunk.CHUNK_SECTION_SIZE;
final int blockIndex = x + sectionLocalY * 16 * 16 + z * 16;
final Block block = chunk.getBlock(x, y, z);
final BlockState hephaistosBlockState = getBlockState(block);
blockPalette.increaseReference(hephaistosBlockState);
palettedBlockStates[blockIndex] = blockPalette.getPaletteIndex(hephaistosBlockState);
// biome are stored for 4x4x4 volumes, avoid unnecessary work
if (x % 4 == 0 && sectionLocalY % 4 == 0 && z % 4 == 0) {
int biomeIndex = (x / 4) + (sectionLocalY / 4) * 4 * 4 + (z / 4) * 4;
final Biome biome = chunk.getBiome(x, y, z);
final String biomeName = biome.name();
biomePalette.increaseReference(biomeName);
palettedBiomes[biomeIndex] = biomePalette.getPaletteIndex(biomeName);
}
// Block entities
final BlockHandler handler = block.handler();
final NBTCompound originalNBT = block.nbt();
if (originalNBT != null || handler != null) {
MutableNBTCompound nbt = originalNBT != null ?
originalNBT.toMutableCompound() : new MutableNBTCompound();
if (handler != null) {
nbt.setString("id", handler.getNamespaceId().asString());
}
nbt.setInt("x", x + Chunk.CHUNK_SIZE_X * chunk.getChunkX());
nbt.setInt("y", y);
nbt.setInt("z", z + Chunk.CHUNK_SIZE_Z * chunk.getChunkZ());
nbt.setByte("keepPacked", (byte) 0);
blockEntities.add(nbt.toCompound());
}
}
}
}
sectionWriter.setPalettedBiomes(biomePalette, palettedBiomes);
sectionWriter.setPalettedBlockStates(blockPalette, palettedBlockStates);
sectionData.add(sectionWriter.toNBT());
}
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);
}
}
}
}
}
}
// private BlockState getBlockState(final Block block) {
// return blockStateId2ObjectCacheTLS.get().computeIfAbsent(block.stateId(), _unused -> new BlockState(block.name(), block.properties()));
// }
//
// 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;
// chunkWriter.setYPos(minY);
// List<NBTCompound> blockEntities = new ArrayList<>();
// chunkWriter.setStatus(ChunkColumn.GenerationStatus.Full);
//
// List<NBTCompound> sectionData = new ArrayList<>((maxY - minY + 1) / Chunk.CHUNK_SECTION_SIZE);
// 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++) {
// final int y = sectionLocalY + sectionY * Chunk.CHUNK_SECTION_SIZE;
//
// final int blockIndex = x + sectionLocalY * 16 * 16 + z * 16;
//
// final Block block = chunk.getBlock(x, y, z);
//
// final BlockState hephaistosBlockState = getBlockState(block);
// blockPalette.increaseReference(hephaistosBlockState);
//
// palettedBlockStates[blockIndex] = blockPalette.getPaletteIndex(hephaistosBlockState);
//
// // biome are stored for 4x4x4 volumes, avoid unnecessary work
// if (x % 4 == 0 && sectionLocalY % 4 == 0 && z % 4 == 0) {
// int biomeIndex = (x / 4) + (sectionLocalY / 4) * 4 * 4 + (z / 4) * 4;
// final Biome biome = chunk.getBiome(x, y, z);
// final String biomeName = biome.name();
//
// biomePalette.increaseReference(biomeName);
// palettedBiomes[biomeIndex] = biomePalette.getPaletteIndex(biomeName);
// }
//
// // Block entities
// final BlockHandler handler = block.handler();
// final NBTCompound originalNBT = block.nbt();
// if (originalNBT != null || handler != null) {
// MutableNBTCompound nbt = originalNBT != null ?
// originalNBT.toMutableCompound() : new MutableNBTCompound();
//
// if (handler != null) {
// nbt.setString("id", handler.getNamespaceId().asString());
// }
// nbt.setInt("x", x + Chunk.CHUNK_SIZE_X * chunk.getChunkX());
// nbt.setInt("y", y);
// nbt.setInt("z", z + Chunk.CHUNK_SIZE_Z * chunk.getChunkZ());
// nbt.setByte("keepPacked", (byte) 0);
// blockEntities.add(nbt.toCompound());
// }
// }
// }
// }
//
// sectionWriter.setPalettedBiomes(biomePalette, palettedBiomes);
// sectionWriter.setPalettedBlockStates(blockPalette, palettedBlockStates);
//
// sectionData.add(sectionWriter.toNBT());
// }
//
// 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);
// }
// }
// }
// }
// }
// }
@Override
public boolean supportsParallelLoading() {

View File

@ -2,6 +2,7 @@ package net.minestom.server.instance;
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
@ -27,8 +28,6 @@ import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -225,7 +224,7 @@ public class DynamicChunk extends Chunk {
}
private @NotNull ChunkDataPacket createChunkPacket() {
final NBTCompound heightmapsNBT = computeHeightmap();
final CompoundBinaryTag heightmapsNBT = computeHeightmap();
// Data
final byte[] data;
@ -242,7 +241,7 @@ public class DynamicChunk extends Chunk {
);
}
protected NBTCompound computeHeightmap() {
protected CompoundBinaryTag computeHeightmap() {
// TODO: don't hardcode heightmaps
// Heightmap
int dimensionHeight = getInstance().getDimensionType().getHeight();
@ -255,9 +254,10 @@ public class DynamicChunk extends Chunk {
}
}
final int bitsForHeight = MathUtils.bitsToRepresent(dimensionHeight);
return NBT.Compound(Map.of(
"MOTION_BLOCKING", NBT.LongArray(encodeBlocks(motionBlocking, bitsForHeight)),
"WORLD_SURFACE", NBT.LongArray(encodeBlocks(worldSurface, bitsForHeight))));
return CompoundBinaryTag.builder()
.putLongArray("MOTION_BLOCKING", encodeBlocks(motionBlocking, bitsForHeight))
.putLongArray("WORLD_SURFACE", encodeBlocks(worldSurface, bitsForHeight))
.build();
}
@NotNull UpdateLightPacket createLightPacket() {

View File

@ -1,6 +1,6 @@
package net.minestom.server.instance;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import net.kyori.adventure.nbt.CompoundBinaryTag;
@FunctionalInterface
public interface ExplosionSupplier {
@ -12,9 +12,9 @@ public interface ExplosionSupplier {
* @param centerY center Y of the explosion
* @param centerZ center Z of the explosion
* @param strength strength of the explosion
* @param additionalData data passed via {@link Instance#explode(float, float, float, float, NBTCompound)} )}. Can be null
* @param additionalData data passed via {@link Instance#explode(float, float, float, float, CompoundBinaryTag)} )}. Can be null
* @return Explosion object representing the algorithm to use
*/
Explosion createExplosion(float centerX, float centerY, float centerZ, float strength, NBTCompound additionalData);
Explosion createExplosion(float centerX, float centerY, float centerZ, float strength, CompoundBinaryTag additionalData);
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.instance;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.pointer.Pointers;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerProcess;
@ -45,7 +46,6 @@ import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.time.Duration;
import java.util.*;
@ -792,7 +792,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
* @param additionalData data to pass to the explosion supplier
* @throws IllegalStateException If no {@link ExplosionSupplier} was supplied
*/
public void explode(float centerX, float centerY, float centerZ, float strength, @Nullable NBTCompound additionalData) {
public void explode(float centerX, float centerY, float centerZ, float strength, @Nullable CompoundBinaryTag additionalData) {
final ExplosionSupplier explosionSupplier = getExplosionSupplier();
Check.stateCondition(explosionSupplier == null, "Tried to create an explosion with no explosion supplier");
final Explosion explosion = explosionSupplier.createExplosion(centerX, centerY, centerZ, strength, additionalData);

View File

@ -1,6 +1,7 @@
package net.minestom.server.instance;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
@ -32,7 +33,6 @@ import net.minestom.server.world.DimensionType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import space.vectrix.flare.fastutil.Long2ObjectSyncMap;
@ -186,7 +186,7 @@ public class InstanceContainer extends Instance {
chunk.sendPacketToViewers(new BlockChangePacket(blockPosition, block.stateId()));
var registry = block.registry();
if (registry.isBlockEntity()) {
final NBTCompound data = BlockUtils.extractClientNbt(block);
final CompoundBinaryTag data = BlockUtils.extractClientNbt(block);
chunk.sendPacketToViewers(new BlockEntityDataPacket(blockPosition, registry.blockEntityId(), data));
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.instance;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.LongArrayBinaryTag;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerFlag;
import net.minestom.server.collision.Shape;
@ -16,8 +18,6 @@ import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@ -202,14 +202,17 @@ public class LightingChunk extends DynamicChunk {
}
@Override
protected NBTCompound computeHeightmap() {
protected CompoundBinaryTag computeHeightmap() {
// Heightmap
int[] heightmap = getHeightmap();
int dimensionHeight = getInstance().getDimensionType().getHeight();
final int bitsForHeight = MathUtils.bitsToRepresent(dimensionHeight);
return NBT.Compound(Map.of(
"MOTION_BLOCKING", NBT.LongArray(encodeBlocks(heightmap, bitsForHeight)),
"WORLD_SURFACE", NBT.LongArray(encodeBlocks(heightmap, bitsForHeight))));
LongArrayBinaryTag encoded = LongArrayBinaryTag.longArrayBinaryTag(encodeBlocks(heightmap, bitsForHeight));
return CompoundBinaryTag.builder()
.put("MOTION_BLOCKING", encoded)
.put("WORLD_SURFACE", encoded)
.build();
}
// Lazy compute heightmap

View File

@ -1,15 +1,15 @@
package net.minestom.server.instance.block;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.batch.Batch;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.*;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
import java.util.Map;
@ -67,7 +67,7 @@ public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks
* @return a new block with different nbt
*/
@Contract(pure = true)
@NotNull Block withNbt(@Nullable NBTCompound compound);
@NotNull Block withNbt(@Nullable CompoundBinaryTag compound);
/**
* Creates a new block with the specified {@link BlockHandler handler}.
@ -86,7 +86,7 @@ public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks
* @return the block nbt, null if not present
*/
@Contract(pure = true)
@Nullable NBTCompound nbt();
@Nullable CompoundBinaryTag nbt();
@Contract(pure = true)
default boolean hasNbt() {

View File

@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.registry.Registry;
import net.minestom.server.tag.Tag;
import net.minestom.server.utils.ArrayUtils;
@ -14,8 +15,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.jetbrains.annotations.Unmodifiable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import java.time.Duration;
import java.util.*;
@ -23,7 +22,7 @@ import java.util.function.Function;
record BlockImpl(@NotNull Registry.BlockEntry registry,
byte @NotNull [] propertiesArray,
@Nullable NBTCompound nbt,
@Nullable CompoundBinaryTag nbt,
@Nullable BlockHandler handler) implements Block {
// Block state -> block object
private static final ObjectArray<Block> BLOCK_STATE_MAP = ObjectArray.singleThread();
@ -86,7 +85,7 @@ record BlockImpl(@NotNull Registry.BlockEntry registry,
final int defaultState = properties.getInt("defaultStateId");
return getState(defaultState);
});
private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder()
private static final Cache<CompoundBinaryTag, CompoundBinaryTag> NBT_CACHE = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMinutes(5))
.weakValues()
.build();
@ -144,14 +143,16 @@ record BlockImpl(@NotNull Registry.BlockEntry registry,
@Override
public @NotNull <T> Block withTag(@NotNull Tag<T> tag, @Nullable T value) {
var temporaryNbt = new MutableNBTCompound(Objects.requireNonNullElse(nbt, NBTCompound.EMPTY));
tag.write(temporaryNbt, value);
final var finalNbt = temporaryNbt.getSize() > 0 ? NBT_CACHE.get(temporaryNbt.toCompound(), Function.identity()) : null;
var builder = CompoundBinaryTag.builder();
if (nbt != null) builder.put(nbt);
tag.write(builder, value);
var temporaryNbt = builder.build();
final var finalNbt = temporaryNbt.size() > 0 ? NBT_CACHE.get(temporaryNbt, Function.identity()) : null;
return new BlockImpl(registry, propertiesArray, finalNbt, handler);
}
@Override
public @NotNull Block withNbt(@Nullable NBTCompound compound) {
public @NotNull Block withNbt(@Nullable CompoundBinaryTag compound) {
return new BlockImpl(registry, propertiesArray, compound, handler);
}
@ -183,7 +184,7 @@ record BlockImpl(@NotNull Registry.BlockEntry registry,
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tag.read(Objects.requireNonNullElse(nbt, NBTCompound.EMPTY));
return tag.read(Objects.requireNonNullElse(nbt, CompoundBinaryTag.empty()));
}
private Map<PropertiesHolder, BlockImpl> possibleProperties() {

View File

@ -1,5 +1,6 @@
package net.minestom.server.item;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.attribute.ItemAttribute;
@ -12,7 +13,6 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
import java.util.function.Consumer;
@ -26,7 +26,7 @@ public sealed interface ItemMeta extends TagReadable, NetworkBuffer.Writer
@Contract(value = "_, -> new", pure = true)
@NotNull ItemMeta with(@NotNull Consumer<@NotNull Builder> builderConsumer);
@NotNull NBTCompound toNBT();
@NotNull CompoundBinaryTag toNBT();
@NotNull String toSNBT();

View File

@ -1,16 +1,17 @@
package net.minestom.server.item;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Consumer;
import static net.minestom.server.network.NetworkBuffer.BYTE;
import static net.minestom.server.network.NetworkBuffer.NBT;
record ItemMetaImpl(TagHandler tagHandler) implements ItemMeta {
@ -29,23 +30,22 @@ record ItemMetaImpl(TagHandler tagHandler) implements ItemMeta {
}
@Override
public @NotNull NBTCompound toNBT() {
public @NotNull CompoundBinaryTag toNBT() {
return tagHandler.asCompound();
}
@Override
public @NotNull String toSNBT() {
return toNBT().toSNBT();
try {
return TagStringIO.get().asString(toNBT());
} catch (IOException e) {
throw new RuntimeException("Failed to convert to SNBT", e);
}
}
@Override
public void write(@NotNull NetworkBuffer writer) {
final NBTCompound nbt = toNBT();
if (nbt.isEmpty()) {
writer.write(BYTE, (byte) 0);
return;
}
writer.write(NBT, nbt);
writer.write(NBT, toNBT());
}
@Override

View File

@ -1,5 +1,6 @@
package net.minestom.server.item;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
@ -11,8 +12,8 @@ import net.minestom.server.tag.TagReadable;
import net.minestom.server.tag.TagWritable;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.*;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
@ -47,13 +48,13 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
}
@Contract(value = "_, _, _ -> new", pure = true)
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound, int amount) {
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable CompoundBinaryTag nbtCompound, int amount) {
if (nbtCompound == null) return of(material, amount);
return builder(material).amount(amount).meta(nbtCompound).build();
}
@Contract(value = "_, _ -> new", pure = true)
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound) {
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable CompoundBinaryTag nbtCompound) {
return fromNBT(material, nbtCompound, 1);
}
@ -63,7 +64,7 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
* @param nbtCompound The nbt representation of the item
*/
@ApiStatus.Experimental
static @NotNull ItemStack fromItemNBT(@NotNull NBTCompound nbtCompound) {
static @NotNull ItemStack fromItemNBT(@NotNull CompoundBinaryTag nbtCompound) {
String id = nbtCompound.getString("id");
Check.notNull(id, "Item NBT must contain an id field.");
Material material = Material.fromNamespaceId(id);
@ -71,7 +72,7 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
Byte amount = nbtCompound.getByte("Count");
if (amount == null) amount = 1;
final NBTCompound tag = nbtCompound.getCompound("tag");
final CompoundBinaryTag tag = nbtCompound.getCompound("tag");
return tag != null ? fromNBT(material, tag, amount) : of(material, amount);
}
@ -168,8 +169,13 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
@Override
default @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
final BinaryTagHolder tagHolder = BinaryTagHolder.encode(meta().toNBT(), MinestomAdventure.NBT_CODEC);
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.showItem(material(), amount(), tagHolder)));
try {
final BinaryTagHolder tagHolder = BinaryTagHolder.encode(meta().toNBT(), MinestomAdventure.NBT_CODEC);
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.showItem(material(), amount(), tagHolder)));
} catch (IOException e) {
//todo(matt): revisit,
throw new RuntimeException(e);
}
}
/**
@ -178,7 +184,7 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
* @return The nbt representation of the item
*/
@ApiStatus.Experimental
@NotNull NBTCompound toItemNBT();
@NotNull CompoundBinaryTag toItemNBT();
@Deprecated
@ -208,7 +214,7 @@ public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEve
@NotNull Builder meta(@NotNull TagHandler tagHandler);
@Contract(value = "_ -> this")
@NotNull Builder meta(@NotNull NBTCompound compound);
@NotNull Builder meta(@NotNull CompoundBinaryTag compound);
@Contract(value = "_ -> this")
@NotNull Builder meta(@NotNull ItemMeta itemMeta);

View File

@ -1,5 +1,6 @@
package net.minestom.server.item;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.ServerFlag;
import net.minestom.server.item.rule.VanillaStackingRule;
import net.minestom.server.tag.Tag;
@ -7,12 +8,7 @@ import net.minestom.server.tag.TagHandler;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTByte;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTString;
import java.util.Map;
import java.util.function.Consumer;
record ItemStackImpl(Material material, int amount, ItemMetaImpl meta) implements ItemStack {
@ -89,12 +85,13 @@ record ItemStackImpl(Material material, int amount, ItemMetaImpl meta) implement
}
@Override
public @NotNull NBTCompound toItemNBT() {
final NBTString material = NBT.String(material().name());
final NBTByte amount = NBT.Byte(amount());
final NBTCompound nbt = meta().toNBT();
if (nbt.isEmpty()) return NBT.Compound(Map.of("id", material, "Count", amount));
return NBT.Compound(Map.of("id", material, "Count", amount, "tag", nbt));
public @NotNull CompoundBinaryTag toItemNBT() {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putString("id", material.name())
.putByte("Count", (byte) amount);
CompoundBinaryTag nbt = meta.toNBT();
if (nbt.size() > 0) builder.put("tag", nbt);
return builder.build();
}
@Contract(value = "-> new", pure = true)
@ -129,7 +126,7 @@ record ItemStackImpl(Material material, int amount, ItemMetaImpl meta) implement
}
@Override
public ItemStack.@NotNull Builder meta(@NotNull NBTCompound compound) {
public ItemStack.@NotNull Builder meta(@NotNull CompoundBinaryTag compound) {
return metaBuilder(new ItemMetaImpl.Builder(TagHandler.fromCompound(compound)));
}

View File

@ -1,20 +1,19 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class TrimManager {
private final Set<TrimMaterial> trimMaterials;
private final Set<TrimPattern> trimPatterns;
private NBTCompound trimMaterialCache = null;
private NBTCompound trimPatternCache = null;
private CompoundBinaryTag trimMaterialCache = null;
private CompoundBinaryTag trimPatternCache = null;
public TrimManager() {
this.trimMaterials = new HashSet<>();
@ -30,38 +29,28 @@ public class TrimManager {
}
public NBTCompound getTrimMaterialNBT() {
public CompoundBinaryTag getTrimMaterialNBT() {
if (trimMaterialCache == null) {
var trimMaterials = this.trimMaterials.stream()
.map((trimMaterial) -> NBT.Compound(Map.of(
"id", NBT.Int(trimMaterial.id()),
"name", NBT.String(trimMaterial.name()),
"element", trimMaterial.asNBT()
)))
.toList();
trimMaterialCache = NBT.Compound(Map.of(
"type", NBT.String("minecraft:trim_material"),
"value", NBT.List(NBTType.TAG_Compound, trimMaterials)
));
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (TrimMaterial trimMaterial : this.trimMaterials)
entries.add(trimMaterial.asNBT());
trimMaterialCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:trim_material")
.put("value", entries.build())
.build();
}
return trimMaterialCache;
}
public NBTCompound getTrimPatternNBT() {
public CompoundBinaryTag getTrimPatternNBT() {
if (trimPatternCache == null) {
var trimPatterns = this.trimPatterns.stream()
.map((trimPattern) -> NBT.Compound(Map.of(
"id", NBT.Int(trimPattern.id()),
"name", NBT.String(trimPattern.name()),
"element", trimPattern.asNBT()
)))
.toList();
trimPatternCache = NBT.Compound(Map.of(
"type", NBT.String("minecraft:trim_pattern"),
"value", NBT.List(NBTType.TAG_Compound, trimPatterns)
));
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (TrimPattern trimPattern : this.trimPatterns)
entries.add(trimPattern.asNBT());
trimPatternCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:trim_pattern")
.put("value", entries.build())
.build();
}
return trimPatternCache;

View File

@ -1,13 +1,13 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
import java.util.Map;
@ -84,6 +84,6 @@ public interface TrimMaterial extends StaticProtocolObject {
return registry().description();
}
NBTCompound asNBT();
CompoundBinaryTag asNBT();
}

View File

@ -1,9 +1,9 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.minestom.server.registry.Registry;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
import java.util.Map;
@ -31,19 +31,15 @@ record TrimMaterialImpl(Registry.TrimMaterialEntry registry, int id) implements
return CONTAINER.values();
}
public NBTCompound asNBT() {
return NBT.Compound(nbt -> {
nbt.setString("asset_name", assetName());
nbt.setString("ingredient", ingredient().namespace().asString());
nbt.setFloat("item_model_index", itemModelIndex());
nbt.set("override_armor_materials", NBT.Compound(overrideArmorMaterials().entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> NBT.String(entry.getValue())
))
));
nbt.set("description", NbtComponentSerializer.nbt().serialize(description()));
});
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
.putString("asset_name", assetName())
.putString("ingredient", ingredient().namespace().asString())
.putFloat("item_model_index", itemModelIndex())
.put("override_armor_materials", CompoundBinaryTag.from(overrideArmorMaterials().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> StringBinaryTag.stringBinaryTag(entry.getValue())))))
.put("description", NbtComponentSerializer.nbt().serialize(description()))
.build();
}
}

View File

@ -1,13 +1,13 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.Material;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
@ -61,6 +61,6 @@ public interface TrimPattern extends StaticProtocolObject {
return registry().decal();
}
NBTCompound asNBT();
CompoundBinaryTag asNBT();
}

View File

@ -1,9 +1,8 @@
package net.minestom.server.item.armor;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.adventure.serializer.nbt.NbtComponentSerializer;
import net.minestom.server.registry.Registry;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
@ -29,13 +28,13 @@ record TrimPatternImpl(Registry.TrimPatternEntry registry, int id) implements Tr
return CONTAINER.values();
}
public NBTCompound asNBT() {
return NBT.Compound(nbt -> {
nbt.setString("asset_id", assetID().asString());
nbt.setString("template_item", template().namespace().asString());
nbt.set("description", NbtComponentSerializer.nbt().serialize(description()));
nbt.setByte("decal", (byte) (decal() ? 1 : 0));
});
public CompoundBinaryTag asNBT() {
return CompoundBinaryTag.builder()
.putString("asset_id", assetID().asString())
.putString("template_item", template().namespace().asString())
.put("description", NbtComponentSerializer.nbt().serialize(description()))
.putBoolean("decal", decal())
.build();
}
}

View File

@ -1,14 +1,11 @@
package net.minestom.server.item.firework;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.color.Color;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTIntArray;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public record FireworkEffect(boolean flicker, boolean trail,
@NotNull FireworkEffectType type,
@ -25,36 +22,34 @@ public record FireworkEffect(boolean flicker, boolean trail,
* @param compound The NBT connection, which should be a fireworks effect.
* @return A new created firework effect.
*/
public static @NotNull FireworkEffect fromCompound(@NotNull NBTCompound compound) {
public static @NotNull FireworkEffect fromCompound(@NotNull CompoundBinaryTag compound) {
List<Color> primaryColor = new ArrayList<>();
List<Color> secondaryColor = new ArrayList<>();
if (compound.get("Colors") instanceof NBTIntArray colors) {
for (int rgb : colors) primaryColor.add(new Color(rgb));
}
if (compound.get("FadeColors") instanceof NBTIntArray fadeColors) {
for (int rgb : fadeColors) secondaryColor.add(new Color(rgb));
}
for (int rgb : compound.getIntArray("Colors"))
primaryColor.add(new Color(rgb));
for (int rgb : compound.getIntArray("FadeColors"))
secondaryColor.add(new Color(rgb));
boolean flicker = compound.containsKey("Flicker") && compound.getBoolean("Flicker");
boolean trail = compound.containsKey("Trail") && compound.getBoolean("Trail");
FireworkEffectType type = compound.containsKey("Type") ?
FireworkEffectType.byId(compound.getAsByte("Type")) : FireworkEffectType.SMALL_BALL;
boolean flicker = compound.getBoolean("Flicker");
boolean trail = compound.getBoolean("Trail");
FireworkEffectType type = FireworkEffectType.byId(compound.getByte("Type"));
return new FireworkEffect(flicker, trail, type, primaryColor, secondaryColor);
}
/**
* Retrieves the {@link FireworkEffect} as a {@link NBTCompound}.
* Retrieves the {@link FireworkEffect} as a {@link CompoundBinaryTag}.
*
* @return The firework effect as a nbt compound.
*/
public @NotNull NBTCompound asCompound() {
return NBT.Compound(Map.of(
"Flicker", NBT.Boolean(flicker),
"Trail", NBT.Boolean(trail),
"Type", NBT.Byte(type.getType()),
"Colors", NBT.IntArray(colors.stream().mapToInt(Color::asRGB).toArray()),
"FadeColors", NBT.IntArray(fadeColors.stream().mapToInt(Color::asRGB).toArray())));
public @NotNull CompoundBinaryTag asCompound() {
return CompoundBinaryTag.builder()
.putBoolean("Flicker", flicker)
.putBoolean("Trail", trail)
.putByte("Type", (byte) type.getType())
.putIntArray("Colors", colors.stream().mapToInt(Color::asRGB).toArray())
.putIntArray("FadeColors", fadeColors.stream().mapToInt(Color::asRGB).toArray())
.build();
}
}

View File

@ -1,32 +1,30 @@
package net.minestom.server.item.metadata;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.item.ItemMetaView;
import net.minestom.server.tag.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public record PlayerHeadMeta(TagReadable readable) implements ItemMetaView<PlayerHeadMeta.Builder> {
public static final Tag<UUID> SKULL_OWNER = Tag.UUID("Id").path("SkullOwner");
public static final Tag<PlayerSkin> SKIN = Tag.Structure("Properties", new TagSerializer<PlayerSkin>() {
private static final Tag<NBT> TEXTURES = Tag.NBT("textures");
private static final Tag<BinaryTag> TEXTURES = Tag.NBT("textures");
@Override
public @Nullable PlayerSkin read(@NotNull TagReadable reader) {
final NBT result = reader.getTag(TEXTURES);
if (!(result instanceof NBTList)) return null;
final NBTList<NBTCompound> textures = (NBTList<NBTCompound>) result;
final NBTCompound texture = textures.get(0);
final BinaryTag result = reader.getTag(TEXTURES);
if (!(result instanceof ListBinaryTag textures)) return null;
final CompoundBinaryTag texture = textures.getCompound(0);
final String value = texture.getString("Value");
final String signature = texture.getString("Signature");
return new PlayerSkin(value, signature);
@ -36,9 +34,9 @@ public record PlayerHeadMeta(TagReadable readable) implements ItemMetaView<Playe
public void write(@NotNull TagWritable writer, @NotNull PlayerSkin playerSkin) {
final String value = Objects.requireNonNullElse(playerSkin.textures(), "");
final String signature = Objects.requireNonNullElse(playerSkin.signature(), "");
NBTList<NBTCompound> textures = new NBTList<>(NBTType.TAG_Compound,
List.of(NBT.Compound(Map.of("Value", NBT.String(value), "Signature", NBT.String(signature)))));
writer.setTag(TEXTURES, textures);
writer.setTag(TEXTURES, ListBinaryTag.listBinaryTag(BinaryTagTypes.COMPOUND, List.of(
CompoundBinaryTag.builder().putString("Value", value).putString("Signature", signature).build()
)));
}
}).path("SkullOwner");

View File

@ -1,5 +1,6 @@
package net.minestom.server.listener;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
@ -22,7 +23,6 @@ import net.minestom.server.network.packet.server.play.AcknowledgeBlockChangePack
import net.minestom.server.network.packet.server.play.BlockEntityDataPacket;
import net.minestom.server.utils.block.BlockUtils;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
public final class PlayerDiggingListener {
@ -58,7 +58,7 @@ public final class PlayerDiggingListener {
// Refresh block on player screen in case it had special data (like a sign)
var registry = diggingResult.block().registry();
if (registry.isBlockEntity()) {
final NBTCompound data = BlockUtils.extractClientNbt(diggingResult.block());
final CompoundBinaryTag data = BlockUtils.extractClientNbt(diggingResult.block());
player.sendPacketToViewersAndSelf(new BlockEntityDataPacket(blockPosition, registry.blockEntityId(), data));
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.message;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
@ -7,11 +9,8 @@ import net.minestom.server.network.packet.server.play.SystemChatPacket;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import java.io.StringReader;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
@ -27,11 +26,11 @@ public final class Messenger {
private static final UUID NO_SENDER = new UUID(0, 0);
private static final SystemChatPacket CANNOT_SEND_PACKET = new SystemChatPacket(CANNOT_SEND_MESSAGE, false);
private static final NBTCompound CHAT_REGISTRY;
private static final CompoundBinaryTag CHAT_REGISTRY;
static {
try {
CHAT_REGISTRY = (NBTCompound) new SNBTParser(new StringReader(
CHAT_REGISTRY = TagStringIO.get().asCompound(
"""
{
"type": "minecraft:chat_type",
@ -57,13 +56,13 @@ public final class Messenger {
}
} ]
}"""
)).parse();
} catch (NBTException e) {
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static @NotNull NBTCompound chatRegistry() {
public static @NotNull CompoundBinaryTag chatRegistry() {
return CHAT_REGISTRY;
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.network;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
@ -32,7 +33,8 @@ import org.jctools.queues.MpscUnboundedArrayQueue;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@ -280,14 +282,15 @@ public final class ConnectionManager {
// Registry data (if it should be sent)
if (event.willSendRegistryData()) {
var registry = new HashMap<String, NBT>();
registry.put("minecraft:chat_type", Messenger.chatRegistry());
registry.put("minecraft:dimension_type", MinecraftServer.getDimensionTypeManager().toNBT());
registry.put("minecraft:worldgen/biome", MinecraftServer.getBiomeManager().toNBT());
registry.put("minecraft:damage_type", DamageType.getNBT());
registry.put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT());
registry.put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT());
player.sendPacket(new RegistryDataPacket(NBT.Compound(registry)));
var registryCompound = CompoundBinaryTag.builder()
.put("minecraft:chat_type", Messenger.chatRegistry())
.put("minecraft:dimension_type", MinecraftServer.getDimensionTypeManager().toNBT())
.put("minecraft:worldgen/biome", MinecraftServer.getBiomeManager().toNBT())
.put("minecraft:damage_type", DamageType.getNBT())
// .put("minecraft:trim_material", MinecraftServer.getTrimManager().getTrimMaterialNBT())
// .put("minecraft:trim_pattern", MinecraftServer.getTrimManager().getTrimPatternNBT())
.build();
player.sendPacket(new RegistryDataPacket(registryCompound));
player.sendPacket(TagsPacket.DEFAULT_TAGS);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.network;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
@ -15,10 +16,9 @@ import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTReader;
import org.jglrxavpok.hephaistos.nbt.NBTWriter;
import java.io.DataInput;
import java.io.DataOutput;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
@ -40,7 +40,7 @@ public final class NetworkBuffer {
public static final Type<Long> VAR_LONG = new NetworkBufferTypeImpl.VarLongType();
public static final Type<byte[]> RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType();
public static final Type<String> STRING = new NetworkBufferTypeImpl.StringType();
public static final Type<NBT> NBT = new NetworkBufferTypeImpl.NbtType();
public static final Type<BinaryTag> NBT = new NetworkBufferTypeImpl.NbtType();
public static final Type<Point> BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType();
public static final Type<Component> COMPONENT = new NetworkBufferTypeImpl.ComponentType();
public static final Type<Component> JSON_COMPONENT = new NetworkBufferTypeImpl.JsonComponentType();
@ -81,8 +81,8 @@ public final class NetworkBuffer {
int writeIndex;
int readIndex;
NBTWriter nbtWriter;
NBTReader nbtReader;
DataOutput nbtWriter;
DataInput nbtReader;
public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) {
this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN);

View File

@ -1,16 +1,16 @@
package net.minestom.server.network.packet.server.configuration;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import static net.minestom.server.network.NetworkBuffer.NBT;
public record RegistryDataPacket(@NotNull NBTCompound data) implements ServerPacket.Configuration {
public record RegistryDataPacket(@NotNull CompoundBinaryTag data) implements ServerPacket.Configuration {
public RegistryDataPacket(@NotNull NetworkBuffer buffer) {
this((NBTCompound) buffer.read(NBT));
this((CompoundBinaryTag) buffer.read(NBT));
}
@Override

View File

@ -1,19 +1,19 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import static net.minestom.server.network.NetworkBuffer.*;
public record BlockEntityDataPacket(@NotNull Point blockPosition, int action,
@Nullable NBTCompound data) implements ServerPacket.Play {
@Nullable CompoundBinaryTag data) implements ServerPacket.Play {
public BlockEntityDataPacket(@NotNull NetworkBuffer reader) {
this(reader.read(BLOCK_POSITION), reader.read(VAR_INT), (NBTCompound) reader.read(NBT));
this(reader.read(BLOCK_POSITION), reader.read(VAR_INT), (CompoundBinaryTag) reader.read(NBT));
}
@Override

View File

@ -1,20 +1,20 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.potion.Potion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import static net.minestom.server.network.NetworkBuffer.*;
public record EntityEffectPacket(int entityId, @NotNull Potion potion,
@Nullable NBTCompound factorCodec) implements ServerPacket.Play {
@Nullable CompoundBinaryTag factorCodec) implements ServerPacket.Play {
public EntityEffectPacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), new Potion(reader),
reader.read(BOOLEAN) ? (NBTCompound) reader.read(NBT) : null);
reader.read(BOOLEAN) ? (CompoundBinaryTag) reader.read(NBT) : null);
}
@Override

View File

@ -1,16 +1,17 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import static net.minestom.server.network.NetworkBuffer.*;
public record NbtQueryResponsePacket(int transactionId, NBTCompound data) implements ServerPacket.Play {
public record NbtQueryResponsePacket(int transactionId, CompoundBinaryTag data) implements ServerPacket.Play {
public NbtQueryResponsePacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), (NBTCompound) reader.read(NBT));
this(reader.read(VAR_INT), (CompoundBinaryTag) reader.read(NBT));
}
@Override

View File

@ -1,12 +1,12 @@
package net.minestom.server.network.packet.server.play.data;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.block.BlockUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.HashMap;
import java.util.Map;
@ -14,7 +14,7 @@ import java.util.stream.Collectors;
import static net.minestom.server.network.NetworkBuffer.*;
public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data,
public record ChunkData(@NotNull CompoundBinaryTag heightmaps, byte @NotNull [] data,
@NotNull Map<Integer, Block> blockEntities) implements NetworkBuffer.Writer {
public ChunkData {
blockEntities = blockEntities.entrySet()
@ -24,7 +24,7 @@ public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data,
}
public ChunkData(@NotNull NetworkBuffer reader) {
this((NBTCompound) reader.read(NBT), reader.read(BYTE_ARRAY),
this((CompoundBinaryTag) reader.read(NBT), reader.read(BYTE_ARRAY),
readBlockEntities(reader));
}
@ -46,7 +46,7 @@ public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data,
writer.write(SHORT, (short) point.blockY()); // y
writer.write(VAR_INT, registry.blockEntityId());
final NBTCompound nbt = BlockUtils.extractClientNbt(block);
final CompoundBinaryTag nbt = BlockUtils.extractClientNbt(block);
assert nbt != null;
writer.write(NBT, nbt); // block nbt
}
@ -59,7 +59,7 @@ public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data,
final byte xz = reader.read(BYTE);
final short y = reader.read(SHORT);
final int blockEntityId = reader.read(VAR_INT);
final NBTCompound nbt = (NBTCompound) reader.read(NBT);
final CompoundBinaryTag nbt = (CompoundBinaryTag) reader.read(NBT);
// TODO create block object
}
return blockEntities;

View File

@ -1,23 +1,23 @@
package net.minestom.server.permission;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Objects;
/**
* Representation of a permission granted to a {@link CommandSender}.
* Each permission has a string representation used as an identifier, and an optional
* {@link NBTCompound} used to store additional data.
* {@link CompoundBinaryTag} used to store additional data.
* <p>
* The class is immutable.
*/
public class Permission {
private final String permissionName;
private final NBTCompound data;
private final CompoundBinaryTag data;
/**
* Creates a new permission object with optional data.
@ -25,7 +25,7 @@ public class Permission {
* @param permissionName the name of the permission
* @param data the optional data of the permission
*/
public Permission(@NotNull String permissionName, @Nullable NBTCompound data) {
public Permission(@NotNull String permissionName, @Nullable CompoundBinaryTag data) {
this.permissionName = permissionName;
this.data = data;
}
@ -55,7 +55,7 @@ public class Permission {
* @return the nbt data of this permission, can be null if not any
*/
@Nullable
public NBTCompound getNBTData() {
public CompoundBinaryTag getNBTData() {
return data;
}

View File

@ -2,8 +2,6 @@ package net.minestom.server.permission;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.parser.SNBTParser;
import java.util.Set;
import java.util.regex.Pattern;
@ -14,8 +12,7 @@ import java.util.regex.Pattern;
* Permissions are in-memory only by default.
* You have however the capacity to store them persistently as the {@link Permission} object
* is serializer-friendly, {@link Permission#getPermissionName()} being a {@link String}
* and {@link Permission#getNBTData()} serializable into a string using {@link NBTCompound#toSNBT()}
* and deserialized back with {@link SNBTParser#parse()}.
* and {@link Permission#getNBTData()} serializable into a string using {@link net.kyori.adventure.nbt.TagStringIO}.
*/
public interface PermissionHandler {

View File

@ -1,10 +1,10 @@
package net.minestom.server.permission;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
/**
* Interface used to check if the {@link NBTCompound nbt data} of a {@link Permission} is correct.
* Interface used to check if the {@link CompoundBinaryTag nbt data} of a {@link Permission} is correct.
*/
@FunctionalInterface
public interface PermissionVerifier {
@ -16,5 +16,5 @@ public interface PermissionVerifier {
* @return true if {@link PermissionHandler#hasPermission(String, PermissionVerifier)}
* should return true, false otherwise
*/
boolean isValid(@Nullable NBTCompound nbtCompound);
boolean isValid(@Nullable CompoundBinaryTag nbtCompound);
}

View File

@ -1,10 +1,10 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.ServerFlag;
import net.minestom.server.item.ItemStack;
import org.jglrxavpok.hephaistos.nbt.*;
import java.util.UUID;
import java.util.function.Function;
@ -13,40 +13,41 @@ import java.util.function.Function;
* Basic serializers for {@link Tag tags}.
*/
final class Serializers {
static final Entry<Byte, NBTByte> BYTE = new Entry<>(NBTType.TAG_Byte, NBTByte::getValue, NBT::Byte);
static final Entry<Boolean, NBTByte> BOOLEAN = new Entry<>(NBTType.TAG_Byte, NBTByte::asBoolean, NBT::Boolean);
static final Entry<Short, NBTShort> SHORT = new Entry<>(NBTType.TAG_Short, NBTShort::getValue, NBT::Short);
static final Entry<Integer, NBTInt> INT = new Entry<>(NBTType.TAG_Int, NBTInt::getValue, NBT::Int);
static final Entry<Long, NBTLong> LONG = new Entry<>(NBTType.TAG_Long, NBTLong::getValue, NBT::Long);
static final Entry<Float, NBTFloat> FLOAT = new Entry<>(NBTType.TAG_Float, NBTFloat::getValue, NBT::Float);
static final Entry<Double, NBTDouble> DOUBLE = new Entry<>(NBTType.TAG_Double, NBTDouble::getValue, NBT::Double);
static final Entry<String, NBTString> STRING = new Entry<>(NBTType.TAG_String, NBTString::getValue, NBT::String);
static final Entry<NBT, NBT> NBT_ENTRY = new Entry<>(null, Function.identity(), Function.identity());
static final Entry<Byte, ByteBinaryTag> BYTE = new Entry<>(BinaryTagTypes.BYTE, ByteBinaryTag::value, ByteBinaryTag::byteBinaryTag);
static final Entry<Boolean, ByteBinaryTag> BOOLEAN = new Entry<>(BinaryTagTypes.BYTE, b -> b.value() != 0, b -> b ? ByteBinaryTag.ONE : ByteBinaryTag.ZERO);
static final Entry<Short, ShortBinaryTag> SHORT = new Entry<>(BinaryTagTypes.SHORT, ShortBinaryTag::value, ShortBinaryTag::shortBinaryTag);
static final Entry<Integer, IntBinaryTag> INT = new Entry<>(BinaryTagTypes.INT, IntBinaryTag::value, IntBinaryTag::intBinaryTag);
static final Entry<Long, LongBinaryTag> LONG = new Entry<>(BinaryTagTypes.LONG, LongBinaryTag::value, LongBinaryTag::longBinaryTag);
static final Entry<Float, FloatBinaryTag> FLOAT = new Entry<>(BinaryTagTypes.FLOAT, FloatBinaryTag::value, FloatBinaryTag::floatBinaryTag);
static final Entry<Double, DoubleBinaryTag> DOUBLE = new Entry<>(BinaryTagTypes.DOUBLE, DoubleBinaryTag::value, DoubleBinaryTag::doubleBinaryTag);
static final Entry<String, StringBinaryTag> STRING = new Entry<>(BinaryTagTypes.STRING, StringBinaryTag::value, StringBinaryTag::stringBinaryTag);
static final Entry<BinaryTag, BinaryTag> NBT_ENTRY = new Entry<>(null, Function.identity(), Function.identity());
static final Entry<java.util.UUID, NBTIntArray> UUID = new Entry<>(NBTType.TAG_Int_Array, intArray -> intArrayToUuid(intArray.getValue().copyArray()),
uuid -> NBT.IntArray(uuidToIntArray(uuid)));
static final Entry<ItemStack, NBTCompound> ITEM = new Entry<>(NBTType.TAG_Compound, ItemStack::fromItemNBT, ItemStack::toItemNBT);
static final Entry<Component, NBTString> COMPONENT = new Entry<>(NBTType.TAG_String, input -> GsonComponentSerializer.gson().deserialize(input.getValue()),
component -> NBT.String(GsonComponentSerializer.gson().serialize(component)));
static final Entry<java.util.UUID, IntArrayBinaryTag> UUID = new Entry<>(BinaryTagTypes.INT_ARRAY,
intArray -> intArrayToUuid(intArray.value()),
uuid -> IntArrayBinaryTag.intArrayBinaryTag(uuidToIntArray(uuid)));
static final Entry<ItemStack, CompoundBinaryTag> ITEM = new Entry<>(BinaryTagTypes.COMPOUND, ItemStack::fromItemNBT, ItemStack::toItemNBT);
static final Entry<Component, StringBinaryTag> COMPONENT = new Entry<>(BinaryTagTypes.STRING, input -> GsonComponentSerializer.gson().deserialize(input.value()),
component -> StringBinaryTag.stringBinaryTag(GsonComponentSerializer.gson().serialize(component)));
static final Entry<Object, NBTByte> EMPTY = new Entry<>(NBTType.TAG_Byte, unused -> null, component -> null);
static final Entry<Object, ByteBinaryTag> EMPTY = new Entry<>(BinaryTagTypes.BYTE, unused -> null, component -> null);
static <T> Entry<T, NBTCompound> fromTagSerializer(TagSerializer<T> serializer) {
return new Serializers.Entry<>(NBTType.TAG_Compound,
(NBTCompound compound) -> {
if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.isEmpty()) return null;
static <T> Entry<T, CompoundBinaryTag> fromTagSerializer(TagSerializer<T> serializer) {
return new Serializers.Entry<>(BinaryTagTypes.COMPOUND,
(CompoundBinaryTag compound) -> {
if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.size() == 0) return null;
return serializer.read(TagHandler.fromCompound(compound));
},
(value) -> {
if (value == null) return NBTCompound.EMPTY;
if (value == null) return CompoundBinaryTag.empty();
TagHandler handler = TagHandler.newHandler();
serializer.write(handler, value);
return handler.asCompound();
});
}
record Entry<T, N extends NBT>(NBTType<N> nbtType, Function<N, T> reader, Function<T, N> writer, boolean isPath) {
Entry(NBTType<N> nbtType, Function<N, T> reader, Function<T, N> writer) {
record Entry<T, N extends BinaryTag>(BinaryTagType<N> nbtType, Function<N, T> reader, Function<T, N> writer, boolean isPath) {
Entry(BinaryTagType<N> nbtType, Function<N, T> reader, Function<T, N> writer) {
this(nbtType, reader, writer, false);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.*;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemStack;
import net.minestom.server.utils.collection.AutoIncrementMap;
@ -7,11 +8,6 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import java.util.Arrays;
import java.util.List;
@ -37,7 +33,7 @@ public class Tag<T> {
final int index;
private final String key;
final Serializers.Entry<T, NBT> entry;
final Serializers.Entry<T, BinaryTag> entry;
private final Supplier<T> defaultValue;
final Function<?, ?> readComparator;
@ -48,7 +44,7 @@ public class Tag<T> {
Tag(int index, String key,
Function<?, ?> readComparator,
Serializers.Entry<T, NBT> entry,
Serializers.Entry<T, BinaryTag> entry,
Supplier<T> defaultValue, PathEntry[] path, UnaryOperator<T> copy, int listScope) {
assert index == INDEX_MAP.get(key);
this.index = index;
@ -61,8 +57,8 @@ public class Tag<T> {
this.listScope = listScope;
}
static <T, N extends NBT> Tag<T> tag(@NotNull String key, @NotNull Serializers.Entry<T, N> entry) {
return new Tag<>(INDEX_MAP.get(key), key, entry.reader(), (Serializers.Entry<T, NBT>) entry,
static <T, N extends BinaryTag> Tag<T> tag(@NotNull String key, @NotNull Serializers.Entry<T, N> entry) {
return new Tag<>(INDEX_MAP.get(key), key, entry.reader(), (Serializers.Entry<T, BinaryTag>) entry,
null, null, null, 0);
}
@ -98,11 +94,11 @@ public class Tag<T> {
public <R> Tag<R> map(@NotNull Function<T, R> readMap,
@NotNull Function<R, T> writeMap) {
var entry = this.entry;
final Function<NBT, R> readFunction = entry.reader().andThen(t -> {
final Function<BinaryTag, R> readFunction = entry.reader().andThen(t -> {
if (t == null) return null;
return readMap.apply(t);
});
final Function<R, NBT> writeFunction = writeMap.andThen(entry.writer());
final Function<R, BinaryTag> writeFunction = writeMap.andThen(entry.writer());
return new Tag<>(index, key, readMap,
new Serializers.Entry<>(entry.nbtType(), readFunction, writeFunction),
// Default value
@ -120,18 +116,18 @@ public class Tag<T> {
var entry = this.entry;
var readFunction = entry.reader();
var writeFunction = entry.writer();
var listEntry = new Serializers.Entry<List<T>, NBTList<?>>(
NBTType.TAG_List,
var listEntry = new Serializers.Entry<List<T>, ListBinaryTag>(
BinaryTagTypes.LIST,
read -> {
if (read.isEmpty()) return List.of();
return read.asListView().stream().map(readFunction).toList();
if (read.size() == 0) return List.of();
return read.stream().map(readFunction).toList();
},
write -> {
if (write.isEmpty())
return NBT.List(NBTType.TAG_String); // String is the default type for lists
final List<NBT> list = write.stream().map(writeFunction).toList();
final NBTType<?> type = list.get(0).getID();
return NBT.List(type, list);
return ListBinaryTag.empty();
final List<BinaryTag> list = write.stream().map(writeFunction).toList();
final BinaryTagType<?> type = list.get(0).type();
return ListBinaryTag.listBinaryTag(type, list);
});
UnaryOperator<List<T>> co = this.copy != null ? ts -> {
final int size = ts.size();
@ -165,8 +161,8 @@ public class Tag<T> {
return new Tag<>(index, key, readComparator, entry, defaultValue, pathEntries, copy, listScope);
}
public @Nullable T read(@NotNull NBTCompoundLike nbt) {
final NBT readable = isView() ? nbt.toCompound() : nbt.get(key);
public @Nullable T read(@NotNull CompoundBinaryTag nbt) {
final BinaryTag readable = isView() ? nbt : nbt.get(key);
final T result;
try {
if (readable == null || (result = entry.read(readable)) == null)
@ -177,18 +173,20 @@ public class Tag<T> {
}
}
public void write(@NotNull MutableNBTCompound nbtCompound, @Nullable T value) {
public void write(@NotNull CompoundBinaryTag.Builder nbtCompound, @Nullable T value) {
if (value != null) {
final NBT nbt = entry.write(value);
if (isView()) nbtCompound.copyFrom((NBTCompoundLike) nbt);
else nbtCompound.set(key, nbt);
final BinaryTag nbt = entry.write(value);
if (isView()) nbtCompound.put((CompoundBinaryTag) nbt);
else nbtCompound.put(key, nbt);
} else {
if (isView()) nbtCompound.clear();
else nbtCompound.remove(key);
if (isView()) {
// Adventure compound builder doesn't currently have a clear method.
nbtCompound.build().keySet().forEach(nbtCompound::remove);
} else nbtCompound.remove(key);
}
}
public void writeUnsafe(@NotNull MutableNBTCompound nbtCompound, @Nullable Object value) {
public void writeUnsafe(@NotNull CompoundBinaryTag.Builder nbtCompound, @Nullable Object value) {
//noinspection unchecked
write(nbtCompound, (T) value);
}
@ -279,11 +277,11 @@ public class Tag<T> {
}
/**
* Creates a flexible tag able to read and write any {@link NBT} objects.
* Creates a flexible tag able to read and write any {@link BinaryTag} objects.
* <p>
* Specialized tags are recommended if the type is known as conversion will be required both way (read and write).
*/
public static @NotNull Tag<NBT> NBT(@NotNull String key) {
public static @NotNull Tag<BinaryTag> NBT(@NotNull String key) {
return tag(key, Serializers.NBT_ENTRY);
}

View File

@ -1,10 +1,9 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
import java.util.function.UnaryOperator;
@ -26,7 +25,7 @@ public interface TagHandler extends TagReadable, TagWritable {
/**
* Creates a copy of this handler.
* <p>
* Similar to {@link #fromCompound(NBTCompoundLike)} using {@link #asCompound()}
* Similar to {@link #fromCompound(CompoundBinaryTag)} using {@link #asCompound()}
* with the advantage that cached objects and adaptive optimizations may be reused.
*
* @return a copy of this handler
@ -36,18 +35,18 @@ public interface TagHandler extends TagReadable, TagWritable {
/**
* Updates the content of this handler.
* <p>
* Can be used as a clearing method with {@link NBTCompound#EMPTY}.
* Can be used as a clearing method with {@link CompoundBinaryTag#empty()}.
*
* @param compound the new content of this handler
*/
void updateContent(@NotNull NBTCompoundLike compound);
void updateContent(@NotNull CompoundBinaryTag compound);
/**
* Converts the content of this handler into a {@link NBTCompound}.
* Converts the content of this handler into a {@link CompoundBinaryTag}.
*
* @return a nbt compound representation of this handler
*/
@NotNull NBTCompound asCompound();
@NotNull CompoundBinaryTag asCompound();
@ApiStatus.Experimental
<T> void updateTag(@NotNull Tag<T> tag,
@ -67,12 +66,12 @@ public interface TagHandler extends TagReadable, TagWritable {
}
/**
* Copy the content of the given {@link NBTCompoundLike} into a new {@link TagHandler}.
* Copy the content of the given {@link CompoundBinaryTag} into a new {@link TagHandler}.
*
* @param compound the compound to read tags from
* @return a new tag handler with the content of the given compound
*/
static @NotNull TagHandler fromCompound(@NotNull NBTCompoundLike compound) {
static @NotNull TagHandler fromCompound(@NotNull CompoundBinaryTag compound) {
return TagHandlerImpl.fromCompound(compound);
}
}

View File

@ -1,22 +1,21 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagType;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.ServerFlag;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import java.lang.invoke.VarHandle;
import java.util.Map;
import java.util.function.UnaryOperator;
final class TagHandlerImpl implements TagHandler {
static final Serializers.Entry<Node, NBTCompound> NODE_SERIALIZER = new Serializers.Entry<>(NBTType.TAG_Compound, entries -> fromCompound(entries).root, Node::compound, true);
static final Serializers.Entry<Node, CompoundBinaryTag> NODE_SERIALIZER = new Serializers.Entry<>(BinaryTagTypes.COMPOUND, entries -> fromCompound(entries).root, Node::compound, true);
private final Node root;
private volatile Node copy;
@ -29,8 +28,7 @@ final class TagHandlerImpl implements TagHandler {
this.root = new Node();
}
static TagHandlerImpl fromCompound(NBTCompoundLike compoundLike) {
final NBTCompound compound = compoundLike.toCompound();
static TagHandlerImpl fromCompound(CompoundBinaryTag compound) {
TagHandlerImpl handler = new TagHandlerImpl();
TagNbtSeparator.separate(compound, entry -> handler.setTag(entry.tag(), entry.value()));
handler.root.compound = compound;
@ -50,7 +48,7 @@ final class TagHandlerImpl implements TagHandler {
synchronized (this) {
Node syncNode = traversePathWrite(root, tag, value != null);
if (syncNode != null) {
syncNode.updateContent(value != null ? (NBTCompound) tag.entry.write(value) : NBTCompound.EMPTY);
syncNode.updateContent(value != null ? (CompoundBinaryTag) tag.entry.write(value) : CompoundBinaryTag.empty());
syncNode.invalidate();
}
}
@ -103,7 +101,7 @@ final class TagHandlerImpl implements TagHandler {
if (tag.isView()) {
final T previousValue = tag.read(node.compound());
final T newValue = value.apply(previousValue);
node.updateContent((NBTCompoundLike) tag.entry.write(newValue));
node.updateContent((CompoundBinaryTag) tag.entry.write(newValue));
node.invalidate();
return returnPrevious ? previousValue : newValue;
}
@ -116,7 +114,7 @@ final class TagHandlerImpl implements TagHandler {
if (previousEntry != null) {
final Object previousTmp = previousEntry.value;
if (previousTmp instanceof Node n) {
final NBTCompound compound = NBT.Compound(Map.of(tag.getKey(), n.compound()));
final CompoundBinaryTag compound = CompoundBinaryTag.from(Map.of(tag.getKey(), n.compound()));
previousValue = tag.read(compound);
} else {
previousValue = (T) previousTmp;
@ -149,12 +147,12 @@ final class TagHandlerImpl implements TagHandler {
}
@Override
public synchronized void updateContent(@NotNull NBTCompoundLike compound) {
public synchronized void updateContent(@NotNull CompoundBinaryTag compound) {
this.root.updateContent(compound);
}
@Override
public @NotNull NBTCompound asCompound() {
public @NotNull CompoundBinaryTag asCompound() {
VarHandle.fullFence();
return root.compound();
}
@ -200,7 +198,7 @@ final class TagHandlerImpl implements TagHandler {
// Slow path is taken if the entry comes from a Structure tag, requiring conversion from NBT
Node tmp = local;
local = new Node(tmp);
if (synEntry != null && synEntry.updatedNbt() instanceof NBTCompound compound) {
if (synEntry != null && synEntry.updatedNbt() instanceof CompoundBinaryTag compound) {
local.updateContent(compound);
}
tmp.entries.put(pathIndex, Entry.makePathEntry(path.name(), local));
@ -211,8 +209,8 @@ final class TagHandlerImpl implements TagHandler {
}
private <T> Entry<?> valueToEntry(Node parent, Tag<T> tag, @NotNull T value) {
if (value instanceof NBT nbt) {
if (nbt instanceof NBTCompound compound) {
if (value instanceof BinaryTag nbt) {
if (nbt instanceof CompoundBinaryTag compound) {
final TagHandlerImpl handler = fromCompound(compound);
return Entry.makePathEntry(tag, new Node(parent, handler.root.entries));
} else {
@ -227,7 +225,7 @@ final class TagHandlerImpl implements TagHandler {
final class Node implements TagReadable {
final Node parent;
final StaticIntMap<Entry<?>> entries;
NBTCompound compound;
CompoundBinaryTag compound;
public Node(Node parent, StaticIntMap<Entry<?>> entries) {
this.parent = parent;
@ -260,44 +258,43 @@ final class TagHandlerImpl implements TagHandler {
return (T) entry.value;
}
// Value must be parsed from nbt if the tag is different
final NBT nbt = entry.updatedNbt();
final Serializers.Entry<T, NBT> serializerEntry = tag.entry;
final NBTType<NBT> type = serializerEntry.nbtType();
return type == null || type == nbt.getID() ? serializerEntry.read(nbt) : tag.createDefault();
final BinaryTag nbt = entry.updatedNbt();
final Serializers.Entry<T, BinaryTag> serializerEntry = tag.entry;
final BinaryTagType<BinaryTag> type = serializerEntry.nbtType();
return type == null || type.equals(nbt.type()) ? serializerEntry.read(nbt) : tag.createDefault();
}
void updateContent(@NotNull NBTCompoundLike compoundLike) {
final NBTCompound compound = compoundLike.toCompound();
void updateContent(@NotNull CompoundBinaryTag compound) {
final TagHandlerImpl converted = fromCompound(compound);
this.entries.updateContent(converted.root.entries);
this.compound = compound;
}
NBTCompound compound() {
NBTCompound compound;
CompoundBinaryTag compound() {
CompoundBinaryTag compound;
if (!ServerFlag.TAG_HANDLER_CACHE_ENABLED || (compound = this.compound) == null) {
MutableNBTCompound tmp = new MutableNBTCompound();
CompoundBinaryTag.Builder tmp = CompoundBinaryTag.builder();
this.entries.forValues(entry -> {
final Tag tag = entry.tag;
final NBT nbt = entry.updatedNbt();
if (nbt != null && (!tag.entry.isPath() || (!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && !((NBTCompound) nbt).isEmpty())) {
final BinaryTag nbt = entry.updatedNbt();
if (nbt != null && (!tag.entry.isPath() || (!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && ((CompoundBinaryTag) nbt).size() > 0)) {
tmp.put(tag.getKey(), nbt);
}
});
this.compound = compound = tmp.toCompound();
this.compound = compound = tmp.build();
}
return compound;
}
@Contract("null -> !null")
Node copy(Node parent) {
MutableNBTCompound tmp = new MutableNBTCompound();
CompoundBinaryTag.Builder tmp = CompoundBinaryTag.builder();
Node result = new Node(parent, new StaticIntMap.Array<>());
StaticIntMap<Entry<?>> entries = result.entries;
this.entries.forValues(entry -> {
Tag tag = entry.tag;
Object value = entry.value;
NBT nbt;
BinaryTag nbt;
if (value instanceof Node node) {
Node copy = node.copy(result);
if (copy == null)
@ -313,9 +310,10 @@ final class TagHandlerImpl implements TagHandler {
tmp.put(tag.getKey(), nbt);
entries.put(tag.index, valueToEntry(result, tag, value));
});
if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && tmp.isEmpty() && parent != null)
var compound = tmp.build();
if ((!ServerFlag.SERIALIZE_EMPTY_COMPOUND) && compound.size() == 0 && parent != null)
return null; // Empty child node
result.compound = tmp.toCompound();
result.compound = compound;
return result;
}
@ -330,7 +328,7 @@ final class TagHandlerImpl implements TagHandler {
private static final class Entry<T> {
private final Tag<T> tag;
T value;
NBT nbt;
BinaryTag nbt;
Entry(Tag<T> tag, T value) {
this.tag = tag;
@ -345,9 +343,9 @@ final class TagHandlerImpl implements TagHandler {
return makePathEntry(tag.getKey(), node);
}
NBT updatedNbt() {
BinaryTag updatedNbt() {
if (tag.entry.isPath()) return ((Node) value).compound();
NBT nbt = this.nbt;
BinaryTag nbt = this.nbt;
if (nbt == null) this.nbt = nbt = tag.entry.write(value);
return nbt;
}
@ -360,7 +358,7 @@ final class TagHandlerImpl implements TagHandler {
Node toNode() {
if (tag.entry.isPath()) return (Node) value;
if (updatedNbt() instanceof NBTCompound compound) {
if (updatedNbt() instanceof CompoundBinaryTag compound) {
// Slow path forcing a conversion of the structure to NBTCompound
// TODO should the handler be cached inside the entry?
return fromCompound(compound).root;

View File

@ -1,9 +1,7 @@
package net.minestom.server.tag;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTList;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import net.kyori.adventure.nbt.*;
import net.minestom.server.utils.NBTUtils;
import java.util.ArrayList;
import java.util.List;
@ -15,30 +13,30 @@ import java.util.function.Function;
import static java.util.Map.entry;
/**
* Handles conversion of {@link NBT} subtypes into one or multiple primitive {@link Tag tags}.
* Handles conversion of {@link BinaryTag} subtypes into one or multiple primitive {@link Tag tags}.
*/
final class TagNbtSeparator {
static final Map<NBTType<?>, Function<String, Tag<?>>> SUPPORTED_TYPES = Map.ofEntries(
entry(NBTType.TAG_Byte, Tag::Byte),
entry(NBTType.TAG_Short, Tag::Short),
entry(NBTType.TAG_Int, Tag::Integer),
entry(NBTType.TAG_Long, Tag::Long),
entry(NBTType.TAG_Float, Tag::Float),
entry(NBTType.TAG_Double, Tag::Double),
entry(NBTType.TAG_String, Tag::String));
static final Map<BinaryTagType<?>, Function<String, Tag<?>>> SUPPORTED_TYPES = Map.ofEntries(
entry(BinaryTagTypes.BYTE, Tag::Byte),
entry(BinaryTagTypes.SHORT, Tag::Short),
entry(BinaryTagTypes.INT, Tag::Integer),
entry(BinaryTagTypes.LONG, Tag::Long),
entry(BinaryTagTypes.FLOAT, Tag::Float),
entry(BinaryTagTypes.DOUBLE, Tag::Double),
entry(BinaryTagTypes.STRING, Tag::String));
static void separate(NBTCompound nbtCompound, Consumer<Entry> consumer) {
static void separate(CompoundBinaryTag nbtCompound, Consumer<Entry> consumer) {
for (var ent : nbtCompound) {
convert(new ArrayList<>(), ent.getKey(), ent.getValue(), consumer);
}
}
static void separate(String key, NBT nbt, Consumer<Entry> consumer) {
static void separate(String key, BinaryTag nbt, Consumer<Entry> consumer) {
convert(new ArrayList<>(), key, nbt, consumer);
}
static Entry separateSingle(String key, NBT nbt) {
assert !(nbt instanceof NBTCompound);
static Entry separateSingle(String key, BinaryTag nbt) {
assert !(nbt instanceof CompoundBinaryTag);
AtomicReference<Entry<?>> entryRef = new AtomicReference<>();
convert(new ArrayList<>(), key, nbt, entry -> {
assert entryRef.getPlain() == null : "Multiple entries found for nbt tag: " + key + " -> " + nbt;
@ -49,28 +47,28 @@ final class TagNbtSeparator {
return entry;
}
private static void convert(List<String> path, String key, NBT nbt, Consumer<Entry> consumer) {
var tagFunction = SUPPORTED_TYPES.get(nbt.getID());
private static void convert(List<String> path, String key, BinaryTag nbt, Consumer<Entry> consumer) {
var tagFunction = SUPPORTED_TYPES.get(nbt.type());
if (tagFunction != null) {
Tag tag = tagFunction.apply(key);
consumer.accept(makeEntry(path, tag, nbt.getValue()));
} else if (nbt instanceof NBTCompound nbtCompound) {
consumer.accept(makeEntry(path, tag, NBTUtils.nbtValueFromTag(nbt)));
} else if (nbt instanceof CompoundBinaryTag nbtCompound) {
for (var ent : nbtCompound) {
var newPath = new ArrayList<>(path);
newPath.add(key);
convert(newPath, ent.getKey(), ent.getValue(), consumer);
}
} else if (nbt instanceof NBTList<?> nbtList) {
tagFunction = SUPPORTED_TYPES.get(nbtList.getSubtagType());
} else if (nbt instanceof ListBinaryTag nbtList) {
tagFunction = SUPPORTED_TYPES.get(nbtList.elementType());
if (tagFunction == null) {
// Invalid list subtype, fallback to nbt
consumer.accept(makeEntry(path, Tag.NBT(key), nbt));
} else {
try {
var tag = tagFunction.apply(key).list();
Object[] values = new Object[nbtList.getSize()];
Object[] values = new Object[nbtList.size()];
for (int i = 0; i < values.length; i++) {
values[i] = nbtList.get(i).getValue();
values[i] = NBTUtils.nbtValueFromTag(nbtList.get(i));
}
consumer.accept(makeEntry(path, Tag.class.cast(tag), List.of(values)));
} catch (Exception e) {

View File

@ -1,11 +1,11 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@ -44,7 +44,7 @@ final class TagRecord {
final Tag<?> tag;
if (componentType.isRecord()) {
tag = Tag.Structure(componentName, serializers.get(componentType));
} else if (NBT.class.isAssignableFrom(componentType)) {
} else if (BinaryTag.class.isAssignableFrom(componentType)) {
tag = Tag.NBT(componentName);
} else {
final var fun = SUPPORTED_TYPES.get(componentType);
@ -73,7 +73,7 @@ final class TagRecord {
static final class Serializer<T extends Record> implements TagSerializer<T> {
final Constructor<T> constructor;
final Entry[] entries;
final Serializers.Entry<T, NBTCompound> serializerEntry;
final Serializers.Entry<T, CompoundBinaryTag> serializerEntry;
Serializer(Constructor<T> constructor, Entry[] entries) {
this.constructor = constructor;

View File

@ -1,9 +1,9 @@
package net.minestom.server.tag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.function.Function;
@ -31,11 +31,11 @@ public interface TagSerializer<T> {
void write(@NotNull TagWritable writer, @NotNull T value);
@ApiStatus.Experimental
TagSerializer<NBTCompound> COMPOUND = TagSerializerImpl.COMPOUND;
TagSerializer<CompoundBinaryTag> COMPOUND = TagSerializerImpl.COMPOUND;
@ApiStatus.Experimental
static <T> TagSerializer<T> fromCompound(@NotNull Function<NBTCompound, T> reader,
@NotNull Function<T, NBTCompound> writer) {
static <T> TagSerializer<T> fromCompound(@NotNull Function<CompoundBinaryTag, T> reader,
@NotNull Function<T, CompoundBinaryTag> writer) {
return TagSerializerImpl.fromCompound(reader, writer);
}
}

View File

@ -1,35 +1,35 @@
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.NBTCompound;
import java.util.function.Function;
final class TagSerializerImpl {
public static final TagSerializer<NBTCompound> COMPOUND = new TagSerializer<>() {
public static final TagSerializer<CompoundBinaryTag> COMPOUND = new TagSerializer<>() {
@Override
public @NotNull NBTCompound read(@NotNull TagReadable reader) {
public @NotNull CompoundBinaryTag read(@NotNull TagReadable reader) {
return ((TagHandler) reader).asCompound();
}
@Override
public void write(@NotNull TagWritable writer, @NotNull NBTCompound value) {
public void write(@NotNull TagWritable writer, @NotNull CompoundBinaryTag value) {
TagNbtSeparator.separate(value, entry -> writer.setTag(entry.tag(), entry.value()));
}
};
static <T> TagSerializer<T> fromCompound(Function<NBTCompound, T> readFunc, Function<T, NBTCompound> writeFunc) {
static <T> TagSerializer<T> fromCompound(Function<CompoundBinaryTag, T> readFunc, Function<T, CompoundBinaryTag> writeFunc) {
return new TagSerializer<>() {
@Override
public @Nullable T read(@NotNull TagReadable reader) {
final NBTCompound compound = COMPOUND.read(reader);
final CompoundBinaryTag compound = COMPOUND.read(reader);
return readFunc.apply(compound);
}
@Override
public void write(@NotNull TagWritable writer, @NotNull T value) {
final NBTCompound compound = writeFunc.apply(value);
final CompoundBinaryTag compound = writeFunc.apply(value);
COMPOUND.write(writer, compound);
}
};

View File

@ -0,0 +1,59 @@
package net.minestom.server.utils;
import net.kyori.adventure.nbt.*;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ApiStatus.Internal
public final class NBTUtils {
private static final BinaryTagType<?>[] TYPES = new BinaryTagType[]{
BinaryTagTypes.END,
BinaryTagTypes.BYTE,
BinaryTagTypes.SHORT,
BinaryTagTypes.INT,
BinaryTagTypes.LONG,
BinaryTagTypes.FLOAT,
BinaryTagTypes.DOUBLE,
BinaryTagTypes.BYTE_ARRAY,
BinaryTagTypes.STRING,
BinaryTagTypes.LIST,
BinaryTagTypes.COMPOUND,
BinaryTagTypes.INT_ARRAY,
BinaryTagTypes.LONG_ARRAY,
};
public static @NotNull BinaryTagType<?> nbtTypeFromId(byte id) {
Check.argCondition(id < 0 || id >= TYPES.length, "Invalid NBT type id: " + id);
return TYPES[id];
}
public static @NotNull Object nbtValueFromTag(@NotNull BinaryTag tag) {
if (tag instanceof ByteBinaryTag byteTag) {
return byteTag.value();
} else if (tag instanceof ShortBinaryTag shortTag) {
return shortTag.value();
} else if (tag instanceof IntBinaryTag intTag) {
return intTag.value();
} else if (tag instanceof LongBinaryTag longTag) {
return longTag.value();
} else if (tag instanceof FloatBinaryTag floatTag) {
return floatTag.value();
} else if (tag instanceof DoubleBinaryTag doubleTag) {
return doubleTag.value();
} else if (tag instanceof ByteArrayBinaryTag byteArrayTag) {
return byteArrayTag.value();
} else if (tag instanceof StringBinaryTag stringTag) {
return stringTag.value();
} else if (tag instanceof IntArrayBinaryTag intArrayTag) {
return intArrayTag.value();
} else if (tag instanceof LongArrayBinaryTag longArrayTag) {
return longArrayTag.value();
} else {
throw new UnsupportedOperationException("Unsupported NBT type: " + tag.getClass());
}
}
private NBTUtils() {
}
}

View File

@ -2,8 +2,6 @@ package net.minestom.server.utils.binary;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTReader;
import org.jglrxavpok.hephaistos.nbt.NBTWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -17,8 +15,6 @@ import java.nio.channels.WritableByteChannel;
@ApiStatus.Internal
public final class BinaryBuffer {
private ByteBuffer nioBuffer; // To become a `MemorySegment` once released
private NBTReader nbtReader;
private NBTWriter nbtWriter;
private final int capacity;
private int readerOffset, writerOffset;

View File

@ -1,5 +1,6 @@
package net.minestom.server.utils.binary;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.coordinate.Point;
@ -8,7 +9,6 @@ import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.Either;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
@ -263,7 +263,7 @@ public class BinaryReader extends InputStream {
return buffer.readableBytes();
}
public NBT readTag() {
public BinaryTag readTag() {
return buffer.read(NBT);
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.utils.binary;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
@ -8,7 +9,6 @@ import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.Either;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import java.io.OutputStream;
import java.nio.ByteBuffer;
@ -167,7 +167,7 @@ public class BinaryWriter extends OutputStream {
this.buffer.write(ITEM, itemStack);
}
public void writeNBT(@NotNull String name, @NotNull NBT tag) {
public void writeNBT(@NotNull String name, @NotNull BinaryTag tag) {
this.buffer.write(NBT, tag);
}

View File

@ -1,16 +1,14 @@
package net.minestom.server.utils.block;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.Instance;
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.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Map;
import java.util.Objects;
@ -90,22 +88,22 @@ public class BlockUtils {
return new Object2ObjectArrayMap<>(keys, values, entryCount);
}
public static @Nullable NBTCompound extractClientNbt(@NotNull Block block) {
public static @Nullable CompoundBinaryTag extractClientNbt(@NotNull Block block) {
if (!block.registry().isBlockEntity()) return null;
// Append handler tags
final BlockHandler handler = block.handler();
final NBTCompound blockNbt = Objects.requireNonNullElseGet(block.nbt(), NBTCompound::new);
final CompoundBinaryTag blockNbt = Objects.requireNonNullElseGet(block.nbt(), CompoundBinaryTag::empty);
if (handler != null) {
// Extract explicitly defined tags and keep the rest server-side
return NBT.Compound(nbt -> {
for (Tag<?> tag : handler.getBlockEntityTags()) {
final var value = tag.read(blockNbt);
if (value != null) {
// Tag is present and valid
tag.writeUnsafe(nbt, value);
}
var builder = CompoundBinaryTag.builder();
for (Tag<?> tag : handler.getBlockEntityTags()) {
final var value = tag.read(blockNbt);
if (value != null) {
// Tag is present and valid
tag.writeUnsafe(builder, value);
}
});
}
return builder.build();
}
// Complete nbt shall be sent if the block has no handler
// Necessary to support all vanilla blocks

View File

@ -1,13 +1,10 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.mcdata.SizesKt;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@ -18,6 +15,9 @@ public class DimensionType {
private static final AtomicInteger idCounter = new AtomicInteger(0);
private static final int VANILLA_MIN_Y = -64;
private static final int VANILLA_MAX_Y = 319;
public static final DimensionType OVERWORLD = DimensionType.builder(NamespaceID.from("minecraft:overworld"))
.ultrawarm(false)
.natural(true)
@ -91,55 +91,56 @@ public class DimensionType {
return new DimensionTypeBuilder();
}
public static DimensionType fromNBT(NBTCompound nbt) {
public static DimensionType fromNBT(CompoundBinaryTag nbt) {
return DimensionType.builder(NamespaceID.from(nbt.getString("name")))
.ambientLight(nbt.getFloat("ambient_light"))
.infiniburn(NamespaceID.from(nbt.getString("infiniburn").replaceFirst("#", "")))
.natural(nbt.getByte("natural") != 0)
.ceilingEnabled(nbt.getByte("has_ceiling") != 0)
.skylightEnabled(nbt.getByte("has_skylight") != 0)
.ultrawarm(nbt.getByte("ultrawarm") != 0)
.raidCapable(nbt.getByte("has_raids") != 0)
.respawnAnchorSafe(nbt.getByte("respawn_anchor_works") != 0)
.bedSafe(nbt.getByte("bed_works") != 0)
.natural(nbt.getBoolean("natural"))
.ceilingEnabled(nbt.getBoolean("has_ceiling"))
.skylightEnabled(nbt.getBoolean("has_skylight"))
.ultrawarm(nbt.getBoolean("ultrawarm"))
.raidCapable(nbt.getBoolean("has_raids"))
.respawnAnchorSafe(nbt.getBoolean("respawn_anchor_works"))
.bedSafe(nbt.getBoolean("bed_works"))
.effects(nbt.getString("effects"))
.piglinSafe(nbt.getByte("piglin_safe") != 0)
.piglinSafe(nbt.getBoolean("piglin_safe"))
.logicalHeight(nbt.getInt("logical_height"))
.coordinateScale(nbt.getDouble("coordinate_scale"))
.build();
}
@NotNull
public NBTCompound toIndexedNBT() {
return NBT.Compound(Map.of(
"name", NBT.String(name.toString()),
"id", NBT.Int(id),
"element", toNBT()));
public CompoundBinaryTag toIndexedNBT() {
return CompoundBinaryTag.builder()
.putString("name", name.toString())
.putInt("id", id)
.put("element", toNBT())
.build();
}
@NotNull
public NBTCompound toNBT() {
return NBT.Compound(nbt -> {
nbt.setFloat("ambient_light", ambientLight);
nbt.setString("infiniburn", "#" + infiniburn.toString());
nbt.setByte("natural", (byte) (natural ? 0x01 : 0x00));
nbt.setByte("has_ceiling", (byte) (ceilingEnabled ? 0x01 : 0x00));
nbt.setByte("has_skylight", (byte) (skylightEnabled ? 0x01 : 0x00));
nbt.setByte("ultrawarm", (byte) (ultrawarm ? 0x01 : 0x00));
nbt.setByte("has_raids", (byte) (raidCapable ? 0x01 : 0x00));
nbt.setByte("respawn_anchor_works", (byte) (respawnAnchorSafe ? 0x01 : 0x00));
nbt.setByte("bed_works", (byte) (bedSafe ? 0x01 : 0x00));
nbt.setString("effects", effects);
nbt.setByte("piglin_safe", (byte) (piglinSafe ? 0x01 : 0x00));
nbt.setInt("min_y", minY);
nbt.setInt("height", height);
nbt.setInt("logical_height", logicalHeight);
nbt.setDouble("coordinate_scale", coordinateScale);
nbt.setString("name", name.toString());
nbt.setInt("monster_spawn_block_light_limit", 0);
nbt.setInt("monster_spawn_light_level", 11);
if (fixedTime != null) nbt.setLong("fixed_time", fixedTime);
});
public CompoundBinaryTag toNBT() {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
builder.putFloat("ambient_light", ambientLight);
builder.putString("infiniburn", "#" + infiniburn.toString());
builder.putByte("natural", (byte) (natural ? 0x01 : 0x00));
builder.putByte("has_ceiling", (byte) (ceilingEnabled ? 0x01 : 0x00));
builder.putByte("has_skylight", (byte) (skylightEnabled ? 0x01 : 0x00));
builder.putByte("ultrawarm", (byte) (ultrawarm ? 0x01 : 0x00));
builder.putByte("has_raids", (byte) (raidCapable ? 0x01 : 0x00));
builder.putByte("respawn_anchor_works", (byte) (respawnAnchorSafe ? 0x01 : 0x00));
builder.putByte("bed_works", (byte) (bedSafe ? 0x01 : 0x00));
builder.putString("effects", effects);
builder.putByte("piglin_safe", (byte) (piglinSafe ? 0x01 : 0x00));
builder.putInt("min_y", minY);
builder.putInt("height", height);
builder.putInt("logical_height", logicalHeight);
builder.putDouble("coordinate_scale", coordinateScale);
builder.putString("name", name.toString());
builder.putInt("monster_spawn_block_light_limit", 0);
builder.putInt("monster_spawn_light_level", 11);
if (fixedTime != null) builder.putLong("fixed_time", fixedTime);
return builder.build();
}
@Override
@ -261,9 +262,9 @@ public class DimensionType {
private boolean bedSafe = true;
private String effects = "minecraft:overworld";
private boolean piglinSafe = false;
private int minY = SizesKt.getVanillaMinY();
private int logicalHeight = SizesKt.getVanillaMaxY() - SizesKt.getVanillaMinY() + 1;
private int height = SizesKt.getVanillaMaxY() - SizesKt.getVanillaMinY() + 1;
private int minY = VANILLA_MIN_Y;
private int logicalHeight = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
private int height = VANILLA_MAX_Y - VANILLA_MIN_Y + 1;
private double coordinateScale = 1.0;
private NamespaceID infiniburn = NamespaceID.from("minecraft:infiniburn_overworld");

View File

@ -1,11 +1,11 @@
package net.minestom.server.world;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.Collections;
import java.util.List;
@ -81,21 +81,19 @@ public final class DimensionTypeManager {
}
/**
* Creates the {@link NBTCompound} containing all the registered dimensions.
* Creates the {@link CompoundBinaryTag} containing all the registered dimensions.
* <p>
* Used when a player connects.
*
* @return an nbt compound containing the registered dimensions
*/
public @NotNull NBTCompound toNBT() {
return NBT.Compound(dimensions -> {
dimensions.setString("type", "minecraft:dimension_type");
dimensions.set("value", NBT.List(
NBTType.TAG_Compound,
dimensionTypes.stream()
.map(DimensionType::toIndexedNBT)
.toList()
));
});
public @NotNull CompoundBinaryTag toNBT() {
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (DimensionType dimensionType : dimensionTypes)
entries.add(dimensionType.toIndexedNBT());
return CompoundBinaryTag.builder()
.putString("type", "minecraft:dimension_type")
.put("value", entries.build())
.build();
}
}

View File

@ -1,5 +1,6 @@
package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.coordinate.Point;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
@ -8,8 +9,6 @@ import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Locale;
@ -54,18 +53,20 @@ sealed public interface Biome extends ProtocolObject permits BiomeImpl {
}
}
default @NotNull NBTCompound toNbt() {
default @NotNull CompoundBinaryTag toNbt() {
Check.notNull(name(), "The biome namespace cannot be null");
Check.notNull(effects(), "The biome effects cannot be null");
return NBT.Compound(element -> {
element.setFloat("temperature", temperature());
element.setFloat("downfall", downfall());
element.setByte("has_precipitation", (byte) (precipitation() == Precipitation.NONE ? 0 : 1));
if (temperatureModifier() != TemperatureModifier.NONE)
element.setString("temperature_modifier", temperatureModifier().name().toLowerCase(Locale.ROOT));
element.set("effects", effects().toNbt());
});
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putFloat("temperature", temperature())
.putFloat("downfall", downfall())
.putByte("has_precipitation", (byte) (precipitation() == Precipitation.NONE ? 0 : 1))
.putString("precipitation", precipitation().name().toLowerCase(Locale.ROOT));
if (temperatureModifier() != TemperatureModifier.NONE)
builder.putString("temperature_modifier", temperatureModifier().name().toLowerCase(Locale.ROOT));
return builder
.put("effects", effects().toNbt())
.build();
}
static @NotNull Builder builder() {

View File

@ -1,12 +1,10 @@
package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Locale;
import java.util.Map;
public record BiomeEffects(int fogColor, int skyColor, int waterColor, int waterFogColor, int foliageColor,
int grassColor,
@ -18,29 +16,29 @@ public record BiomeEffects(int fogColor, int skyColor, int waterColor, int water
return new Builder();
}
public NBTCompound toNbt() {
return NBT.Compound(nbt -> {
nbt.setInt("fog_color", fogColor);
if (foliageColor != -1)
nbt.setInt("foliage_color", foliageColor);
if (grassColor != -1)
nbt.setInt("grass_color", grassColor);
nbt.setInt("sky_color", skyColor);
nbt.setInt("water_color", waterColor);
nbt.setInt("water_fog_color", waterFogColor);
if (grassColorModifier != null)
nbt.setString("grass_color_modifier", grassColorModifier.name().toLowerCase(Locale.ROOT));
if (biomeParticle != null)
nbt.set("particle", biomeParticle.toNbt());
if (ambientSound != null)
nbt.setString("ambient_sound", ambientSound.toString());
if (moodSound != null)
nbt.set("mood_sound", moodSound.toNbt());
if (additionsSound != null)
nbt.set("additions_sound", additionsSound.toNbt());
if (music != null)
nbt.set("music", music.toNbt());
});
public CompoundBinaryTag toNbt() {
var builder = CompoundBinaryTag.builder();
builder.putInt("fog_color", fogColor);
if (foliageColor != -1)
builder.putInt("foliage_color", foliageColor);
if (grassColor != -1)
builder.putInt("grass_color", grassColor);
builder.putInt("sky_color", skyColor);
builder.putInt("water_color", waterColor);
builder.putInt("water_fog_color", waterFogColor);
if (grassColorModifier != null)
builder.putString("grass_color_modifier", grassColorModifier.name().toLowerCase(Locale.ROOT));
if (biomeParticle != null)
builder.put("particle", biomeParticle.toNbt());
if (ambientSound != null)
builder.putString("ambient_sound", ambientSound.toString());
if (moodSound != null)
builder.put("mood_sound", moodSound.toNbt());
if (additionsSound != null)
builder.put("additions_sound", additionsSound.toNbt());
if (music != null)
builder.put("music", music.toNbt());
return builder.build();
}
public enum GrassColorModifier {
@ -48,30 +46,33 @@ public record BiomeEffects(int fogColor, int skyColor, int waterColor, int water
}
public record MoodSound(NamespaceID sound, int tickDelay, int blockSearchExtent, double offset) {
public @NotNull NBTCompound toNbt() {
return NBT.Compound(Map.of(
"sound", NBT.String(sound.toString()),
"tick_delay", NBT.Int(tickDelay),
"block_search_extent", NBT.Int(blockSearchExtent),
"offset", NBT.Double(offset)));
public @NotNull CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putString("sound", sound.toString())
.putInt("tick_delay", tickDelay)
.putInt("block_search_extent", blockSearchExtent)
.putDouble("offset", offset)
.build();
}
}
public record AdditionsSound(NamespaceID sound, double tickChance) {
public @NotNull NBTCompound toNbt() {
return NBT.Compound(Map.of(
"sound", NBT.String(sound.toString()),
"tick_chance", NBT.Double(tickChance)));
public @NotNull CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putString("sound", sound.toString())
.putDouble("tick_chance", tickChance)
.build();
}
}
public record Music(NamespaceID sound, int minDelay, int maxDelay, boolean replaceCurrentMusic) {
public @NotNull NBTCompound toNbt() {
return NBT.Compound(Map.of(
"sound", NBT.String(sound.toString()),
"min_delay", NBT.Int(minDelay),
"max_delay", NBT.Int(maxDelay),
"replace_current_music", NBT.Boolean(replaceCurrentMusic)));
public @NotNull CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putString("sound", sound.toString())
.putInt("min_delay", minDelay)
.putInt("max_delay", maxDelay)
.putBoolean("replace_current_music", replaceCurrentMusic)
.build();
}
}

View File

@ -1,12 +1,12 @@
package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.Collection;
import java.util.Collections;
@ -25,7 +25,7 @@ public final class BiomeManager {
private final Map<NamespaceID, Integer> idMappings = new ConcurrentHashMap<>();
private final AtomicInteger ID_COUNTER = new AtomicInteger(0);
private NBTCompound nbtCache = null;
private CompoundBinaryTag nbtCache = null;
public BiomeManager() {
// Need to register plains for the client to work properly
@ -101,18 +101,21 @@ public final class BiomeManager {
return getByName(namespace);
}
public @NotNull NBTCompound toNBT() {
public @NotNull CompoundBinaryTag toNBT() {
if (nbtCache != null) return nbtCache;
nbtCache = NBT.Compound(Map.of(
"type", NBT.String("minecraft:worldgen/biome"),
"value", NBT.List(NBTType.TAG_Compound, biomes.values().stream().map(biome -> {
return NBT.Compound(Map.of(
"id", NBT.Int(getId(biome)),
"name", NBT.String(biome.namespace().toString()),
"element", biome.toNbt()
));
}).toList())));
ListBinaryTag.Builder<CompoundBinaryTag> entries = ListBinaryTag.builder(BinaryTagTypes.COMPOUND);
for (Biome biome : biomes.values()) {
entries.add(CompoundBinaryTag.builder()
.putInt("id", getId(biome))
.putString("name", biome.namespace().toString())
.put("element", biome.toNbt())
.build());
}
nbtCache = CompoundBinaryTag.builder()
.putString("type", "minecraft:worldgen/biome")
.put("value", entries.build())
.build();
return nbtCache;
}

View File

@ -1,22 +1,22 @@
package net.minestom.server.world.biomes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.utils.NamespaceID;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.Map;
public record BiomeParticle(float probability, Option option) {
public NBTCompound toNbt() {
return NBT.Compound(Map.of(
"probability", NBT.Float(probability),
"options", option.toNbt()));
public CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putFloat("probability", probability)
.put("options", option.toNbt())
.build();
}
public interface Option {
NBTCompound toNbt();
CompoundBinaryTag toNbt();
}
public record BlockOption(Block block) implements Option {
@ -24,15 +24,17 @@ public record BiomeParticle(float probability, Option option) {
private static final String type = "block";
@Override
public NBTCompound toNbt() {
return NBT.Compound(nbtCompound -> {
nbtCompound.setString("type", type);
nbtCompound.setString("Name", block.name());
Map<String, String> propertiesMap = block.properties();
if (propertiesMap.size() != 0) {
nbtCompound.set("Properties", NBT.Compound(p -> propertiesMap.forEach(p::setString)));
}
});
public CompoundBinaryTag toNbt() {
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
builder.putString("type", type);
builder.putString("Name", block.name());
Map<String, String> propertiesMap = block.properties();
if (!propertiesMap.isEmpty()) {
CompoundBinaryTag.Builder properties = CompoundBinaryTag.builder();
propertiesMap.forEach(properties::putString);
builder.put("Properties", properties.build());
}
return builder.build();
}
}
@ -40,13 +42,14 @@ public record BiomeParticle(float probability, Option option) {
private static final String type = "dust";
@Override
public NBTCompound toNbt() {
return NBT.Compound(Map.of(
"type", NBT.String(type),
"r", NBT.Float(red),
"g", NBT.Float(green),
"b", NBT.Float(blue),
"scale", NBT.Float(scale)));
public CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putString("type", type)
.putFloat("r", red)
.putFloat("g", green)
.putFloat("b", blue)
.putFloat("scale", scale)
.build();
}
}
@ -54,17 +57,19 @@ public record BiomeParticle(float probability, Option option) {
private static final String type = "item";
@Override
public NBTCompound toNbt() {
public CompoundBinaryTag toNbt() {
//todo test count might be wrong type
NBTCompound nbtCompound = item.meta().toNBT();
return nbtCompound.modify(n -> n.setString("type", type));
return item.meta().toNBT()
.putString("type", type);
}
}
public record NormalOption(NamespaceID type) implements Option {
@Override
public NBTCompound toNbt() {
return NBT.Compound(Map.of("type", NBT.String(type.toString())));
public CompoundBinaryTag toNbt() {
return CompoundBinaryTag.builder()
.putString("type", type.toString())
.build();
}
}
}