Compare commits

..
Author SHA1 Message Date
Florian Reuth 4be33cbe2c Fix FilterableComponent hashing
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-29 10:34:12 +02:00
Florian Reuth e813796d9a Handle remaining components in registry data (#5077) 2026-09-28 19:31:54 +02:00
Florian Reuth 271b6e7ff7 Fix splash potion sounds not playing in 26.2->26.3 (#5076) 2026-09-28 19:30:57 +02:00
LeonTG 7bf4a30075 Fix ender dragon having the wrong dragon phase in 1.8->1.9 (#5070) 2026-09-28 08:55:09 +02:00
PolangandFlorian Reuth 414e978829 Skip removal markers in 1.20.5+ item cost type (#5073)
Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-09-28 08:45:15 +02:00
Nassim Jahnke 637a81e569 Handle dripstone particles dimension attribute registry data
Fixes #5074
2026-09-27 14:21:22 +02:00
Florian Reuth b4023127f6 Force set health back >0 in 1.19.1->1.19.3 (#5061) 2026-09-20 14:54:56 +02:00
Nassim Jahnke 46a61058dd Add missing tags across protocols, reduce stored tags size 2026-09-20 13:06:35 +02:00
Nassim Jahnke 66b90a5b2a Add missing tag for newly placed solid blocks letting through rain
Fixes #5060
2026-09-20 11:59:58 +02:00
Florian Reuth 8f03d6bcef Cancel axe stripping for pre 1.13, shovel dirth paths for pre 1.9 (#5057) 2026-09-20 09:44:19 +02:00
Florian Reuth e1809674fd Send brewing recipes for vanilla servers not syncing the inventory in 26.2->26.3 (#5056) 2026-09-19 19:50:12 +02:00
Pantera 4d8b0d1f54 Add missing 26.3 block tags for client predictions 2026-09-19 15:16:57 +02:00
Rocco d1a0c58f5f Restore 26.2 interpolation steps for 26.2->26.3 (#5052) 2026-09-19 12:55:02 +02:00
Dainsleif 724460d2e7 Skip repeated end credits on 1.21+ clients (#5055) 2026-09-19 12:37:59 +02:00
chico ferreira f2e3f53486 Send original client version in Velocity player details (#5043) 2026-09-19 12:20:59 +02:00
Nassim Jahnke 7190e365e4 Bump version to 5.12.1-SNAPSHOT 2026-09-19 12:20:06 +02:00
38 changed files with 279 additions and 58 deletions

No files matched your search

@@ -26,6 +26,8 @@ import com.viaversion.nbt.tag.ByteArrayTag;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.function.IntConsumer;
@@ -49,6 +51,12 @@ public final class IdRanges {
return ids;
}
public static IntList decodeAsList(final ByteArrayTag rangesTag) {
final IntList ids = new IntArrayList();
forEachId(rangesTag, ids::add);
return ids;
}
public static void forEachId(final ByteArrayTag rangesTag, final IntConsumer consumer) {
final ByteBuf buf = Unpooled.wrappedBuffer(rangesTag.getValue());
int prevEnd = 0;
@@ -22,6 +22,7 @@
*/
package com.viaversion.viaversion.api.data;
import com.viaversion.nbt.tag.ByteArrayTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntArrayTag;
import com.viaversion.nbt.tag.ListTag;
@@ -198,8 +199,14 @@ public class MappingDataBase implements MappingData {
final List<TagData> tagsList = new ArrayList<>(tags.size());
for (final Map.Entry<String, Tag> entry : tag.entrySet()) {
final IntArrayTag entries = (IntArrayTag) entry.getValue();
tagsList.add(new TagData(entry.getKey(), entries.getValue()));
final int[] entries;
if (entry.getValue() instanceof final ByteArrayTag rangesTag) {
entries = IdRanges.decodeAsList(rangesTag).toIntArray();
} else {
// Old format / if the values need to be sorted
entries = ((IntArrayTag) entry.getValue()).getValue();
}
tagsList.add(new TagData(entry.getKey(), entries));
}
this.tags.put(type, tagsList);
@@ -34,7 +34,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class FilterableComponent extends Filterable<Tag> implements Copyable, Rewritable {
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TEXT_COMPONENT_TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
@Override
protected FilterableComponent create(final Tag raw, final Tag filtered) {
return new FilterableComponent(raw, filtered);
@@ -34,7 +34,6 @@ import io.netty.buffer.ByteBuf;
// Very similar to normal items (and just results in an item), except it allows non-positive amounts and has id/amount swapped because ???
public final class ItemCostType1_20_5 extends Type<Item> {
private static final StructuredData<?>[] EMPTY_DATA_ARRAY = new StructuredData[0];
private final Type<StructuredData<?>[]> dataArrayType;
public ItemCostType1_20_5(final Type<StructuredData<?>[]> dataArrayType) {
@@ -54,7 +53,8 @@ public final class ItemCostType1_20_5 extends Type<Item> {
public void write(final ByteBuf buffer, final Item object) {
Types.VAR_INT.writePrimitive(buffer, object.identifier());
Types.VAR_INT.writePrimitive(buffer, object.amount());
dataArrayType.write(buffer, object.dataContainer().data().values().toArray(EMPTY_DATA_ARRAY));
// Item costs are exact-match predicates without removal markers, so empty data can't be written
dataArrayType.write(buffer, object.dataContainer().data().values().stream().filter(StructuredData::isPresent).toArray(StructuredData[]::new));
}
public static final class OptionalItemCostType extends OptionalType<Item> {
@@ -51,6 +51,7 @@ import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.ComponentRew
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.EntityPacketRewriter1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.ItemPacketRewriter1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.NonceStorage1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.PlayerHealthStorage;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.ReceivedMessagesStorage;
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPackets1_19_1;
@@ -380,6 +381,7 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
@Override
public void init(final UserConnection user) {
user.put(new ReceivedMessagesStorage());
user.put(new PlayerHealthStorage());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19_3.PLAYER));
}
@@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.type.types.version.Types1_19;
import com.viaversion.viaversion.api.type.types.version.Types1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.Protocol1_19_1To1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.PlayerHealthStorage;
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import java.util.BitSet;
@@ -75,6 +76,13 @@ public final class EntityPacketRewriter1_19_3 extends EntityRewriter<Clientbound
}
});
protocol.registerClientbound(ClientboundPackets1_19_1.SET_HEALTH, wrapper -> {
final float health = wrapper.passthrough(Types.FLOAT);
final int food = wrapper.passthrough(Types.VAR_INT);
final float saturation = wrapper.passthrough(Types.FLOAT);
wrapper.user().get(PlayerHealthStorage.class).update(health, food, saturation);
});
protocol.registerClientbound(ClientboundPackets1_19_1.RESPAWN, new PacketHandlers() {
@Override
public void register() {
@@ -94,6 +102,18 @@ public final class EntityPacketRewriter1_19_3 extends EntityRewriter<Clientbound
}
wrapper.write(Types.BYTE, keepDataMask);
});
handler(wrapper -> {
final PlayerHealthStorage healthStorage = wrapper.user().get(PlayerHealthStorage.class);
if (healthStorage.dead()) {
// The client takes over the entity data of the dead player, its health of 0 would instantly re-open the death screen
final PacketWrapper setHealth = wrapper.create(ClientboundPackets1_19_3.SET_HEALTH);
setHealth.write(Types.FLOAT, 20F); // Overwritten by the server right after the respawn
setHealth.write(Types.VAR_INT, healthStorage.food());
setHealth.write(Types.FLOAT, healthStorage.saturation());
setHealth.send(Protocol1_19_1To1_19_3.class);
healthStorage.setDead(false);
}
});
}
});
@@ -15,22 +15,35 @@
* 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;
package com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public final class LastMovement implements StorableObject {
private long lastMovementTime;
public final class PlayerHealthStorage implements StorableObject {
public LastMovement(final long lastMovementTime) {
this.lastMovementTime = lastMovementTime;
private boolean dead;
private int food;
private float saturation;
public void update(final float health, final int food, final float saturation) {
this.dead = health <= 0;
this.food = food;
this.saturation = saturation;
}
public long lastMovementTime() {
return lastMovementTime;
public boolean dead() {
return this.dead;
}
public void setLastMovementTime(final long lastMovementTime) {
this.lastMovementTime = lastMovementTime;
public void setDead(final boolean dead) {
this.dead = dead;
}
public int food() {
return this.food;
}
public float saturation() {
return this.saturation;
}
}
@@ -24,6 +24,7 @@ import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
@@ -139,6 +140,29 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
final double accelerationPower = Math.sqrt(xPower * xPower + yPower * yPower + zPower * zPower);
wrapper.write(Types.DOUBLE, accelerationPower);
});
// Servers up to 1.20.6 resend the win game event with a value of 0 to players that already saw the credits,
// telling the client to skip the end poem and to confirm the credits right away.
// 1.21+ clients ignore the value and always open the full end poem screen instead
appendClientbound(ClientboundPackets1_20_5.GAME_EVENT, wrapper -> {
wrapper.resetReader();
final short event = wrapper.passthrough(Types.UNSIGNED_BYTE);
if (event != 4) { // Win game
return;
}
final float value = wrapper.passthrough(Types.FLOAT);
if (value >= 0.5F) { // Rounded by clients, meaning the end poem is being shown for the first time
return;
}
wrapper.cancel();
// The server only teleports the player out of the end once the client confirms the credits being over
final PacketWrapper clientCommandPacket = wrapper.create(ServerboundPackets1_20_5.CLIENT_COMMAND);
clientCommandPacket.write(Types.VAR_INT, 0); // Perform respawn
clientCommandPacket.sendToServer(Protocol1_20_5To1_21.class);
});
}
public static String mapAttributeUUID(final UUID uuid, final String name) {
@@ -191,9 +215,10 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
tagRewriter.addEmptyTags(RegistryType.ENTITY, "minecraft:can_turn_in_boats", "minecraft:deflects_projectiles", "minecraft:immune_to_infested",
"minecraft:immune_to_oozing", "minecraft:no_anger_from_wind_charge");
tagRewriter.addTag(RegistryType.ENCHANTMENT, "minecraft:curse", 10, 41); // Binding and vanishing curse
tagRewriter.addTag(RegistryType.ENCHANTMENT, "minecraft:prevents_decorated_pot_shattering", 21); // Silk touch
// Add other enchantment tags empty
tagRewriter.addEmptyTags(RegistryType.ENCHANTMENT, "double_trade_price", "in_enchanting_table", "non_treasure", "on_mob_spawn_equipment", "on_random_loot",
"on_traded_equipment", "prevents_bee_spawns_when_mining", "prevents_decorated_pot_shattering", "prevents_ice_melting", "prevents_infested_spawns", "smelts_loot",
"on_traded_equipment", "prevents_bee_spawns_when_mining", "prevents_ice_melting", "prevents_infested_spawns", "smelts_loot",
"tooltip_order", "tradeable", "treasure", "exclusive_set/armor", "exclusive_set/boots", "exclusive_set/bow", "exclusive_set/crossbow", "exclusive_set/damage",
"exclusive_set/mining", "exclusive_set/riptide");
}
@@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;
@@ -191,6 +192,9 @@ public final class Protocol1_21_4To1_21_5 extends AbstractProtocol<ClientboundPa
StructuredDataKey.PAINTING_VARIANT, StructuredDataKey.LLAMA_VARIANT, StructuredDataKey.AXOLOTL_VARIANT, StructuredDataKey.CAT_VARIANT,
StructuredDataKey.CAT_COLLAR, StructuredDataKey.SHEEP_COLOR, StructuredDataKey.SHULKER_COLOR, StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5,
StructuredDataKey.BREAK_SOUND, StructuredDataKey.COW_VARIANT, StructuredDataKey.CHICKEN_VARIANT1_21_5, StructuredDataKey.WOLF_SOUND_VARIANT);
tagRewriter.renameTag(RegistryType.BLOCK, "dead_bush_may_place_on", "dry_vegetation_may_place_on");
super.onMappingDataLoaded();
}
@@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.minecraft.GameMode;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
@@ -141,6 +142,9 @@ public final class Protocol1_21_5To1_21_6 extends AbstractProtocol<ClientboundPa
StructuredDataKey.PAINTING_VARIANT, StructuredDataKey.LLAMA_VARIANT, StructuredDataKey.AXOLOTL_VARIANT, StructuredDataKey.CAT_VARIANT,
StructuredDataKey.CAT_COLLAR, StructuredDataKey.SHEEP_COLOR, StructuredDataKey.SHULKER_COLOR, StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5,
StructuredDataKey.BREAK_SOUND, StructuredDataKey.COW_VARIANT, StructuredDataKey.CHICKEN_VARIANT1_21_5, StructuredDataKey.WOLF_SOUND_VARIANT);
tagRewriter.renameTag(RegistryType.BLOCK, "plays_ambient_desert_block_sounds", "triggers_ambient_desert_dry_vegetation_block_sounds");
super.onMappingDataLoaded();
}
@@ -216,6 +216,11 @@ public class SpawnPacketRewriter1_9 {
int entityID = wrapper.get(Types.VAR_INT, 0);
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
tracker.handleEntityData(entityID, entityDataList);
// dragon default phase on 1.9+ is 10 (hovering), so we set it to match the only phase 1.8 had, which was 0 (circling)
if (tracker.entityType(entityID) == EntityTypes1_9.EntityType.ENDER_DRAGON) {
entityDataList.add(new EntityData(11, EntityDataTypes1_9.VAR_INT, 0)); // 11 = dragon phase id
}
});
}
});
@@ -21,8 +21,10 @@ import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys26_2;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys26_3;
@@ -62,7 +64,6 @@ import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.rewriter.text.NBTComponentRewriter;
import java.util.BitSet;
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
@@ -70,6 +71,10 @@ import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket26_1, ClientboundPacket26_3, ServerboundPacket26_1, ServerboundPacket26_3> {
public static final MappingData26_3 MAPPINGS = new MappingData26_3();
private static final int SPLASH_POTION_SOUND = 1053;
private static final int INSTANT_SPLASH_POTION_SOUND = 1054;
private static final int SPLASH_POTION_PARTICLES = 2002;
private static final int INSTANT_SPLASH_POTION_PARTICLES = 2007;
private static final String[] POT_PATTERNS = {
"angler",
"archer",
@@ -172,6 +177,40 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
});
// 26.3 plays the potion splash sound through its own level event, older clients played it together with the particles
appendClientbound(ClientboundPackets26_1.LEVEL_EVENT, wrapper -> {
final int id = wrapper.get(Types.INT, 0);
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_14, 0);
final int soundId;
if (id == SPLASH_POTION_PARTICLES) {
soundId = SPLASH_POTION_SOUND;
} else if (id == INSTANT_SPLASH_POTION_PARTICLES) {
soundId = INSTANT_SPLASH_POTION_SOUND;
} else {
return;
}
final PacketWrapper soundPacket = wrapper.create(ClientboundPackets26_3.LEVEL_EVENT);
soundPacket.write(Types.INT, soundId);
soundPacket.write(Types.BLOCK_POSITION1_14, position);
soundPacket.write(Types.INT, 0); // Data
soundPacket.write(Types.BOOLEAN, false); // Global
soundPacket.scheduleSend(Protocol26_2To26_3.class);
});
// 26.3 reads the items the brewing stand slots accept from the server, older clients have them hardcoded
appendClientbound(ClientboundPackets26_1.UPDATE_RECIPES, wrapper -> {
wrapper.resetReader();
final int size = wrapper.read(Types.VAR_INT);
wrapper.write(Types.VAR_INT, size + 2);
// Copies, as later protocols rewrite the ids in place
wrapper.write(Types.STRING, "minecraft:brewing_input");
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, MAPPINGS.brewingInputIds().clone());
wrapper.write(Types.STRING, "minecraft:brewing_reagent");
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, MAPPINGS.brewingReagentIds().clone());
});
}
@Override
@@ -209,6 +248,9 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
StructuredDataKey.PROVIDES_POTTERY_PATTERN, StructuredDataKey.BLOCK_TRANSFORMER, StructuredDataKey.COMPOSTABLE, StructuredDataKey.VILLAGER_FOOD,
StructuredDataKey.COOKING_FUEL, StructuredDataKey.BREWING_FUEL, StructuredDataKey.MOB_VISIBILITY, StructuredDataKey.SIGN_TEXT_BACK, StructuredDataKey.SIGN_TEXT_FRONT,
StructuredDataKey.WAXED, StructuredDataKey.CUSHION_COLOR, StructuredDataKey.ATTACK_ANIMATION, StructuredDataKey.INTERACT_ANIMATION);
tagRewriter.renameTag(RegistryType.BLOCK, "convertable_to_mud", "convertible_to_mud");
super.onMappingDataLoaded();
}
@@ -20,10 +20,44 @@ package com.viaversion.viaversion.protocols.v26_2to26_3.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
public final class MappingData26_3 extends MappingDataBase {
private static final String[] BREWING_INPUTS = {
"potion",
"splash_potion",
"lingering_potion",
"glass_bottle"
};
private static final String[] BREWING_REAGENTS = {
"blaze_powder",
"breeze_rod",
"cobweb",
"dragon_breath",
"fermented_spider_eye",
"ghast_tear",
"glistering_melon_slice",
"glowstone_dust",
"golden_carrot",
"gunpowder",
"magma_cream",
"nether_wart",
"phantom_membrane",
"pufferfish",
"rabbit_foot",
"redstone",
"slime_block",
"spider_eye",
"stone",
"sugar",
"turtle_helmet"
};
private CompoundTag blockTransformerRegistry;
private int[] brewingInputIds;
private int[] brewingReagentIds;
public MappingData26_3() {
super("26.2", "26.3");
@@ -32,9 +66,31 @@ public final class MappingData26_3 extends MappingDataBase {
@Override
protected void loadExtras(final CompoundTag data) {
blockTransformerRegistry = MappingDataLoader.INSTANCE.loadNBTFromFile("block-transformer-registry-26.3.nbt");
brewingInputIds = itemIds(BREWING_INPUTS);
brewingReagentIds = itemIds(BREWING_REAGENTS);
}
private int[] itemIds(final String[] identifiers) {
final IntList ids = new IntArrayList(identifiers.length);
for (final String identifier : identifiers) {
final int id = getFullItemMappings().mappedId(identifier);
if (id != -1) {
ids.add(id);
}
}
return ids.toIntArray();
}
public CompoundTag blockTransformerRegistry() {
return blockTransformerRegistry;
}
public int[] brewingInputIds() {
return brewingInputIds;
}
public int[] brewingReagentIds() {
return brewingReagentIds;
}
}
@@ -36,6 +36,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrim26_3;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial26_3;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
@@ -201,6 +202,10 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
@Override
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {
upgradeData(container);
// Add data components to fix issues in older protocols
appendItemDataFixComponents(connection, item, container);
super.handleItemDataComponentsToClient(connection, item, container);
}
@@ -210,6 +215,22 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
super.handleItemDataComponentsToServer(connection, item, container);
}
private void appendItemDataFixComponents(final UserConnection connection, final Item item, final StructuredDataContainer container) {
final ProtocolVersion serverVersion = connection.getProtocolInfo().serverProtocolVersion();
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_12_2)) {
// Axes only started stripping logs in 1.13
if (item.identifier() == 1043 || item.identifier() == 1033 || item.identifier() == 1053 || item.identifier() == 1058 || item.identifier() == 1048 || item.identifier() == 1038 || item.identifier() == 1028) {
container.setEmpty(StructuredDataKey.BLOCK_TRANSFORMER);
}
}
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_8)) {
// Shovels only started creating dirt paths in 1.9
if (item.identifier() == 1036 || item.identifier() == 1046 || item.identifier() == 1041 || item.identifier() == 1026 || item.identifier() == 1056 || item.identifier() == 1031 || item.identifier() == 1051) {
container.setEmpty(StructuredDataKey.BLOCK_TRANSFORMER);
}
}
}
public static void upgradeData(final StructuredDataContainer container) {
container.remove(StructuredDataKey.MAP_COLOR);
@@ -17,8 +17,6 @@
*/
package com.viaversion.viaversion.protocols.v26_2to26_3.rewriter;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
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;
@@ -32,13 +30,14 @@ 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.LastMovement;
import com.viaversion.viaversion.rewriter.EntityRewriter;
public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPacket26_1, Protocol26_2To26_3> {
private static final int SINGLE_STEP = 1 << 1;
private static final int MAX_TICK_OFFSET = 5;
// All entities that care about steps use 3 steps in 26.2. 26.3 still lerps every tick within each step, so this
// is equivalent
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 CHANGE_DESTROY_DIRECTION_ACTION = 1;
@@ -51,20 +50,20 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
@Override
public void registerPackets() {
protocol.registerClientbound(ClientboundPackets26_1.MOVE_ENTITY_POS, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
wrapper.passthrough(Types.VAR_INT); // Entity id
final short xa = wrapper.read(Types.SHORT);
final short ya = wrapper.read(Types.SHORT);
final short za = wrapper.read(Types.SHORT);
final boolean onGround = wrapper.read(Types.BOOLEAN);
wrapper.write(Types.VAR_INT, (onGround ? 1 : 0) | SINGLE_STEP); // Only the first bit set, otherwise empty = no step count
wrapper.write(Types.VAR_INT, tickOffset(wrapper.user(), entityId));
wrapper.write(Types.VAR_INT, INTERPOLATION_STEP_TICKS);
wrapper.write(Types.SHORT, xa);
wrapper.write(Types.SHORT, ya);
wrapper.write(Types.SHORT, za);
});
protocol.registerClientbound(ClientboundPackets26_1.MOVE_ENTITY_POS_ROT, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
wrapper.passthrough(Types.VAR_INT); // Entity id
final short xa = wrapper.read(Types.SHORT);
final short ya = wrapper.read(Types.SHORT);
final short za = wrapper.read(Types.SHORT);
@@ -72,7 +71,7 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
final byte xRot = wrapper.read(Types.BYTE);
final boolean onGround = wrapper.read(Types.BOOLEAN);
wrapper.write(Types.VAR_INT, (onGround ? 1 : 0) | SINGLE_STEP); // Only the first bit set, otherwise empty = no extra steps
wrapper.write(Types.VAR_INT, tickOffset(wrapper.user(), entityId));
wrapper.write(Types.VAR_INT, INTERPOLATION_STEP_TICKS);
wrapper.write(Types.SHORT, xa);
wrapper.write(Types.SHORT, ya);
wrapper.write(Types.SHORT, za);
@@ -92,10 +91,13 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
protocol.registerClientbound(ClientboundPackets26_1.ENTITY_POSITION_SYNC, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Entity ID
wrapper.write(Types.VAR_INT, 0); // Linear
wrapper.write(Types.VAR_INT, 1); // Stepped
wrapper.write(Types.VAR_INT, 1); // 1 step
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.write(Types.VAR_INT, INTERPOLATION_STEP_TICKS);
wrapper.read(Types.DOUBLE); // Delta x
wrapper.read(Types.DOUBLE); // Delta y
@@ -177,30 +179,6 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
wrapper.write(Types.OPTIONAL_VAR_INT, previousGamemode == -1 ? null : (int) previousGamemode);
}
// To make the stepped interpolation handler work properly, the offset has to match the server's actual updateInterval (e.g. 2 for players).
// An offset that's too small makes the client snap, a too large one makes it queue up steps and fall behind - this makes it match the actual rate.
// Estimate it from the time between move packets; errors cancel out across consecutive packets as the timestamps telescope.
private int tickOffset(final UserConnection connection, final int entityId) {
final TrackedEntity entity = tracker(connection).entity(entityId);
if (entity == null) {
return 1;
}
final long now = System.nanoTime();
final LastMovement timestamp = entity.get(LastMovement.class);
if (timestamp == null) {
entity.put(new LastMovement(now));
return 1;
}
final long deltaMillis = (now - timestamp.lastMovementTime()) / 1_000_000L;
timestamp.setLastMovementTime(now);
if (deltaMillis > MAX_TICK_OFFSET * 50L) {
return 1; // No recent movement. Don't stretch the first step of a new movement burst
}
return (int) Math.max(1, (deltaMillis + 25) / 50); // Round to the nearest tick count
}
@Override
protected void registerRewrites() {
final EntityDataTypes26_3 entityDataTypes = protocol.mappedTypes().entityDataTypes();
@@ -86,8 +86,10 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
switch (key) {
case "enchantment" -> updateEnchantments(connection, entries);
case "trim_material" -> updateTrimMaterials(entries);
case "jukebox_song" -> updateJukeboxSongs(entries);
case "trim_material" -> updateTrimMaterials(connection, entries);
case "jukebox_song" -> updateJukeboxSongs(connection, entries);
case "trim_pattern", "instrument" -> updateTextComponents(connection, entries, "description");
case "painting_variant" -> updateTextComponents(connection, entries, "title", "author");
case "worldgen/biome" -> updateBiomes(entries);
case "worldgen/block_state_provider" -> updateBlockStateProviders(entries);
case "dimension_type" -> updateDimensionTypes(entries);
@@ -291,6 +293,18 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
updateTextComponent(connection, tag, "tooltip");
}
protected void updateTextComponents(final UserConnection connection, final RegistryEntry[] entries, final String... keys) {
for (final RegistryEntry entry : entries) {
if (!(entry.tag() instanceof final CompoundTag tag)) {
continue;
}
for (final String key : keys) {
updateTextComponent(connection, tag, key);
}
}
}
protected void updateTextComponent(final UserConnection connection, @Nullable final CompoundTag tag, final String key) {
if (tag != null && protocol.getComponentRewriter() != null) {
protocol.getComponentRewriter().processTag(connection, tag.get(key));
@@ -333,6 +347,7 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
}
final CompoundTag tag = (CompoundTag) entry.tag();
updateTextComponent(connection, tag, "description");
if (!Mappings.isFullIdentity(protocol.getMappingData().getFullItemMappings())) {
updateItemList(tag.getListTag("supported_items", StringTag.class));
updateItemList(tag.getListTag("primary_items", StringTag.class));
@@ -365,7 +380,8 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
}
}
public void updateTrimMaterials(final RegistryEntry[] entries) {
public void updateTrimMaterials(final UserConnection connection, final RegistryEntry[] entries) {
updateTextComponents(connection, entries, "description");
if (Mappings.isFullIdentity(protocol.getMappingData().getFullItemMappings())) {
return;
}
@@ -384,7 +400,8 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
}
}
public void updateJukeboxSongs(final RegistryEntry[] entries) {
public void updateJukeboxSongs(final UserConnection connection, final RegistryEntry[] entries) {
updateTextComponents(connection, entries, "description");
// can be overridden
}
@@ -454,6 +471,14 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
}
}
}
Tag dripstoneParticle = TagUtil.getNamespacedTag(tag, "visual/default_dripstone_particle");
if (dripstoneParticle instanceof CompoundTag modifierTag && modifierTag.contains("argument")) {
dripstoneParticle = modifierTag.get("argument");
}
if (dripstoneParticle instanceof CompoundTag particle) {
handleParticleData(particle);
}
}
private void updateNestedEffect(final CompoundTag effectsTag) {
+2 -2
View File
@@ -1,10 +1,10 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=5.12.0
projectVersion=5.12.1-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
mcVersionRange=1.8-26.3
velocityVersion=3.4-3.5
velocityVersion=3.4-4.2.0
# Gradle properties
org.gradle.daemon=true
@@ -17,6 +17,8 @@
*/
package com.viaversion.viaversion.velocity.listeners;
import java.util.UUID;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
@@ -24,6 +26,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.viaversion.viaversion.VelocityPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.connection.ConnectionDetails;
@@ -32,9 +35,17 @@ public class ConnectionDetailsListener {
@Subscribe
public void onPostServerJoin(final ServerPostConnectEvent event) {
final UserConnection connection = Via.getManager().getConnectionManager().getClientConnection(event.getPlayer().getUniqueId());
if (connection != null) {
ConnectionDetails.sendConnectionDetails(connection, ConnectionDetails.PROXY_CHANNEL);
ConnectionManager manager = Via.getManager().getConnectionManager();
UUID uuid = event.getPlayer().getUniqueId();
UserConnection frontend = manager.getServerConnection(uuid);
if (frontend != null) {
ConnectionDetails.sendPlayerDetails(frontend, ConnectionDetails.PROXY_CHANNEL);
}
UserConnection backend = manager.getClientConnection(uuid);
if (backend != null) {
ConnectionDetails.sendServerDetails(backend, ConnectionDetails.PROXY_CHANNEL);
}
}