Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "heterogeneous" NBT lists somehow #1735

Closed
SPGoding opened this issue Jan 27, 2025 · 2 comments
Closed

Support "heterogeneous" NBT lists somehow #1735

SPGoding opened this issue Jan 27, 2025 · 2 comments
Labels
🔷 nbt Relates to nbt, but is not directly handled by the nbt package

Comments

@SPGoding
Copy link
Member

SPGoding commented Jan 27, 2025

When heterogeneous SNBT lists are deserialized into NBT, non-compound entries (or wrapper compound entries? The logic of wrapIfNeeded() makes it seem like you could have double wrapped things if I'm not mistaken) are transformed into a wrapper compound. e.g. ['a', {'b': 3}] is stored as [{'': 'a'}, {'b': 3}].

net/minecraft/nbt/NbtOps
private static class HeterogenousListCollector implements NbtOps.ListCollector {
   private final ListTag result = new ListTag();

   public HeterogenousListCollector() {
      super();
   }

   public HeterogenousListCollector(Collection<Tag> list) {
      super();
      this.result.addAll(list);
   }

   public HeterogenousListCollector(IntArrayList list) {
      super();
      list.forEach((var1x) -> {
         this.result.add(wrapElement(IntTag.valueOf(var1x)));
      });
   }

   public HeterogenousListCollector(ByteArrayList list) {
      super();
      list.forEach((var1x) -> {
         this.result.add(wrapElement(ByteTag.valueOf(var1x)));
      });
   }

   public HeterogenousListCollector(LongArrayList list) {
      super();
      list.forEach((var1x) -> {
         this.result.add(wrapElement(LongTag.valueOf(var1x)));
      });
   }

   private static boolean isWrapper(CompoundTag compound) {
      return compound.size() == 1 && compound.contains("");
   }

   private static Tag wrapIfNeeded(Tag tag) {
      if (tag instanceof CompoundTag) {
         CompoundTag compound = (CompoundTag)tag;
         if (!isWrapper(compound)) {
            return compound;
         }
      }

      return wrapElement(tag);
   }

   private static CompoundTag wrapElement(Tag tag) {
      CompoundTag wrapper = new CompoundTag();
      wrapper.put("", tag);
      return wrapper;
   }

   public NbtOps.ListCollector accept(Tag tag) {
      this.result.add(wrapIfNeeded(tag));
      return this;
   }

   public Tag result() {
      return this.result;
   }
}

private static class HomogenousListCollector implements NbtOps.ListCollector {
   private final ListTag result = new ListTag();

   HomogenousListCollector(Tag tag) {
      super();
      this.result.add(tag);
   }

   HomogenousListCollector(ListTag list) {
      super();
      this.result.addAll(list);
   }

   public NbtOps.ListCollector accept(Tag tag) {
      if (tag.getId() != this.result.getElementType()) {
         return (new NbtOps.HeterogenousListCollector()).acceptAll(this.result).accept(tag);
      } else {
         this.result.add(tag);
         return this;
      }
   }

   public Tag result() {
      return this.result;
   }
}

Annoyingly, this transformation is not undone when NBT is serialized, so users could see those wrapper compounds in /data command output. We need to somehow support usage of wrapper compounds in commands. This was improved in 25w09a (#1771).

@SPGoding SPGoding added 🐛 bug Something isn't working 🔷 nbt Relates to nbt, but is not directly handled by the nbt package labels Jan 27, 2025
@SPGoding SPGoding removed the 🐛 bug Something isn't working label Feb 11, 2025
@misode
Copy link
Member

misode commented Mar 2, 2025

Since 25w09a, I don't believe we need to change anything.

@SPGoding
Copy link
Member Author

SPGoding commented Mar 6, 2025

Yeah cuz you already added this

// Check if every element is of the same type.
if (ans.valueType) {
// TODO: don't have this inline java-edition version check
const release = ctx.project['loadedVersion'] as string | undefined
if (release && Number(release.slice(2)) < Number('1.21.5'.slice(2))) {
for (const { value } of ans.children) {
if (value && value.type !== ans.valueType) {
ctx.err.report(
localize('expected-got', localizeTag(ans.valueType), localizeTag(value.type)),
value,
)
}
}
}
}

@SPGoding SPGoding closed this as completed Mar 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔷 nbt Relates to nbt, but is not directly handled by the nbt package
Projects
None yet
Development

No branches or pull requests

2 participants