diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 925038fa7..a6ed992bf 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -11,4 +11,4 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@v1 + uses: gradle-update/update-gradle-wrapper-action@v2 diff --git a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java index 16406afc3..23b844129 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java +++ b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java @@ -471,4 +471,11 @@ public interface ViaVersionConfig extends Config { * @return true if enabled */ boolean hideScoreboardNumbers(); + + /** + * Fixes 1.21+ clients on 1.20.5 servers placing water/lava buckets at the wrong location when moving fast. + * + * @return true if enabled + */ + boolean fix1_21PlacementRotation(); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java b/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java index 2618ad59d..a4923e8a8 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java +++ b/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java @@ -24,6 +24,7 @@ package com.viaversion.viaversion.api.connection; import com.viaversion.viaversion.api.configuration.ViaVersionConfig; import com.viaversion.viaversion.api.data.entity.EntityTracker; +import com.viaversion.viaversion.api.minecraft.ClientWorld; import com.viaversion.viaversion.api.protocol.Protocol; import com.viaversion.viaversion.api.protocol.packet.PacketTracker; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; @@ -99,14 +100,33 @@ public interface UserConnection { void addEntityTracker(Class protocolClass, EntityTracker tracker); /** - * Clear stored objects and entity trackers. + * Returns the client world by the given protocol class if present. + * + * @param protocolClass protocol class + * @param client world type + * @return client world if present + */ + @Nullable + T getClientWorld(Class protocolClass); + + /** + * Adds a client world to the user connection. + * Does not override existing client worlds. + * + * @param protocolClass protocol class + * @param clientWorld client world + */ + void addClientWorld(Class protocolClass, ClientWorld clientWorld); + + /** + * Clear stored objects, entity trackers and client worlds. */ default void clearStoredObjects() { clearStoredObjects(false); } /** - * Clear stored objects and entity trackers. + * Clear stored objects, entity trackers and client worlds. * If cleared for a proxy server switch, some stored objects and tracker data will be retained. * * @param isServerSwitch whether the clear is due to a server switch diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java index b31d51fa5..b46fa9524 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java @@ -83,6 +83,10 @@ public interface MappingData { int getNewAttributeId(int id); + int getNewSoundId(int id); + + int getOldSoundId(int i); + /** * Returns a list of tags to send if present. * diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java index e0fed837d..06283302e 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataBase.java @@ -203,6 +203,16 @@ public class MappingDataBase implements MappingData { return checkValidity(id, attributeMappings.getNewId(id), "attributes"); } + @Override + public int getNewSoundId(final int id) { + return checkValidity(id, soundMappings.getNewId(id), "sound"); + } + + @Override + public int getOldSoundId(final int i) { + return soundMappings.getNewIdOrDefault(i, 0); + } + @Override public @Nullable List getTags(final RegistryType type) { return tags != null ? tags.get(type) : null; @@ -315,4 +325,4 @@ public class MappingDataBase implements MappingData { protected void loadExtras(final CompoundTag data) { } -} \ No newline at end of file +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java index e5b1ad257..58ff10001 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingDataLoader.java @@ -30,7 +30,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; import com.viaversion.nbt.io.NBTIO; import com.viaversion.nbt.io.TagReader; -import com.viaversion.nbt.tag.ByteTag; import com.viaversion.nbt.tag.CompoundTag; import com.viaversion.nbt.tag.IntArrayTag; import com.viaversion.nbt.tag.IntTag; @@ -211,68 +210,64 @@ public class MappingDataLoader { return null; } - final ByteTag serializationStragetyTag = tag.getUnchecked("id"); - final IntTag mappedSizeTag = tag.getUnchecked("mappedSize"); - final byte strategy = serializationStragetyTag.asByte(); + final int mappedSize = tag.getIntTag("mappedSize").asInt(); + final byte strategy = tag.getByteTag("id").asByte(); final V mappings; if (strategy == DIRECT_ID) { final IntArrayTag valuesTag = tag.getIntArrayTag("val"); - return IntArrayMappings.of(valuesTag.getValue(), mappedSizeTag.asInt()); + return IntArrayMappings.of(valuesTag.getValue(), mappedSize); } else if (strategy == SHIFTS_ID) { - final IntArrayTag shiftsAtTag = tag.getIntArrayTag("at"); - final IntArrayTag shiftsTag = tag.getIntArrayTag("to"); - final IntTag sizeTag = tag.getUnchecked("size"); - final int[] shiftsAt = shiftsAtTag.getValue(); - final int[] shiftsTo = shiftsTag.getValue(); - final int size = sizeTag.asInt(); + final int[] shiftsAt = tag.getIntArrayTag("at").getValue(); + final int[] shiftsTo = tag.getIntArrayTag("to").getValue(); + final int size = tag.getIntTag("size").asInt(); mappings = holderSupplier.get(size); - // Handle values until first shift if (shiftsAt[0] != 0) { + // Add identity values before the first shift final int to = shiftsAt[0]; for (int id = 0; id < to; id++) { addConsumer.addTo(mappings, id, id); } } - // Handle shifts + // Read shifts for (int i = 0; i < shiftsAt.length; i++) { + final boolean isLast = i == shiftsAt.length - 1; final int from = shiftsAt[i]; - final int to = i == shiftsAt.length - 1 ? size : shiftsAt[i + 1]; + final int to = isLast ? size : shiftsAt[i + 1]; int mappedId = shiftsTo[i]; for (int id = from; id < to; id++) { addConsumer.addTo(mappings, id, mappedId++); } } } else if (strategy == CHANGES_ID) { - final IntArrayTag changesAtTag = tag.getIntArrayTag("at"); - final IntArrayTag valuesTag = tag.getIntArrayTag("val"); - final IntTag sizeTag = tag.getUnchecked("size"); + final int[] changesAt = tag.getIntArrayTag("at").getValue(); + final int[] values = tag.getIntArrayTag("val").getValue(); + final int size = tag.getIntTag("size").asInt(); final boolean fillBetween = tag.get("nofill") == null; - final int[] changesAt = changesAtTag.getValue(); - final int[] values = valuesTag.getValue(); - mappings = holderSupplier.get(sizeTag.asInt()); + mappings = holderSupplier.get(size); + int nextUnhandledId = 0; for (int i = 0; i < changesAt.length; i++) { - final int id = changesAt[i]; + final int changedId = changesAt[i]; if (fillBetween) { // Fill from after the last change to before this change with unchanged ids - final int previousId = i != 0 ? changesAt[i - 1] + 1 : 0; - for (int identity = previousId; identity < id; identity++) { - addConsumer.addTo(mappings, identity, identity); + for (int id = nextUnhandledId; id < changedId; id++) { + addConsumer.addTo(mappings, id, id); } + nextUnhandledId = changedId + 1; } // Assign the changed value - addConsumer.addTo(mappings, id, values[i]); + addConsumer.addTo(mappings, changedId, values[i]); } } else if (strategy == IDENTITY_ID) { - final IntTag sizeTag = tag.getUnchecked("size"); - return new IdentityMappings(sizeTag.asInt(), mappedSizeTag.asInt()); + final IntTag sizeTag = tag.getIntTag("size"); + return new IdentityMappings(sizeTag.asInt(), mappedSize); } else { throw new IllegalArgumentException("Unknown serialization strategy: " + strategy); } - return mappingsSupplier.create(mappings, mappedSizeTag.asInt()); + return mappingsSupplier.create(mappings, mappedSize); } public @Nullable List identifiersFromGlobalIds(final CompoundTag mappingsTag, final String key) { @@ -295,7 +290,7 @@ public class MappingDataLoader { * @return map with indexes hashed by their id value */ public Object2IntMap indexedObjectToMap(final JsonObject object) { - final Object2IntMap map = new Object2IntOpenHashMap<>(object.size(), .99F); + final Object2IntMap map = new Object2IntOpenHashMap<>(object.size()); map.defaultReturnValue(-1); for (final Map.Entry entry : object.entrySet()) { map.put(entry.getValue().getAsString(), Integer.parseInt(entry.getKey())); @@ -310,7 +305,7 @@ public class MappingDataLoader { * @return map with indexes hashed by their id value */ public Object2IntMap arrayToMap(final JsonArray array) { - final Object2IntMap map = new Object2IntOpenHashMap<>(array.size(), .99F); + final Object2IntMap map = new Object2IntOpenHashMap<>(array.size()); map.defaultReturnValue(-1); for (int i = 0; i < array.size(); i++) { map.put(array.get(i).getAsString(), i); diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/entity/EntityTracker.java b/api/src/main/java/com/viaversion/viaversion/api/data/entity/EntityTracker.java index 11f194e49..71ca92fe1 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/entity/EntityTracker.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/entity/EntityTracker.java @@ -78,7 +78,7 @@ public interface EntityTracker { void removeEntity(int id); /** - * Clears stored entity types and data. + * Clears stored entity types and data, only leaving behind the client entity. */ void clearEntities(); @@ -102,11 +102,19 @@ public interface EntityTracker { @Nullable StoredEntityData entityDataIfPresent(int id); /** - * Returns the client entity id or -1 if unset. + * Returns whether the client entity id has been set. * - * @return client entity id or -1 if unset + * @return whether the client entity id has been set */ - int clientEntityId(); + boolean hasClientEntityId(); + + /** + * Returns the client entity id. Should be wrapped around {@link #hasClientEntityId()}. + * + * @return client entity id + * @throws IllegalStateException if the client entity id has not been set + */ + int clientEntityId() throws IllegalStateException; /** * Sets the client entity id. @@ -169,12 +177,4 @@ public interface EntityTracker { @Nullable DimensionData dimensionData(int dimensionId); void setDimensions(Map dimensions); - - /** - * Adds the client player entity to the tracker. - * If the client entity has not been set yet, this will return false. - * - * @return whether the client has been tracked - */ - boolean trackClientEntity(); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/ClientWorld.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/ClientWorld.java index e4e0809bb..ec8a7333b 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/ClientWorld.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/ClientWorld.java @@ -23,13 +23,12 @@ package com.viaversion.viaversion.api.minecraft; import com.viaversion.viaversion.api.connection.StorableObject; -import org.checkerframework.checker.nullness.qual.Nullable; /** * Stored up until 1.14 to be used in chunk sending. */ public class ClientWorld implements StorableObject { - private Environment environment; + private Environment environment = Environment.NORMAL; public ClientWorld() { } @@ -38,11 +37,20 @@ public class ClientWorld implements StorableObject { this.environment = environment; } - public @Nullable Environment getEnvironment() { + public Environment getEnvironment() { return environment; } - public void setEnvironment(final int environmentId) { + /** + * Sets the environment of the world and returns whether the environment was changed. + * + * @param environmentId the id of the environment to set + * @return whether the environment was changed + */ + public boolean setEnvironment(final int environmentId) { + final int previousEnvironmentId = environment.id(); this.environment = Environment.getEnvironmentById(environmentId); + + return previousEnvironmentId != environmentId; } } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/Holder.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/Holder.java index 59bc32109..d597ba9ca 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/Holder.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/Holder.java @@ -22,6 +22,8 @@ */ package com.viaversion.viaversion.api.minecraft; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; + public interface Holder { /** @@ -79,4 +81,12 @@ public interface Holder { * @see #hasId() */ int id(); + + /** + * Returns a new holder with the id rewritten using the given function, or self if this is a direct holder or the id did not change. + * + * @param rewriteFunction the function to rewrite the id + * @return a new holder with the id rewritten, or self + */ + Holder updateId(final Int2IntFunction rewriteFunction); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderImpl.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderImpl.java index 7020d968a..bcbaaf390 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderImpl.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderImpl.java @@ -23,6 +23,7 @@ package com.viaversion.viaversion.api.minecraft; import com.google.common.base.Preconditions; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; final class HolderImpl implements Holder { @@ -61,6 +62,22 @@ final class HolderImpl implements Holder { return id; } + @Override + public Holder updateId(final Int2IntFunction rewriteFunction) { + if (isDirect()) { + return this; + } + + final int rewrittenId = rewriteFunction.applyAsInt(id); + if (rewrittenId == id) { + return this; + } + if (rewrittenId == -1) { + throw new IllegalArgumentException("Received invalid id in updateId"); + } + return Holder.of(rewrittenId); + } + @Override public String toString() { return "HolderImpl{" + diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java index 7d024799c..5557da917 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java @@ -64,7 +64,7 @@ final class HolderSetImpl extends EitherImpl implements HolderSet final int[] ids = ids(); final int[] mappedIds = new int[ids.length]; for (int i = 0; i < mappedIds.length; i++) { - mappedIds[i] = idRewriter.apply(ids[i]); + mappedIds[i] = idRewriter.applyAsInt(ids[i]); } return new HolderSetImpl(mappedIds); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java index 45e110e62..4bd08908a 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java @@ -52,16 +52,14 @@ public interface StructuredData extends IdHolder { return new EmptyStructuredData<>(key, id); } - void setValue(final T value); + @Nullable T value(); - void write(final ByteBuf buffer); + void setValue(final T value); void setId(final int id); StructuredDataKey key(); - @Nullable T value(); - /** * Returns whether the structured data is present. Even if true, the value may be null. * @@ -77,4 +75,6 @@ public interface StructuredData extends IdHolder { * @return true if the structured data is empty */ boolean isEmpty(); + + void write(final ByteBuf buffer); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java index 740994c3d..3907748d2 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java @@ -33,6 +33,26 @@ import java.util.Map; import java.util.function.Function; import org.checkerframework.checker.nullness.qual.Nullable; +/** + * Loosely represents Minecraft's data component patch, but may also be used for an item's full data components. + *

+ * The most commonly used methods will ignore empty data (aka empty overrides that remove item defaults) since those will rarely be needed. + * These are: + *

    + *
  • {@link #get(StructuredDataKey)}
  • + *
  • {@link #set(StructuredDataKey, Object)}
  • + *
  • {@link #set(StructuredDataKey)}
  • + *
  • {@link #getNonEmptyData(StructuredDataKey)}
  • + *
  • {@link #hasValue(StructuredDataKey)}
  • + *
+ * + * To interact with empty patches specifically, use: + *
    + *
  • {@link #setEmpty(StructuredDataKey)}
  • + *
  • {@link #hasEmpty(StructuredDataKey)}
  • + *
+ * Other methods (e.g. {@link #getData(StructuredDataKey)} and {@link #has(StructuredDataKey)}) will handle both empty and non-empty data. + */ public final class StructuredDataContainer { private final Map, StructuredData> data; @@ -55,66 +75,45 @@ public final class StructuredDataContainer { } /** - * Returns structured data by id if present. + * Returns the non-empty value by id if present. + * + * @param key serializer id + * @param data type + * @return structured data + * @see #hasEmpty(StructuredDataKey) + */ + public @Nullable T get(final StructuredDataKey key) { + final StructuredData data = this.data.get(key); + if (data == null || data.isEmpty()) { + return null; + } + //noinspection unchecked + return ((StructuredData) data).value(); + } + + /** + * Returns structured data by id if present, either empty or non-empty. * * @param key serializer id * @param data type * @return structured data */ - public @Nullable StructuredData get(final StructuredDataKey key) { + public @Nullable StructuredData getData(final StructuredDataKey key) { //noinspection unchecked return (StructuredData) this.data.get(key); } /** - * Returns structured data by id if not empty. + * Returns non-empty structured data by id if present. * * @param key serializer id * @param data type - * @return structured data if not empty + * @return non-empty structured data */ - public @Nullable StructuredData getNonEmpty(final StructuredDataKey key) { + public @Nullable StructuredData getNonEmptyData(final StructuredDataKey key) { + final StructuredData data = this.data.get(key); //noinspection unchecked - final StructuredData data = (StructuredData) this.data.get(key); - return data != null && data.isPresent() ? data : null; - } - - /** - * Returns structured data by id if not empty, or creates it. - * - * @param key serializer id - * @param mappingFunction function to create structured data if not present - * @param data type - * @return structured data if not empty - */ - public StructuredData computeIfAbsent(final StructuredDataKey key, final Function, T> mappingFunction) { - final StructuredData data = this.getNonEmpty(key); - if (data != null) { - return data; - } - - final int id = serializerId(key); - final StructuredData empty = StructuredData.of(key, mappingFunction.apply(key), id); - this.data.put(key, empty); - return empty; - } - - /** - * Updates and returns the structured data by id if not empty. - * - * @param key serializer id - * @param mappingFunction function to update existing data - * @param data type - * @return updated structured data if not empty - */ - public @Nullable StructuredData updateIfPresent(final StructuredDataKey key, final Function mappingFunction) { - final StructuredData data = this.getNonEmpty(key); - if (data == null) { - return null; - } - - data.setValue(mappingFunction.apply(data.value())); - return data; + return data != null && data.isPresent() ? (StructuredData) data : null; } public void set(final StructuredDataKey key, final T value) { @@ -124,49 +123,105 @@ public final class StructuredDataContainer { } } - public void replaceKey(final StructuredDataKey key, final StructuredDataKey toKey) { - replace(key, toKey, Function.identity()); - } - - public void replace(final StructuredDataKey key, final StructuredDataKey toKey, final Function valueMapper) { - final StructuredData data = remove(key); - if (data == null) { - return; - } - - if (data.isPresent()) { - set(toKey, valueMapper.apply(data.value())); - } else { - addEmpty(toKey); - } - } - public void set(final StructuredDataKey key) { this.set(key, Unit.INSTANCE); } - public void addEmpty(final StructuredDataKey key) { + public void setEmpty(final StructuredDataKey key) { // Empty optional to override the Minecraft default this.data.put(key, StructuredData.empty(key, serializerId(key))); } /** - * Removes and returns structured data by the given key. + * Updates the structured data by id if not empty. * - * @param key serializer key - * @param data type - * @return removed structured data + * @param key serializer id + * @param valueMapper function to update existing data + * @param data type */ - public @Nullable StructuredData remove(final StructuredDataKey key) { - final StructuredData data = this.data.remove(key); - //noinspection unchecked - return data != null ? (StructuredData) data : null; + public void replace(final StructuredDataKey key, final Function valueMapper) { + final StructuredData data = this.getNonEmptyData(key); + if (data == null) { + return; + } + + final T replacement = valueMapper.apply(data.value()); + if (replacement != null) { + data.setValue(replacement); + } else { + this.data.remove(key); + } } - public boolean contains(final StructuredDataKey key) { + public void replaceKey(final StructuredDataKey key, final StructuredDataKey toKey) { + replace(key, toKey, Function.identity()); + } + + public void replace(final StructuredDataKey key, final StructuredDataKey toKey, final Function valueMapper) { + final StructuredData data = this.data.remove(key); + if (data == null) { + return; + } + + if (data.isPresent()) { + //noinspection unchecked + final T value = (T) data.value(); + final V replacement = valueMapper.apply(value); + if (replacement != null) { + set(toKey, replacement); + } + } else { + // Also replace the key for empty data + setEmpty(toKey); + } + } + + /** + * Removes data by the given key. + * + * @param key data key + * @see #replace(StructuredDataKey, Function) + * @see #replace(StructuredDataKey, StructuredDataKey, Function) + * @see #replaceKey(StructuredDataKey, StructuredDataKey) + */ + public void remove(final StructuredDataKey key) { + this.data.remove(key); + } + + /** + * Returns whether there is data for the given key, either empty or non-empty. + * + * @param key data key + * @return whether there data for the given key + * @see #hasEmpty(StructuredDataKey) + * @see #hasValue(StructuredDataKey) + */ + public boolean has(final StructuredDataKey key) { return this.data.containsKey(key); } + /** + * Returns whether there is non-empty data for the given key. + * + * @param key data key + * @return whether there is non-empty data for the given key + */ + public boolean hasValue(final StructuredDataKey key) { + final StructuredData data = this.data.get(key); + return data != null && data.isPresent(); + } + + /** + * Returns whether the structured data has an empty patch/override. + * + * @param key serializer id + * @return whether the structured data has an empty patch/override + */ + public boolean hasEmpty(final StructuredDataKey key) { + final StructuredData data = this.data.get(key); + return data != null && data.isEmpty(); + } + /** * Sets the lookup for serializer ids. Required to call most of the other methods. * diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_12.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_12.java index 9a63fc145..6a2b140bb 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_12.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_12.java @@ -101,7 +101,7 @@ public class EntityTypes1_12 { LIVING_ENTITY_BASE(ENTITY), ARMOR_STAND(30, LIVING_ENTITY_BASE), - PLAYER(ENTITY), // Needed for entity (un)tracking + PLAYER(LIVING_ENTITY_BASE), // Needed for entity (un)tracking // Living entities as a larger subclass LIVING_ENTITY(LIVING_ENTITY_BASE), diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_9.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_9.java index 81545ea6e..f32f53e43 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_9.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_9.java @@ -99,7 +99,7 @@ public class EntityTypes1_9 { LIVING_ENTITY_BASE(ENTITY), ARMOR_STAND(30, LIVING_ENTITY_BASE), - PLAYER(ENTITY), // Needed for entity (un)tracking + PLAYER(LIVING_ENTITY_BASE), // Needed for entity (un)tracking // Living entities as a larger subclass LIVING_ENTITY(48, LIVING_ENTITY_BASE), diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/AttributeModifiers1_21.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/AttributeModifiers1_21.java index 8c095452c..ffcb7ce0a 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/AttributeModifiers1_21.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/AttributeModifiers1_21.java @@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.ArrayType; import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; public record AttributeModifiers1_21(AttributeModifier[] modifiers, boolean showInTooltip) { @@ -44,6 +45,15 @@ public record AttributeModifiers1_21(AttributeModifier[] modifiers, boolean show } }; + public AttributeModifiers1_21 rewrite(final Int2IntFunction rewriteFunction) { + final AttributeModifier[] modifiers = new AttributeModifier[this.modifiers.length]; + for (int i = 0; i < this.modifiers.length; i++) { + final AttributeModifier modifier = this.modifiers[i]; + modifiers[i] = new AttributeModifier(rewriteFunction.applyAsInt(modifier.attribute()), modifier.modifier(), modifier.slotType()); + } + return new AttributeModifiers1_21(modifiers, showInTooltip); + } + public record AttributeModifier(int attribute, ModifierData modifier, int slotType) { public static final Type TYPE = new Type<>(AttributeModifier.class) { diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Instrument.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Instrument.java index 21384c5a2..bb81c61cf 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Instrument.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Instrument.java @@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.minecraft.SoundEvent; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.misc.HolderType; import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; public record Instrument(Holder soundEvent, int useDuration, float range) { @@ -47,4 +48,8 @@ public record Instrument(Holder soundEvent, int useDuration, float r } }; + public Instrument rewrite(final Int2IntFunction soundIdRewriteFunction) { + final Holder soundEvent = this.soundEvent.updateId(soundIdRewriteFunction); + return soundEvent == this.soundEvent ? this : new Instrument(soundEvent, useDuration, range); + } } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/JukeboxPlayable.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/JukeboxPlayable.java index e3a848a95..d485aca8b 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/JukeboxPlayable.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/JukeboxPlayable.java @@ -30,6 +30,7 @@ import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.misc.HolderType; import com.viaversion.viaversion.util.Either; import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; public record JukeboxPlayable(Either, String> song, boolean showInTooltip) { @@ -56,6 +57,20 @@ public record JukeboxPlayable(Either, String> song, boolean } }; + public JukeboxPlayable rewrite(final Int2IntFunction soundIdRewriteFunction) { + if (song.isRight()) { + return this; + } + + final Holder songHolder = this.song.left(); + if (songHolder.hasId()) { + return this; + } + + final JukeboxSong rewrittenSong = songHolder.value().rewrite(soundIdRewriteFunction); + return rewrittenSong == songHolder.value() ? this : new JukeboxPlayable(Holder.of(rewrittenSong), showInTooltip); + } + public record JukeboxSong(Holder soundEvent, Tag description, float lengthInSeconds, int comparatorOutput) { @@ -77,5 +92,10 @@ public record JukeboxPlayable(Either, String> song, boolean Types.VAR_INT.writePrimitive(buffer, value.comparatorOutput); } }; + + public JukeboxSong rewrite(final Int2IntFunction soundIdRewriteFunction) { + final Holder soundEvent = this.soundEvent.updateId(soundIdRewriteFunction); + return soundEvent == this.soundEvent ? this : new JukeboxSong(soundEvent, description, lengthInSeconds, comparatorOutput); + } } } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/MapDecorations.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/MapDecorations.java deleted file mode 100644 index 047f4e106..000000000 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/MapDecorations.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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; -import io.netty.buffer.ByteBuf; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import java.util.Map; - -public record MapDecorations(Map decorations) { - - public static final Type TYPE = new Type<>(MapDecorations.class) { - @Override - public MapDecorations read(final ByteBuf buffer) { - final Object2ObjectMap decorations = new Object2ObjectOpenHashMap<>(); - final int size = Types.VAR_INT.readPrimitive(buffer); - for (int i = 0; i < size; i++) { - final String id = Types.STRING.read(buffer); - final MapDecoration decoration = MapDecoration.TYPE.read(buffer); - decorations.put(id, decoration); - } - return new MapDecorations(decorations); - } - - @Override - public void write(final ByteBuf buffer, final MapDecorations value) { - Types.VAR_INT.writePrimitive(buffer, value.decorations.size()); - for (final Map.Entry entry : value.decorations.entrySet()) { - Types.STRING.write(buffer, entry.getKey()); - MapDecoration.TYPE.write(buffer, entry.getValue()); - } - } - }; -} diff --git a/build-logic/src/main/kotlin/via.base-conventions.gradle.kts b/build-logic/src/main/kotlin/via.base-conventions.gradle.kts index c67bf7a5a..d094aff73 100644 --- a/build-logic/src/main/kotlin/via.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/via.base-conventions.gradle.kts @@ -5,7 +5,7 @@ plugins { tasks { // Variable replacements processResources { - filesMatching(listOf("plugin.yml", "fabric.mod.json")) { + filesMatching(listOf("plugin.yml", "META-INF/sponge_plugins.json", "fabric.mod.json")) { expand("version" to project.version, "description" to project.description, "url" to "https://viaversion.com") } } diff --git a/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/JoinListener.java b/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/JoinListener.java index 959bd118a..4796a3ac2 100644 --- a/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/JoinListener.java +++ b/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/JoinListener.java @@ -112,7 +112,10 @@ public class JoinListener implements Listener { return; } // The connection has already closed, that was a quick leave - if (!channel.isOpen()) return; + // Channel may be null if a plugin is manually calling the event for a non-player... + if (channel == null || !channel.isOpen()) { + return; + } UserConnection user = getUserConnection(channel); if (user == null) { diff --git a/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java b/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java index 3f32e31cc..7c4c81451 100644 --- a/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java +++ b/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java @@ -25,14 +25,14 @@ import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.protocol.BlockedProtocolVersionsImpl; import com.viaversion.viaversion.util.Config; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; import org.checkerframework.checker.nullness.qual.Nullable; public abstract class AbstractViaConfig extends Config implements ViaVersionConfig { @@ -95,6 +95,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf private boolean handleInvalidItemCount; private boolean cancelBlockSounds; private boolean hideScoreboardNumbers; + private boolean fix1_21PlacementRotation; protected AbstractViaConfig(final File configFile, final Logger logger) { super(configFile, logger); @@ -163,6 +164,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf handleInvalidItemCount = getBoolean("handle-invalid-item-count", false); cancelBlockSounds = getBoolean("cancel-block-sounds", true); hideScoreboardNumbers = getBoolean("hide-scoreboard-numbers", false); + fix1_21PlacementRotation = getBoolean("fix-1_21-placement-rotation", false); } private BlockedProtocolVersions loadBlockedProtocolVersions() { @@ -549,4 +551,9 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf public boolean hideScoreboardNumbers() { return hideScoreboardNumbers; } + + @Override + public boolean fix1_21PlacementRotation() { + return false && fix1_21PlacementRotation; // TODO Can't always set onGround to true + } } diff --git a/common/src/main/java/com/viaversion/viaversion/connection/UserConnectionImpl.java b/common/src/main/java/com/viaversion/viaversion/connection/UserConnectionImpl.java index 8ec84a0e7..4a7f82dc4 100644 --- a/common/src/main/java/com/viaversion/viaversion/connection/UserConnectionImpl.java +++ b/common/src/main/java/com/viaversion/viaversion/connection/UserConnectionImpl.java @@ -23,6 +23,7 @@ import com.viaversion.viaversion.api.connection.ProtocolInfo; import com.viaversion.viaversion.api.connection.StorableObject; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.data.entity.EntityTracker; +import com.viaversion.viaversion.api.minecraft.ClientWorld; import com.viaversion.viaversion.api.platform.ViaInjector; import com.viaversion.viaversion.api.protocol.Protocol; import com.viaversion.viaversion.api.protocol.packet.Direction; @@ -58,6 +59,7 @@ public class UserConnectionImpl implements UserConnection { private final long id = IDS.incrementAndGet(); private final Map, StorableObject> storedObjects = new ConcurrentHashMap<>(); private final Map, EntityTracker> entityTrackers = new HashMap<>(); + private final Map, ClientWorld> clientWorlds = new HashMap<>(); private final PacketTracker packetTracker = new PacketTracker(this); private final Set passthroughTokens = Collections.newSetFromMap(CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) @@ -125,9 +127,17 @@ public class UserConnectionImpl implements UserConnection { @Override public void addEntityTracker(Class protocolClass, EntityTracker tracker) { - if (!entityTrackers.containsKey(protocolClass)) { - entityTrackers.put(protocolClass, tracker); - } + entityTrackers.putIfAbsent(protocolClass, tracker); + } + + @Override + public @Nullable T getClientWorld(final Class protocolClass) { + return (T) clientWorlds.get(protocolClass); + } + + @Override + public void addClientWorld(final Class protocolClass, final ClientWorld clientWorld) { + clientWorlds.putIfAbsent(protocolClass, clientWorld); } @Override @@ -142,7 +152,6 @@ public class UserConnectionImpl implements UserConnection { }); for (EntityTracker tracker : entityTrackers.values()) { tracker.clearEntities(); - tracker.trackClientEntity(); } } else { for (StorableObject object : storedObjects.values()) { @@ -150,6 +159,7 @@ public class UserConnectionImpl implements UserConnection { } storedObjects.clear(); entityTrackers.clear(); + clientWorlds.clear(); } } diff --git a/common/src/main/java/com/viaversion/viaversion/data/entity/EntityTrackerBase.java b/common/src/main/java/com/viaversion/viaversion/data/entity/EntityTrackerBase.java index 666e4d5d5..f568bfded 100644 --- a/common/src/main/java/com/viaversion/viaversion/data/entity/EntityTrackerBase.java +++ b/common/src/main/java/com/viaversion/viaversion/data/entity/EntityTrackerBase.java @@ -37,7 +37,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis protected final Int2ObjectMap entities = new Int2ObjectOpenHashMap<>(); private final UserConnection connection; private final EntityType playerType; - private int clientEntityId = -1; + private Integer clientEntityId; private int currentWorldSectionHeight = -1; private int currentMinY; private String currentWorld; @@ -87,7 +87,6 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis return entity != null && entity.hasData() ? entity.data() : null; } - //TODO Soft memory leak: Remove entities on respawn in protocols prior to 1.18 (1.16+ only when the worldname is different) @Override public void removeEntity(int id) { entities.remove(id); @@ -95,11 +94,27 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis @Override public void clearEntities() { - entities.clear(); + // Call wrapper function in case protocols need to do additional removals + for (final int id : entities.keySet().toIntArray()) { + removeEntity(id); + } + + // Re-add the client entity. Keep the call above to untrack attached data if necessary + if (clientEntityId != null) { + entities.put(clientEntityId.intValue(), new TrackedEntityImpl(playerType)); + } } @Override - public int clientEntityId() { + public boolean hasClientEntityId() { + return clientEntityId != null; + } + + @Override + public int clientEntityId() throws IllegalStateException { + if (clientEntityId == null) { + throw new IllegalStateException("Client entity id not set"); + } return clientEntityId; } @@ -107,7 +122,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis public void setClientEntityId(int clientEntityId) { Preconditions.checkNotNull(playerType); final TrackedEntity oldEntity; - if (this.clientEntityId != -1 && (oldEntity = entities.remove(this.clientEntityId)) != null) { + if (this.clientEntityId != null && (oldEntity = entities.remove(this.clientEntityId.intValue())) != null) { entities.put(clientEntityId, oldEntity); } else { entities.put(clientEntityId, new TrackedEntityImpl(playerType)); @@ -116,15 +131,6 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis this.clientEntityId = clientEntityId; } - @Override - public boolean trackClientEntity() { - if (clientEntityId != -1) { - entities.put(clientEntityId, new TrackedEntityImpl(playerType)); - return true; - } - return false; - } - @Override public int currentWorldSectionHeight() { return currentWorldSectionHeight; diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/Protocol1_10To1_11.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/Protocol1_10To1_11.java index 2c0d5ce81..b2c2b779f 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/Protocol1_10To1_11.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/Protocol1_10To1_11.java @@ -99,7 +99,7 @@ public class Protocol1_10To1_11 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_10To1_11.class); Chunk chunk = wrapper.passthrough(ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment())); @@ -120,31 +120,6 @@ public class Protocol1_10To1_11 extends AbstractProtocol { - ClientWorld clientChunks = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 1); - clientChunks.setEnvironment(dimensionId); - }); - } - }); - registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() { - @Override - public void register() { - map(Types.INT); - handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); - }); - } - }); - this.registerClientbound(ClientboundPackets1_9_3.LEVEL_EVENT, new PacketHandlers() { @Override public void register() { @@ -238,11 +213,8 @@ public class Protocol1_10To1_11 extends AbstractProtocol to mapping - private static final Int2ObjectMap> POTIONS = new Int2ObjectOpenHashMap<>(37, 0.99F); + private static final Int2ObjectMap> POTIONS = new Int2ObjectOpenHashMap<>(37); static { addRewrite(0, 3694022, false); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/EntityPacketRewriter1_11.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/EntityPacketRewriter1_11.java index a1e3f15fd..943bc4714 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/EntityPacketRewriter1_11.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/EntityPacketRewriter1_11.java @@ -20,6 +20,7 @@ package com.viaversion.viaversion.protocols.v1_10to1_11.rewriter; import com.viaversion.nbt.tag.CompoundTag; import com.viaversion.nbt.tag.StringTag; import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.minecraft.ClientWorld; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_10; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11.EntityType; @@ -48,6 +49,34 @@ public class EntityPacketRewriter1_11 extends EntityRewriter { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_10To1_11.class); + int dimensionId = wrapper.get(Types.INT, 1); + clientWorld.setEnvironment(dimensionId); + }); + } + }); + + protocol.registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() { + @Override + public void register() { + map(Types.INT); + handler(wrapper -> { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_10To1_11.class); + int dimensionId = wrapper.get(Types.INT, 0); + if (clientWorld.setEnvironment(dimensionId)) { + tracker(wrapper.user()).clearEntities(); + } + }); + } + }); + protocol.registerClientbound(ClientboundPackets1_9_3.ADD_ENTITY, new PacketHandlers() { @Override public void register() { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/ItemPacketRewriter1_11.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/ItemPacketRewriter1_11.java index f7dcd7075..587f7f448 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/ItemPacketRewriter1_11.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_10to1_11/rewriter/ItemPacketRewriter1_11.java @@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocols.v1_10to1_11.rewriter; import com.viaversion.nbt.tag.ByteTag; import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.nbt.tag.ListTag; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.type.Types; @@ -55,6 +56,22 @@ public class ItemPacketRewriter1_11 extends ItemRewriterremoveUnchecked(nbtTagName()).asByte()); - if (item.tag().isEmpty()) { - item.setTag(null); + CompoundTag tag = item.tag(); + if (tag != null) { + if (tag.contains(nbtTagName())) { + item.setAmount(tag.removeUnchecked(nbtTagName()).asByte()); + if (tag.isEmpty()) { + item.setTag(null); + } + } + if (tag.remove(nbtTagName("clearEnch")) != null) { + tag.put("ench", new ListTag()); } } EntityMappings1_11.toServerItem(item); @@ -78,4 +101,4 @@ public class ItemPacketRewriter1_11 extends ItemRewriter { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_11_1To1_12.class); ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); Chunk chunk = wrapper.passthrough(type); @@ -101,38 +101,6 @@ public class Protocol1_11_1To1_12 extends AbstractProtocol { - UserConnection user = wrapper.user(); - ClientWorld clientChunks = user.get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 1); - clientChunks.setEnvironment(dimensionId); - - // Reset recipes - if (user.getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_13)) { - wrapper.create(ClientboundPackets1_13.UPDATE_RECIPES, packetWrapper -> packetWrapper.write(Types.VAR_INT, 0)) - .scheduleSend(Protocol1_12_2To1_13.class); - } - }); - } - }); - registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() { - @Override - public void register() { - map(Types.INT); - handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); - }); - } - }); - new SoundRewriter<>(this, this::getNewSoundId).registerSound(ClientboundPackets1_9_3.SOUND); @@ -205,9 +173,7 @@ public class Protocol1_11_1To1_12 extends AbstractProtocol ACHIEVEMENTS = new Object2ObjectOpenHashMap<>(150, 0.99f); + private static final Object2ObjectOpenHashMap ACHIEVEMENTS = new Object2ObjectOpenHashMap<>(150); private static final Set SPECIAL_ACHIEVEMENTS = new HashSet<>(10); static { @@ -196,4 +196,4 @@ public class AchievementTranslations1_12 { return SPECIAL_ACHIEVEMENTS.contains(key); } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/rewriter/EntityPacketRewriter1_12.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/rewriter/EntityPacketRewriter1_12.java index fe711670d..359e91064 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/rewriter/EntityPacketRewriter1_12.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_11_1to1_12/rewriter/EntityPacketRewriter1_12.java @@ -17,14 +17,18 @@ */ package com.viaversion.viaversion.protocols.v1_11_1to1_12.rewriter; +import com.viaversion.viaversion.api.minecraft.ClientWorld; import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_12; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.version.Types1_12; import com.viaversion.viaversion.api.type.types.version.Types1_9; import com.viaversion.viaversion.protocols.v1_11_1to1_12.Protocol1_11_1To1_12; +import com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13; +import com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13; import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3; import com.viaversion.viaversion.rewriter.EntityRewriter; @@ -36,6 +40,40 @@ public class EntityPacketRewriter1_12 extends EntityRewriter { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_11_1To1_12.class); + int dimensionId = wrapper.get(Types.INT, 1); + clientWorld.setEnvironment(dimensionId); + + // Reset recipes + if (wrapper.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_13)) { + wrapper.create(ClientboundPackets1_13.UPDATE_RECIPES, packetWrapper -> packetWrapper.write(Types.VAR_INT, 0)) + .scheduleSend(Protocol1_12_2To1_13.class); + } + }); + } + }); + + protocol.registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() { + @Override + public void register() { + map(Types.INT); + handler(wrapper -> { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_11_1To1_12.class); + int dimensionId = wrapper.get(Types.INT, 0); + if (clientWorld.setEnvironment(dimensionId)) { + tracker(wrapper.user()).clearEntities(); + } + }); + } + }); + protocol.registerClientbound(ClientboundPackets1_9_3.ADD_ENTITY, new PacketHandlers() { @Override public void register() { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/Protocol1_12_2To1_13.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/Protocol1_12_2To1_13.java index 7d5c3cf42..d679f1849 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/Protocol1_12_2To1_13.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/Protocol1_12_2To1_13.java @@ -401,23 +401,6 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); - - if (Via.getConfig().isServersideBlockConnections()) { - ConnectionData.clearBlockStorage(wrapper.user()); - } - }); - handler(SEND_DECLARE_COMMANDS_AND_TAGS); - } - }); - registerClientbound(ClientboundPackets1_12_1.SET_OBJECTIVE, new PacketHandlers() { @Override public void register() { @@ -844,9 +827,9 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol KEY_TO_ID = new Object2IntOpenHashMap<>(8582, .99F); - static final IntSet OCCLUDING_STATES = new IntOpenHashSet(377, .99F); + static final Object2IntMap KEY_TO_ID = new Object2IntOpenHashMap<>(8582); + static final IntSet OCCLUDING_STATES = new IntOpenHashSet(377); static Int2ObjectMap connectionHandlerMap = new Int2ObjectOpenHashMap<>(); static Int2ObjectMap blockConnectionData = new Int2ObjectOpenHashMap<>(); private static final BlockChangeRecord1_8[] EMPTY_RECORDS = new BlockChangeRecord1_8[0]; @@ -173,7 +173,7 @@ public final class ConnectionData { KEY_TO_ID.put(key, id); } - connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650, .99F); + connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650); if (!Via.getConfig().isReduceBlockStorageMemory()) { blockConnectionData = new Int2ObjectOpenHashMap<>(2048); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/data/EntityIdMappings1_13.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/data/EntityIdMappings1_13.java index e3972c6a2..8cc9ec6b6 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/data/EntityIdMappings1_13.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/data/EntityIdMappings1_13.java @@ -21,7 +21,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; public class EntityIdMappings1_13 { - private static final Int2IntMap ENTITY_TYPES = new Int2IntOpenHashMap(83, .99F); + private static final Int2IntMap ENTITY_TYPES = new Int2IntOpenHashMap(83); static { ENTITY_TYPES.defaultReturnValue(-1); @@ -121,4 +121,4 @@ public class EntityIdMappings1_13 { public static Int2IntMap getEntityTypes() { return ENTITY_TYPES; } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/EntityPacketRewriter1_13.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/EntityPacketRewriter1_13.java index 25c2714bd..f243b9fcf 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/EntityPacketRewriter1_13.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/EntityPacketRewriter1_13.java @@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.version.Types1_12; import com.viaversion.viaversion.api.type.types.version.Types1_13; import com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13; +import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData; import com.viaversion.viaversion.protocols.v1_12_2to1_13.data.EntityIdMappings1_13; import com.viaversion.viaversion.protocols.v1_12_2to1_13.data.ParticleIdMappings1_13; import com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1; @@ -132,7 +133,7 @@ public class EntityPacketRewriter1_13 extends EntityRewriter { - ClientWorld clientChunks = wrapper.user().get(ClientWorld.class); + ClientWorld clientChunks = wrapper.user().getClientWorld(Protocol1_12_2To1_13.class); int dimensionId = wrapper.get(Types.INT, 1); clientChunks.setEnvironment(dimensionId); }); @@ -141,6 +142,24 @@ public class EntityPacketRewriter1_13 extends EntityRewriter { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_12_2To1_13.class); + int dimensionId = wrapper.get(Types.INT, 0); + if (clientWorld.setEnvironment(dimensionId)) { + if (Via.getConfig().isServersideBlockConnections()) { + ConnectionData.clearBlockStorage(wrapper.user()); + } + tracker(wrapper.user()).clearEntities(); + } + }); + handler(Protocol1_12_2To1_13.SEND_DECLARE_COMMANDS_AND_TAGS); + } + }); + protocol.registerClientbound(ClientboundPackets1_12_1.UPDATE_MOB_EFFECT, new PacketHandlers() { @Override public void register() { @@ -235,4 +254,4 @@ public class EntityPacketRewriter1_13 extends EntityRewriter enchantments = new ListTag<>(CompoundTag.class); for (CompoundTag enchEntry : ench) { - NumberTag idTag = enchEntry.getNumberTag("id"); - if (idTag == null) { - continue; - } + short oldId = enchEntry.getShort("id", (short) 0); CompoundTag enchantmentEntry = new CompoundTag(); - short oldId = idTag.asShort(); String newId = Protocol1_12_2To1_13.MAPPINGS.getOldEnchantmentsIds().get(oldId); if (newId == null) { newId = "viaversion:legacy/" + oldId; } enchantmentEntry.putString("id", newId); - - NumberTag levelTag = enchEntry.getNumberTag("lvl"); - if (levelTag != null) { - enchantmentEntry.putShort("lvl", levelTag.asShort()); - } + enchantmentEntry.putShort("lvl", enchEntry.getShort("lvl", (short) 0)); enchantments.add(enchantmentEntry); } @@ -589,10 +581,7 @@ public class ItemPacketRewriter1_13 extends ItemRewriter { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_12_2To1_13.class); BlockStorage storage = wrapper.user().get(BlockStorage.class); ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/storage/BlockStorage.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/storage/BlockStorage.java index c908a2976..87c2cfa07 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/storage/BlockStorage.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/storage/BlockStorage.java @@ -26,7 +26,7 @@ import java.util.Map; // TODO Fix memory leak lolz (only a smol one tho) public class BlockStorage implements StorableObject { - private static final IntSet WHITELIST = new IntOpenHashSet(46, .99F); + private static final IntSet WHITELIST = new IntOpenHashSet(46); private final Map blocks = new HashMap<>(); static { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/Protocol1_13_2To1_14.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/Protocol1_13_2To1_14.java index 7efa794bd..f663cfaab 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/Protocol1_13_2To1_14.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/Protocol1_13_2To1_14.java @@ -121,9 +121,7 @@ public class Protocol1_13_2To1_14 extends AbstractProtocol getTagRewriter() { return tagRewriter; } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/EntityPacketRewriter1_14.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/EntityPacketRewriter1_14.java index 2a10716af..8d41df011 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/EntityPacketRewriter1_14.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/EntityPacketRewriter1_14.java @@ -204,7 +204,7 @@ public class EntityPacketRewriter1_14 extends EntityRewriter { // Store the player - ClientWorld clientChunks = wrapper.user().get(ClientWorld.class); + ClientWorld clientChunks = wrapper.user().getClientWorld(Protocol1_13_2To1_14.class); int dimensionId = wrapper.get(Types.INT, 1); clientChunks.setEnvironment(dimensionId); }); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/WorldPacketRewriter1_14.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/WorldPacketRewriter1_14.java index 6b3053701..7d8e0567c 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/WorldPacketRewriter1_14.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13_2to1_14/rewriter/WorldPacketRewriter1_14.java @@ -125,7 +125,7 @@ public class WorldPacketRewriter1_14 { }); protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13_2To1_14.class); Chunk chunk = wrapper.read(ChunkType1_13.forEnvironment(clientWorld.getEnvironment())); wrapper.write(ChunkType1_14.TYPE, chunk); @@ -282,21 +282,26 @@ public class WorldPacketRewriter1_14 { public void register() { map(Types.INT); // 0 - Dimension ID handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + short difficulty = wrapper.read(Types.UNSIGNED_BYTE); // 19w11a removed difficulty from respawn + + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13_2To1_14.class); int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); + + if (!clientWorld.setEnvironment(dimensionId)) { + return; + } + EntityTracker1_14 entityTracker = wrapper.user().getEntityTracker(Protocol1_13_2To1_14.class); + entityTracker.clearEntities(); + // The client may reset the center chunk if dimension is changed entityTracker.setForceSendCenterChunk(true); - }); - handler(wrapper -> { - short difficulty = wrapper.read(Types.UNSIGNED_BYTE); // 19w11a removed difficulty from respawn + PacketWrapper difficultyPacket = wrapper.create(ClientboundPackets1_14.CHANGE_DIFFICULTY); difficultyPacket.write(Types.UNSIGNED_BYTE, difficulty); difficultyPacket.write(Types.BOOLEAN, false); // Unknown value added in 19w11a difficultyPacket.scheduleSend(protocol.getClass()); - }); - handler(wrapper -> { + // Manually send the packet and update the viewdistance after wrapper.send(Protocol1_13_2To1_14.class); wrapper.cancel(); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/Protocol1_13To1_13_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/Protocol1_13To1_13_1.java index d4710ef76..24cd8b7f0 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/Protocol1_13To1_13_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/Protocol1_13To1_13_1.java @@ -134,9 +134,7 @@ public class Protocol1_13To1_13_1 extends AbstractProtocol { + // Store the player + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_13_1.class); + int dimensionId = wrapper.get(Types.INT, 1); + clientWorld.setEnvironment(dimensionId); + }); + } + }); + + protocol.registerClientbound(ClientboundPackets1_13.RESPAWN, new PacketHandlers() { + @Override + public void register() { + map(Types.INT); // 0 - Dimension ID + handler(wrapper -> { + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_13_1.class); + int dimensionId = wrapper.get(Types.INT, 0); + if (clientWorld.setEnvironment(dimensionId)) { + tracker(wrapper.user()).clearEntities(); + } + }); + } + }); + protocol.registerClientbound(ClientboundPackets1_13.ADD_ENTITY, new PacketHandlers() { @Override public void register() { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/rewriter/WorldPacketRewriter1_13_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/rewriter/WorldPacketRewriter1_13_1.java index 8fa820fdc..dfe08d66b 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/rewriter/WorldPacketRewriter1_13_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_13to1_13_1/rewriter/WorldPacketRewriter1_13_1.java @@ -19,9 +19,6 @@ package com.viaversion.viaversion.protocols.v1_13to1_13_1.rewriter; import com.viaversion.viaversion.api.minecraft.ClientWorld; import com.viaversion.viaversion.api.minecraft.chunks.Chunk; -import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; -import com.viaversion.viaversion.api.minecraft.chunks.DataPalette; -import com.viaversion.viaversion.api.minecraft.chunks.PaletteType; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_13; @@ -35,7 +32,7 @@ public class WorldPacketRewriter1_13_1 { BlockRewriter blockRewriter = BlockRewriter.legacy(protocol); protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_13_1.class); Chunk chunk = wrapper.passthrough(ChunkType1_13.forEnvironment(clientWorld.getEnvironment())); blockRewriter.handleChunk(chunk); @@ -84,33 +81,5 @@ public class WorldPacketRewriter1_13_1 { }); } }); - - protocol.registerClientbound(ClientboundPackets1_13.LOGIN, new PacketHandlers() { - @Override - public void register() { - map(Types.INT); // 0 - Entity ID - map(Types.UNSIGNED_BYTE); // 1 - Gamemode - map(Types.INT); // 2 - Dimension - - handler(wrapper -> { - // Store the player - ClientWorld clientChunks = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 1); - clientChunks.setEnvironment(dimensionId); - }); - } - }); - - protocol.registerClientbound(ClientboundPackets1_13.RESPAWN, new PacketHandlers() { - @Override - public void register() { - map(Types.INT); // 0 - Dimension ID - handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); - }); - } - }); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_14_4to1_15/rewriter/EntityPacketRewriter1_15.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_14_4to1_15/rewriter/EntityPacketRewriter1_15.java index a851eaebf..dd1ce1045 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_14_4to1_15/rewriter/EntityPacketRewriter1_15.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_14_4to1_15/rewriter/EntityPacketRewriter1_15.java @@ -86,7 +86,10 @@ public class EntityPacketRewriter1_15 extends EntityRewriter wrapper.write(Types.LONG, 0L)); // Level Seed + handler(wrapper -> { + tracker(wrapper.user()).clearEntities(); + wrapper.write(Types.LONG, 0L); // Level Seed + }); } }); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_15_2to1_16/rewriter/EntityPacketRewriter1_16.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_15_2to1_16/rewriter/EntityPacketRewriter1_16.java index f85c45f8a..c5a7786ea 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_15_2to1_16/rewriter/EntityPacketRewriter1_16.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_15_2to1_16/rewriter/EntityPacketRewriter1_16.java @@ -119,6 +119,8 @@ public class EntityPacketRewriter1_16 extends EntityRewriter { + tracker(wrapper.user()).clearEntities(); + wrapper.write(Types.BYTE, (byte) -1); // Previous gamemode, set to none // <= 1.14.4 didn't keep attributes on respawn and 1.15.x always kept them @@ -145,9 +147,8 @@ public class EntityPacketRewriter1_16 extends EntityRewriter { - wrapper.user().getEntityTracker(Protocol1_15_2To1_16.class).addEntity(wrapper.get(Types.INT, 0), EntityTypes1_16.PLAYER); - final String type = wrapper.read(Types.STRING);// level type wrapper.passthrough(Types.VAR_INT); // View distance wrapper.passthrough(Types.BOOLEAN); // Reduced debug info diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_1to1_16_2/rewriter/EntityPacketRewriter1_16_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_1to1_16_2/rewriter/EntityPacketRewriter1_16_2.java index d32776ec2..cb4cac5c3 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_1to1_16_2/rewriter/EntityPacketRewriter1_16_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_1to1_16_2/rewriter/EntityPacketRewriter1_16_2.java @@ -72,6 +72,8 @@ public class EntityPacketRewriter1_16_2 extends EntityRewriter { String dimensionType = wrapper.read(Types.STRING); wrapper.write(Types.NAMED_COMPOUND_TAG, getDimensionData(dimensionType)); + + tracker(wrapper.user()).clearEntities(); }); } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/Protocol1_16_4To1_17.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/Protocol1_16_4To1_17.java index fe692871b..41004b37d 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/Protocol1_16_4To1_17.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/Protocol1_16_4To1_17.java @@ -37,7 +37,6 @@ import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPacke import com.viaversion.viaversion.protocols.v1_16_4to1_17.rewriter.EntityPacketRewriter1_17; import com.viaversion.viaversion.protocols.v1_16_4to1_17.rewriter.ItemPacketRewriter1_17; import com.viaversion.viaversion.protocols.v1_16_4to1_17.rewriter.WorldPacketRewriter1_17; -import com.viaversion.viaversion.protocols.v1_16_4to1_17.storage.InventoryAcknowledgements; import com.viaversion.viaversion.rewriter.SoundRewriter; import com.viaversion.viaversion.rewriter.StatisticsRewriter; import com.viaversion.viaversion.rewriter.TagRewriter; @@ -192,7 +191,6 @@ public final class Protocol1_16_4To1_17 extends AbstractProtocol { CompoundTag dimensionData = wrapper.passthrough(Types.NAMED_COMPOUND_TAG); addNewDimensionData(dimensionData); + + tracker(wrapper.user()).clearEntities(); }); protocol.registerClientbound(ClientboundPackets1_16_2.UPDATE_ATTRIBUTES, new PacketHandlers() { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/rewriter/ItemPacketRewriter1_17.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/rewriter/ItemPacketRewriter1_17.java index 27d39f7f4..24e46a119 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/rewriter/ItemPacketRewriter1_17.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/rewriter/ItemPacketRewriter1_17.java @@ -29,7 +29,6 @@ import com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPac import com.viaversion.viaversion.protocols.v1_16_4to1_17.Protocol1_16_4To1_17; import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17; import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17; -import com.viaversion.viaversion.protocols.v1_16_4to1_17.storage.InventoryAcknowledgements; import com.viaversion.viaversion.rewriter.ItemRewriter; import com.viaversion.viaversion.rewriter.RecipeRewriter; @@ -97,7 +96,6 @@ public final class ItemPacketRewriter1_17 extends ItemRewriter { int id = wrapper.read(Types.INT); // Check extra bit for fast dismissal - if ((id & (1 << 30)) != 0 && wrapper.user().get(InventoryAcknowledgements.class).removeId(id)) { + if ((id & (1 << 30)) != 0) { // Decode our requested inventory acknowledgement short inventoryId = (short) ((id >> 16) & 0xFF); short confirmationId = (short) (id & 0xFFFF); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/storage/InventoryAcknowledgements.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/storage/InventoryAcknowledgements.java deleted file mode 100644 index b07d4dee7..000000000 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_16_4to1_17/storage/InventoryAcknowledgements.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 . - */ -package com.viaversion.viaversion.protocols.v1_16_4to1_17.storage; - -import com.viaversion.viaversion.api.connection.StorableObject; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; - -public final class InventoryAcknowledgements implements StorableObject { - private final IntList ids = new IntArrayList(); - - public void addId(int id) { - ids.add(id); - } - - public boolean removeId(int id) { - return ids.rem(id); - } -} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_19_4to1_20/rewriter/EntityPacketRewriter1_20.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_19_4to1_20/rewriter/EntityPacketRewriter1_20.java index 65e4a623c..de9d57fa4 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_19_4to1_20/rewriter/EntityPacketRewriter1_20.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_19_4to1_20/rewriter/EntityPacketRewriter1_20.java @@ -79,8 +79,8 @@ public final class EntityPacketRewriter1_20 extends EntityRewriter damageTypes = TagUtil.getRegistryEntries(registry, "damage_type"); int highestId = -1; for (final CompoundTag damageType : damageTypes) { - final IntTag id = damageType.getUnchecked("id"); - highestId = Math.max(highestId, id.asInt()); + final int id = damageType.getInt("id"); + highestId = Math.max(highestId, id); } // AaaaAAAaa diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/Protocol1_20_3To1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/Protocol1_20_3To1_20_5.java index 75e4c4ccd..89e92006c 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/Protocol1_20_3To1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/Protocol1_20_3To1_20_5.java @@ -103,6 +103,7 @@ public final class Protocol1_20_3To1_20_5 extends AbstractProtocol { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java index fde9af2f4..39204b2da 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/BlockItemPacketRewriter1_20_5.java @@ -416,7 +416,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + final StructuredData customData = data.getNonEmptyData(StructuredDataKey.CUSTOM_DATA); final CompoundTag tag = customData != null ? customData.value() : new CompoundTag(); final DataItem dataItem = new DataItem(item.identifier(), (byte) item.amount(), tag); if (!dataConverter.backupInconvertibleData() && customData != null && tag.remove(nbtTagName()) != null) { @@ -488,7 +488,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter pagesTag = tag.getListTag("pages", StringTag.class); - final CompoundTag filteredPagesTag = tag.getCompoundTag("filtered_pages"); - if (pagesTag == null) { - return; + + boolean valid = author != null && title != null && title.length() <= 32 && pagesTag != null; + if (valid) { + for (final StringTag page : pagesTag) { + if (page.getValue().length() > Short.MAX_VALUE) { + valid = false; + break; + } + } } final List pages = new ArrayList<>(); - for (int i = 0; i < pagesTag.size(); i++) { - final StringTag page = pagesTag.get(i); - Tag filtered = null; - if (filteredPagesTag != null) { - final StringTag filteredPage = filteredPagesTag.getStringTag(String.valueOf(i)); - if (filteredPage != null) { - try { - filtered = jsonToTag(connection, filteredPage); - } catch (final Exception e) { - // A 1.20.4 client would display the broken json raw, but a 1.20.5 client would die - continue; + if (valid) { + final CompoundTag filteredPagesTag = tag.getCompoundTag("filtered_pages"); + + for (int i = 0; i < pagesTag.size(); i++) { + final StringTag page = pagesTag.get(i); + Tag filtered = null; + if (filteredPagesTag != null) { + final StringTag filteredPage = filteredPagesTag.getStringTag(String.valueOf(i)); + if (filteredPage != null) { + try { + filtered = jsonToTag(connection, filteredPage); + } catch (final Exception e) { + // A 1.20.4 client would display the broken json raw, but a 1.20.5 client would die + continue; + } } } - } - final Tag parsedPage; - try { - parsedPage = jsonToTag(connection, page); - } catch (final Exception e) { - // Same as above - continue; - } + final Tag parsedPage; + try { + parsedPage = jsonToTag(connection, page); + } catch (final Exception e) { + // Same as above + continue; + } - pages.add(new FilterableComponent(parsedPage, filtered)); + pages.add(new FilterableComponent(parsedPage, filtered)); + } + } else { + final CompoundTag invalidPage = new CompoundTag(); + invalidPage.putString("text", "* Invalid book tag *"); + invalidPage.putString("color", "#AA0000"); // dark red + + pages.add(new FilterableComponent(invalidPage, null)); } - final String title = tag.getString("title", ""); final String filteredTitle = tag.getString("filtered_title"); // Nullable - final String author = tag.getString("author", ""); final int generation = tag.getInt("generation"); final boolean resolved = tag.getBoolean("resolved"); final WrittenBook writtenBook = new WrittenBook( - new FilterableString(limit(title, 32), limit(filteredTitle, 32)), - author, + new FilterableString(limit(title == null ? "" : title, 32), limit(filteredTitle, 32)), + author == null ? "" : author, clamp(generation, 0, 3), pages.toArray(new FilterableComponent[0]), resolved diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/ComponentRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/ComponentRewriter1_20_5.java index 9d55d3631..14c64fed7 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/ComponentRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/ComponentRewriter1_20_5.java @@ -459,6 +459,14 @@ public class ComponentRewriter1_20_5 extends Co } protected CompoundTag canPlaceOnToTag(final AdventureModePredicate value) { + return blockPredicateToTag(value); + } + + protected CompoundTag canBreakToTag(final AdventureModePredicate value) { + return blockPredicateToTag(value); + } + + protected CompoundTag blockPredicateToTag(final AdventureModePredicate value) { final CompoundTag tag = new CompoundTag(); final ListTag predicates = new ListTag<>(CompoundTag.class); for (final BlockPredicate predicate : value.predicates()) { @@ -467,8 +475,7 @@ public class ComponentRewriter1_20_5 extends Co holderSetToTag(predicateTag, "blocks", predicate.holderSet()); } if (predicate.propertyMatchers() != null) { - final CompoundTag state = predicateToTag(predicate); - predicateTag.put("state", state); + predicateTag.put("state", createState(predicate)); } if (predicate.tag() != null) { predicateTag.put("nbt", predicate.tag()); @@ -483,8 +490,10 @@ public class ComponentRewriter1_20_5 extends Co return tag; } - protected CompoundTag predicateToTag(final BlockPredicate predicate) { + // Not an own conversion method, just to avoid high nesting + protected CompoundTag createState(final BlockPredicate predicate) { final CompoundTag state = new CompoundTag(); + for (final StatePropertyMatcher matcher : predicate.propertyMatchers()) { final Either match = matcher.matcher(); if (match.isLeft()) { @@ -504,10 +513,6 @@ public class ComponentRewriter1_20_5 extends Co return state; } - protected CompoundTag canBreakToTag(final AdventureModePredicate value) { - return canPlaceOnToTag(value); - } - protected CompoundTag attributeModifiersToTag(final AttributeModifiers1_20_5 value) { final CompoundTag tag = new CompoundTag(); final ListTag modifiers = new ListTag<>(CompoundTag.class); @@ -1009,7 +1014,17 @@ public class ComponentRewriter1_20_5 extends Co if (set.hasTagKey()) { tag.putString(name, set.tagKey()); } else { - tag.put(name, new IntArrayTag(set.ids())); + final ListTag identifiers = new ListTag<>(StringTag.class); + for (final int id : set.ids()) { + // Can use old block list because new ids are only at the end :tm: + final String identifier = Protocol1_20_3To1_20_5.MAPPINGS.blockName(id); + if (identifier == null) { + continue; + } + + identifiers.add(new StringTag(identifier)); + } + tag.put(name, identifiers); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/EntityPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/EntityPacketRewriter1_20_5.java index 1ffc81397..5c552b269 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/EntityPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_3to1_20_5/rewriter/EntityPacketRewriter1_20_5.java @@ -83,13 +83,16 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/BlockItemPacketRewriter1_21.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/BlockItemPacketRewriter1_21.java index 803add1c2..d507ba5c5 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/BlockItemPacketRewriter1_21.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/BlockItemPacketRewriter1_21.java @@ -19,13 +19,14 @@ package com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter; import com.viaversion.nbt.tag.ByteTag; import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; -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.data.AttributeModifiers1_20_5; import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2; import com.viaversion.viaversion.api.type.types.version.Types1_20_5; @@ -106,8 +107,22 @@ public final class BlockItemPacketRewriter1_21 extends StructuredItemRewriter { wrapper.passthrough(Types.VAR_INT); // Hand wrapper.passthrough(Types.VAR_INT); // Sequence - wrapper.read(Types.FLOAT); // Y rotation - wrapper.read(Types.FLOAT); // X rotation + final float yaw = wrapper.read(Types.FLOAT); // Y rotation + final float pitch = wrapper.read(Types.FLOAT); // X rotation + + if (!Via.getConfig().fix1_21PlacementRotation()) { + return; + } + + // Not correct but *enough* for vanilla/normal servers to have block placement synchronized + final PacketWrapper playerRotation = wrapper.create(ServerboundPackets1_20_5.MOVE_PLAYER_ROT); + playerRotation.write(Types.FLOAT, yaw); + playerRotation.write(Types.FLOAT, pitch); + playerRotation.write(Types.BOOLEAN, true); // On Ground + + playerRotation.sendToServer(Protocol1_20_5To1_21.class); + wrapper.sendToServer(Protocol1_20_5To1_21.class); + wrapper.cancel(); }); new RecipeRewriter1_20_3<>(protocol).register1_20_5(ClientboundPackets1_20_5.UPDATE_RECIPES); @@ -123,7 +138,7 @@ public final class BlockItemPacketRewriter1_21 extends StructuredItemRewriter customData = dataContainer.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + final CompoundTag customData = dataContainer.get(StructuredDataKey.CUSTOM_DATA); if (customData == null) { return; } - if (customData.value().remove(tagName) != null) { + + if (customData.remove(tagName) != null) { dataContainer.remove(StructuredDataKey.RARITY); - if (customData.value().isEmpty()) { + if (customData.isEmpty()) { dataContainer.remove(StructuredDataKey.CUSTOM_DATA); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/Protocol1_8To1_9.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/Protocol1_8To1_9.java index 1796fd814..8604ef723 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/Protocol1_8To1_9.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/Protocol1_8To1_9.java @@ -43,7 +43,7 @@ import com.viaversion.viaversion.protocols.v1_8to1_9.rewriter.ItemPacketRewriter import com.viaversion.viaversion.protocols.v1_8to1_9.rewriter.PlayerPacketRewriter1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.rewriter.SpawnPacketRewriter1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.rewriter.WorldPacketRewriter1_9; -import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientChunks; +import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientWorld1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.CommandBlockStorage; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.EntityTracker1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.InventoryTracker; @@ -104,20 +104,12 @@ public class Protocol1_8To1_9 extends AbstractProtocol POTION_NAME_TO_ID = new HashMap<>(); public static final Map POTION_ID_TO_NAME = new HashMap<>(); - public static final Int2IntMap POTION_INDEX = new Int2IntOpenHashMap(36, .99F); + public static final Int2IntMap POTION_INDEX = new Int2IntOpenHashMap(36); static { register(-1, "empty"); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/PlayerPacketRewriter1_9.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/PlayerPacketRewriter1_9.java index 7e50a3d01..8842b96d7 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/PlayerPacketRewriter1_9.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/PlayerPacketRewriter1_9.java @@ -36,7 +36,7 @@ import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_ import com.viaversion.viaversion.protocols.v1_8to1_9.provider.CommandBlockProvider; import com.viaversion.viaversion.protocols.v1_8to1_9.provider.CompressionProvider; import com.viaversion.viaversion.protocols.v1_8to1_9.provider.MainHandProvider; -import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientChunks; +import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientWorld1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.EntityTracker1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.MovementTracker; import com.viaversion.viaversion.util.ComponentUtil; @@ -203,7 +203,7 @@ public class PlayerPacketRewriter1_9 { // Track player's dimension handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_8To1_9.class); int dimensionId = wrapper.get(Types.BYTE, 0); clientWorld.setEnvironment(dimensionId); }); @@ -295,27 +295,25 @@ public class PlayerPacketRewriter1_9 { map(Types.UNSIGNED_BYTE); // 2 - GameMode map(Types.STRING); // 3 - Level Type - // Track player's dimension - handler(wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - int dimensionId = wrapper.get(Types.INT, 0); - clientWorld.setEnvironment(dimensionId); - }); - - handler(wrapper -> { - // Client unloads chunks on respawn - wrapper.user().get(ClientChunks.class).getLoadedChunks().clear(); - - int gamemode = wrapper.get(Types.UNSIGNED_BYTE, 0); - EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_9.class); - tracker.setGameMode(GameMode.getById(gamemode)); - }); - - // Fake permissions to get Commandblocks working handler(wrapper -> { CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + // Fake permissions to get Commandblocks working provider.sendPermission(wrapper.user()); - provider.unloadChunks(wrapper.user()); + + EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_9.class); + int gamemode = wrapper.get(Types.UNSIGNED_BYTE, 0); + tracker.setGameMode(GameMode.getById(gamemode)); + + ClientWorld1_9 clientWorld = wrapper.user().getClientWorld(Protocol1_8To1_9.class); + int dimensionId = wrapper.get(Types.INT, 0); + + // Track player's dimension + if (clientWorld.setEnvironment(dimensionId)) { + tracker.clearEntities(); + + clientWorld.getLoadedChunks().clear(); + provider.unloadChunks(wrapper.user()); + } }); } }); @@ -496,4 +494,4 @@ public class PlayerPacketRewriter1_9 { } }); } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/WorldPacketRewriter1_9.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/WorldPacketRewriter1_9.java index ead2b6d1c..d0bb3c3a8 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/WorldPacketRewriter1_9.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/rewriter/WorldPacketRewriter1_9.java @@ -45,7 +45,7 @@ import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_ import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.provider.CommandBlockProvider; import com.viaversion.viaversion.protocols.v1_8to1_9.provider.HandItemProvider; -import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientChunks; +import com.viaversion.viaversion.protocols.v1_8to1_9.storage.ClientWorld1_9; import com.viaversion.viaversion.protocols.v1_8to1_9.storage.EntityTracker1_9; import com.viaversion.viaversion.util.ComponentUtil; import com.viaversion.viaversion.util.Key; @@ -130,11 +130,10 @@ public class WorldPacketRewriter1_9 { }); protocol.registerClientbound(ClientboundPackets1_8.LEVEL_CHUNK, wrapper -> { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - ClientChunks clientChunks = wrapper.user().get(ClientChunks.class); + ClientWorld1_9 clientWorld = wrapper.user().getClientWorld(Protocol1_8To1_9.class); Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(clientWorld.getEnvironment())); - long chunkHash = ClientChunks.toLong(chunk.getX(), chunk.getZ()); + long chunkHash = ClientWorld1_9.toLong(chunk.getX(), chunk.getZ()); // Check if the chunk should be handled as an unload packet if (chunk.isFullChunk() && chunk.getBitmask() == 0) { @@ -146,14 +145,14 @@ public class WorldPacketRewriter1_9 { CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); provider.unloadChunk(wrapper.user(), chunk.getX(), chunk.getZ()); - clientChunks.getLoadedChunks().remove(chunkHash); + clientWorld.getLoadedChunks().remove(chunkHash); // Unload the empty chunks if (Via.getConfig().isChunkBorderFix()) { for (BlockFace face : BlockFace.HORIZONTAL) { int chunkX = chunk.getX() + face.modX(); int chunkZ = chunk.getZ() + face.modZ(); - if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) { + if (!clientWorld.getLoadedChunks().contains(ClientWorld1_9.toLong(chunkX, chunkZ))) { PacketWrapper unloadChunk = wrapper.create(ClientboundPackets1_9.FORGET_LEVEL_CHUNK); unloadChunk.write(Types.INT, chunkX); unloadChunk.write(Types.INT, chunkZ); @@ -165,14 +164,14 @@ public class WorldPacketRewriter1_9 { Type chunkType = ChunkType1_9_1.forEnvironment(clientWorld.getEnvironment()); wrapper.write(chunkType, chunk); - clientChunks.getLoadedChunks().add(chunkHash); + clientWorld.getLoadedChunks().add(chunkHash); // Send empty chunks surrounding the loaded chunk to force 1.9+ clients to render the new chunk if (Via.getConfig().isChunkBorderFix()) { for (BlockFace face : BlockFace.HORIZONTAL) { int chunkX = chunk.getX() + face.modX(); int chunkZ = chunk.getZ() + face.modZ(); - if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) { + if (!clientWorld.getLoadedChunks().contains(ClientWorld1_9.toLong(chunkX, chunkZ))) { PacketWrapper emptyChunk = wrapper.create(ClientboundPackets1_9.LEVEL_CHUNK); Chunk c = new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], new int[256], new ArrayList<>()); emptyChunk.write(chunkType, c); @@ -185,8 +184,7 @@ public class WorldPacketRewriter1_9 { protocol.registerClientbound(ClientboundPackets1_8.MAP_BULK_CHUNK, null, wrapper -> { wrapper.cancel(); // Cancel the packet from being sent - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - ClientChunks clientChunks = wrapper.user().get(ClientChunks.class); + ClientWorld1_9 clientWorld = wrapper.user().getClientWorld(Protocol1_8To1_9.class); Chunk[] chunks = wrapper.read(BulkChunkType1_8.TYPE); Type chunkType = ChunkType1_9_1.forEnvironment(clientWorld.getEnvironment()); @@ -196,14 +194,14 @@ public class WorldPacketRewriter1_9 { chunkData.write(chunkType, chunk); chunkData.send(Protocol1_8To1_9.class); - clientChunks.getLoadedChunks().add(ClientChunks.toLong(chunk.getX(), chunk.getZ())); + clientWorld.getLoadedChunks().add(ClientWorld1_9.toLong(chunk.getX(), chunk.getZ())); // Send empty chunks surrounding the loaded chunk to force 1.9+ clients to render the new chunk if (Via.getConfig().isChunkBorderFix()) { for (BlockFace face : BlockFace.HORIZONTAL) { int chunkX = chunk.getX() + face.modX(); int chunkZ = chunk.getZ() + face.modZ(); - if (!clientChunks.getLoadedChunks().contains(ClientChunks.toLong(chunkX, chunkZ))) { + if (!clientWorld.getLoadedChunks().contains(ClientWorld1_9.toLong(chunkX, chunkZ))) { PacketWrapper emptyChunk = wrapper.create(ClientboundPackets1_9.LEVEL_CHUNK); Chunk c = new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], new int[256], new ArrayList<>()); emptyChunk.write(chunkType, c); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientChunks.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientWorld1_9.java similarity index 90% rename from common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientChunks.java rename to common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientWorld1_9.java index 561e56233..7defcd00e 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientChunks.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_8to1_9/storage/ClientWorld1_9.java @@ -18,10 +18,10 @@ package com.viaversion.viaversion.protocols.v1_8to1_9.storage; import com.google.common.collect.Sets; -import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.minecraft.ClientWorld; import java.util.Set; -public class ClientChunks implements StorableObject { +public class ClientWorld1_9 extends ClientWorld { private final Set loadedChunks = Sets.newConcurrentHashSet(); public static long toLong(int msw, int lsw) { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_9_1to1_9_3/Protocol1_9_1To1_9_3.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_9_1to1_9_3/Protocol1_9_1To1_9_3.java index 0067e2ad9..6e7ddf529 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_9_1to1_9_3/Protocol1_9_1To1_9_3.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_9_1to1_9_3/Protocol1_9_1To1_9_3.java @@ -87,7 +87,7 @@ public class Protocol1_9_1To1_9_3 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_1To1_9_3.class); Chunk chunk = wrapper.read(ChunkType1_9_1.forEnvironment(clientWorld.getEnvironment())); wrapper.write(ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()), chunk); @@ -120,7 +120,7 @@ public class Protocol1_9_1To1_9_3 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_1To1_9_3.class); int dimensionId = wrapper.get(Types.INT, 1); clientWorld.setEnvironment(dimensionId); }); @@ -132,7 +132,7 @@ public class Protocol1_9_1To1_9_3 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_1To1_9_3.class); int dimensionId = wrapper.get(Types.INT, 0); clientWorld.setEnvironment(dimensionId); }); @@ -156,8 +156,6 @@ public class Protocol1_9_1To1_9_3 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_10.class); int dimensionId = wrapper.get(Types.INT, 1); clientWorld.setEnvironment(dimensionId); @@ -168,7 +168,7 @@ public class Protocol1_9_3To1_10 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_10.class); int dimensionId = wrapper.get(Types.INT, 0); clientWorld.setEnvironment(dimensionId); @@ -178,7 +178,7 @@ public class Protocol1_9_3To1_10 extends AbstractProtocol { - ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_10.class); Chunk chunk = wrapper.passthrough(ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment())); if (Via.getConfig().isReplacePistons()) { @@ -234,11 +234,9 @@ public class Protocol1_9_3To1_10 extends AbstractProtocol implements com.v }); } + public void registerPlayerInfoUpdate1_20_3(final C packetType) { + protocol.registerClientbound(packetType, wrapper -> { + final BitSet actions = wrapper.passthrough(Types.PROFILE_ACTIONS_ENUM); + if (!actions.get(5)) { // Update display name + return; + } + final int entries = wrapper.passthrough(Types.VAR_INT); + for (int i = 0; i < entries; i++) { + wrapper.passthrough(Types.UUID); + if (actions.get(0)) { + wrapper.passthrough(Types.STRING); // Player Name + + final int properties = wrapper.passthrough(Types.VAR_INT); + for (int j = 0; j < properties; j++) { + wrapper.passthrough(Types.STRING); // Name + wrapper.passthrough(Types.STRING); // Value + wrapper.passthrough(Types.OPTIONAL_STRING); // Signature + } + } + if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) { + wrapper.passthrough(Types.UUID); // Session UUID + wrapper.passthrough(Types.PROFILE_KEY); + } + if (actions.get(2)) { + wrapper.passthrough(Types.VAR_INT); // Gamemode + } + if (actions.get(3)) { + wrapper.passthrough(Types.BOOLEAN); // Listed + } + if (actions.get(4)) { + wrapper.passthrough(Types.VAR_INT); // Latency + } + processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); + } + }); + } + public void registerPlayerCombatKill(final C packetType) { protocol.registerClientbound(packetType, new PacketHandlers() { @Override diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java index 74a3a4f73..4cc4354d5 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java @@ -367,6 +367,11 @@ public abstract class EntityRewriter loreData = dataContainer.getNonEmpty(StructuredDataKey.LORE); - if (loreData != null) { - for (final Tag tag : loreData.value()) { - componentRewriter.processTag(connection, tag); - } - } - } - - Int2IntFunction itemIdRewriter = null; - Int2IntFunction blockIdRewriter = null; - if (mappingData != null) { - itemIdRewriter = mappingData.getItemMappings() != null ? mappingData::getNewItemId : null; - blockIdRewriter = mappingData.getBlockMappings() != null ? mappingData::getNewBlockId : null; - } - updateItemComponents(connection, dataContainer, this::handleItemToClient, itemIdRewriter, blockIdRewriter); + updateItemDataComponentTypeIds(item.dataContainer(), true); + updateItemDataComponents(connection, item, true); return item; } @@ -107,44 +79,69 @@ public class StructuredItemRewriter dataComponentMappings.inverse().getNewId(id)); - } + if (mappingData != null && mappingData.getItemMappings() != null) { + item.setIdentifier(mappingData.getOldItemId(item.identifier())); } + updateItemDataComponentTypeIds(item.dataContainer(), false); + updateItemDataComponents(connection, item, false); restoreTextComponents(item); - - Int2IntFunction itemIdRewriter = null; - Int2IntFunction blockIdRewriter = null; - if (mappingData != null) { - itemIdRewriter = mappingData.getItemMappings() != null ? mappingData::getOldItemId : null; - blockIdRewriter = mappingData.getBlockMappings() != null ? mappingData::getOldBlockId : null; - } - updateItemComponents(connection, dataContainer, this::handleItemToServer, itemIdRewriter, blockIdRewriter); return item; } - protected void updateItemComponents(UserConnection connection, StructuredDataContainer container, ItemHandler itemHandler, @Nullable Int2IntFunction idRewriter, @Nullable Int2IntFunction blockIdRewriter) { - // Specific types that need deep handling - if (idRewriter != null) { - container.updateIfPresent(StructuredDataKey.TRIM, value -> value.rewrite(idRewriter)); - container.updateIfPresent(StructuredDataKey.POT_DECORATIONS, value -> value.rewrite(idRewriter)); + protected void updateItemDataComponentTypeIds(final StructuredDataContainer container, final boolean mappedNames) { + final MappingData mappingData = protocol.getMappingData(); + if (mappingData == null) { + return; } - if (blockIdRewriter != null) { - container.updateIfPresent(StructuredDataKey.TOOL, value -> value.rewrite(blockIdRewriter)); - container.updateIfPresent(StructuredDataKey.CAN_PLACE_ON, value -> value.rewrite(blockIdRewriter)); - container.updateIfPresent(StructuredDataKey.CAN_BREAK, value -> value.rewrite(blockIdRewriter)); + + FullMappings dataComponentMappings = mappingData.getDataComponentSerializerMappings(); + if (dataComponentMappings == null) { + return; + } + + if (!mappedNames) { + dataComponentMappings = dataComponentMappings.inverse(); + } + + container.setIdLookup(protocol, mappedNames); // Necessary to be set before trying to add values to the container + container.updateIds(protocol, dataComponentMappings::getNewId); + } + + protected void updateItemDataComponents(final UserConnection connection, final Item item, final boolean clientbound) { + // Specific types that need deep handling + final StructuredDataContainer container = item.dataContainer(); + final MappingData mappingData = protocol.getMappingData(); + if (mappingData.getItemMappings() != null) { + final Int2IntFunction itemIdRewriter = clientbound ? mappingData::getNewItemId : mappingData::getOldItemId; + container.replace(StructuredDataKey.TRIM, value -> value.rewrite(itemIdRewriter)); + container.replace(StructuredDataKey.POT_DECORATIONS, value -> value.rewrite(itemIdRewriter)); + } + if (mappingData.getBlockMappings() != null) { + final Int2IntFunction blockIdRewriter = clientbound ? mappingData::getNewBlockId : mappingData::getOldBlockId; + container.replace(StructuredDataKey.TOOL, value -> value.rewrite(blockIdRewriter)); + container.replace(StructuredDataKey.CAN_PLACE_ON, value -> value.rewrite(blockIdRewriter)); + container.replace(StructuredDataKey.CAN_BREAK, value -> value.rewrite(blockIdRewriter)); + } + if (mappingData.getSoundMappings() != null) { + final Int2IntFunction soundIdRewriter = clientbound ? mappingData::getNewSoundId : mappingData::getOldSoundId; + container.replace(StructuredDataKey.INSTRUMENT, value -> value.isDirect() ? Holder.of(value.value().rewrite(soundIdRewriter)) : value); + container.replace(StructuredDataKey.JUKEBOX_PLAYABLE, value -> value.rewrite(soundIdRewriter)); + } + if (clientbound && protocol.getComponentRewriter() != null) { + updateComponent(connection, item, StructuredDataKey.ITEM_NAME, "item_name"); + updateComponent(connection, item, StructuredDataKey.CUSTOM_NAME, "custom_name"); + + final Tag[] lore = container.get(StructuredDataKey.LORE); + if (lore != null) { + for (final Tag tag : lore) { + protocol.getComponentRewriter().processTag(connection, tag); + } + } } // Look for item types + final ItemHandler itemHandler = clientbound ? this::handleItemToClient : this::handleItemToServer; for (final Map.Entry, StructuredData> entry : container.data().entrySet()) { final StructuredData data = entry.getValue(); if (data.isEmpty()) { @@ -169,35 +166,35 @@ public class StructuredItemRewriter key, final String backupKey) { - final StructuredData name = item.dataContainer().getNonEmpty(key); + final Tag name = item.dataContainer().get(key); if (name == null) { return; } - final Tag originalName = name.value().copy(); - protocol.getComponentRewriter().processTag(connection, name.value()); - if (!name.value().equals(originalName)) { + final Tag originalName = name.copy(); + protocol.getComponentRewriter().processTag(connection, name); + if (!name.equals(originalName)) { saveTag(createCustomTag(item), originalName, backupKey); } } protected void restoreTextComponents(final Item item) { final StructuredDataContainer data = item.dataContainer(); - final StructuredData customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA); if (customData == null) { return; } // Remove custom name - if (customData.value().remove(nbtTagName("added_custom_name")) != null) { + if (customData.remove(nbtTagName("added_custom_name")) != null) { data.remove(StructuredDataKey.CUSTOM_NAME); } else { - final Tag customName = removeBackupTag(customData.value(), "custom_name"); + final Tag customName = removeBackupTag(customData, "custom_name"); if (customName != null) { data.set(StructuredDataKey.CUSTOM_NAME, customName); } - final Tag itemName = removeBackupTag(customData.value(), "item_name"); + final Tag itemName = removeBackupTag(customData, "item_name"); if (itemName != null) { data.set(StructuredDataKey.ITEM_NAME, itemName); } @@ -206,14 +203,12 @@ public class StructuredItemRewriter customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); - if (customData != null) { - return customData.value(); + CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA); + if (customData == null) { + customData = new CompoundTag(); + data.set(StructuredDataKey.CUSTOM_DATA, customData); } - - final CompoundTag tag = new CompoundTag(); - data.set(StructuredDataKey.CUSTOM_DATA, tag); - return tag; + return customData; } protected void saveTag(final CompoundTag customData, final Tag tag, final String name) { @@ -228,7 +223,7 @@ public class StructuredItemRewriter