Once again refactor structured data storage

Optionals are no more
This commit is contained in:
Nassim Jahnke 2024-02-29 19:59:18 +01:00
parent 6ffa24b50b
commit f68aed464d
No known key found for this signature in database
GPG Key ID: EF6771C01F6EF02F
13 changed files with 429 additions and 182 deletions

View File

@ -0,0 +1,70 @@
/*
* 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.data;
import io.netty.buffer.ByteBuf;
final class EmptyStructuredData<T> implements StructuredData<T> {
private final StructuredDataKey<T> key;
private int id;
EmptyStructuredData(final StructuredDataKey<T> key, final int id) {
this.key = key;
this.id = id;
}
@Override
public void setValue(final T value) {
throw new UnsupportedOperationException();
}
@Override
public void write(final ByteBuf buffer) {
}
@Override
public void setId(final int id) {
this.id = id;
}
@Override
public StructuredDataKey<T> key() {
return this.key;
}
@Override
public T value() {
return null;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public int id() {
return this.id;
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.data;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
final class FilledStructuredData<T> implements StructuredData<T> {
private final StructuredDataKey<T> key;
private T value;
private int id;
FilledStructuredData(final StructuredDataKey<T> key, final T value, final int id) {
Preconditions.checkNotNull(key);
this.key = key;
this.value = value;
this.id = id;
}
@Override
public void setValue(final T value) {
this.value = value;
}
@Override
public void write(final ByteBuf buffer) throws Exception {
key.type().write(buffer, value);
}
@Override
public void setId(final int id) {
this.id = id;
}
@Override
public StructuredDataKey<T> key() {
return key;
}
@Override
public T value() {
return value;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public int id() {
return id;
}
}

View File

@ -22,53 +22,59 @@
*/
package com.viaversion.viaversion.api.minecraft.data;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.IdHolder;
import com.viaversion.viaversion.util.Unit;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class StructuredData<T> implements IdHolder {
public interface StructuredData<T> extends IdHolder {
private final StructuredDataKey<T> key;
private T value;
private int id;
public StructuredData(final StructuredDataKey<T> key, final T value, final int id) {
this.key = key;
this.value = value;
this.id = id;
/**
* Returns filled structured data, equivalent to an Optional with a value in vanilla.
*
* @param key serializer key
* @param value value
* @param id serializer id
* @param <T> serializer type
* @return filled structured data
*/
static <T> StructuredData<T> of(final StructuredDataKey<T> key, final T value, final int id) {
return new FilledStructuredData<>(key, value, id);
}
public boolean isEmpty() {
return key.type() == Type.UNIT;
/**
* Returns empty structured data, equivalent to an empty Optional in vanilla.
*
* @param key serializer key
* @param id serializer id
* @return empty structured data
*/
static <T> StructuredData<T> empty(final StructuredDataKey<T> key, final int id) {
return new EmptyStructuredData<>(key, id);
}
public void setValue(final T value) {
if (value != null && !key.type().getOutputClass().isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("Item data type and value are incompatible. Type=" + key
+ ", value=" + value + " (" + value.getClass().getSimpleName() + ")");
}
this.value = value;
void setValue(final T value);
void write(final ByteBuf buffer) throws Exception;
void setId(final int id);
StructuredDataKey<T> key();
@Nullable T value();
/**
* Returns whether the structured data is present. Even if true, the value may be null.
*
* @return true if the structured data is present
*/
default boolean isPresent() {
return !isEmpty();
}
public void write(final ByteBuf buffer) throws Exception {
key.type().write(buffer, value);
}
public void setId(final int id) {
this.id = id;
}
public StructuredDataKey<T> key() {
return key;
}
public T value() {
return value;
}
@Override
public int id() {
return id;
}
/**
* Returns whether the structured data is empty. Not to be confused with a null value.
*
* @return true if the structured data is empty
*/
boolean isEmpty();
}

View File

@ -23,96 +23,106 @@
package com.viaversion.viaversion.api.minecraft.data;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.FullMappings;
import com.viaversion.viaversion.api.protocol.Protocol;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Optional;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class StructuredDataContainer {
private final Int2ObjectMap<Optional<StructuredData<?>>> data; // Bless Optionals in a map
private final Map<StructuredDataKey<?>, StructuredData<?>> data;
private FullMappings lookup;
private boolean mappedNames;
public StructuredDataContainer(final Int2ObjectMap<Optional<StructuredData<?>>> data) {
public StructuredDataContainer(final Map<StructuredDataKey<?>, StructuredData<?>> data) {
this.data = data;
}
public StructuredDataContainer() {
this(new Int2ObjectOpenHashMap<>());
this(new Reference2ObjectOpenHashMap<>());
}
/**
* Returns structured data by id. You might want to call {@link #contains(int)} first.
* Returns structured data by id if present.
*
* @param id serializer id
* @param key serializer id
* @param <T> data type
* @return structured data
*/
public Optional<StructuredData<?>> get(final int id) {
return this.data.getOrDefault(id, Optional.empty());
public @Nullable <T> StructuredData<T> get(final StructuredDataKey<T> key) {
//noinspection unchecked
return (StructuredData<T>) this.data.get(key);
}
/**
* Returns structured data by id. You might want to call {@link #contains(Protocol, StructuredDataKey)} first.
* Returns structured data by id if not empty.
*
* @param protocol protocol to retreive the id of the serializer from
* @param key serializer id
* @param <T> data type
* @return structured data
* @param key serializer id
* @param <T> data type
* @return structured data if not empty
*/
public <T> Optional<StructuredData<T>> get(final Protocol<?, ?, ?, ?> protocol, final StructuredDataKey<T> key) {
final Optional<StructuredData<?>> data = this.data.getOrDefault(serializerId(protocol, key), Optional.empty());
public @Nullable <T> StructuredData<T> getNonEmpty(final StructuredDataKey<T> key) {
//noinspection unchecked
return data.map(value -> (StructuredData<T>) value);
final StructuredData<T> data = (StructuredData<T>) this.data.get(key);
return data != null && data.isPresent() ? data : null;
}
public void add(final StructuredData<?> data) {
this.data.put(data.id(), Optional.of(data));
}
public <T> void add(final Protocol<?, ?, ?, ?> protocol, final StructuredDataKey<T> key, final T value) {
final int id = serializerId(protocol, key);
public <T> void add(final StructuredDataKey<T> key, final T value) {
final int id = serializerId(key);
if (id != -1) {
add(new StructuredData<>(key, value, id));
this.data.put(key, StructuredData.of(key, value, id));
}
}
public void addEmpty(final Protocol<?, ?, ?, ?> protocol, final StructuredDataKey<?> key) {
final int id = serializerId(protocol, key);
if (id != -1) {
this.data.put(id, Optional.empty());
}
}
public void remove(final int id) {
this.data.remove(id);
}
public void removeDefault(final int id) {
public void addEmpty(final StructuredDataKey<?> key) {
// Empty optional to override the Minecraft default
this.data.put(id, Optional.empty());
this.data.put(key, StructuredData.empty(key, serializerId(key)));
}
public boolean contains(final int id) {
return this.data.containsKey(id);
/**
* Removes and returns structured data by the given key.
*
* @param key serializer key
* @param <T> data type
* @return removed structured data
*/
public @Nullable <T> StructuredData<T> remove(final StructuredDataKey<T> key) {
final StructuredData<?> data = this.data.remove(key);
//noinspection unchecked
return data != null ? (StructuredData<T>) data : null;
}
public boolean contains(final Protocol<?, ?, ?, ?> protocol, final StructuredDataKey<?> key) {
return this.data.containsKey(serializerId(protocol, key));
public boolean contains(final StructuredDataKey<?> key) {
return this.data.containsKey(key);
}
/**
* Sets the lookup for serializer ids. Required to call most of the other methods.
*
* @param protocol protocol to retreive the id of the serializer from
* @param mappedNames if the names are mapped (true if structures from the mapped version are added, false for the unmapped version)
*/
public void setIdLookup(final Protocol<?, ?, ?, ?> protocol, final boolean mappedNames) {
this.lookup = protocol.getMappingData().getDataComponentSerializerMappings();
this.mappedNames = mappedNames;
}
public StructuredDataContainer copy() {
return new StructuredDataContainer(new Int2ObjectOpenHashMap<>(data));
final StructuredDataContainer copy = new StructuredDataContainer(new Reference2ObjectOpenHashMap<>(data));
copy.lookup = this.lookup;
return copy;
}
private int serializerId(final Protocol<?, ?, ?, ?> protocol, final StructuredDataKey<?> key) {
final int id = protocol.getMappingData().getDataComponentSerializerMappings().mappedId(key.identifier());
private int serializerId(final StructuredDataKey<?> key) {
final int id = mappedNames ? lookup.mappedId(key.identifier()) : lookup.id(key.identifier());
if (id == -1) {
Via.getPlatform().getLogger().severe("Could not find item data serializer for type " + key);
}
return id;
}
public Int2ObjectMap<Optional<StructuredData<?>>> data() {
public Map<StructuredDataKey<?>, StructuredData<?>> data() {
return data;
}
}

View File

@ -22,24 +22,25 @@
*/
package com.viaversion.viaversion.api.type.types.item;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Optional;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ItemType1_20_5 extends Type<Item> {
private final Type<StructuredData<?>> dataType;
private final StructuredDataType dataType;
public ItemType1_20_5(final Type<StructuredData<?>> itemDataType) {
public ItemType1_20_5(final StructuredDataType dataType) {
super(Item.class);
this.dataType = itemDataType;
this.dataType = dataType;
}
@Override
@ -50,26 +51,30 @@ public class ItemType1_20_5 extends Type<Item> {
}
final int id = Type.VAR_INT.readPrimitive(buffer);
final Int2ObjectMap<Optional<StructuredData<?>>> data = readData(buffer);
final Map<StructuredDataKey<?>, StructuredData<?>> data = readData(buffer);
return new StructuredItem(id, amount, new StructuredDataContainer(data));
}
private Int2ObjectMap<Optional<StructuredData<?>>> readData(final ByteBuf buffer) throws Exception {
private Map<StructuredDataKey<?>, StructuredData<?>> readData(final ByteBuf buffer) throws Exception {
final int valuesSize = Type.VAR_INT.readPrimitive(buffer);
final int markersSize = Type.VAR_INT.readPrimitive(buffer);
if (valuesSize == 0 && markersSize == 0) {
return new Int2ObjectOpenHashMap<>();
return new Reference2ObjectOpenHashMap<>();
}
final Int2ObjectMap<Optional<StructuredData<?>>> map = new Int2ObjectOpenHashMap<>(valuesSize + markersSize);
final Map<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>(valuesSize + markersSize);
for (int i = 0; i < valuesSize; i++) {
final StructuredData<?> value = dataType.read(buffer);
map.put(value.id(), Optional.of(value));
final StructuredDataKey<?> key = dataType.key(value.id());
Preconditions.checkNotNull(key, "No data component serializer found for $s", value);
map.put(key, value);
}
for (int i = 0; i < markersSize; i++) {
final int key = Type.VAR_INT.readPrimitive(buffer);
map.put(key, Optional.empty());
final int id = Type.VAR_INT.readPrimitive(buffer);
final StructuredDataKey<?> key = dataType.key(id);
Preconditions.checkNotNull(key, "No data component serializer found for empty id $s", id);
map.put(key, StructuredData.empty(key, id));
}
return map;
}
@ -84,11 +89,11 @@ public class ItemType1_20_5 extends Type<Item> {
buffer.writeByte(object.amount());
Type.VAR_INT.writePrimitive(buffer, object.identifier());
final Int2ObjectMap<Optional<StructuredData<?>>> data = object.structuredData().data();
final Map<StructuredDataKey<?>, StructuredData<?>> data = object.structuredData().data();
int valuesSize = 0;
int markersSize = 0;
for (final Int2ObjectMap.Entry<Optional<StructuredData<?>>> entry : data.int2ObjectEntrySet()) {
if (entry.getValue().isPresent()) {
for (final StructuredData<?> value : data.values()) {
if (value.isPresent()) {
valuesSize++;
} else {
markersSize++;
@ -98,14 +103,14 @@ public class ItemType1_20_5 extends Type<Item> {
Type.VAR_INT.writePrimitive(buffer, valuesSize);
Type.VAR_INT.writePrimitive(buffer, markersSize);
for (final Int2ObjectMap.Entry<Optional<StructuredData<?>>> entry : data.int2ObjectEntrySet()) {
if (entry.getValue().isPresent()) {
dataType.write(buffer, entry.getValue().get());
for (final StructuredData<?> value : data.values()) {
if (value.isPresent()) {
dataType.write(buffer, value);
}
}
for (final Int2ObjectMap.Entry<Optional<StructuredData<?>>> entry : data.int2ObjectEntrySet()) {
if (!entry.getValue().isPresent()) {
Type.VAR_INT.writePrimitive(buffer, entry.getIntKey());
for (final StructuredData<?> value : data.values()) {
if (value.isEmpty()) {
Type.VAR_INT.writePrimitive(buffer, value.id());
}
}
}

View File

@ -30,6 +30,7 @@ import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public class StructuredDataType extends Type<StructuredData<?>> {
@ -55,8 +56,12 @@ public class StructuredDataType extends Type<StructuredData<?>> {
throw new IllegalArgumentException("No data component serializer found for id " + id);
}
public @Nullable StructuredDataKey<?> key(final int id) {
return types.get(id);
}
private <T> StructuredData<T> readData(final ByteBuf buffer, final StructuredDataKey<T> key, final int id) throws Exception {
return new StructuredData<>(key, key.type().read(buffer), id);
return StructuredData.of(key, key.type().read(buffer), id);
}
public DataFiller filler(final Protocol<?, ?, ?, ?> protocol) {

View File

@ -38,8 +38,8 @@ public final class Types1_20_5 {
// Most of these are only safe to use after protocol loading
public static final ParticleType PARTICLE = new ParticleType();
public static final StructuredDataType ITEM_DATA = new StructuredDataType();
public static final Type<Item> ITEM = new ItemType1_20_5(ITEM_DATA);
public static final StructuredDataType STRUCTURED_DATA = new StructuredDataType();
public static final Type<Item> ITEM = new ItemType1_20_5(STRUCTURED_DATA);
public static final Type<Item[]> ITEM_ARRAY = new ArrayType<>(ITEM);
public static final MetaTypes1_20_5 META_TYPES = new MetaTypes1_20_5(PARTICLE);
public static final Type<Metadata> METADATA = new MetadataType(META_TYPES);

View File

@ -29,9 +29,10 @@ fun ShadowJar.configureRelocations() {
}
fun ShadowJar.configureExcludes() {
// FastUtil - we only want object, int, and reference maps
// FastUtil - we only want object, int, and certain reference maps
// Object types
exclude("it/unimi/dsi/fastutil/*/*Reference*")
exclude("it/unimi/dsi/fastutil/*/*2Reference*")
exclude("it/unimi/dsi/fastutil/*/*Reference2Int*")
exclude("it/unimi/dsi/fastutil/*/*Boolean*")
exclude("it/unimi/dsi/fastutil/*/*Byte*")
exclude("it/unimi/dsi/fastutil/*/*Short*")
@ -48,4 +49,6 @@ fun ShadowJar.configureExcludes() {
exclude("it/unimi/dsi/fastutil/*/*Big*")
exclude("it/unimi/dsi/fastutil/*/*Synchronized*")
exclude("it/unimi/dsi/fastutil/*/*Unmodifiable*")
// More
exclude("it/unimi/dsi/fastutil/io/TextIO")
}

View File

@ -23,19 +23,11 @@ import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.BannerPattern;
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.GameProfile;
import com.viaversion.viaversion.api.minecraft.item.data.LodestoneTarget;
import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.minecraft.item.data.Enchantments;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
@ -120,7 +112,7 @@ public final class Protocol1_20_5To1_20_3 extends AbstractProtocol<ClientboundPa
.reader("vibration", ParticleType.Readers.VIBRATION1_20_3)
.reader("sculk_charge", ParticleType.Readers.SCULK_CHARGE)
.reader("shriek", ParticleType.Readers.SHRIEK);
Types1_20_5.ITEM_DATA.filler(this)
Types1_20_5.STRUCTURED_DATA.filler(this)
.add(StructuredDataKey.CUSTOM_DATA)
.add(StructuredDataKey.DAMAGE)
.add(StructuredDataKey.UNBREAKABLE)

View File

@ -49,7 +49,6 @@ import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<ClientboundPacket1_20_3, ServerboundPacket1_20_5, Protocol1_20_5To1_20_3> {
@ -189,7 +188,10 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
public Item toOldItem(final Item item) {
// Start out with custom data and add the rest on top
final StructuredDataContainer data = item.structuredData();
final CompoundTag tag = data.get(protocol, StructuredDataKey.CUSTOM_DATA).map(StructuredData::value).orElse(new CompoundTag());
data.setIdLookup(protocol, true);
final StructuredData<CompoundTag> customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA);
final CompoundTag tag = customData != null ? customData.value() : new CompoundTag();
// TODO
@ -200,6 +202,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
final CompoundTag tag = old.tag();
final StructuredItem item = new StructuredItem(old.identifier(), (byte) old.amount(), new StructuredDataContainer());
final StructuredDataContainer data = item.structuredData();
data.setIdLookup(protocol, true);
if (tag == null) {
return item;
}
@ -208,19 +211,19 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
final NumberTag damage = tag.getNumberTag("Damage");
if (damage != null && damage.asInt() != 0) {
tag.remove("Damage");
data.add(protocol, StructuredDataKey.DAMAGE, damage.asInt());
data.add(StructuredDataKey.DAMAGE, damage.asInt());
}
final NumberTag repairCost = tag.getNumberTag("RepairCost");
if (repairCost != null && repairCost.asInt() != 0) {
tag.remove("RepairCost");
data.add(protocol, StructuredDataKey.REPAIR_COST, repairCost.asInt());
data.add(StructuredDataKey.REPAIR_COST, repairCost.asInt());
}
final NumberTag customModelData = tag.getNumberTag("CustomModelData");
if (customModelData != null) {
tag.remove("CustomModelData");
data.add(protocol, StructuredDataKey.CUSTOM_MODEL_DATA, customModelData.asInt());
data.add(StructuredDataKey.CUSTOM_MODEL_DATA, customModelData.asInt());
}
final CompoundTag blockState = tag.getCompoundTag("BlockStateTag");
@ -232,13 +235,13 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
properties.put(entry.getKey(), ((StringTag) entry.getValue()).getValue());
}
}
data.add(protocol, StructuredDataKey.BLOCK_STATE, new BlockStateProperties(properties));
data.add(StructuredDataKey.BLOCK_STATE, new BlockStateProperties(properties));
}
final CompoundTag entityTag = tag.getCompoundTag("EntityTag");
if (entityTag != null) {
tag.remove("EntityTag");
data.add(protocol, StructuredDataKey.ENTITY_DATA, entityTag);
data.add(StructuredDataKey.ENTITY_DATA, entityTag);
}
final CompoundTag blockEntityTag = tag.getCompoundTag("BlockEntityTag");
@ -255,62 +258,62 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
if (unbreakable != null && unbreakable.asBoolean()) {
tag.remove("Unbreakable");
if ((hideFlagsValue & 0x04) != 0) {
data.add(protocol, StructuredDataKey.UNBREAKABLE, true); // TODO Value is hide, should have a wrapper
data.add(StructuredDataKey.UNBREAKABLE, true); // TODO Value is hide, should have a wrapper
} else {
data.addEmpty(protocol, StructuredDataKey.UNBREAKABLE);
data.addEmpty(StructuredDataKey.UNBREAKABLE);
}
}
updateEnchantments(data, tag, "Enchantments", StructuredDataKey.ENCHANTMENTS, (hideFlagsValue & 0x01) != 0);
updateEnchantments(data, tag, "StoredEnchantments", StructuredDataKey.STORED_ENCHANTMENTS, (hideFlagsValue & 0x20) != 0);
updateEnchantments(data, tag, "Enchantments", StructuredDataKey.ENCHANTMENTS, (hideFlagsValue & 0x01) == 0);
updateEnchantments(data, tag, "StoredEnchantments", StructuredDataKey.STORED_ENCHANTMENTS, (hideFlagsValue & 0x20) == 0);
// TODO
// tructuredDataKey.CUSTOM_NAME
// tructuredDataKey.LORE
// tructuredDataKey.CAN_PLACE_ON
// tructuredDataKey.CAN_BREAK
// tructuredDataKey.ATTRIBUTE_MODIFIERS
// tructuredDataKey.HIDE_ADDITIONAL_TOOLTIP
// tructuredDataKey.REPAIR_COST
// tructuredDataKey.CREATIVE_SLOT_LOCK
// tructuredDataKey.INTANGIBLE_PROJECTILE
// tructuredDataKey.DYED_COLOR
// tructuredDataKey.MAP_COLOR
// tructuredDataKey.MAP_ID
// tructuredDataKey.MAP_DECORATIONS
// tructuredDataKey.MAP_POST_PROCESSING
// tructuredDataKey.CHARGED_PROJECTILES
// tructuredDataKey.BUNDLE_CONTENTS
// tructuredDataKey.POTION_CONTENTS
// tructuredDataKey.SUSPICIOUS_STEW_EFFECTS
// tructuredDataKey.WRITABLE_BOOK_CONTENT
// tructuredDataKey.WRITTEN_BOOK_CONTENT
// tructuredDataKey.TRIM
// tructuredDataKey.DEBUG_STICK_STATE
// tructuredDataKey.BUCKET_ENTITY_DATA
// tructuredDataKey.BLOCK_ENTITY_DATA
// tructuredDataKey.INSTRUMENT
// tructuredDataKey.RECIPES
// tructuredDataKey.LODESTONE_TARGET
// tructuredDataKey.FIREWORK_EXPLOSION
// tructuredDataKey.FIREWORKS
// tructuredDataKey.PROFILE
// tructuredDataKey.NOTE_BLOCK_SOUND
// tructuredDataKey.BANNER_PATTERNS
// tructuredDataKey.BASE_COLOR
// tructuredDataKey.POT_DECORATIONS
// tructuredDataKey.CONTAINER
// tructuredDataKey.BEES
// tructuredDataKey.LOCK
// tructuredDataKey.CONTAINER_LOOT
// StructuredDataKey.CUSTOM_NAME
// StructuredDataKey.LORE
// StructuredDataKey.CAN_PLACE_ON
// StructuredDataKey.CAN_BREAK
// StructuredDataKey.ATTRIBUTE_MODIFIERS
// StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP
// StructuredDataKey.REPAIR_COST
// StructuredDataKey.CREATIVE_SLOT_LOCK
// StructuredDataKey.INTANGIBLE_PROJECTILE
// StructuredDataKey.DYED_COLOR
// StructuredDataKey.MAP_COLOR
// StructuredDataKey.MAP_ID
// StructuredDataKey.MAP_DECORATIONS
// StructuredDataKey.MAP_POST_PROCESSING
// StructuredDataKey.CHARGED_PROJECTILES
// StructuredDataKey.BUNDLE_CONTENTS
// StructuredDataKey.POTION_CONTENTS
// StructuredDataKey.SUSPICIOUS_STEW_EFFECTS
// StructuredDataKey.WRITABLE_BOOK_CONTENT
// StructuredDataKey.WRITTEN_BOOK_CONTENT
// StructuredDataKey.TRIM
// StructuredDataKey.DEBUG_STICK_STATE
// StructuredDataKey.BUCKET_ENTITY_DATA
// StructuredDataKey.BLOCK_ENTITY_DATA
// StructuredDataKey.INSTRUMENT
// StructuredDataKey.RECIPES
// StructuredDataKey.LODESTONE_TARGET
// StructuredDataKey.FIREWORK_EXPLOSION
// StructuredDataKey.FIREWORKS
// StructuredDataKey.PROFILE
// StructuredDataKey.NOTE_BLOCK_SOUND
// StructuredDataKey.BANNER_PATTERNS
// StructuredDataKey.BASE_COLOR
// StructuredDataKey.POT_DECORATIONS
// StructuredDataKey.CONTAINER
// StructuredDataKey.BEES
// StructuredDataKey.LOCK
// StructuredDataKey.CONTAINER_LOOT
// Add the rest as custom data
data.add(protocol, StructuredDataKey.CUSTOM_DATA, tag);
data.add(StructuredDataKey.CUSTOM_DATA, tag);
return item;
}
private void updateEnchantments(final StructuredDataContainer data, final CompoundTag tag, final String key,
final StructuredDataKey<Enchantments> newKey, final boolean hide) {
final StructuredDataKey<Enchantments> newKey, final boolean show) {
final ListTag enchantmentsTag = tag.getListTag(key);
if (enchantmentsTag == null) {
return;
@ -318,7 +321,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
tag.remove(key);
final Enchantments enchantments = new Enchantments(new Int2IntOpenHashMap(), !hide);
final Enchantments enchantments = new Enchantments(new Int2IntOpenHashMap(), show);
for (final Tag enchantment : enchantmentsTag) {
if (!(enchantment instanceof CompoundTag)) {
continue;
@ -339,11 +342,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
enchantments.enchantments().put(intId, lvl.asInt());
}
data.add(protocol, newKey, enchantments);
data.add(newKey, enchantments);
// Add glint if none of the enchantments were valid
if (enchantments.size() == 0 && !enchantmentsTag.isEmpty()) {
data.add(protocol, StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);
data.add(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);
}
}
}

View File

@ -50,8 +50,6 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
this(protocol, itemType, itemArrayType, itemType, itemArrayType);
}
// These two methods always return the same item instance *for now*
// It is made this way, so it's easy to handle new instance creation/implementation changes
@Override
public @Nullable Item handleItemToClient(@Nullable Item item) {
if (item == null) return null;

View File

@ -0,0 +1,81 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2024 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.rewriter;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.Mappings;
import com.viaversion.viaversion.api.data.ParticleMappings;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Type;
import org.checkerframework.checker.nullness.qual.Nullable;
public class StructuredItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends Protocol<C, ?, ?, S>> extends ItemRewriter<C, S, T> {
public StructuredItemRewriter(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType, Type<Item> mappedItemType, Type<Item[]> mappedItemArrayType) {
super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType);
}
public StructuredItemRewriter(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType) {
super(protocol, itemType, itemArrayType, itemType, itemArrayType);
}
@Override
public @Nullable Item handleItemToClient(@Nullable Item item) {
if (item == null) {
return null;
}
final MappingData mappingData = protocol.getMappingData();
if (mappingData != null) {
if (mappingData.getItemMappings() != null) {
item.setIdentifier(mappingData.getNewItemId(item.identifier()));
}
if (mappingData.getDataComponentSerializerMappings() != null) {
item.structuredData().setIdLookup(protocol, false);
}
}
return item;
}
@Override
public @Nullable Item handleItemToServer(@Nullable Item item) {
if (item == null) {
return null;
}
final MappingData mappingData = protocol.getMappingData();
if (mappingData != null) {
if (mappingData.getItemMappings() != null) {
item.setIdentifier(mappingData.getOldItemId(item.identifier()));
}
if (mappingData.getDataComponentSerializerMappings() != null) {
item.structuredData().setIdLookup(protocol, true);
}
}
return item;
}
}

View File

@ -17,7 +17,6 @@
*/
package com.viaversion.viaversion.template.protocols.rewriter;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.protocols.protocol1_20_3to1_20_2.rewriter.RecipeRewriter1_20_3;
@ -26,13 +25,13 @@ import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.Clientb
import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.ServerboundPacket1_20_5;
import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.template.protocols.Protocol1_99To_98;
// To replace if needed:
// ChunkType1_20_2
// RecipeRewriter1_20_3
public final class BlockItemPacketRewriter1_99 extends ItemRewriter<ClientboundPacket1_20_5, ServerboundPacket1_20_5, Protocol1_99To_98> {
public final class BlockItemPacketRewriter1_99 extends StructuredItemRewriter<ClientboundPacket1_20_5, ServerboundPacket1_20_5, Protocol1_99To_98> {
public BlockItemPacketRewriter1_99(final Protocol1_99To_98 protocol) {
super(protocol, /*TypesOLD.ITEM, TypesOLD.ITEM_ARRAY, */Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY);