diff --git a/.gitignore b/.gitignore index 2a52076ca..09ea7f7c4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ classes/ *.ipr *.iws +benchmark/ + # VSCode .settings/ diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entitydata/types/AbstractEntityDataTypes.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entitydata/types/AbstractEntityDataTypes.java index 1383a6f40..d8a5bcf7a 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entitydata/types/AbstractEntityDataTypes.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entitydata/types/AbstractEntityDataTypes.java @@ -43,6 +43,10 @@ public abstract class AbstractEntityDataTypes implements EntityDataTypes { return values; } + public Class getDataTypeClass() { + return byId(0).getClass(); + } + protected EntityDataType add(final int typeId, final Type type) { if (values[typeId] != null) { throw new IllegalArgumentException("Entity data type ID " + typeId + " is already registered as " + values[typeId]); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/template/EntityPacketRewriter99_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/template/EntityPacketRewriter99_1.java index 219ba917e..8fc5a9076 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/template/EntityPacketRewriter99_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/template/EntityPacketRewriter99_1.java @@ -41,15 +41,13 @@ final class EntityPacketRewriter99_1 extends EntityRewriter { - int id = typeId; - if (id >= SomeAddedIndex) { - id++; - } - return entityDataTypes.byId(id); - });*/ + dataTypeMapper().register(); + /* ... or like this for additions and removals that are not at the very end + dataTypeMapper() + .added(entityDataTypes.catSoundVariant) + .removed(entityDataTypes.cowSoundVariant) + .skip(entityDataTypes.pigSoundVariant) // if neither removed nor added, but the value type has to be changed separately + .register();*/ // Registers registry type id changes registerEntityDataTypeHandler( diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/EntityPacketRewriter1_21.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/EntityPacketRewriter1_21.java index 1f37c1c37..70ae63e9e 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/EntityPacketRewriter1_21.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/EntityPacketRewriter1_21.java @@ -26,7 +26,6 @@ import com.viaversion.viaversion.api.minecraft.PaintingVariant; import com.viaversion.viaversion.api.minecraft.RegistryEntry; import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5; -import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.version.VersionedTypes; @@ -177,17 +176,17 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter { - final EntityDataType type = data.dataType(); - if (type == VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType) { - final int variant = data.value(); - data.setTypeAndValue(VersionedTypes.V1_21.entityDataTypes.wolfVariantType, Holder.of(variant)); - } else if (type == VersionedTypes.V1_20_5.entityDataTypes.paintingVariantType) { - final int variant = data.value(); - data.setTypeAndValue(VersionedTypes.V1_21.entityDataTypes.paintingVariantType, Holder.of(variant)); - } else { - data.setDataType(VersionedTypes.V1_21.entityDataTypes.byId(type.typeId())); - } + dataTypeMapper() + .skip(VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType) + .skip(VersionedTypes.V1_20_5.entityDataTypes.paintingVariantType) + .register(); + filter().dataType(VersionedTypes.V1_20_5.entityDataTypes.paintingVariantType).handler((event, data) -> { + final int variant = data.value(); + data.setTypeAndValue(VersionedTypes.V1_21.entityDataTypes.paintingVariantType, Holder.of(variant)); + }); + filter().dataType(VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType).handler((event, data) -> { + final int variant = data.value(); + data.setTypeAndValue(VersionedTypes.V1_21.entityDataTypes.wolfVariantType, Holder.of(variant)); }); registerEntityDataTypeHandler( VersionedTypes.V1_21.entityDataTypes.itemType, diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_11to26_1/rewriter/EntityPacketRewriter26_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_11to26_1/rewriter/EntityPacketRewriter26_1.java index d402b11e0..e2c97ad87 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_11to26_1/rewriter/EntityPacketRewriter26_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_11to26_1/rewriter/EntityPacketRewriter26_1.java @@ -80,22 +80,12 @@ public final class EntityPacketRewriter26_1 extends EntityRewriter { - int mappedId = id; - if (mappedId >= entityDataTypes.catSoundVariant.typeId()) { - mappedId++; - } - if (mappedId >= entityDataTypes.cowSoundVariant.typeId()) { - mappedId++; - } - if (mappedId >= entityDataTypes.pigSoundVariant.typeId()) { - mappedId++; - } - if (mappedId >= entityDataTypes.chickenSoundVariant.typeId()) { - mappedId++; - } - return entityDataTypes.byId(mappedId); - }); + dataTypeMapper() + .added(entityDataTypes.catSoundVariant) + .added(entityDataTypes.cowSoundVariant) + .added(entityDataTypes.pigSoundVariant) + .added(entityDataTypes.chickenSoundVariant) + .register(); registerEntityDataTypeHandler( entityDataTypes.itemType, entityDataTypes.blockStateType, diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_2to1_21_4/rewriter/EntityPacketRewriter1_21_4.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_2to1_21_4/rewriter/EntityPacketRewriter1_21_4.java index d5bf05de6..bde142357 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_2to1_21_4/rewriter/EntityPacketRewriter1_21_4.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_2to1_21_4/rewriter/EntityPacketRewriter1_21_4.java @@ -23,12 +23,10 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.version.VersionedTypes; -import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21; import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4; import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4; import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2; import com.viaversion.viaversion.rewriter.EntityRewriter; -import com.viaversion.viaversion.rewriter.RegistryDataRewriter; public final class EntityPacketRewriter1_21_4 extends EntityRewriter { @@ -67,7 +65,7 @@ public final class EntityPacketRewriter1_21_4 extends EntityRewriter { - final int id = data.dataType().typeId(); - if (id == VersionedTypes.V1_21_4.entityDataTypes.wolfVariantType.typeId()) { - final Holder wolfVariant = data.value(); - data.setTypeAndValue(VersionedTypes.V1_21_5.entityDataTypes.wolfVariantType, wolfVariant.hasId() ? wolfVariant.id() : 0); - return; - } - - int mappedId = id; - if (mappedId >= VersionedTypes.V1_21_5.entityDataTypes.cowVariantType.typeId()) { - mappedId++; - } - if (mappedId >= VersionedTypes.V1_21_5.entityDataTypes.wolfSoundVariantType.typeId()) { - mappedId++; - } - if (mappedId >= VersionedTypes.V1_21_5.entityDataTypes.pigVariantType.typeId()) { - mappedId++; - } - if (mappedId >= VersionedTypes.V1_21_5.entityDataTypes.chickenVariantType.typeId()) { - mappedId++; - } - data.setDataType(VersionedTypes.V1_21_5.entityDataTypes.byId(mappedId)); + dataTypeMapper() + .added(VersionedTypes.V1_21_5.entityDataTypes.cowVariantType) + .added(VersionedTypes.V1_21_5.entityDataTypes.wolfSoundVariantType) + .added(VersionedTypes.V1_21_5.entityDataTypes.pigVariantType) + .added(VersionedTypes.V1_21_5.entityDataTypes.chickenVariantType) + .skip(VersionedTypes.V1_21_4.entityDataTypes.wolfVariantType) + .register(); + filter().dataType(VersionedTypes.V1_21_4.entityDataTypes.wolfVariantType).handler((event, data) -> { + final Holder wolfVariant = data.value(); + data.setTypeAndValue(VersionedTypes.V1_21_5.entityDataTypes.wolfVariantType, wolfVariant.hasId() ? wolfVariant.id() : 0); }); registerEntityDataTypeHandler( diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_5to1_21_6/rewriter/EntityPacketRewriter1_21_6.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_5to1_21_6/rewriter/EntityPacketRewriter1_21_6.java index 044dffe1c..097124387 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_5to1_21_6/rewriter/EntityPacketRewriter1_21_6.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_5to1_21_6/rewriter/EntityPacketRewriter1_21_6.java @@ -101,7 +101,7 @@ public final class EntityPacketRewriter1_21_6 extends EntityRewriter { - int id = data.dataType().typeId(); - if (id == unmappedEntityDataTypes.compoundTagType.typeId()) { - if (event.entityType() == null) { - // Remove unhandled data from bad packets here - event.cancel(); - } - return; // Handled below - } - if (id > unmappedEntityDataTypes.compoundTagType.typeId()) { - id--; - } - if (id > entityDataTypes.armadilloState.typeId()) { - id += 2; // copper golem and weathering copper state - } - data.setDataType(entityDataTypes.byId(id)); - }); - + dataTypeMapper() + .added(entityDataTypes.copperGolemState) + .added(entityDataTypes.weatheringCopperState) + .removed(unmappedEntityDataTypes.compoundTagType) + .register(); registerEntityDataTypeHandler( entityDataTypes.itemType, entityDataTypes.blockStateType, diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_9to1_21_11/rewriter/EntityPacketRewriter1_21_11.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_9to1_21_11/rewriter/EntityPacketRewriter1_21_11.java index 2f074bc8f..356ee9421 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_9to1_21_11/rewriter/EntityPacketRewriter1_21_11.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21_9to1_21_11/rewriter/EntityPacketRewriter1_21_11.java @@ -53,12 +53,7 @@ public final class EntityPacketRewriter1_21_11 extends EntityRewriter { - if (id >= entityDataTypes.zombieNautilusVariantType.typeId()) { - id++; - } - return entityDataTypes.byId(id); - }); + dataTypeMapper().added(entityDataTypes.zombieNautilusVariantType).register(); registerEntityDataTypeHandler( entityDataTypes.itemType, entityDataTypes.blockStateType, diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java index f9b117b38..8f07afbed 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java @@ -475,7 +475,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter + * Removed source types are intentionally left untouched during the initial handler, so entity-specific handlers can + * still manually remap them before the final cancellation runs (unless something sent invalid packets for untracked entities). + * + * @return configurable type remapper + */ + public EntityDataFilter.DataTypeMapper dataTypeMapper() { + Preconditions.checkNotNull(protocol.mappedTypes(), "Protocol does not override mappedTypes, use filter().mapDataType instead"); + return new EntityDataFilter.DataTypeMapper(this); + } + /** * Registers an entity data filter. * Note that {@link EntityDataFilter.Builder#register()} already calls this method. @@ -99,7 +112,6 @@ public abstract class EntityRewriter updateFunction) { handler((event, data) -> { EntityDataType mappedType = updateFunction.apply(data.dataType().typeId()); @@ -293,7 +304,112 @@ public record EntityDataFilter(@Nullable EntityType type, boolean filterFamily, * @return created data filter */ public EntityDataFilter build() { + Preconditions.checkNotNull(handler, "EntityDataHandler cannot be null"); return new EntityDataFilter(type, filterFamily, dataType, index, handler); } } + + public static final class DataTypeMapper { + private final IntList addedTypeIds = new IntArrayList(); + private final IntList removedTypeIds = new IntArrayList(); + private final IntSet skippedTypeIds = new IntArraySet(); + private final EntityRewriter rewriter; + private final AbstractEntityDataTypes mappedDataTypes; + + public DataTypeMapper(final EntityRewriter rewriter) { + this.rewriter = rewriter; + this.mappedDataTypes = rewriter.protocol().mappedTypes().entityDataTypes(); + } + + /** + * Marks a mapped entity data type as added. + * + * @param dataType added mapped entity data type + * @return self + */ + public DataTypeMapper added(final EntityDataType dataType) { + Preconditions.checkArgument(dataType.getClass() == mappedDataTypes.getDataTypeClass()); + this.addedTypeIds.add(dataType.typeId()); + return this; + } + + /** + * Marks an unmapped entity data type as removed. + *

