From 0961de898d250a5734c12e339df94b7d2b97041a Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Wed, 6 Mar 2024 18:52:23 +0100 Subject: [PATCH] Handle books, among other things --- .../api/minecraft/data/StructuredDataKey.java | 3 +- .../api/minecraft/item/data/Filterable.java | 71 +++++++ .../item/data/FilterableComponent.java | 43 ++++ .../minecraft/item/data/FilterableString.java | 42 ++++ .../api/minecraft/item/data/WrittenBook.java | 18 +- .../BlockItemPacketRewriter1_20_3.java | 2 +- .../Protocol1_20_5To1_20_3.java | 63 ++---- .../BlockItemPacketRewriter1_20_5.java | 191 +++++++++++++----- .../rewriter/EntityPacketRewriter1_20_5.java | 5 +- 9 files changed, 333 insertions(+), 105 deletions(-) create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableComponent.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableString.java diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java index 82f088a0d..a6bf680d3 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java @@ -35,6 +35,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.Bee; import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties; import com.viaversion.viaversion.api.minecraft.item.data.DyedColor; import com.viaversion.viaversion.api.minecraft.item.data.Enchantments; +import com.viaversion.viaversion.api.minecraft.item.data.FilterableString; import com.viaversion.viaversion.api.minecraft.item.data.FireworkExplosion; import com.viaversion.viaversion.api.minecraft.item.data.Fireworks; import com.viaversion.viaversion.api.minecraft.item.data.Instrument; @@ -73,7 +74,7 @@ public final class StructuredDataKey { public static final StructuredDataKey BUNDLE_CONTENTS = new StructuredDataKey<>("bundle_contents", Types1_20_5.ITEM_ARRAY); public static final StructuredDataKey POTION_CONTENTS = new StructuredDataKey<>("potion_contents", PotionContents.TYPE); public static final StructuredDataKey SUSPICIOUS_STEW_EFFECTS = new StructuredDataKey<>("suspicious_stew_effects", SuspiciousStewEffect.ARRAY_TYPE); - public static final StructuredDataKey WRITABLE_BOOK_CONTENT = new StructuredDataKey<>("writable_book_content", Type.STRING_ARRAY); + public static final StructuredDataKey WRITABLE_BOOK_CONTENT = new StructuredDataKey<>("writable_book_content", FilterableString.ARRAY_TYPE); public static final StructuredDataKey WRITTEN_BOOK_CONTENT = new StructuredDataKey<>("written_book_content", WrittenBook.TYPE); public static final StructuredDataKey TRIM = new StructuredDataKey<>("trim", ArmorTrim.TYPE); public static final StructuredDataKey DEBUG_STICK_STATE = new StructuredDataKey<>("debug_stick_state", Type.COMPOUND_TAG); diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java new file mode 100644 index 000000000..707ffadb3 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java @@ -0,0 +1,71 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.item.data; + +import com.viaversion.viaversion.api.type.Type; +import io.netty.buffer.ByteBuf; +import org.checkerframework.checker.nullness.qual.Nullable; + +public abstract class Filterable { + private final T raw; + private final T filtered; + + protected Filterable(final T raw, @Nullable final T filtered) { + this.raw = raw; + this.filtered = filtered; + } + + public T raw() { + return raw; + } + + public T filtered() { + return filtered; + } + + public abstract static class FilterableType> extends Type { + private final Type elementType; + private final Type optionalElementType; + + protected FilterableType(final Type elementType, final Type optionalElementType) { + super(Filterable.class); + this.elementType = elementType; + this.optionalElementType = optionalElementType; + } + + @Override + public F read(final ByteBuf buffer) throws Exception { + final T raw = elementType.read(buffer); + final T filtered = optionalElementType.read(buffer); + return create(raw, filtered); + } + + @Override + public void write(final ByteBuf buffer, final F value) throws Exception { + elementType.write(buffer, value.raw()); + optionalElementType.write(buffer, value.filtered()); + } + + protected abstract F create(T raw, T filtered); + } +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableComponent.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableComponent.java new file mode 100644 index 000000000..799ad8015 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableComponent.java @@ -0,0 +1,43 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.item.data; + +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.viaversion.viaversion.api.type.Type; +import com.viaversion.viaversion.api.type.types.ArrayType; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class FilterableComponent extends Filterable { + + public static final Type TYPE = new FilterableType(Type.TAG, Type.OPTIONAL_TAG) { + @Override + protected FilterableComponent create(final Tag raw, final Tag filtered) { + return new FilterableComponent(raw, filtered); + } + }; + public static final Type ARRAY_TYPE = new ArrayType<>(TYPE); + + public FilterableComponent(final Tag raw, @Nullable final Tag filtered) { + super(raw, filtered); + } +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableString.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableString.java new file mode 100644 index 000000000..200892b34 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/FilterableString.java @@ -0,0 +1,42 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.item.data; + +import com.viaversion.viaversion.api.type.Type; +import com.viaversion.viaversion.api.type.types.ArrayType; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class FilterableString extends Filterable { + + public static final Type TYPE = new FilterableType(Type.STRING, Type.OPTIONAL_STRING) { + @Override + protected FilterableString create(final String raw, final String filtered) { + return new FilterableString(raw, filtered); + } + }; + public static final Type ARRAY_TYPE = new ArrayType<>(TYPE); + + public FilterableString(final String raw, @Nullable final String filtered) { + super(raw, filtered); + } +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/WrittenBook.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/WrittenBook.java index 87cecd848..44cdcebc3 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/WrittenBook.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/WrittenBook.java @@ -30,31 +30,31 @@ public final class WrittenBook { public static final Type TYPE = new Type(WrittenBook.class) { @Override public WrittenBook read(final ByteBuf buffer) throws Exception { - final String title = Type.STRING.read(buffer); + final FilterableString title = FilterableString.TYPE.read(buffer); final String author = Type.STRING.read(buffer); final int generation = Type.VAR_INT.readPrimitive(buffer); - final String[] pages = Type.STRING_ARRAY.read(buffer); + final FilterableComponent[] pages = FilterableComponent.ARRAY_TYPE.read(buffer); final boolean resolved = buffer.readBoolean(); return new WrittenBook(title, author, generation, pages, resolved); } @Override public void write(final ByteBuf buffer, final WrittenBook value) throws Exception { - Type.STRING.write(buffer, value.title); + FilterableString.TYPE.write(buffer, value.title); Type.STRING.write(buffer, value.author); Type.VAR_INT.writePrimitive(buffer, value.generation); - Type.STRING_ARRAY.write(buffer, value.pages); + FilterableComponent.ARRAY_TYPE.write(buffer, value.pages); buffer.writeBoolean(value.resolved); } }; - private final String title; + private final FilterableString title; private final String author; private final int generation; - private final String[] pages; + private final FilterableComponent[] pages; private final boolean resolved; - public WrittenBook(final String title, final String author, final int generation, final String[] pages, final boolean resolved) { + public WrittenBook(final FilterableString title, final String author, final int generation, final FilterableComponent[] pages, final boolean resolved) { this.title = title; this.author = author; this.generation = generation; @@ -62,7 +62,7 @@ public final class WrittenBook { this.resolved = resolved; } - public String title() { + public FilterableString title() { return title; } @@ -74,7 +74,7 @@ public final class WrittenBook { return generation; } - public String[] pages() { + public FilterableComponent[] pages() { return pages; } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_3to1_20_2/rewriter/BlockItemPacketRewriter1_20_3.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_3to1_20_2/rewriter/BlockItemPacketRewriter1_20_3.java index d99fc5119..514c425d9 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_3to1_20_2/rewriter/BlockItemPacketRewriter1_20_3.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_3to1_20_2/rewriter/BlockItemPacketRewriter1_20_3.java @@ -147,7 +147,7 @@ public final class BlockItemPacketRewriter1_20_3 extends ItemRewriter { + private static final GameProfile.Property[] EMPTY_PROPERTIES = new GameProfile.Property[0]; + public BlockItemPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) { super(protocol, Type.ITEM1_20_2, Type.ITEM1_20_2_ARRAY, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY); } @@ -183,7 +189,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); final CompoundTag tag = customData != null ? customData.value() : new CompoundTag(); final DataItem dataItem = new DataItem(item.identifier(), (byte) item.amount(), (short) 0, tag); - if (customData != null && tag.remove(tagMarker) != null) { + if (customData != null && tag.remove(nbtTagName()) != null) { return dataItem; } @@ -212,7 +218,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter items = new ArrayList<>(); - for (final Tag chargedProjectile : chargedProjectiles) { - if (!(chargedProjectile instanceof CompoundTag)) { - continue; - } - items.add(itemFromTag((CompoundTag) chargedProjectile)); - } - data.set(StructuredDataKey.CHARGED_PROJECTILES, items.toArray(new Item[0])); + final CompoundTag explosionTag = tag.getCompoundTag("Explosion"); + if (explosionTag != null) { + final NumberTag shape = explosionTag.getNumberTag("Type"); + final IntArrayTag colors = explosionTag.getIntArrayTag("Colors"); + final IntArrayTag fadeColors = explosionTag.getIntArrayTag("FadeColors"); + final NumberTag trail = explosionTag.getNumberTag("Trail"); + final NumberTag flicker = explosionTag.getNumberTag("Flicker"); + final FireworkExplosion explosion = new FireworkExplosion( + shape != null ? shape.asInt() : 0, + colors != null ? colors.getValue() : new int[0], + fadeColors != null ? fadeColors.getValue() : new int[0], + trail != null && trail.asBoolean(), + flicker != null && flicker.asBoolean() + ); + data.set(StructuredDataKey.FIREWORK_EXPLOSION, explosion); } + updateWritableBookPages(data, tag); + updateWrittenBookPages(data, tag); + + updateItemList(data, tag, "ChargedProjectiles", StructuredDataKey.CHARGED_PROJECTILES); + updateItemList(data, tag, "Items", StructuredDataKey.BUNDLE_CONTENTS); + updateEnchantments(data, tag, "Enchantments", StructuredDataKey.ENCHANTMENTS, (hideFlagsValue & 0x01) == 0); updateEnchantments(data, tag, "StoredEnchantments", StructuredDataKey.STORED_ENCHANTMENTS, (hideFlagsValue & 0x20) == 0); @@ -317,12 +334,8 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter pages = new ArrayList<>(); + for (int i = 0; i < pagesTag.size(); i++) { + final Tag page = pagesTag.get(i); + if (!(page instanceof StringTag)) { + continue; + } + + String filtered = null; + if (filteredPagesTag != null) { + final StringTag filteredPage = filteredPagesTag.getStringTag(String.valueOf(i)); + if (filteredPage != null) { + filtered = filteredPage.getValue(); + } + } + pages.add(new FilterableString(((StringTag) page).getValue(), filtered)); + + } + data.set(StructuredDataKey.WRITABLE_BOOK_CONTENT, pages.toArray(new FilterableString[0])); + } + + private void updateWrittenBookPages(final StructuredDataContainer data, final CompoundTag tag) { + final ListTag pagesTag = tag.getListTag("pages"); + final CompoundTag filteredPagesTag = tag.getCompoundTag("filtered_pages"); + if (pagesTag == null) { + return; + } + + final List pages = new ArrayList<>(); + for (int i = 0; i < pagesTag.size(); i++) { + final Tag page = pagesTag.get(i); + if (!(page instanceof StringTag)) { + continue; + } + + Tag filtered = null; + if (filteredPagesTag != null) { + final StringTag filteredPage = filteredPagesTag.getStringTag(String.valueOf(i)); + if (filteredPage != null) { + filtered = ComponentUtil.jsonStringToTag(filteredPage.getValue()); + } + } + + final Tag parsedPage = ComponentUtil.jsonStringToTag(((StringTag) page).getValue()); + pages.add(new FilterableComponent(parsedPage, filtered)); + } + + final StringTag title = tag.getStringTag("title"); + final StringTag filteredTitle = tag.getStringTag("filtered_title"); + final StringTag author = tag.getStringTag("author"); + final NumberTag generation = tag.getNumberTag("generation"); + final NumberTag resolved = tag.getNumberTag("resolved"); + final WrittenBook writtenBook = new WrittenBook( + new FilterableString(title != null ? title.getValue() : "", filteredTitle != null ? filteredTitle.getValue() : null), + author != null ? author.getValue() : "", + generation != null ? generation.asInt() : 0, + pages.toArray(new FilterableComponent[0]), + resolved != null && resolved.asBoolean() + ); + data.set(StructuredDataKey.WRITTEN_BOOK_CONTENT, writtenBook); + } + + private void updateItemList(final StructuredDataContainer data, final CompoundTag tag, final String key, final StructuredDataKey dataKey) { + final ListTag chargedProjectiles = tag.getListTag(key); + if (chargedProjectiles == null) { + return; + } + + final List items = new ArrayList<>(); + for (final Tag item : chargedProjectiles) { + if (!(item instanceof CompoundTag)) { + continue; + } + items.add(itemFromTag((CompoundTag) item)); + } + data.set(dataKey, items.toArray(new Item[0])); + } + private Item itemFromTag(final CompoundTag item) { final StringTag id = item.getStringTag("id"); final NumberTag count = item.getNumberTag("Count"); @@ -388,49 +486,50 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter properties = new ArrayList<>(1); - UUID uuid = null; if (skullOwnerTag instanceof StringTag) { - name = ((StringTag) skullOwnerTag).getValue(); + final String name = ((StringTag) skullOwnerTag).getValue(); + data.set(StructuredDataKey.PROFILE, new GameProfile(name, null, EMPTY_PROPERTIES)); } else if (skullOwnerTag instanceof CompoundTag) { final CompoundTag skullOwner = (CompoundTag) skullOwnerTag; final StringTag nameTag = skullOwner.getStringTag("Name"); - name = nameTag != null ? nameTag.getValue() : ""; + final String name = nameTag != null ? nameTag.getValue() : ""; final IntArrayTag idTag = skullOwner.getIntArrayTag("Id"); + UUID uuid = null; if (idTag != null) { uuid = UUIDUtil.fromIntArray(idTag.getValue()); } final CompoundTag propertiesTag = skullOwner.getCompoundTag("Properties"); if (propertiesTag != null) { - for (final Map.Entry entry : propertiesTag.entrySet()) { - if (!(entry.getValue() instanceof ListTag)) { - continue; - } - - for (final Tag propertyTag : (ListTag) entry.getValue()) { - if (!(propertyTag instanceof CompoundTag)) { - continue; - } - - final StringTag valueTag = ((CompoundTag) propertyTag).getStringTag("Value"); - final StringTag signatureTag = ((CompoundTag) propertyTag).getStringTag("Signature"); - final GameProfile.Property property = new GameProfile.Property( - entry.getKey(), - valueTag != null ? valueTag.getValue() : "", - signatureTag != null ? signatureTag.getValue() : null - ); - properties.add(property); - } - } + updateProperties(propertiesTag, properties); } - } else { - return; + data.set(StructuredDataKey.PROFILE, new GameProfile(name, uuid, properties.toArray(EMPTY_PROPERTIES))); } + } - data.set(StructuredDataKey.PROFILE, new GameProfile(name, uuid, properties.toArray(new GameProfile.Property[0]))); + private void updateProperties(final CompoundTag propertiesTag, final List properties) { + for (final Map.Entry entry : propertiesTag.entrySet()) { + if (!(entry.getValue() instanceof ListTag)) { + continue; + } + + for (final Tag propertyTag : (ListTag) entry.getValue()) { + if (!(propertyTag instanceof CompoundTag)) { + continue; + } + + final StringTag valueTag = ((CompoundTag) propertyTag).getStringTag("Value"); + final StringTag signatureTag = ((CompoundTag) propertyTag).getStringTag("Signature"); + final GameProfile.Property property = new GameProfile.Property( + entry.getKey(), + valueTag != null ? valueTag.getValue() : "", + signatureTag != null ? signatureTag.getValue() : null + ); + properties.add(property); + } + } } private void updateMapDecorations(final StructuredDataContainer data, final ListTag decorationsTag) { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/EntityPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/EntityPacketRewriter1_20_5.java index 6a8d48fcb..886e49690 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/EntityPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/EntityPacketRewriter1_20_5.java @@ -192,7 +192,10 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter { int id = typeId; - if (id >= Types1_20_5.META_TYPES.armadilloState.typeId()) { + if (typeId >= Types1_20_5.META_TYPES.armadilloState.typeId()) { + id++; + } + if (typeId >= Types1_20_5.META_TYPES.wolfVariantType.typeId()) { id++; } return Types1_20_5.META_TYPES.byId(id);