Compare commits

...
Author SHA1 Message Date
Florian Reuth 075181f06f Backup hidden attribute modifiers in 1.21.4->1.21.5
Without show_in_tooltip, hidden modifiers can equal the item default and are then dropped by the client.
Fixes https://github.com/ViaVersion/ViaVersion/issues/4843

Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-10-18 22:54:57 +02:00
Nassim Jahnke b712c0055d Add some missing sound rewriting 2026-10-18 22:07:58 +02:00
mfishma 08776de832 [ci skip] Update README (#5118) 2026-10-12 18:40:57 +02:00
Florian Reuth 77b7054629 Remove duplicated items tag in 1.20.3->1.20.5 (#5121) 2026-10-12 18:40:14 +02:00
Nassim Jahnke 63769b3661 Fix continuous block breaking sounds
Fixes #5120
2026-10-11 20:08:24 +02:00
mfishma 12d75daf04 Fix item_name restoration when added_custom_name is present (#5109) 2026-10-11 09:48:29 +02:00
Florian Reuth 413268eb15 Delete empty tag in 1.12.2->1.13 (#5115) 2026-10-11 09:41:02 +02:00
Florian Reuth b27e2a5514 Handle existing entity tag properly in 1.12.2->1.13 (#5114) 2026-10-11 09:40:49 +02:00
Florian Reuth c59dfb4d93 Fix NPE if color is missing in patterns in 1.12.2->1.13 (#5113) 2026-10-11 09:40:31 +02:00
Florian Reuth f66cbd6941 Remove empty tags in 1.8->1.9 (#5112) 2026-10-11 09:40:16 +02:00
mfishma 1574f343f7 Downgrade new 26.3 map decorations to target_x (#5110) 2026-10-11 09:39:41 +02:00
Nassim Jahnke 022fb96f83 Handle visible items in block entities
Fixes #1351
2026-10-09 16:43:07 +02:00
Nassim Jahnke 7cbc751fea Bump version to 5.12.2-SNAPSHOT 2026-10-09 16:42:46 +02:00
24 changed files with 382 additions and 36 deletions

No files matched your search

+13 -8
View File
@@ -14,18 +14,23 @@ or as [a standalone proxy](https://github.com/ViaVersion/ViaProxy) to join from
any server version from the past decade. **See [HERE](https://viaversion.com) for an overview of the different Via\*
projects.**
Note that ViaVersion will be able to **run best on either Paper servers or
through [ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus?tab=readme-ov-file#viafabricplus)** due to having
direct access to client/server state and more extensive API.
**Note:** While ViaVersion works reliably across all listed platforms, it has the highest performance and state accuracy on **Paper** servers and through **[ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus)** on clients, since both offer more extensive APIs and direct access to client/server state.
Supported Versions:
### Supported Versions
![Table (https://ci.viaversion.com/userContent/images/T.png)](https://ci.viaversion.com/userContent/images/T.png)
| Players joining with… | Install |
| :--- | :--- |
| **Newer** versions than the server | ViaVersion |
| **Older** versions than the server, down to 1.9 | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) |
| **1.7.x – 1.8.x** | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) + [ViaRewind](https://github.com/ViaVersion/ViaRewind) |
**User Docs:** https://docs.viaversion.com
If you're stuck, try [this tool](https://viaversion.com/setup) to work out what plugins you need.
**User Docs:** [docs.viaversion.com](https://docs.viaversion.com)
Snapshot Support
----------
Snapshot support
--------
**ViaVersion will only be released a few days *after* a Minecraft update** unless the protocol changes of the update
were trivial. If you want early-access, usually days or even weeks before the final release, you can subscribe to
either:
@@ -39,4 +39,11 @@ public record SoundEvent(String identifier, @Nullable Float fixedRange) {
}
return holder.updateId(soundRewriteFunction, () -> UNKNOWN);
}
public static @Nullable Holder<SoundEvent> rewriteOptionalHolder(@Nullable final Holder<SoundEvent> holder, final Int2IntFunction soundRewriteFunction) {
if (holder == null) {
return null;
}
return holder.updateId(soundRewriteFunction, () -> null);
}
}
@@ -30,7 +30,6 @@ import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.PaintingVariant;
import com.viaversion.viaversion.api.minecraft.RegistryKey;
import com.viaversion.viaversion.api.minecraft.ResolvableProfile;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_20_5;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
@@ -48,6 +47,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.Bee;
import com.viaversion.viaversion.api.minecraft.item.data.BlockEntityData;
import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties;
import com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks;
import com.viaversion.viaversion.api.minecraft.item.data.BreakSound;
import com.viaversion.viaversion.api.minecraft.item.data.BrewingFuel;
import com.viaversion.viaversion.api.minecraft.item.data.Compostable;
import com.viaversion.viaversion.api.minecraft.item.data.consumable.Consumable1_21_2;
@@ -239,7 +239,7 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Tag> LOCK1_20_5 = new StructuredDataKey<>("lock", Types.TAG);
public static final StructuredDataKey<LockCode> LOCK1_21_2 = new StructuredDataKey<>("lock", LockCode.TYPE);
public static final StructuredDataKey<CompoundTag> CONTAINER_LOOT = new StructuredDataKey<>("container_loot", Types.COMPOUND_TAG);
public static final StructuredDataKey<Holder<SoundEvent>> BREAK_SOUND = new StructuredDataKey<>("break_sound", Types.SOUND_EVENT);
public static final StructuredDataKey<BreakSound> BREAK_SOUND = new StructuredDataKey<>("break_sound", BreakSound.TYPE);
public static final StructuredDataKey<Compostable> COMPOSTABLE = new StructuredDataKey<>("compostable", Compostable.TYPE);
public static final StructuredDataKey<CookingFuel> COOKING_FUEL = new StructuredDataKey<>("cooking_fuel", CookingFuel.TYPE);
public static final StructuredDataKey<BrewingFuel> BREWING_FUEL = new StructuredDataKey<>("brewing_fuel", BrewingFuel.TYPE);
@@ -130,8 +130,8 @@ public record BlocksAttacks(
@Override
public BlocksAttacks rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> blockSound = SoundEvent.rewriteHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> disableSound = SoundEvent.rewriteHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> blockSound = SoundEvent.rewriteOptionalHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> disableSound = SoundEvent.rewriteOptionalHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return new BlocksAttacks(this.blockDelaySeconds, this.disableCooldownScale, this.damageReductions, this.itemDamage, this.bypassedBy, blockSound, disableSound);
}
@@ -0,0 +1,43 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 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.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
public record BreakSound(Holder<SoundEvent> sound) implements Rewritable {
public static final Type<BreakSound> TYPE = TransformingType.of(Types.SOUND_EVENT, BreakSound.class, BreakSound::new, BreakSound::sound);
@Override
public BreakSound rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return this.sound == sound ? this : new BreakSound(sound);
}
}
@@ -22,13 +22,16 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.consumable.ConsumeEffect;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Rewritable;
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable {
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable, Rewritable {
public static final Type<DeathProtection> TYPE1_21_2 = new TransformingType<>(ConsumeEffect.ARRAY_TYPE1_21_2, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
@Override
@@ -43,6 +46,12 @@ public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyab
}
};
@Override
public DeathProtection rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final ConsumeEffect<?>[] deathEffects = ConsumeEffect.rewrite(this.deathEffects, connection, protocol, clientbound);
return deathEffects == this.deathEffects ? this : new DeathProtection(deathEffects);
}
@Override
public DeathProtection copy() {
return new DeathProtection(Copyable.copy(deathEffects));
@@ -22,19 +22,22 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable Condition dismountConditions,
@Nullable Condition knockbackConditions, @Nullable Condition damageConditions,
float forwardMovement, float damageMultiplier,
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
public static final Type<KineticWeapon> TYPE = new Type<>(KineticWeapon.class) {
@Override
@@ -80,6 +83,13 @@ public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable
}
};
@Override
public KineticWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return sound == this.sound && hitSound == this.hitSound ? this : new KineticWeapon(contactCooldownTicks, delayTicks, dismountConditions, knockbackConditions, damageConditions, forwardMovement, damageMultiplier, sound, hitSound);
}
public record Condition(int maxDurationTicks, float minSpeed, float minRelativeSpeed) {
public static final Type<Condition> TYPE = new Type<>(Condition.class) {
@@ -22,16 +22,19 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
public static final Type<PiercingWeapon> TYPE = new Type<>(PiercingWeapon.class) {
@Override
@@ -61,4 +64,11 @@ public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
);
}
};
@Override
public PiercingWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return sound == this.sound && hitSound == this.hitSound ? this : new PiercingWeapon(dealsKnockback, dismounts, sound, hitSound);
}
}
@@ -89,7 +89,9 @@ public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<S
@Override
public Consumable1_21_2 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> soundHolder = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return soundHolder == this.sound ? this : new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
final ConsumeEffect<?>[] consumeEffects = ConsumeEffect.rewrite(this.consumeEffects, connection, protocol, clientbound);
return soundHolder == this.sound && consumeEffects == this.consumeEffects ? this
: new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
}
@Override
@@ -22,20 +22,23 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.EnumTypes;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
import com.viaversion.viaversion.api.type.types.misc.HolderType;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record ConsumeEffect<T>(int id, Type<T> type, T value) {
public record ConsumeEffect<T>(int id, Type<T> type, T value) implements Rewritable {
private static final HolderSetType REMOVE_EFFECTS_TYPE = new HolderSetType() {
@Override
@@ -123,6 +126,32 @@ public record ConsumeEffect<T>(int id, Type<T> type, T value) {
return new ConsumeEffect<>(id, type, (T) value);
}
@Override
public ConsumeEffect<T> rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
if (this.type != PLAY_SOUND_TYPE) {
return this;
}
//noinspection unchecked
final Holder<SoundEvent> sound = (Holder<SoundEvent>) this.value;
final Holder<SoundEvent> rewrittenSound = SoundEvent.rewriteHolder(sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return rewrittenSound == sound ? this : ConsumeEffect.of(this.id, this.type, rewrittenSound);
}
public static ConsumeEffect<?>[] rewrite(final ConsumeEffect<?>[] effects, final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
ConsumeEffect<?>[] rewrittenEffects = effects;
for (int i = 0; i < effects.length; i++) {
final ConsumeEffect<?> rewrittenEffect = effects[i].rewrite(connection, protocol, clientbound);
if (rewrittenEffect != effects[i]) {
if (rewrittenEffects == effects) {
rewrittenEffects = effects.clone();
}
rewrittenEffects[i] = rewrittenEffect;
}
}
return rewrittenEffects;
}
void writeValue(final ByteBuf buf) {
this.type.write(buf, this.value);
}
@@ -80,7 +80,7 @@ public interface Rewritable {
static Int2IntFunction soundRewriteFunction(final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
return protocol.getMappingData().getSoundMappings() == null ? Int2IntFunction.identity()
: (clientbound ? protocol.getMappingData().getSoundMappings()::getNewId : protocol.getMappingData()::getOldSoundId);
: (clientbound ? protocol.getMappingData().getSoundMappings()::getNewId : protocol.getMappingData().getSoundMappings().inverse()::getNewId);
}
static Int2IntFunction entityRewriteFunction(final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
@@ -233,6 +233,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
callOnMappingDataLoaded(getEntityRewriter());
callOnMappingDataLoaded(getItemRewriter());
callOnMappingDataLoaded(getTagRewriter());
callOnMappingDataLoaded(getBlockRewriter());
}
private void callRegister(@Nullable Rewriter<?> rewriter) {
@@ -494,9 +494,12 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
if (tag == null)
item.setTag(tag = new CompoundTag());
if (!tag.contains("EntityTag")) {
CompoundTag entityTag = new CompoundTag();
entityTag.put("id", new StringTag(eggEntityId.get()));
tag.put("EntityTag", entityTag);
tag.put("EntityTag", new CompoundTag());
}
// The id is removed from a remaining EntityTag in handleItemToClient
CompoundTag entityTag = tag.getCompoundTag("EntityTag");
if (entityTag != null && !entityTag.contains("id")) {
entityTag.putString("id", eggEntityId.get());
}
} else {
rawId = IdAndData.getId(oldId) << 16 | oldId & 0xF;
@@ -548,6 +551,10 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
if (patternsTag != null) {
for (CompoundTag pattern : patternsTag) {
NumberTag colorTag = pattern.getNumberTag("Color");
if (colorTag == null) {
continue;
}
pattern.putInt("Color", 15 - colorTag.asInt()); // Invert color id
}
}
@@ -659,6 +666,11 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
}
tag.put("CanDestroy", newCanDestroy);
}
// Damage, map and the original id are stored in a tag created in handleItemToClient
if (tag.isEmpty()) {
item.setTag(null);
}
}
return item;
}
@@ -1552,6 +1552,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
}
}
tag.remove("Items");
data.set(StructuredDataKey.V1_20_5.container, filteredItems);
addBlockEntityId(tag, "shulker_box");
}
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.LongArrayTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
@@ -272,6 +273,7 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
final StructuredDataContainer dataContainer = item.dataContainer();
updateItemDataComponentTypeIds(dataContainer, true);
backupHiddenAttributeModifiers(item);
handleRewritablesToClient(connection, dataContainer, null);
updateItemData(item);
@@ -298,6 +300,53 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
super.handleItemDataComponentsToServer(connection, item, container);
}
private void backupHiddenAttributeModifiers(final Item item) {
final AttributeModifiers1_21 attributeModifiers = item.dataContainer().get(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21);
if (attributeModifiers == null || attributeModifiers.showInTooltip()) {
return;
}
final ListTag<CompoundTag> modifiersTag = new ListTag<>(CompoundTag.class);
for (final AttributeModifiers1_21.AttributeModifier modifier : attributeModifiers.modifiers()) {
final CompoundTag modifierTag = new CompoundTag();
modifierTag.putInt("attribute", modifier.attribute());
modifierTag.putString("id", modifier.modifier().id());
modifierTag.putDouble("amount", modifier.modifier().amount());
modifierTag.putInt("operation", modifier.modifier().operation());
modifierTag.putInt("slot", modifier.slotType());
modifiersTag.add(modifierTag);
}
saveTag(createCustomTag(item), modifiersTag, "hidden_attribute_modifiers");
}
@Override
protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {
super.restoreBackupData(item, container, customData);
final ListTag<CompoundTag> modifiersTag = customData.getListTag(nbtTagName("hidden_attribute_modifiers"), CompoundTag.class);
if (modifiersTag == null) {
return;
}
removeBackupTag(customData, "hidden_attribute_modifiers");
if (container.has(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_5)) {
return;
}
final AttributeModifiers1_21.AttributeModifier[] modifiers = new AttributeModifiers1_21.AttributeModifier[modifiersTag.size()];
for (int i = 0; i < modifiers.length; i++) {
final CompoundTag modifierTag = modifiersTag.get(i);
final AttributeModifiers1_21.ModifierData modifierData = new AttributeModifiers1_21.ModifierData(
modifierTag.getString("id"),
modifierTag.getDouble("amount"),
modifierTag.getInt("operation")
);
modifiers[i] = new AttributeModifiers1_21.AttributeModifier(modifierTag.getInt("attribute"), modifierData, modifierTag.getInt("slot"));
}
// Show state is applied from tooltip_display afterwards
container.set(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_5, new AttributeModifiers1_21(modifiers, true));
}
public static void updateItemData(final Item item) {
final StructuredDataContainer dataContainer = item.dataContainer();
dataContainer.replaceKey(StructuredDataKey.TOOL1_20_5, StructuredDataKey.TOOL1_21_5);
@@ -409,7 +409,10 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
if (tag == null) {
tag = new CompoundTag();
}
CompoundTag entityTag = new CompoundTag();
CompoundTag entityTag = tag.getCompoundTag("EntityTag");
if (entityTag == null) {
entityTag = new CompoundTag();
}
String entityName = EntityIds1_8.ENTITY_ID_TO_NAME.get((int) item.data());
if (entityName != null) {
StringTag id = new StringTag(entityName);
@@ -493,7 +496,13 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
data = EntityIds1_8.ENTITY_NAME_TO_ID.get(id.getValue());
}
}
tag.remove("EntityTag");
entityTag.remove("id");
if (entityTag.isEmpty()) {
tag.remove("EntityTag");
}
}
if (tag != null && tag.isEmpty()) {
tag = null;
}
item.setTag(tag);
item.setData((short) data);
@@ -509,6 +518,9 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
}
tag.remove("Potion");
}
if (tag != null && tag.isEmpty()) {
tag = null;
}
item.setTag(tag);
item.setData((short) data);
}
@@ -525,6 +537,9 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
}
tag.remove("Potion");
}
if (tag != null && tag.isEmpty()) {
tag = null;
}
item.setTag(tag);
item.setData((short) data);
}
@@ -40,6 +40,7 @@ import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPacket26_1;
@@ -58,6 +59,7 @@ import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.ComponentRewrite
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.EntityPacketRewriter26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.RecipeDisplayRewriter26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.RegistryDataRewriter26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.storage.ProtocolStorables26_3;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
@@ -254,6 +256,11 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
super.onMappingDataLoaded();
}
@Override
public ProtocolStorablesBase createStorables() {
return new ProtocolStorables26_3();
}
@Override
public void init(final UserConnection connection) {
addEntityTracker(connection);
@@ -299,7 +299,7 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
for (final Map.Entry<String, Tag> entry : mapDecorations.entrySet()) {
final StringTag typeTag = ((CompoundTag) entry.getValue()).getStringTag("type");
if (NEW_MAP_DECORATION_TYPES.contains(Key.stripMinecraftNamespace(typeTag.getValue()))) {
typeTag.setValue("village_plains");
typeTag.setValue("target_x");
}
}
}
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion.protocols.v26_2to26_3.rewriter;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes26_3;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_3;
@@ -30,6 +31,7 @@ import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPack
import com.viaversion.viaversion.protocols.v26_2to26_3.Protocol26_2To26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundPackets26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ServerboundPackets26_3;
import com.viaversion.viaversion.protocols.v26_2to26_3.storage.ProtocolStorables26_3;
import com.viaversion.viaversion.rewriter.EntityRewriter;
public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPacket26_1, Protocol26_2To26_3> {
@@ -40,7 +42,12 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
private static final int INTERPOLATION_STEP_TICKS = 3;
private static final int MAIN_HAND = 0;
private static final int OFF_HAND = 1;
private static final int START_DESTROY_BLOCK_ACTION = 0;
private static final int CHANGE_DESTROY_DIRECTION_ACTION = 1;
private static final int ABORT_DESTROY_BLOCK_ACTION = 2;
private static final int STOP_DESTROY_BLOCK_ACTION = 3;
private static final int DESTROY_PROGRESS_PARTICLES = 2019;
private static final int DESTROY_PROGRESS_PARTICLES_AND_SOUND = 2020;
private static final SwingAnimation DEFAULT_SWING_ANIMATION = new SwingAnimation(EnumTypes.SWING_ANIMATION1_21_11.idFromName("whack"), 6);
public EntityPacketRewriter26_3(final Protocol26_2To26_3 protocol) {
@@ -139,16 +146,45 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
protocol.registerServerbound(ServerboundPackets26_3.PUNCH, ServerboundPackets26_1.SWING, wrapper -> {
wrapper.write(Types.VAR_INT, 0); // Main hand
// Sent every tick while destroying a block
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
storables.setPunched();
});
protocol.registerServerbound(ServerboundPackets26_3.CLIENT_TICK_END, wrapper -> {
// Needs to be sent by the server every tick after starting now
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
final int ticks = storables.endDestroyTick();
if (ticks <= 0) {
return;
}
final PacketWrapper levelEvent = wrapper.create(ClientboundPackets26_3.LEVEL_EVENT);
levelEvent.write(Types.INT, ticks % 4 == 0 ? DESTROY_PROGRESS_PARTICLES_AND_SOUND : DESTROY_PROGRESS_PARTICLES);
levelEvent.write(Types.BLOCK_POSITION1_14, storables.destroyPosition());
levelEvent.write(Types.INT, storables.destroyDirection());
levelEvent.write(Types.BOOLEAN, false); // Global
levelEvent.scheduleSend(Protocol26_2To26_3.class);
});
protocol.registerServerbound(ServerboundPackets26_3.PLAYER_ACTION, wrapper -> {
final int action = wrapper.read(Types.VAR_INT);
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
if (action == CHANGE_DESTROY_DIRECTION_ACTION) {
wrapper.read(Types.BLOCK_POSITION1_14); // Position
storables.setDestroyDirection(wrapper.read(Types.UNSIGNED_BYTE));
wrapper.cancel(); // 26.2 has no equivalent and never sends anything here
return;
}
wrapper.write(Types.VAR_INT, action > CHANGE_DESTROY_DIRECTION_ACTION ? action - 1 : action);
if (action == START_DESTROY_BLOCK_ACTION) {
final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);
storables.startDestroying(position, wrapper.passthrough(Types.UNSIGNED_BYTE));
} else if (action == ABORT_DESTROY_BLOCK_ACTION || action == STOP_DESTROY_BLOCK_ACTION) {
storables.stopDestroying();
}
});
protocol.registerClientbound(ClientboundPackets26_1.ANIMATE, wrapper -> {
@@ -0,0 +1,75 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v26_2to26_3.storage;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class ProtocolStorables26_3 extends ProtocolStorablesBase {
private BlockPosition destroyPosition;
private short destroyDirection;
private int destroyTicks;
private boolean punched;
public @Nullable BlockPosition destroyPosition() {
return destroyPosition;
}
public int destroyDirection() {
return destroyDirection;
}
public void setDestroyDirection(final short destroyDirection) {
this.destroyDirection = destroyDirection;
}
public void startDestroying(final BlockPosition position, final short direction) {
this.destroyPosition = position;
this.destroyDirection = direction;
this.destroyTicks = 0;
this.punched = false;
}
public void stopDestroying() {
this.destroyPosition = null;
}
public void setPunched() {
this.punched = true;
}
/**
* Returns the destroy tick that just ended, or -1 if the client did not continue destroying in it.
*
* @return destroy tick that just ended, or -1
*/
public int endDestroyTick() {
if (destroyPosition == null) {
return -1;
}
if (!punched) {
destroyPosition = null;
return -1;
}
punched = false;
return destroyTicks++;
}
}
@@ -35,6 +35,7 @@ import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.rewriter.MappingDataListener;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.MathUtil;
@@ -44,7 +45,7 @@ import java.util.List;
import java.util.function.BiConsumer;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockRewriter<C extends ClientboundPacketType> {
public class BlockRewriter<C extends ClientboundPacketType> implements MappingDataListener {
protected final Protocol<C, ?, ?, ?> protocol;
private final Type<BlockPosition> positionType;
private final Type<CompoundTag> compoundTagType;
@@ -423,11 +423,11 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
if (customName != null) {
container.set(StructuredDataKey.CUSTOM_NAME, customName);
}
}
final Tag itemName = removeBackupTag(customData, "item_name");
if (itemName != null) {
container.set(StructuredDataKey.ITEM_NAME, itemName);
}
final Tag itemName = removeBackupTag(customData, "item_name");
if (itemName != null) {
container.set(StructuredDataKey.ITEM_NAME, itemName);
}
}
@@ -29,6 +29,11 @@ import com.viaversion.viaversion.rewriter.BlockRewriter;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockRewriter1_21_5<C extends ClientboundPacketType> extends BlockRewriter<C> {
private int signId = -1;
private int hangingSignId = -1;
private int shelfId = -1;
private int campfireId = -1;
private int vaultId = -1;
public BlockRewriter1_21_5(final Protocol<C, ?, ?, ?> protocol, final ChunkTypeSupplier chunkTypeSupplier, @Nullable final ChunkTypeSupplier mappedChunkTypeSupplier) {
super(protocol, Types.BLOCK_POSITION1_14, Types.TRUSTED_COMPOUND_TAG, chunkTypeSupplier, mappedChunkTypeSupplier);
@@ -41,17 +46,46 @@ public class BlockRewriter1_21_5<C extends ClientboundPacketType> extends BlockR
@Override
public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {
final CompoundTag tag = blockEntity.tag();
if (tag == null) {
if (tag == null || protocol.getComponentRewriter() == null) {
return;
}
final FullMappings blockEntityMappings = protocol.getMappingData().getBlockEntityMappings();
if (blockEntityMappings != null && protocol.getComponentRewriter() != null) {
final int typeId = blockEntity.typeId();
if (typeId == signId || typeId == hangingSignId) {
// Update sign text components, relevant as of 1.21.5 given they are properly parsed and should not error on the client
if (blockEntity.typeId() == blockEntityMappings.mappedId("sign") || blockEntity.typeId() == blockEntityMappings.mappedId("hanging_sign")) {
updateSignMessages(connection, tag.getCompoundTag("front_text"));
updateSignMessages(connection, tag.getCompoundTag("back_text"));
updateSignMessages(connection, tag.getCompoundTag("front_text"));
updateSignMessages(connection, tag.getCompoundTag("back_text"));
} else if (typeId == shelfId || typeId == campfireId) {
// Update items that are actually visible to the client
final ListTag<CompoundTag> items = tag.getListTag("Items", CompoundTag.class);
if (items != null) {
for (final CompoundTag item : items) {
updateItem(connection, item);
}
}
} else if (typeId == vaultId) {
final CompoundTag sharedData = tag.getCompoundTag("shared_data");
if (sharedData != null) {
updateItem(connection, sharedData.getCompoundTag("display_item"));
}
}
}
@Override
public void onMappingDataLoaded() {
final FullMappings blockEntityMappings = protocol.getMappingData() != null ? protocol.getMappingData().getBlockEntityMappings() : null;
if (blockEntityMappings != null) {
signId = blockEntityMappings.mappedId("sign");
hangingSignId = blockEntityMappings.mappedId("hanging_sign");
shelfId = blockEntityMappings.mappedId("shelf");
campfireId = blockEntityMappings.mappedId("campfire");
vaultId = blockEntityMappings.mappedId("vault");
}
}
private void updateItem(final UserConnection connection, @Nullable final CompoundTag item) {
if (item != null && item.getStringTag("id") != null) {
protocol.getComponentRewriter().handleShowItem(connection, item);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=5.12.1
projectVersion=5.12.2-SNAPSHOT
# Smile emoji (note that modrinth may not have added the version on release yet)
mcVersions=26.3, 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9