+ * Matching unmapped types are left untouched for the regular handlers to process and must be canceled or updated manually. + * + * @param dataType removed unmapped entity data type + * @return self + */ + public DataTypeMapper removed(final EntityDataType dataType) { + Preconditions.checkArgument(dataType.getClass() == rewriter.protocol().types().entityDataTypes().getDataTypeClass()); + this.removedTypeIds.add(dataType.typeId()); + return this; + } + + /** + * Skips automatic remapping for an unmapped entity data type so another handler can process it later. + * + * @param dataType unmapped entity data type to leave untouched + * @return self + */ + public DataTypeMapper skip(final EntityDataType dataType) { + Preconditions.checkArgument(dataType.getClass() == rewriter.protocol().types().entityDataTypes().getDataTypeClass()); + this.skippedTypeIds.add(dataType.typeId()); + return this; + } + + public void register() { + if (addedTypeIds.isEmpty() && removedTypeIds.isEmpty() && skippedTypeIds.isEmpty()) { + // Simple handler + rewriter.filter().handler((event, data) -> { + final EntityDataType dataType = mappedDataTypes.byId(data.dataType().typeId()); + data.setDataType(dataType); + }); + return; + } + + this.addedTypeIds.sort(IntComparators.NATURAL_COMPARATOR); + this.removedTypeIds.sort(IntComparators.OPPOSITE_COMPARATOR); + + rewriter.registerFilter(new EntityDataFilter((event, data) -> { + // Do not cancel removed elements yet so that filters can handle them (unless something sent an invalid packet for untracked entities) + final EntityDataType mappedType = mappedType(data.dataType()); + if (mappedType != null) { + data.setDataType(mappedType); + } else if (event.entityType() == null) { + event.cancel(); + } + })); + } + + private @Nullable EntityDataType mappedType(final EntityDataType dataType) { + if (skippedTypeIds.contains(dataType.typeId()) || shouldCancel(dataType)) { + return null; + } + + final int mappedId = mappedTypeId(dataType.typeId()); + return mappedDataTypes.byId(mappedId); + } + + private boolean shouldCancel(final EntityDataType dataType) { + return !removedTypeIds.isEmpty() && removedTypeIds.contains(dataType.typeId()); + } + + private int mappedTypeId(int typeId) { + // Starts with the highest removed + for (final int removedTypeId : removedTypeIds) { + if (typeId > removedTypeId) { + typeId--; + } + } + // Starts with the first added + for (final int addedTypeId : addedTypeIds) { + if (typeId >= addedTypeId) { + typeId++; + } + } + return typeId; + } + } }