Make Metadata fully type safe, remove "Discontinued" meta types

The Discontinued entry was a special edge case that could lead to a Metadata type returning null. Instead, just directly use null in the 1.8->1.9 code where it is checked against. Also renamed the Meta1_17Types entries to be in uppercase and properly represent their value type.
This commit is contained in:
KennyTV 2021-05-26 13:04:15 +02:00
parent bc89f57088
commit 2d0a597f74
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
16 changed files with 85 additions and 76 deletions

View File

@ -26,7 +26,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface EntityTracker extends ClientEntityIdChangeListener {
public interface EntityTracker {
/**
* User connection the tracker belongs to.

View File

@ -38,14 +38,12 @@ public final class Metadata {
* @param id metadata index
* @param metaType metadata type
* @param value value if present
* @throws NullPointerException if the given metaType is null
* @throws IllegalArgumentException if the value and metaType are incompatible
*/
public Metadata(int id, MetaType metaType, @Nullable Object value) {
Preconditions.checkNotNull(metaType);
this.id = id;
this.metaType = metaType;
this.value = checkValue(value);
this.value = checkValue(metaType, value);
}
public int id() {
@ -61,13 +59,14 @@ public final class Metadata {
}
/**
* Sets the metadata type.
* Update the value with {@link #setValue(Object)} in case value and type are no longer compatible.
* Sets the metadata type if compatible with the current value.
*
* @param metaType metadata type
* @throws IllegalArgumentException if the metadata type and current value are incompatible
* @see #setTypeAndValue(MetaType, Object)
*/
public void setMetaType(MetaType metaType) {
Preconditions.checkNotNull(metaType);
checkValue(metaType, this.value);
this.metaType = metaType;
}
@ -80,28 +79,46 @@ public final class Metadata {
}
/**
* Sets the metadata value.
* Always call {@link #setMetaType(MetaType)} first if the output type changes.
* Sets the metadata value if compatible with the current meta type.
*
* @param value value
* @throws IllegalArgumentException if the value and metaType are incompatible
* @throws IllegalArgumentException if the value and current metaType are incompatible
* @see #setTypeAndValue(MetaType, Object)
*/
public void setValue(@Nullable Object value) {
this.value = checkValue(value);
this.value = checkValue(this.metaType, value);
}
private Object checkValue(Object value) {
/**
* Sets metadata type and value.
*
* @param metaType metadata type
* @param value value
* @throws IllegalArgumentException if the value and metaType are incompatible
*/
public void setTypeAndValue(MetaType metaType, @Nullable Object value) {
this.value = checkValue(metaType, value);
this.metaType = metaType;
}
private Object checkValue(MetaType metaType, @Nullable Object value) {
Preconditions.checkNotNull(metaType);
if (value != null && !metaType.type().getOutputClass().isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("Metadata value and metaType are incompatible. Type=" + metaType + ", value=" + value);
throw new IllegalArgumentException("Metadata value and metaType are incompatible. Type=" + metaType
+ ", value=" + (value != null ? value + " (" + value.getClass().getSimpleName() + ")" : "null"));
}
return value;
}
@Deprecated
public void setMetaTypeUnsafe(MetaType type) {
this.metaType = type;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Metadata metadata = (Metadata) o;
if (id != metadata.id) return false;
if (metaType != metaType) return false;
@ -111,7 +128,7 @@ public final class Metadata {
@Override
public int hashCode() {
int result = id;
result = 31 * result + (metaType != null ? metaType.hashCode() : 0);
result = 31 * result + metaType.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}

View File

@ -39,8 +39,7 @@ public enum MetaType1_12 implements MetaType {
Direction(10, Type.VAR_INT),
OptUUID(11, Type.OPTIONAL_UUID),
BlockID(12, Type.VAR_INT),
NBTTag(13, Type.NBT),
Discontinued(99, null);
NBTTag(13, Type.NBT);
private final int typeID;
private final Type type;

View File

@ -42,8 +42,7 @@ public enum MetaType1_13 implements MetaType {
OptUUID(12, Type.OPTIONAL_UUID),
BlockID(13, Type.VAR_INT),
NBTTag(14, Type.NBT),
PARTICLE(15, Types1_13.PARTICLE),
Discontinued(99, null);
PARTICLE(15, Types1_13.PARTICLE);
private final int typeID;
private final Type type;

View File

@ -42,8 +42,7 @@ public enum MetaType1_13_2 implements MetaType {
OptUUID(12, Type.OPTIONAL_UUID),
BlockID(13, Type.VAR_INT),
NBTTag(14, Type.NBT),
PARTICLE(15, Types1_13_2.PARTICLE),
Discontinued(99, null);
PARTICLE(15, Types1_13_2.PARTICLE);
private final int typeID;
private final Type type;

View File

@ -45,8 +45,7 @@ public enum MetaType1_14 implements MetaType {
PARTICLE(15, Types1_14.PARTICLE),
VillagerData(16, Type.VILLAGER_DATA),
OptVarInt(17, Type.OPTIONAL_VAR_INT),
Pose(18, Type.VAR_INT),
Discontinued(99, null);
Pose(18, Type.VAR_INT);
private final int typeID;
private final Type type;

View File

@ -27,32 +27,31 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.version.Types1_17;
public enum MetaType1_17 implements MetaType {
Byte(0, Type.BYTE),
VarInt(1, Type.VAR_INT),
Float(2, Type.FLOAT),
String(3, Type.STRING),
Chat(4, Type.COMPONENT),
OptChat(5, Type.OPTIONAL_COMPONENT),
Slot(6, Type.FLAT_VAR_INT_ITEM),
Boolean(7, Type.BOOLEAN),
Vector3F(8, Type.ROTATION),
Position(9, Type.POSITION1_14),
OptPosition(10, Type.OPTIONAL_POSITION_1_14),
Direction(11, Type.VAR_INT),
OptUUID(12, Type.OPTIONAL_UUID),
BlockID(13, Type.VAR_INT),
NBTTag(14, Type.NBT),
BYTE(0, Type.BYTE),
VAR_INT(1, Type.VAR_INT),
FLOAT(2, Type.FLOAT),
STRING(3, Type.STRING),
COMPONENT(4, Type.COMPONENT),
OPT_COMPONENT(5, Type.OPTIONAL_COMPONENT),
ITEM(6, Type.FLAT_VAR_INT_ITEM),
BOOLEAN(7, Type.BOOLEAN),
ROTATION(8, Type.ROTATION),
POSITION(9, Type.POSITION1_14),
OPT_POSITION(10, Type.OPTIONAL_POSITION_1_14),
DIRECTION(11, Type.VAR_INT),
OPT_UUID(12, Type.OPTIONAL_UUID),
BLOCK_STATE(13, Type.VAR_INT),
NBT(14, Type.NBT),
PARTICLE(15, Types1_17.PARTICLE),
VillagerData(16, Type.VILLAGER_DATA),
OptVarInt(17, Type.OPTIONAL_VAR_INT),
Pose(18, Type.VAR_INT),
Discontinued(99, null);
VILLAGER_DATA(16, Type.VILLAGER_DATA),
OPT_VAR_INT(17, Type.OPTIONAL_VAR_INT),
POSE(18, Type.VAR_INT);
private final int typeID;
private final int typeId;
private final Type type;
MetaType1_17(int typeID, Type type) {
this.typeID = typeID;
MetaType1_17(int typeId, Type type) {
this.typeId = typeId;
this.type = type;
}
@ -62,7 +61,7 @@ public enum MetaType1_17 implements MetaType {
@Override
public int typeId() {
return typeID;
return typeId;
}
@Override

View File

@ -34,6 +34,7 @@ public enum MetaType1_8 implements MetaType {
Slot(5, Type.ITEM),
Position(6, Type.VECTOR),
Rotation(7, Type.ROTATION),
@Deprecated
NonExistent(-1, Type.NOTHING);
private final int typeID;

View File

@ -38,8 +38,7 @@ public enum MetaType1_9 implements MetaType {
OptPosition(9, Type.OPTIONAL_POSITION),
Direction(10, Type.VAR_INT),
OptUUID(11, Type.OPTIONAL_UUID),
BlockID(12, Type.VAR_INT),
Discontinued(99, null);
BlockID(12, Type.VAR_INT);
private final int typeID;
private final Type type;

View File

@ -20,6 +20,7 @@ package com.viaversion.viaversion.data.entity;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.ClientEntityIdChangeListener;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
@ -28,7 +29,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class EntityTrackerBase implements EntityTracker {
public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeListener {
private final Map<Integer, EntityType> entityTypes = new ConcurrentHashMap<>();
private final Map<Integer, StoredEntityData> entityData;
private final UserConnection connection;
@ -68,9 +69,9 @@ public class EntityTrackerBase implements EntityTracker {
}
@Override
public StoredEntityData entityData(int id) {
public @Nullable StoredEntityData entityData(int id) {
EntityType type = entityType(id);
return entityData.computeIfAbsent(id, s -> new StoredEntityImpl(type));
return type != null ? entityData.computeIfAbsent(id, s -> new StoredEntityImpl(type)) : null;
}
@Override

View File

@ -51,9 +51,8 @@ public class MetadataRewriter1_11To1_10 extends EntityRewriter<Protocol1_11To1_1
if (type.is(EntityType.ELDER_GUARDIAN) || type.is(EntityType.GUARDIAN)) { // Guardians
int oldid = metadata.id();
if (oldid == 12) {
metadata.setMetaType(MetaType1_9.Boolean);
boolean val = (((byte) metadata.getValue()) & 0x02) == 0x02;
metadata.setValue(val);
metadata.setTypeAndValue(MetaType1_9.Boolean, val);
}
}

View File

@ -51,11 +51,10 @@ public class MetadataRewriter1_13To1_12_2 extends EntityRewriter<Protocol1_13To1
// Handle String -> Chat DisplayName
if (metadata.id() == 2) {
metadata.setMetaType(MetaType1_13.OptChat);
if (metadata.getValue() != null && !((String) metadata.getValue()).isEmpty()) {
metadata.setValue(ChatRewriter.legacyTextToJson((String) metadata.getValue()));
metadata.setTypeAndValue(MetaType1_13.OptChat, ChatRewriter.legacyTextToJson((String) metadata.getValue()));
} else {
metadata.setValue(null);
metadata.setTypeAndValue(MetaType1_13.OptChat, null);
}
}

View File

@ -139,14 +139,12 @@ public class MetadataRewriter1_14To1_13_2 extends EntityRewriter<Protocol1_14To1
} else if (type.is(Entity1_14Types.VILLAGER)) {
if (metadata.id() == 15) {
// plains
metadata.setMetaType(MetaType1_14.VillagerData);
metadata.setValue(new VillagerData(2, getNewProfessionId((int) metadata.getValue()), 0));
metadata.setTypeAndValue(MetaType1_14.VillagerData, new VillagerData(2, getNewProfessionId((int) metadata.getValue()), 0));
}
} else if (type.is(Entity1_14Types.ZOMBIE_VILLAGER)) {
if (metadata.id() == 18) {
// plains
metadata.setMetaType(MetaType1_14.VillagerData);
metadata.setValue(new VillagerData(2, getNewProfessionId((int) metadata.getValue()), 0));
metadata.setTypeAndValue(MetaType1_14.VillagerData, new VillagerData(2, getNewProfessionId((int) metadata.getValue()), 0));
}
} else if (type.isOrHasParent(Entity1_14Types.ABSTRACT_ARROW)) {
if (metadata.id() >= 9) { // New piercing

View File

@ -37,7 +37,7 @@ public class MetadataRewriter1_17To1_16_4 extends EntityRewriter {
filter().handler((event, meta) -> {
meta.setMetaType(MetaType1_17.byId(meta.metaType().typeId()));
if (meta.metaType() == MetaType1_17.Pose) {
if (meta.metaType() == MetaType1_17.POSE) {
int pose = meta.value();
if (pose > 5) {
// Added LONG_JUMP at 6
@ -45,7 +45,7 @@ public class MetadataRewriter1_17To1_16_4 extends EntityRewriter {
}
}
});
registerDumMetaTypeHandler(MetaType1_17.Slot, MetaType1_17.BlockID, MetaType1_17.PARTICLE, InventoryPackets::toClient);
registerDumMetaTypeHandler(MetaType1_17.ITEM, MetaType1_17.BLOCK_STATE, MetaType1_17.PARTICLE, InventoryPackets::toClient);
// Ticks frozen added with id 7
filter().filterFamily(Entity1_17Types.ENTITY).addIndex(7);

View File

@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaType1_8;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaType1_9;
import com.viaversion.viaversion.util.Pair;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashMap;
import java.util.Optional;
@ -54,11 +55,11 @@ public enum MetaIndex {
STAND_RL_POS(ARMOR_STAND, 16, MetaType1_8.Rotation, MetaType1_9.Vector3F),
// human, discountined?
PLAYER_SKIN_FLAGS(ENTITY_HUMAN, 10, MetaType1_8.Byte, 12, MetaType1_9.Byte), // unsigned on 1.8
PLAYER_HUMAN_BYTE(ENTITY_HUMAN, 16, MetaType1_8.Byte, MetaType1_9.Discontinued), // unused on 1.8
PLAYER_HUMAN_BYTE(ENTITY_HUMAN, 16, MetaType1_8.Byte, null), // unused on 1.8
PLAYER_ADDITIONAL_HEARTS(ENTITY_HUMAN, 17, MetaType1_8.Float, 10, MetaType1_9.Float),
PLAYER_SCORE(ENTITY_HUMAN, 18, MetaType1_8.Int, 11, MetaType1_9.VarInt),
PLAYER_HAND(ENTITY_HUMAN, -1, MetaType1_8.NonExistent, 5, MetaType1_9.Byte), // new in 1.9
SOMETHING_ANTICHEAT_PLUGINS_FOR_SOME_REASON_USE(ENTITY_HUMAN, 11, MetaType1_8.Byte, MetaType1_9.Discontinued), //For what we know, This doesn't exists. If you think it exists and knows what it does. Please tell us.
SOMETHING_ANTICHEAT_PLUGINS_FOR_SOME_REASON_USE(ENTITY_HUMAN, 11, MetaType1_8.Byte, null), //For what we know, This doesn't exists. If you think it exists and knows what it does. Please tell us.
// horse
HORSE_INFO(HORSE, 16, MetaType1_8.Int, 12, MetaType1_9.Byte),
HORSE_TYPE(HORSE, 19, MetaType1_8.Byte, 13, MetaType1_9.VarInt),
@ -86,7 +87,7 @@ public enum MetaIndex {
VILLAGER_PROFESSION(VILLAGER, 16, MetaType1_8.Int, 12, MetaType1_9.VarInt),
// enderman
ENDERMAN_BLOCKSTATE(ENDERMAN, 16, MetaType1_8.Short, 11, MetaType1_9.BlockID),
ENDERMAN_BLOCKDATA(ENDERMAN, 17, MetaType1_8.Byte, MetaType1_9.Discontinued), //always 0 when sent, never read by the client
ENDERMAN_BLOCKDATA(ENDERMAN, 17, MetaType1_8.Byte, null), //always 0 when sent, never read by the client
ENDERMAN_ISSCREAMING(ENDERMAN, 18, MetaType1_8.Byte, 12, MetaType1_9.Boolean),
// zombie
ZOMBIE_ISCHILD(ZOMBIE, 12, MetaType1_8.Byte, 11, MetaType1_9.Boolean),
@ -117,7 +118,7 @@ public enum MetaIndex {
WITHER_TARGET3(WITHER, 19, MetaType1_8.Int, 13, MetaType1_9.VarInt),
WITHER_INVULN_TIME(WITHER, 20, MetaType1_8.Int, 14, MetaType1_9.VarInt),
WITHER_PROPERTIES(WITHER, 10, MetaType1_8.Byte, MetaType1_9.Byte),
WITHER_UNKNOWN(WITHER, 11, MetaType1_8.Byte, MetaType1_9.Discontinued),
WITHER_UNKNOWN(WITHER, 11, MetaType1_8.Byte, null),
// wither skull
WITHERSKULL_INVULN(WITHER_SKULL, 10, MetaType1_8.Byte, 5, MetaType1_9.Boolean),
// guardian
@ -150,12 +151,12 @@ public enum MetaIndex {
ITEMFRAME_ITEM(ITEM_FRAME, 8, MetaType1_8.Slot, 5, MetaType1_9.Slot),
ITEMFRAME_ROTATION(ITEM_FRAME, 9, MetaType1_8.Byte, 6, MetaType1_9.VarInt),
// ender crystal
ENDERCRYSTAL_HEALTH(ENDER_CRYSTAL, 8, MetaType1_8.Int, MetaType1_9.Discontinued),
ENDERCRYSTAL_HEALTH(ENDER_CRYSTAL, 8, MetaType1_8.Int, null),
// Ender dragon boss bar issues
ENDERDRAGON_UNKNOWN(ENDER_DRAGON, 5, MetaType1_8.Byte, MetaType1_9.Discontinued),
ENDERDRAGON_NAME(ENDER_DRAGON, 10, MetaType1_8.String, MetaType1_9.Discontinued),
ENDERDRAGON_UNKNOWN(ENDER_DRAGON, 5, MetaType1_8.Byte, null),
ENDERDRAGON_NAME(ENDER_DRAGON, 10, MetaType1_8.String, null),
// Normal Ender dragon
ENDERDRAGON_FLAG(ENDER_DRAGON, 15, MetaType1_8.Byte, MetaType1_9.Discontinued),
ENDERDRAGON_FLAG(ENDER_DRAGON, 15, MetaType1_8.Byte, null),
ENDERDRAGON_PHASE(ENDER_DRAGON, 11, MetaType1_8.Byte, MetaType1_9.VarInt);
private static final HashMap<Pair<Entity1_10Types.EntityType, Integer>, MetaIndex> metadataRewrites = new HashMap<>();
@ -171,7 +172,7 @@ public enum MetaIndex {
private final MetaType1_8 oldType;
private final int index;
MetaIndex(Entity1_10Types.EntityType type, int index, MetaType1_8 oldType, MetaType1_9 newType) {
MetaIndex(Entity1_10Types.EntityType type, int index, MetaType1_8 oldType, @Nullable MetaType1_9 newType) {
this.clazz = type;
this.index = index;
this.newIndex = index;
@ -179,7 +180,7 @@ public enum MetaIndex {
this.newType = newType;
}
MetaIndex(Entity1_10Types.EntityType type, int index, MetaType1_8 oldType, int newIndex, MetaType1_9 newType) {
MetaIndex(Entity1_10Types.EntityType type, int index, MetaType1_8 oldType, int newIndex, @Nullable MetaType1_9 newType) {
this.clazz = type;
this.index = index;
this.oldType = oldType;
@ -195,7 +196,7 @@ public enum MetaIndex {
return newIndex;
}
public MetaType1_9 getNewType() {
public @Nullable MetaType1_9 getNewType() {
return newType;
}

View File

@ -26,7 +26,6 @@ import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.metadata.MetaType;
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaType1_8;
import com.viaversion.viaversion.api.minecraft.metadata.types.MetaType1_9;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ItemRewriter;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.rewriter.EntityRewriter;
@ -47,13 +46,13 @@ public class MetadataRewriter1_9To1_8 extends EntityRewriter<Protocol1_9To1_8> {
throw new Exception("Could not find valid metadata");
}
if (metaIndex.getNewType() == MetaType1_9.Discontinued) {
if (metaIndex.getNewType() == null) {
metadatas.remove(metadata);
return;
}
metadata.setId(metaIndex.getNewIndex());
metadata.setMetaType(metaIndex.getNewType());
metadata.setMetaTypeUnsafe(metaIndex.getNewType());
Object value = metadata.getValue();
switch (metaIndex.getNewType()) {