Compare commits

...
58 Commits
Author SHA1 Message Date
Nassim Jahnke 488a6b6919 5.2.0 Release 2024-12-06 13:12:43 +01:00
Nassim Jahnke 3cd01978df Implement block and entity picking 2024-12-06 13:02:22 +01:00
Nassim Jahnke 0c4dbbe0bb Warn when not using Java 21+ 2024-12-06 10:56:14 +01:00
Nassim Jahnke 1750131831 Update FUNDING.yml 2024-12-04 22:04:55 +01:00
FlorianMichael 5ac7aaccb7 Add snapshot support section to README 2024-12-04 20:42:40 +01:00
Nassim Jahnke ae60e2f28d Update template protocol 2024-12-04 15:38:24 +01:00
Nassim Jahnke 17a73dff82 Handle set_player_inventory 2024-12-04 15:23:56 +01:00
Nassim Jahnke 5e9d213082 Remove snapshot version 2024-12-02 10:12:41 +01:00
Nassim Jahnke 210b0a6f0e 1.21.4-rc3 2024-12-01 09:38:29 +01:00
Nassim Jahnke f0f87b26d7 Fix 1.21.2 trim material override transition 2024-11-30 13:55:45 +01:00
Nassim Jahnke 45a55a81c7 1.21.4-rc2 2024-11-30 13:55:45 +01:00
Nassim Jahnke b2ec9eab50 1.21.4-rc1 2024-11-30 13:55:45 +01:00
Nassim Jahnke 2f08f0d58c Set max capacity for remaining buf
This allows the returned buffer to be a cached empty buf if the original buffer was already fully read
2024-11-30 13:55:45 +01:00
Nassim Jahnke d3f92ce9b8 Don't swap teleport accept and move packets for 1.21.4 clients
They changed it again. Probably missing the same swap in 1.21.4<->1.21.2 now
2024-11-30 13:55:45 +01:00
Nassim Jahnke cfa20ba418 1.21.4-pre3 2024-11-30 13:55:44 +01:00
Nassim Jahnke de6ac47a2f 1.21.4-pre2 2024-11-30 13:55:44 +01:00
Nassim Jahnke b41733bdf6 1.21.4-pre1 2024-11-30 13:55:44 +01:00
Nassim Jahnke 8f8bc55bd0 24w46a 2024-11-30 13:55:44 +01:00
Nassim Jahnke 81d92a728d Add old custom model data to new float list 2024-11-30 13:55:44 +01:00
Nassim Jahnke 68666d2d44 24w45a 2024-11-30 13:55:44 +01:00
RK_01 e539375143 Only apply chunk loading fix for 1.21.2 clients (#14) 2024-11-30 13:55:43 +01:00
FlorianMichael 0acec719b1 Small cleanup 2024-11-30 13:55:43 +01:00
Nassim Jahnke 93e92ffc82 Handle item name changes in trim material registry 2024-11-30 13:55:43 +01:00
Nassim Jahnke 82c55b1d78 24w44a 2024-11-30 13:55:43 +01:00
Nassim Jahnke dd660a2de0 Add missing UUID to attribute in 1.20.5->1.20.4
Fixes #4293
2024-11-30 13:54:15 +01:00
nwayo 96c12ef483 Use global region scheduler in runSync methods on Folia (#4292) 2024-11-30 13:42:59 +01:00
EnZaXD 1653de70c8 Update block connections file (#4291) 2024-11-29 17:14:43 +01:00
EnZaXD b57265e64f Send full position update packet in 1.20.5->1.21 placement rotation fix (#4290) 2024-11-29 16:02:47 +01:00
lowercasebtw 9d4af84872 Fix footstep particle causing null kick (#4287) 2024-11-28 12:05:22 +01:00
Valaphee The Meerkat 40665782e7 Rewrite ingredient items in 1.20.5 recipe rewriter (#4285) 2024-11-26 17:20:06 +01:00
Valaphee The Meerkat 2d663db53f Fix 1.12 recipes containing stone as placeholder, improve empty handling in 1.20.5/1.21.2 (#4284) 2024-11-26 17:04:50 +01:00
Nassim Jahnke 896c6accfb Fix 1.13 recipe ingredient writing 2024-11-26 16:29:44 +01:00
Valaphee The Meerkat 1785677901 Fix BlockRewriter block event handling (#4282) 2024-11-26 15:53:44 +01:00
Nassim Jahnke e32205ddaf Fix 1.21 food using_converts_to handling
Fixes #4280
2024-11-25 10:14:12 +01:00
Pablo Herrera f93c64d6aa Improve 1.21 client enchantments on legacy servers (#4255)
Signed-off-by: Pablo Herrera <pabloherrerapalacio@gmail.com>
2024-11-24 11:19:05 +01:00
github-actions[bot]andgradle-update-robot b5eb568515 Update Gradle Wrapper from 8.11 to 8.11.1 (#4279)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2024-11-24 02:09:07 +01:00
EnZaXD 7897fca5be Add config option for 1.20.4->1.20.5 sword blocking fix (#4276) 2024-11-21 19:26:25 +01:00
EnZaXD 3ba86741f9 Send default entity data for items in 1.10->1.11 (#4265) 2024-11-18 15:05:25 +01:00
Nassim Jahnke 5515011644 Address Gradle deprecation warnings 2024-11-17 13:43:10 +01:00
DavyCraft648 f59e2a2de4 Fix Equippable allowed entities reading (#4267) 2024-11-17 12:21:02 +01:00
github-actions[bot]andgradle-update-robot 88c103c0a5 Update Gradle Wrapper from 8.10.2 to 8.11 (#4266)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2024-11-17 12:19:47 +01:00
Nassim Jahnke 5772ee4a93 Only send tags early for 1.20.5+ clients, track early send properly in 1.20->1.20.2 2024-11-17 11:47:33 +01:00
Nassim Jahnke c525575d37 Revert hard limit in 1.17 book size check 2024-11-11 14:54:38 +01:00
Nassim Jahnke b8a170873d Add more info to missing light data warning 2024-11-11 14:38:10 +01:00
Nassim Jahnke 04e936d487 Some more pre-sizing 2024-11-11 14:33:29 +01:00
EnZaXD 6553fc9ae9 Only print invalid custom payload channel warnings in debug mode (#4257)
We previously had people complaining about this being an error and not understanding what it means + servers can't do anything about these errors anyway.
2024-11-10 21:45:06 +01:00
EnZaXD ddbe8ef0fb Apply page network limit when converting book pages in 1.17->1.17.1 (#4256) 2024-11-10 21:44:10 +01:00
EnZaXD 8e4da81022 Move config option check for placement fix into packet handlers (#4248)
Fixes the config option breaking when using the reload command, also changes the message when using the /reload sub command.
2024-11-06 15:46:11 +01:00
Pro 1620fc35f5 Change "fix-1_21-placement-rotation" to true by default (#4245) 2024-11-06 15:32:15 +01:00
Nassim Jahnke e878d77959 Trim override materials are string to string 2024-11-06 10:42:18 +01:00
Nassim Jahnke deab6a0cac Don't allocate bufs in 1.18/1.20 chunk types
Unlike Mojang, we don't sneak a byte array of data outside of the actual packet reading, so there's no reason to not just use the original buffer.
Slicing doesn't allocate anything while also setting the reader index correctly, as also used in the previous types
2024-11-05 13:43:54 +01:00
Nassim Jahnke 77f0826ec8 Skip extra buf copying entirely if wrapper had no changes 2024-11-04 22:55:40 +01:00
Nassim Jahnke 76db43cab3 Don't copy full transformed buf during every transform
The resulting modified buf is the exact same as before
2024-11-04 22:12:39 +01:00
Nassim Jahnke f0eab71644 Pre-size most allocated byte buffers
Also avoids the default allocation size of 256 bytes for bufs that are much smaller than that
2024-11-04 21:27:09 +01:00
Nassim Jahnke e9985adafc Update mcstructs 2024-11-04 20:34:03 +01:00
Nassim Jahnke 157a1fe249 Small cleanup here and there 2024-11-03 17:12:29 +01:00
Nassim Jahnke d48d0a21db Prettify error messages 2024-11-02 23:13:56 +01:00
Nassim Jahnke 908078becb Bump version to 5.1.2-SNAPSHOT 2024-11-02 22:52:47 +01:00
114 changed files with 3054 additions and 1193 deletions

No files matched your search

+1
View File
@@ -1,2 +1,3 @@
github: kennytv
patreon: kennytv
custom: ['https://florianmichael.de/donate']
+7
View File
@@ -20,6 +20,13 @@ Supported Versions:
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
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:
- [GitHub Sponsors](https://github.com/sponsors/kennytv/sponsorships?sponsor=kennytv&tier_id=385613&preview=false) (preferred option. Use the `/verify` command on this Discord after), or alternatively
- [Patreon](https://www.patreon.com/kennytv/membership) (see the highest tier and make sure to link Patreon to your Discord account under Settings->Connections)
This also includes access to a private repository with the code, which will be pushed to the public repository after the given delay on a Minecraft update.
Releases/Dev Builds
--------
You can find official releases in the following places:
@@ -478,4 +478,12 @@ public interface ViaVersionConfig extends Config {
* @return true if enabled
*/
boolean fix1_21PlacementRotation();
/**
* If enabled, 1.20.5+ clients will have sword blocking mechanics on 1.8 servers using the consumable item component.
* Note that you won't be able to see the blocking in first person if the client is older than 1.21.4.
*
* @return true if enabled
*/
boolean swordBlockingViaConsumable();
}
@@ -95,7 +95,7 @@ public class FullMappingsBase implements FullMappings {
}
@Override
public String mappedIdentifier(final int mappedId) {
public @Nullable String mappedIdentifier(final int mappedId) {
if (mappedId < 0 || mappedId >= mappedIdToString.length) {
return null;
}
@@ -37,6 +37,13 @@ public class BlockPosition {
return new BlockPosition(x + face.modX(), y + face.modY(), z + face.modZ());
}
public double distanceFromCenterSquared(final double x, final double y, final double z) {
final double dx = this.x + 0.5 - x;
final double dy = this.y + 0.5 - y;
final double dz = this.z + 0.5 - z;
return dx * dx + dy * dy + dz * dz;
}
public int x() {
return x;
}
@@ -66,6 +66,17 @@ public final class ChunkPosition {
return (long) chunkX & 0xffffffffL | ((long) chunkZ & 0xffffffffL) << 32;
}
/**
* Returns a long key for the given block coordinates.
*
* @param x the block X coordinate
* @param z the block Z coordinate
* @return the chunk key
*/
public static long chunkKeyForBlock(final int x, final int z) {
return chunkKey(x >> 4, z >> 4);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
@@ -36,6 +36,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.BannerPatternLayer;
import com.viaversion.viaversion.api.minecraft.item.data.Bee;
import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties;
import com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2;
import com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant;
import com.viaversion.viaversion.api.minecraft.item.data.DeathProtection;
import com.viaversion.viaversion.api.minecraft.item.data.DyedColor;
@@ -59,9 +60,11 @@ import com.viaversion.viaversion.api.minecraft.item.data.UseCooldown;
import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook;
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.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.util.Unit;
public record StructuredDataKey<T>(String identifier, Type<T> type) {
@@ -74,14 +77,15 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Tag> CUSTOM_NAME = new StructuredDataKey<>("custom_name", Types.TAG);
public static final StructuredDataKey<Tag> ITEM_NAME = new StructuredDataKey<>("item_name", Types.TAG);
public static final StructuredDataKey<String> ITEM_MODEL = new StructuredDataKey<>("item_model", Types.STRING);
public static final StructuredDataKey<Tag[]> LORE = new StructuredDataKey<>("lore", Types.TAG_ARRAY);
public static final StructuredDataKey<Tag[]> LORE = new StructuredDataKey<>("lore", new ArrayType<>(Types.TAG, 256));
public static final StructuredDataKey<Integer> RARITY = new StructuredDataKey<>("rarity", Types.VAR_INT);
public static final StructuredDataKey<Enchantments> ENCHANTMENTS = new StructuredDataKey<>("enchantments", Enchantments.TYPE);
public static final StructuredDataKey<AdventureModePredicate> CAN_PLACE_ON = new StructuredDataKey<>("can_place_on", AdventureModePredicate.TYPE);
public static final StructuredDataKey<AdventureModePredicate> CAN_BREAK = new StructuredDataKey<>("can_break", AdventureModePredicate.TYPE);
public static final StructuredDataKey<AttributeModifiers1_20_5> ATTRIBUTE_MODIFIERS1_20_5 = new StructuredDataKey<>("attribute_modifiers", AttributeModifiers1_20_5.TYPE);
public static final StructuredDataKey<AttributeModifiers1_21> ATTRIBUTE_MODIFIERS1_21 = new StructuredDataKey<>("attribute_modifiers", AttributeModifiers1_21.TYPE);
public static final StructuredDataKey<Integer> CUSTOM_MODEL_DATA = new StructuredDataKey<>("custom_model_data", Types.VAR_INT);
public static final StructuredDataKey<Integer> CUSTOM_MODEL_DATA1_20_5 = new StructuredDataKey<>("custom_model_data", Types.VAR_INT);
public static final StructuredDataKey<CustomModelData1_21_4> CUSTOM_MODEL_DATA1_21_4 = new StructuredDataKey<>("custom_model_data", CustomModelData1_21_4.TYPE);
public static final StructuredDataKey<Unit> HIDE_ADDITIONAL_TOOLTIP = new StructuredDataKey<>("hide_additional_tooltip", Types.EMPTY);
public static final StructuredDataKey<Unit> HIDE_TOOLTIP = new StructuredDataKey<>("hide_tooltip", Types.EMPTY);
public static final StructuredDataKey<Integer> REPAIR_COST = new StructuredDataKey<>("repair_cost", Types.VAR_INT);
@@ -92,7 +96,8 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<FoodProperties1_20_5> FOOD1_21 = new StructuredDataKey<>("food", FoodProperties1_20_5.TYPE1_21);
public static final StructuredDataKey<FoodProperties1_21_2> FOOD1_21_2 = new StructuredDataKey<>("food", FoodProperties1_21_2.TYPE);
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE1_21_2 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE);
public static final StructuredDataKey<Item> USE_REMAINDER = new StructuredDataKey<>("use_remainder", Types1_21_2.ITEM);
public static final StructuredDataKey<Item> USE_REMAINDER1_21_2 = new StructuredDataKey<>("use_remainder", Types1_21_2.ITEM);
public static final StructuredDataKey<Item> USE_REMAINDER1_21_4 = new StructuredDataKey<>("use_remainder", Types1_21_4.ITEM);
public static final StructuredDataKey<UseCooldown> USE_COOLDOWN = new StructuredDataKey<>("use_cooldown", UseCooldown.TYPE);
public static final StructuredDataKey<Unit> FIRE_RESISTANT = new StructuredDataKey<>("fire_resistant", Types.EMPTY);
public static final StructuredDataKey<DamageResistant> DAMAGE_RESISTANT = new StructuredDataKey<>("damage_resistant", DamageResistant.TYPE);
@@ -112,15 +117,19 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Item[]> CHARGED_PROJECTILES1_20_5 = new StructuredDataKey<>("charged_projectiles", Types1_20_5.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CHARGED_PROJECTILES1_21 = new StructuredDataKey<>("charged_projectiles", Types1_21.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CHARGED_PROJECTILES1_21_2 = new StructuredDataKey<>("charged_projectiles", Types1_21_2.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CHARGED_PROJECTILES1_21_4 = new StructuredDataKey<>("charged_projectiles", Types1_21_4.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> BUNDLE_CONTENTS1_20_5 = new StructuredDataKey<>("bundle_contents", Types1_20_5.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> BUNDLE_CONTENTS1_21 = new StructuredDataKey<>("bundle_contents", Types1_21.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> BUNDLE_CONTENTS1_21_2 = new StructuredDataKey<>("bundle_contents", Types1_21_2.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> BUNDLE_CONTENTS1_21_4 = new StructuredDataKey<>("bundle_contents", Types1_21_4.ITEM_ARRAY);
public static final StructuredDataKey<PotionContents> POTION_CONTENTS1_20_5 = new StructuredDataKey<>("potion_contents", PotionContents.TYPE1_20_5);
public static final StructuredDataKey<PotionContents> POTION_CONTENTS1_21_2 = new StructuredDataKey<>("potion_contents", PotionContents.TYPE1_21_2);
public static final StructuredDataKey<SuspiciousStewEffect[]> SUSPICIOUS_STEW_EFFECTS = new StructuredDataKey<>("suspicious_stew_effects", SuspiciousStewEffect.ARRAY_TYPE);
public static final StructuredDataKey<FilterableString[]> WRITABLE_BOOK_CONTENT = new StructuredDataKey<>("writable_book_content", FilterableString.ARRAY_TYPE);
public static final StructuredDataKey<WrittenBook> WRITTEN_BOOK_CONTENT = new StructuredDataKey<>("written_book_content", WrittenBook.TYPE);
public static final StructuredDataKey<ArmorTrim> TRIM = new StructuredDataKey<>("trim", ArmorTrim.TYPE);
public static final StructuredDataKey<ArmorTrim> TRIM1_20_5 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_20_5);
public static final StructuredDataKey<ArmorTrim> TRIM1_21_2 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_21_2);
public static final StructuredDataKey<ArmorTrim> TRIM1_21_4 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_21_4);
public static final StructuredDataKey<CompoundTag> DEBUG_STICK_STATE = new StructuredDataKey<>("debug_stick_state", Types.COMPOUND_TAG);
public static final StructuredDataKey<CompoundTag> ENTITY_DATA = new StructuredDataKey<>("entity_data", Types.COMPOUND_TAG);
public static final StructuredDataKey<CompoundTag> BUCKET_ENTITY_DATA = new StructuredDataKey<>("bucket_entity_data", Types.COMPOUND_TAG);
@@ -140,7 +149,8 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<PotDecorations> POT_DECORATIONS = new StructuredDataKey<>("pot_decorations", PotDecorations.TYPE);
public static final StructuredDataKey<Item[]> CONTAINER1_20_5 = new StructuredDataKey<>("container", Types1_20_5.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CONTAINER1_21 = new StructuredDataKey<>("container", Types1_21.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CONTAINER1_21_2 = new StructuredDataKey<>("container", Types1_21_2.ITEM_ARRAY);
public static final StructuredDataKey<Item[]> CONTAINER1_21_2 = new StructuredDataKey<>("container", new ArrayType<>(Types1_21_2.ITEM, 256));
public static final StructuredDataKey<Item[]> CONTAINER1_21_4 = new StructuredDataKey<>("container", Types1_21_4.ITEM_ARRAY);
public static final StructuredDataKey<BlockStateProperties> BLOCK_STATE = new StructuredDataKey<>("block_state", BlockStateProperties.TYPE);
public static final StructuredDataKey<Bee[]> BEES = new StructuredDataKey<>("bees", Bee.ARRAY_TYPE);
public static final StructuredDataKey<Tag> LOCK = new StructuredDataKey<>("lock", Types.TAG);
@@ -0,0 +1,313 @@
/*
* 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.entities;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.util.EntityTypeUtil;
import com.viaversion.viaversion.util.Key;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.Nullable;
public enum EntityTypes1_21_4 implements EntityType {
ENTITY(null, null),
AREA_EFFECT_CLOUD(ENTITY),
END_CRYSTAL(ENTITY),
EVOKER_FANGS(ENTITY),
EXPERIENCE_ORB(ENTITY),
EYE_OF_ENDER(ENTITY),
FALLING_BLOCK(ENTITY),
ITEM(ENTITY),
TNT(ENTITY),
OMINOUS_ITEM_SPAWNER(ENTITY),
MARKER(ENTITY),
LIGHTNING_BOLT(ENTITY),
INTERACTION(ENTITY),
DISPLAY(ENTITY, null),
BLOCK_DISPLAY(DISPLAY),
ITEM_DISPLAY(DISPLAY),
TEXT_DISPLAY(DISPLAY),
// Hanging entities
HANGING_ENTITY(ENTITY, null),
LEASH_KNOT(HANGING_ENTITY),
PAINTING(HANGING_ENTITY),
ITEM_FRAME(HANGING_ENTITY),
GLOW_ITEM_FRAME(ITEM_FRAME),
// Projectiles
PROJECTILE(ENTITY, null),
ITEM_PROJECTILE(PROJECTILE, null),
SNOWBALL(ITEM_PROJECTILE),
ENDER_PEARL(ITEM_PROJECTILE),
EGG(ITEM_PROJECTILE),
POTION(ITEM_PROJECTILE),
EXPERIENCE_BOTTLE(ITEM_PROJECTILE),
FIREWORK_ROCKET(PROJECTILE),
LLAMA_SPIT(PROJECTILE),
SHULKER_BULLET(PROJECTILE),
FISHING_BOBBER(PROJECTILE),
WITHER_SKULL(PROJECTILE),
DRAGON_FIREBALL(PROJECTILE), // Doesn't actually inherit fireball
ABSTRACT_ARROW(PROJECTILE, null),
ARROW(ABSTRACT_ARROW),
SPECTRAL_ARROW(ABSTRACT_ARROW),
TRIDENT(ABSTRACT_ARROW),
ABSTRACT_FIREBALL(ENTITY, null),
FIREBALL(ABSTRACT_FIREBALL),
SMALL_FIREBALL(ABSTRACT_FIREBALL),
ABSTRACT_WIND_CHARGE(PROJECTILE, null),
WIND_CHARGE(ABSTRACT_WIND_CHARGE),
BREEZE_WIND_CHARGE(ABSTRACT_WIND_CHARGE),
// Vehicles
VEHICLE(ENTITY, null),
ABSTRACT_MINECART(VEHICLE, null),
MINECART(ABSTRACT_MINECART),
FURNACE_MINECART(ABSTRACT_MINECART),
COMMAND_BLOCK_MINECART(ABSTRACT_MINECART),
TNT_MINECART(ABSTRACT_MINECART),
SPAWNER_MINECART(ABSTRACT_MINECART),
ABSTRACT_MINECART_CONTAINER(ABSTRACT_MINECART, null),
CHEST_MINECART(ABSTRACT_MINECART_CONTAINER),
HOPPER_MINECART(ABSTRACT_MINECART_CONTAINER),
ABSTRACT_BOAT(VEHICLE, null),
ABSTRACT_CHEST_BOAT(ABSTRACT_BOAT, null),
ACACIA_BOAT(ABSTRACT_BOAT),
ACACIA_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
BAMBOO_CHEST_RAFT(ABSTRACT_CHEST_BOAT),
BAMBOO_RAFT(ABSTRACT_BOAT),
BIRCH_BOAT(ABSTRACT_BOAT),
BIRCH_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
CHERRY_BOAT(ABSTRACT_BOAT),
CHERRY_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
DARK_OAK_BOAT(ABSTRACT_BOAT),
DARK_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
JUNGLE_BOAT(ABSTRACT_BOAT),
JUNGLE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
MANGROVE_BOAT(ABSTRACT_BOAT),
MANGROVE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
OAK_BOAT(ABSTRACT_BOAT),
OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
PALE_OAK_BOAT(ABSTRACT_BOAT),
PALE_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
SPRUCE_BOAT(ABSTRACT_BOAT),
SPRUCE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
// Living entities as a larger subclass
LIVING_ENTITY(ENTITY, null),
ARMOR_STAND(LIVING_ENTITY),
PLAYER(LIVING_ENTITY),
// Mobs as a larger subclass
MOB(LIVING_ENTITY, null),
ENDER_DRAGON(MOB),
SLIME(MOB),
MAGMA_CUBE(SLIME),
// Ambient mobs
AMBIENT_CREATURE(MOB, null),
BAT(AMBIENT_CREATURE),
// Flying mobs
FLYING_MOB(MOB, null),
GHAST(FLYING_MOB),
PHANTOM(FLYING_MOB),
// Pathfinder mobs and its subclasses
PATHFINDER_MOB(MOB, null),
ALLAY(PATHFINDER_MOB),
ABSTRACT_GOLEM(PATHFINDER_MOB, null),
SNOW_GOLEM(ABSTRACT_GOLEM),
IRON_GOLEM(ABSTRACT_GOLEM),
SHULKER(ABSTRACT_GOLEM),
// Ageable mobs and (tamable) animals
ABSTRACT_AGEABLE(PATHFINDER_MOB, null),
ABSTRACT_VILLAGER(ABSTRACT_AGEABLE, null),
VILLAGER(ABSTRACT_VILLAGER),
WANDERING_TRADER(ABSTRACT_VILLAGER),
ABSTRACT_ANIMAL(ABSTRACT_AGEABLE, null),
AXOLOTL(ABSTRACT_ANIMAL),
CHICKEN(ABSTRACT_ANIMAL),
PANDA(ABSTRACT_ANIMAL),
PIG(ABSTRACT_ANIMAL),
POLAR_BEAR(ABSTRACT_ANIMAL),
RABBIT(ABSTRACT_ANIMAL),
SHEEP(ABSTRACT_ANIMAL),
BEE(ABSTRACT_ANIMAL),
TURTLE(ABSTRACT_ANIMAL),
FOX(ABSTRACT_ANIMAL),
FROG(ABSTRACT_ANIMAL),
GOAT(ABSTRACT_ANIMAL),
HOGLIN(ABSTRACT_ANIMAL),
STRIDER(ABSTRACT_ANIMAL),
SNIFFER(ABSTRACT_ANIMAL),
ARMADILLO(ABSTRACT_ANIMAL),
COW(ABSTRACT_ANIMAL),
MOOSHROOM(COW),
TAMABLE_ANIMAL(ABSTRACT_ANIMAL, null),
CAT(TAMABLE_ANIMAL),
OCELOT(TAMABLE_ANIMAL),
WOLF(TAMABLE_ANIMAL),
PARROT(TAMABLE_ANIMAL),
ABSTRACT_HORSE(ABSTRACT_ANIMAL, null),
HORSE(ABSTRACT_HORSE),
SKELETON_HORSE(ABSTRACT_HORSE),
ZOMBIE_HORSE(ABSTRACT_HORSE),
CAMEL(ABSTRACT_HORSE),
ABSTRACT_CHESTED_HORSE(ABSTRACT_HORSE, null),
DONKEY(ABSTRACT_CHESTED_HORSE),
MULE(ABSTRACT_CHESTED_HORSE),
LLAMA(ABSTRACT_CHESTED_HORSE),
TRADER_LLAMA(LLAMA),
// Monsters
ABSTRACT_MONSTER(PATHFINDER_MOB, null),
BLAZE(ABSTRACT_MONSTER),
CREEPER(ABSTRACT_MONSTER),
ENDERMITE(ABSTRACT_MONSTER),
ENDERMAN(ABSTRACT_MONSTER),
GIANT(ABSTRACT_MONSTER),
SILVERFISH(ABSTRACT_MONSTER),
VEX(ABSTRACT_MONSTER),
WITHER(ABSTRACT_MONSTER),
BREEZE(ABSTRACT_MONSTER),
ZOGLIN(ABSTRACT_MONSTER),
WARDEN(ABSTRACT_MONSTER),
CREAKING(ABSTRACT_MONSTER),
ABSTRACT_SKELETON(ABSTRACT_MONSTER, null),
SKELETON(ABSTRACT_SKELETON),
STRAY(ABSTRACT_SKELETON),
WITHER_SKELETON(ABSTRACT_SKELETON),
BOGGED(ABSTRACT_SKELETON),
ZOMBIE(ABSTRACT_MONSTER),
DROWNED(ZOMBIE),
HUSK(ZOMBIE),
ZOMBIFIED_PIGLIN(ZOMBIE),
ZOMBIE_VILLAGER(ZOMBIE),
GUARDIAN(ABSTRACT_MONSTER),
ELDER_GUARDIAN(GUARDIAN),
SPIDER(ABSTRACT_MONSTER),
CAVE_SPIDER(SPIDER),
ABSTRACT_PIGLIN(ABSTRACT_MONSTER, null),
PIGLIN(ABSTRACT_PIGLIN),
PIGLIN_BRUTE(ABSTRACT_PIGLIN),
// Water mobs
AGEABLE_WATER_CREATURE(ABSTRACT_AGEABLE, null),
DOLPHIN(AGEABLE_WATER_CREATURE),
SQUID(AGEABLE_WATER_CREATURE),
GLOW_SQUID(SQUID),
WATER_ANIMAL(PATHFINDER_MOB, null),
ABSTRACT_FISH(WATER_ANIMAL, null),
PUFFERFISH(ABSTRACT_FISH),
TADPOLE(ABSTRACT_FISH),
ABSTRACT_SCHOOLING_FISH(ABSTRACT_FISH, null),
COD(ABSTRACT_SCHOOLING_FISH),
SALMON(ABSTRACT_SCHOOLING_FISH),
TROPICAL_FISH(ABSTRACT_SCHOOLING_FISH),
// Raiders
ABSTRACT_RAIDER(ABSTRACT_MONSTER, null),
WITCH(ABSTRACT_RAIDER),
RAVAGER(ABSTRACT_RAIDER),
ABSTRACT_ILLAGER(ABSTRACT_RAIDER, null),
SPELLCASTER_ILLAGER(ABSTRACT_ILLAGER, null),
VINDICATOR(ABSTRACT_ILLAGER),
PILLAGER(ABSTRACT_ILLAGER),
EVOKER(SPELLCASTER_ILLAGER),
ILLUSIONER(SPELLCASTER_ILLAGER);
private static final EntityType[] TYPES = EntityTypeUtil.createSizedArray(values());
private final EntityType parent;
private final String identifier;
private int id = -1;
EntityTypes1_21_4(final EntityType parent) {
this.parent = parent;
this.identifier = Key.namespaced(name().toLowerCase(Locale.ROOT));
}
EntityTypes1_21_4(final EntityType parent, @Nullable final String identifier) {
this.parent = parent;
this.identifier = identifier;
}
@Override
public int getId() {
if (id == -1) {
throw new IllegalStateException("Ids have not been initialized yet (type " + name() + ")");
}
return id;
}
@Override
public String identifier() {
Preconditions.checkArgument(identifier != null, "Called identifier method on abstract type");
return identifier;
}
@Override
public @Nullable EntityType getParent() {
return parent;
}
@Override
public boolean isAbstractType() {
return identifier == null;
}
public static EntityType getTypeFromId(final int typeId) {
return EntityTypeUtil.getTypeFromId(TYPES, typeId, ENTITY);
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -29,10 +29,10 @@ import it.unimi.dsi.fastutil.ints.Int2IntFunction;
public record ArmorTrim(Holder<ArmorTrimMaterial> material, Holder<ArmorTrimPattern> pattern, boolean showInTooltip) {
public static final Type<ArmorTrim> TYPE = new Type<>(ArmorTrim.class) {
public static final Type<ArmorTrim> TYPE1_20_5 = new Type<>(ArmorTrim.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE.read(buffer);
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_20_5.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
@@ -40,7 +40,39 @@ public record ArmorTrim(Holder<ArmorTrimMaterial> material, Holder<ArmorTrimPatt
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE.write(buffer, value.material);
ArmorTrimMaterial.TYPE1_20_5.write(buffer, value.material);
ArmorTrimPattern.TYPE.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
};
public static final Type<ArmorTrim> TYPE1_21_2 = new Type<>(ArmorTrim.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_21_2.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_21_2.write(buffer, value.material);
ArmorTrimPattern.TYPE.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
};
public static final Type<ArmorTrim> TYPE1_21_4 = new Type<>(ArmorTrim.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_21_4.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_21_4.write(buffer, value.material);
ArmorTrimPattern.TYPE.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
@@ -27,13 +27,19 @@ 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;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import java.util.Map;
public record ArmorTrimMaterial(String assetName, int itemId, float itemModelIndex,
Int2ObjectMap<String> overrideArmorMaterials, Tag description) {
Map<String, String> overrideArmorMaterials, Tag description) {
public static final HolderType<ArmorTrimMaterial> TYPE = new HolderType<>() {
public ArmorTrimMaterial(final String assetName, final int itemId, final Map<String, String> overrideArmorMaterials, final Tag description) {
this(assetName, itemId, 0F, overrideArmorMaterials, description);
}
public static final HolderType<ArmorTrimMaterial> TYPE1_20_5 = new HolderType<>() {
// The override key is an int, but given we don't use it at all and that creating a new type is annoying,
// we'll just store it in the string map:tm:
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
@@ -41,10 +47,45 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
final float itemModelIndex = buffer.readFloat();
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Int2ObjectMap<String> overrideArmorMaterials = new Int2ObjectOpenHashMap<>(overrideArmorMaterialsSize);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>(overrideArmorMaterialsSize);
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final int key = Types.VAR_INT.readPrimitive(buffer);
final String value = Types.STRING.read(buffer);
overrideArmorMaterials.put(Integer.toString(key), value);
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, item, itemModelIndex, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.itemId());
buffer.writeFloat(value.itemModelIndex());
Types.VAR_INT.writePrimitive(buffer, value.overrideArmorMaterials().size());
for (final Map.Entry<String, String> entry : value.overrideArmorMaterials().entrySet()) {
Types.VAR_INT.writePrimitive(buffer, Integer.parseInt(entry.getKey()));
Types.STRING.write(buffer, entry.getValue());
}
Types.TAG.write(buffer, value.description());
}
};
public static final HolderType<ArmorTrimMaterial> TYPE1_21_2 = new HolderType<>() {
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int item = Types.VAR_INT.readPrimitive(buffer);
final float itemModelIndex = buffer.readFloat();
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>(overrideArmorMaterialsSize);
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final String key = Types.STRING.read(buffer);
final String value = Types.STRING.read(buffer);
overrideArmorMaterials.put(key, value);
}
@@ -59,8 +100,41 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
buffer.writeFloat(value.itemModelIndex());
Types.VAR_INT.writePrimitive(buffer, value.overrideArmorMaterials().size());
for (final Int2ObjectMap.Entry<String> entry : value.overrideArmorMaterials().int2ObjectEntrySet()) {
Types.VAR_INT.writePrimitive(buffer, entry.getIntKey());
for (final Map.Entry<String, String> entry : value.overrideArmorMaterials().entrySet()) {
Types.STRING.write(buffer, entry.getKey());
Types.STRING.write(buffer, entry.getValue());
}
Types.TAG.write(buffer, value.description());
}
};
public static final HolderType<ArmorTrimMaterial> TYPE1_21_4 = new HolderType<>() {
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int item = Types.VAR_INT.readPrimitive(buffer);
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>(overrideArmorMaterialsSize);
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final String key = Types.STRING.read(buffer);
final String value = Types.STRING.read(buffer);
overrideArmorMaterials.put(key, value);
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, item, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.itemId());
Types.VAR_INT.writePrimitive(buffer, value.overrideArmorMaterials().size());
for (final Map.Entry<String, String> entry : value.overrideArmorMaterials().entrySet()) {
Types.STRING.write(buffer, entry.getKey());
Types.STRING.write(buffer, entry.getValue());
}
@@ -0,0 +1,49 @@
/*
* 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;
public record CustomModelData1_21_4(float[] floats, boolean[] booleans, String[] strings, int[] colors) {
public static final Type<CustomModelData1_21_4> TYPE = new Type<>(CustomModelData1_21_4.class) {
@Override
public CustomModelData1_21_4 read(final ByteBuf buffer) {
final float[] floats = Types.FLOAT_ARRAY_PRIMITIVE.read(buffer);
final boolean[] booleans = Types.BOOLEAN_ARRAY_PRIMITIVE.read(buffer);
final String[] strings = Types.STRING_ARRAY.read(buffer);
final int[] colors = Types.INT_ARRAY_PRIMITIVE.read(buffer);
return new CustomModelData1_21_4(floats, booleans, strings, colors);
}
@Override
public void write(final ByteBuf buffer, final CustomModelData1_21_4 value) {
Types.FLOAT_ARRAY_PRIMITIVE.write(buffer, value.floats());
Types.BOOLEAN_ARRAY_PRIMITIVE.write(buffer, value.booleans());
Types.STRING_ARRAY.write(buffer, value.strings());
Types.INT_ARRAY_PRIMITIVE.write(buffer, value.colors());
}
};
}
@@ -42,7 +42,7 @@ public record Equippable(int equipmentSlot, Holder<SoundEvent> soundEvent, @Null
final Holder<SoundEvent> soundEvent = Types.SOUND_EVENT.read(buffer);
final String model = Types.OPTIONAL_STRING.read(buffer);
final String cameraOverlay = Types.OPTIONAL_STRING.read(buffer);
final HolderSet allowedEntities = Types.HOLDER_SET.read(buffer);
final HolderSet allowedEntities = Types.OPTIONAL_HOLDER_SET.read(buffer);
final boolean dispensable = buffer.readBoolean();
final boolean swappable = buffer.readBoolean();
final boolean damageOnHurt = buffer.readBoolean();
@@ -55,7 +55,7 @@ public record Equippable(int equipmentSlot, Holder<SoundEvent> soundEvent, @Null
Types.SOUND_EVENT.write(buffer, value.soundEvent());
Types.OPTIONAL_STRING.write(buffer, value.model());
Types.OPTIONAL_STRING.write(buffer, value.cameraOverlay());
Types.HOLDER_SET.write(buffer, value.allowedEntities());
Types.OPTIONAL_HOLDER_SET.write(buffer, value.allowedEntities());
buffer.writeBoolean(value.dispensable());
buffer.writeBoolean(value.swappable());
buffer.writeBoolean(value.damageOnHurt());
@@ -28,9 +28,10 @@ import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record FoodProperties1_20_5(int nutrition, float saturationModifier, boolean canAlwaysEat, float eatSeconds,
Item usingConvertsTo, FoodEffect[] possibleEffects) {
@Nullable Item usingConvertsTo, FoodEffect[] possibleEffects) {
public static final Type<FoodProperties1_20_5> TYPE1_20_5 = new Type<>(FoodProperties1_20_5.class) {
@Override
@@ -87,6 +87,7 @@ public class ProtocolVersion implements Comparable<ProtocolVersion> {
public static final ProtocolVersion v1_20_5 = register(766, "1.20.5-1.20.6", new SubVersionRange("1.20", 5, 6));
public static final ProtocolVersion v1_21 = register(767, "1.21-1.21.1", new SubVersionRange("1.21", 0, 1));
public static final ProtocolVersion v1_21_2 = register(768, "1.21.2-1.21.3", new SubVersionRange("1.21", 2, 3));
public static final ProtocolVersion v1_21_4 = register(769, "1.21.4");
public static final ProtocolVersion unknown = new ProtocolVersion(VersionType.SPECIAL, -1, -1, "UNKNOWN", null);
static {
@@ -45,12 +45,14 @@ import com.viaversion.viaversion.api.minecraft.item.data.Enchantments;
import com.viaversion.viaversion.api.minecraft.ChunkPosition;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.BitSetType;
import com.viaversion.viaversion.api.type.types.BooleanArrayType;
import com.viaversion.viaversion.api.type.types.BooleanType;
import com.viaversion.viaversion.api.type.types.ByteArrayType;
import com.viaversion.viaversion.api.type.types.ByteType;
import com.viaversion.viaversion.api.type.types.ComponentType;
import com.viaversion.viaversion.api.type.types.DoubleType;
import com.viaversion.viaversion.api.type.types.EmptyType;
import com.viaversion.viaversion.api.type.types.FloatArrayType;
import com.viaversion.viaversion.api.type.types.FloatType;
import com.viaversion.viaversion.api.type.types.IntArrayType;
import com.viaversion.viaversion.api.type.types.IntType;
@@ -115,14 +117,17 @@ public final class Types {
public static final Type<byte[]> OPTIONAL_BYTE_ARRAY_PRIMITIVE = new ByteArrayType.OptionalByteArrayType();
public static final Type<byte[]> SHORT_BYTE_ARRAY = new ShortByteArrayType();
public static final Type<byte[]> REMAINING_BYTES = new RemainingBytesType();
public static final Type<int[]> INT_ARRAY_PRIMITIVE = new IntArrayType();
public static final ShortType SHORT = new ShortType();
public static final UnsignedShortType UNSIGNED_SHORT = new UnsignedShortType();
public static final IntType INT = new IntType();
public static final Type<int[]> INT_ARRAY_PRIMITIVE = new IntArrayType();
public static final FloatType FLOAT = new FloatType();
public static final FloatType.OptionalFloatType OPTIONAL_FLOAT = new FloatType.OptionalFloatType();
public static final Type<float[]> FLOAT_ARRAY_PRIMITIVE = new FloatArrayType();
public static final DoubleType DOUBLE = new DoubleType();
public static final LongType LONG = new LongType();
@@ -130,6 +135,7 @@ public final class Types {
public static final BooleanType BOOLEAN = new BooleanType();
public static final BooleanType.OptionalBooleanType OPTIONAL_BOOLEAN = new BooleanType.OptionalBooleanType();
public static final Type<boolean[]> BOOLEAN_ARRAY_PRIMITIVE = new BooleanArrayType();
/* Other Types */
public static final Type<JsonElement> COMPONENT = new ComponentType();
@@ -149,6 +155,8 @@ public final class Types {
public static final VarLongType VAR_LONG = new VarLongType();
/* MC Types */
public static final Type<byte[]> SERVERBOUND_CUSTOM_PAYLOAD_DATA = new RemainingBytesType(Short.MAX_VALUE);
public static final Type<BlockPosition> BLOCK_POSITION1_8 = new BlockPositionType1_8();
public static final Type<BlockPosition> OPTIONAL_POSITION1_8 = new BlockPositionType1_8.OptionalBlockPositionType();
public static final Type<BlockPosition> BLOCK_POSITION1_14 = new BlockPositionType1_14();
@@ -191,6 +199,7 @@ public final class Types {
public static final BitSetType PROFILE_ACTIONS_ENUM1_19_3 = new BitSetType(6);
public static final BitSetType PROFILE_ACTIONS_ENUM1_21_2 = new BitSetType(7);
public static final BitSetType PROFILE_ACTIONS_ENUM1_21_4 = new BitSetType(8);
public static final ByteArrayType SIGNATURE_BYTES = new ByteArrayType(256);
public static final BitSetType ACKNOWLEDGED_BIT_SET = new BitSetType(20);
@@ -26,13 +26,22 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
public class ArrayType<T> extends Type<T[]> {
private final Type<T> elementType;
private final int maxLength;
public ArrayType(Type<T> type) {
this(type, -1);
}
public ArrayType(Type<T> type, int maxLength) {
//noinspection unchecked
super(type.getTypeName() + " Array", (Class<T[]>) getArrayClass(type.getOutputClass()));
this.elementType = type;
this.maxLength = maxLength;
}
public static Class<?> getArrayClass(Class<?> componentType) {
@@ -43,16 +52,40 @@ public class ArrayType<T> extends Type<T[]> {
@Override
public T[] read(ByteBuf buffer) {
int amount = Types.VAR_INT.readPrimitive(buffer);
T[] array = (T[]) Array.newInstance(elementType.getOutputClass(), amount);
if (maxLength != -1 && amount > maxLength) {
throw new IllegalArgumentException("Array length " + amount + " is longer than maximum " + maxLength);
}
for (int i = 0; i < amount; i++) {
return amount < Short.MAX_VALUE ? readArray(buffer, amount) : readList(buffer, amount);
}
private T[] readArray(ByteBuf buffer, int length) {
T[] array = createArray(length);
for (int i = 0; i < length; i++) {
array[i] = elementType.read(buffer);
}
return array;
}
private T[] readList(ByteBuf buffer, int length) {
List<T> list = new ArrayList<>();
for (int i = 0; i < length; i++) {
list.add(elementType.read(buffer));
}
return list.toArray(createArray(0));
}
private T[] createArray(int length) {
//noinspection unchecked
return (T[]) Array.newInstance(elementType.getOutputClass(), length);
}
@Override
public void write(ByteBuf buffer, T[] object) {
if (maxLength != -1 && object.length > maxLength) {
throw new IllegalArgumentException("Array length " + object.length + " is longer than maximum " + maxLength);
}
Types.VAR_INT.writePrimitive(buffer, object.length);
for (T o : object) {
elementType.write(buffer, o);
@@ -0,0 +1,67 @@
/*
* 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.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
// Ok...
public class BooleanArrayType extends Type<boolean[]> {
private final int length;
public BooleanArrayType(final int length) {
super(boolean[].class);
this.length = length;
}
public BooleanArrayType() {
super(boolean[].class);
this.length = -1;
}
@Override
public void write(final ByteBuf buffer, final boolean[] object) {
if (this.length != -1) {
Preconditions.checkArgument(length == object.length, "Length does not match expected length");
} else {
Types.VAR_INT.writePrimitive(buffer, object.length);
}
for (final boolean b : object) {
buffer.writeBoolean(b);
}
}
@Override
public boolean[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final boolean[] array = new boolean[length];
for (int i = 0; i < length; i++) {
array[i] = buffer.readBoolean();
}
return array;
}
}
@@ -0,0 +1,66 @@
/*
* 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.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
public class FloatArrayType extends Type<float[]> {
private final int length;
public FloatArrayType(final int length) {
super(float[].class);
this.length = length;
}
public FloatArrayType() {
super(float[].class);
this.length = -1;
}
@Override
public void write(final ByteBuf buffer, final float[] object) {
if (this.length != -1) {
Preconditions.checkArgument(length == object.length, "Length does not match expected length");
} else {
Types.VAR_INT.writePrimitive(buffer, object.length);
}
for (final float f : object) {
buffer.writeFloat(f);
}
}
@Override
public float[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final float[] array = new float[length];
for (int i = 0; i < length; i++) {
array[i] = buffer.readFloat();
}
return array;
}
}
@@ -26,19 +26,35 @@ import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class RemainingBytesType extends Type<byte[]> {
private final int maxLength;
public RemainingBytesType() {
this(-1);
}
public RemainingBytesType(final int maxLength) {
super(byte[].class);
this.maxLength = maxLength;
}
@Override
public byte[] read(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
public byte[] read(final ByteBuf buffer) {
final int bytes = buffer.readableBytes();
if (maxLength != -1 && bytes > maxLength) {
throw new RuntimeException("Remaining bytes cannot be longer than " + maxLength + " (got " + bytes + ")");
}
final byte[] array = new byte[bytes];
buffer.readBytes(array);
return array;
}
@Override
public void write(ByteBuf buffer, byte[] object) {
public void write(final ByteBuf buffer, final byte[] object) {
if (maxLength != -1 && object.length > maxLength) {
throw new RuntimeException("Remaining bytes cannot be longer than " + maxLength + " (got " + object.length + ")");
}
buffer.writeBytes(object);
}
}
@@ -32,6 +32,15 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
private static final int VALUE_BITS = 0x7F;
private static final int MULTI_BYTE_BITS = ~VALUE_BITS;
private static final int MAX_BYTES = 5;
private static final int[] VAR_INT_LENGTHS = new int[65];
static {
// Copied from Velocity https://github.com/PaperMC/Velocity/blob/08a42b3723633ea5eb6b96c0bb42180f3c2b07eb/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java#L166
for (int i = 0; i <= 32; ++i) {
VAR_INT_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
}
VAR_INT_LENGTHS[32] = 1; // Special case for the number 0.
}
public VarIntType() {
super("VarInt", Integer.class);
@@ -88,4 +97,8 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
}
throw new UnsupportedOperationException();
}
}
public static int varIntLength(final int value) {
return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)];
}
}
@@ -22,6 +22,7 @@
*/
package com.viaversion.viaversion.api.type.types.chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
@@ -54,4 +55,14 @@ public final class ChunkSectionType1_18 extends Type<ChunkSection> {
blockPaletteType.write(buffer, section.palette(PaletteType.BLOCKS));
biomePaletteType.write(buffer, section.palette(PaletteType.BIOMES));
}
public int serializedSize(final Chunk chunk) {
int length = 0;
for (final ChunkSection section : chunk.getSections()) {
length += Short.BYTES
+ blockPaletteType.serializedSize(section.palette(PaletteType.BLOCKS))
+ biomePaletteType.serializedSize(section.palette(PaletteType.BIOMES));
}
return length;
}
}
@@ -52,14 +52,10 @@ public final class ChunkType1_18 extends Type<Chunk> {
final CompoundTag heightMap = Types.NAMED_COMPOUND_TAG.read(buffer);
// Read sections
final ByteBuf sectionsBuf = buffer.readBytes(Types.VAR_INT.readPrimitive(buffer));
final ByteBuf sectionsBuf = buffer.readSlice(Types.VAR_INT.readPrimitive(buffer));
final ChunkSection[] sections = new ChunkSection[ySectionCount];
try {
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
} finally {
sectionsBuf.release();
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
@@ -78,16 +74,9 @@ public final class ChunkType1_18 extends Type<Chunk> {
Types.NAMED_COMPOUND_TAG.write(buffer, chunk.getHeightMap());
final ByteBuf sectionBuffer = buffer.alloc().buffer();
try {
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(sectionBuffer, section);
}
sectionBuffer.readerIndex(0);
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
buffer.writeBytes(sectionBuffer);
} finally {
sectionBuffer.release(); // release buffer
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(buffer, section);
}
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
@@ -52,14 +52,10 @@ public final class ChunkType1_20_2 extends Type<Chunk> {
final CompoundTag heightMap = Types.COMPOUND_TAG.read(buffer);
// Read sections
final ByteBuf sectionsBuf = buffer.readBytes(Types.VAR_INT.readPrimitive(buffer));
final ByteBuf sectionsBuf = buffer.readSlice(Types.VAR_INT.readPrimitive(buffer));
final ChunkSection[] sections = new ChunkSection[ySectionCount];
try {
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
} finally {
sectionsBuf.release();
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
@@ -78,16 +74,9 @@ public final class ChunkType1_20_2 extends Type<Chunk> {
Types.COMPOUND_TAG.write(buffer, chunk.getHeightMap());
final ByteBuf sectionBuffer = buffer.alloc().buffer();
try {
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(sectionBuffer, section);
}
sectionBuffer.readerIndex(0);
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
buffer.writeBytes(sectionBuffer);
} finally {
sectionBuffer.release(); // release buffer
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(buffer, section);
}
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.minecraft.chunks.DataPaletteImpl;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.VarIntType;
import com.viaversion.viaversion.util.CompactArrayUtil;
import com.viaversion.viaversion.util.MathUtil;
import io.netty.buffer.ByteBuf;
@@ -94,13 +95,7 @@ public final class PaletteType1_18 extends Type<DataPalette> {
return;
}
// 1, 2, and 3 bit linear block palettes can't be read by the client
final int min = type == PaletteType.BLOCKS ? 4 : 1;
int bitsPerValue = Math.max(min, MathUtil.ceilLog2(size));
if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = globalPaletteBits;
}
final int bitsPerValue = bitsPerValue(size);
buffer.writeByte(bitsPerValue);
if (bitsPerValue != globalPaletteBits) {
@@ -113,4 +108,39 @@ public final class PaletteType1_18 extends Type<DataPalette> {
Types.LONG_ARRAY_PRIMITIVE.write(buffer, CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt));
}
private int bitsPerValue(final int size) {
// 1, 2, and 3 bit linear block palettes can't be read by the client
final int min = type == PaletteType.BLOCKS ? 4 : 1;
int bitsPerValue = Math.max(min, MathUtil.ceilLog2(size));
if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = globalPaletteBits;
}
return bitsPerValue;
}
public int serializedSize(final DataPalette palette) {
// This is a bit of extra work, but worth it to avoid otherwise having to allocate and write to an extra buffer.
// On top of saving memory, it provides small but measurable speedup compared to writing to a separate buffer and then back
final int size = palette.size();
final int bitsPerValue = bitsPerValue(size);
int serializedTypesSize = 0;
int serializedValuesSize = 1; // At least one byte for 0 length
if (size == 1) {
serializedTypesSize = VarIntType.varIntLength(palette.idByIndex(0));
} else {
if (bitsPerValue != globalPaletteBits) {
serializedTypesSize = VarIntType.varIntLength(size);
for (int i = 0; i < size; i++) {
serializedTypesSize += VarIntType.varIntLength(palette.idByIndex(i));
}
}
final int valuesPerLong = 64 / bitsPerValue;
final int values = (type.size() + valuesPerLong - 1) / valuesPerLong;
serializedValuesSize = VarIntType.varIntLength(values) + (Long.BYTES * values);
}
return Byte.BYTES + serializedTypesSize + serializedValuesSize;
}
}
@@ -63,7 +63,7 @@ public class ItemType1_20_5 extends Type<Item> {
return new Reference2ObjectOpenHashMap<>();
}
final Map<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>();
final Map<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>(Math.min(valuesSize + markersSize, 128));
for (int i = 0; i < valuesSize; i++) {
final StructuredData<?> value = dataType.read(buffer);
final StructuredDataKey<?> key = dataType.key(value.id());
@@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.minecraft.item.Item;
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.version.Types1_20_5;
import com.viaversion.viaversion.util.Key;
import io.netty.buffer.ByteBuf;
@@ -148,12 +147,19 @@ public class ParticleType extends DynamicType<Particle> {
public static final DataReader<Particle> COLOR = (buf, particle) -> {
particle.add(Types.INT, Types.INT.readPrimitive(buf));
};
public static final DataReader<Particle> TRAIL = (buf, particle) -> {
public static final DataReader<Particle> TRAIL1_21_2 = (buf, particle) -> {
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target X
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target Y
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target Z
particle.add(Types.INT, Types.INT.readPrimitive(buf)); // Color
};
public static final DataReader<Particle> TRAIL1_21_4 = (buf, particle) -> {
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target X
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target Y
particle.add(Types.DOUBLE, Types.DOUBLE.readPrimitive(buf)); // Target Z
particle.add(Types.INT, Types.INT.readPrimitive(buf)); // Color
particle.add(Types.VAR_INT, Types.VAR_INT.readPrimitive(buf)); // Duration
};
public static DataReader<Particle> item(Type<Item> item) {
return (buf, particle) -> particle.add(item, item.read(buf));
@@ -0,0 +1,55 @@
/*
* 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.type.types.version;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.entitydata.EntityDataListType;
import com.viaversion.viaversion.api.type.types.entitydata.EntityDataType;
import com.viaversion.viaversion.api.type.types.item.ItemCostType1_20_5;
import com.viaversion.viaversion.api.type.types.item.ItemType1_20_5;
import com.viaversion.viaversion.api.type.types.item.StructuredDataType;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
import java.util.List;
// Most of these are only safe to use after protocol loading
public final class Types1_21_4 {
public static final StructuredDataType STRUCTURED_DATA = new StructuredDataType();
public static final Type<StructuredData<?>[]> STRUCTURED_DATA_ARRAY = new ArrayType<>(STRUCTURED_DATA);
public static final ItemType1_20_5 ITEM = new ItemType1_20_5(STRUCTURED_DATA);
public static final Type<Item[]> ITEM_ARRAY = new ArrayType<>(ITEM);
public static final Type<Item> ITEM_COST = new ItemCostType1_20_5(STRUCTURED_DATA_ARRAY);
public static final Type<Item> OPTIONAL_ITEM_COST = new ItemCostType1_20_5.OptionalItemCostType(ITEM_COST);
public static final ParticleType PARTICLE = new ParticleType();
public static final ArrayType<Particle> PARTICLES = new ArrayType<>(PARTICLE);
public static final EntityDataTypes1_21 ENTITY_DATA_TYPES = new EntityDataTypes1_21(ITEM, PARTICLE, PARTICLES);
public static final Type<EntityData> ENTITY_DATA = new EntityDataType(ENTITY_DATA_TYPES);
public static final Type<List<EntityData>> ENTITY_DATA_LIST = new EntityDataListType(ENTITY_DATA);
}
@@ -22,11 +22,13 @@
*/
package com.viaversion.viaversion.exception;
import com.viaversion.viaversion.api.Via;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
public class InformativeException extends RuntimeException {
private static final int MAX_MESSAGE_LENGTH = 5_000;
private final List<DataEntry> dataEntries = new ArrayList<>();
private boolean shouldBePrinted = true;
private int sources;
@@ -58,14 +60,21 @@ public class InformativeException extends RuntimeException {
@Override
public String getMessage() {
StringBuilder builder = new StringBuilder("Please report this on the Via support Discord or open an issue on the relevant GitHub repository\n");
final StringBuilder builder = new StringBuilder("Please report this on the Via support Discord or open an issue on the relevant GitHub repository\n");
boolean first = true;
for (DataEntry entry : dataEntries) {
for (final DataEntry entry : dataEntries) {
if (!first) {
builder.append(", ");
} else {
first = false;
}
builder.append(entry.name()).append(": ").append(entry.value());
first = false;
builder.append(entry.name()).append(": ");
String s = String.valueOf(entry.value());
if (!Via.getManager().isDebug() && s.length() > 10 && builder.length() + s.length() > MAX_MESSAGE_LENGTH) {
s = s.substring(0, MAX_MESSAGE_LENGTH - builder.length()) + "...";
}
builder.append(s.replaceAll("\\s", ""));
}
return builder.toString();
}
@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.util;
public final class Limit {
public static int max(final int value, final int max) {
if (value > max) {
throw new IllegalArgumentException("Value " + value + " is higher than the maximum " + max);
}
return value;
}
}
+2 -6
View File
@@ -1,7 +1,6 @@
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.jvm.toolchain.JavaLanguageVersion
import java.io.ByteArrayOutputStream
fun Project.latestCommitHash(): String {
return runGitCommand(listOf("rev-parse", "--short", "HEAD"))
@@ -16,12 +15,9 @@ fun Project.branchName(): String {
}
fun Project.runGitCommand(args: List<String>): String {
val byteOut = ByteArrayOutputStream()
exec {
return providers.exec {
commandLine = listOf("git") + args
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
}.standardOutput.asBytes.get().toString(Charsets.UTF_8).trim()
}
fun JavaPluginExtension.javaTarget(version: Int) {
@@ -1,5 +1,6 @@
plugins {
`java-library`
`jvm-test-suite`
}
tasks {
+2 -2
View File
@@ -16,10 +16,10 @@ val main = setOf(
projects.viaversionBukkit,
projects.viaversionVelocity,
projects.viaversionFabric
).map { it.dependencyProject }
).map { it.path }
subprojects {
when (this) {
when (path) {
in main -> plugins.apply("via.shadow-conventions")
else -> plugins.apply("via.base-conventions")
}
@@ -33,6 +33,7 @@ import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
import com.viaversion.viaversion.bukkit.platform.BukkitViaLoader;
import com.viaversion.viaversion.bukkit.platform.BukkitViaTask;
import com.viaversion.viaversion.bukkit.platform.BukkitViaTaskTask;
import com.viaversion.viaversion.bukkit.platform.FoliaViaTask;
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.unsupported.UnsupportedPlugin;
@@ -171,11 +172,18 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
@Override
public PlatformTask runSync(Runnable runnable, long delay) {
if (FOLIA) {
// Set the delayed tick to at least 1, as Folia requires this.
return new FoliaViaTask(getServer().getGlobalRegionScheduler().runDelayed(this, (e) -> runnable.run(), delay <= 0L ? 1L : delay));
}
return new BukkitViaTask(getServer().getScheduler().runTaskLater(this, runnable, delay));
}
@Override
public PlatformTask runRepeatingSync(Runnable runnable, long period) {
if (FOLIA) {
return new FoliaViaTask(getServer().getGlobalRegionScheduler().runAtFixedRate(this, (e) -> runnable.run(), 1, period));
}
return new BukkitViaTask(getServer().getScheduler().runTaskTimer(this, runnable, 0, period));
}
@@ -22,6 +22,7 @@ import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelDecoderException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.ByteBufUtil;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
@@ -39,7 +40,7 @@ public final class BukkitDecodeHandler extends MessageToMessageDecoder<ByteBuf>
}
@Override
protected void decode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) throws Exception {
protected void decode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) {
if (!connection.checkServerboundPacket()) {
throw CancelDecoderException.generate(null);
}
@@ -48,7 +49,7 @@ public final class BukkitDecodeHandler extends MessageToMessageDecoder<ByteBuf>
return;
}
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
final ByteBuf transformedBuf = ByteBufUtil.copy(ctx.alloc(), bytebuf);
try {
connection.transformIncoming(transformedBuf, CancelDecoderException::generate);
out.add(transformedBuf.retain());
@@ -22,6 +22,7 @@ import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelEncoderException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.util.ByteBufUtil;
import com.viaversion.viaversion.util.PipelineUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
@@ -52,7 +53,7 @@ public final class BukkitEncodeHandler extends MessageToMessageEncoder<ByteBuf>
return;
}
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
final ByteBuf transformedBuf = ByteBufUtil.copy(ctx.alloc(), bytebuf);
try {
final boolean needsCompression = !handledCompression && handleCompressionOrder(ctx, transformedBuf);
connection.transformClientbound(transformedBuf, CancelEncoderException::generate);
@@ -0,0 +1,72 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
public class LegacyChangeItemListener extends PlayerChangeItemListener {
public LegacyChangeItemListener(final ViaVersionPlugin plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockDamageEvent(final BlockDamageEvent event) {
final Player player = event.getPlayer();
final ItemStack item = event.getItemInHand();
sendAttributeUpdate(player, item, Slot.HAND);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onInventoryClose(final InventoryCloseEvent event) {
if (event.getPlayer() instanceof Player player &&
(event.getInventory().getType() == InventoryType.CRAFTING ||
event.getInventory().getType() == InventoryType.PLAYER)) {
sendArmorUpdate(player);
}
}
private void sendArmorUpdate(final Player player) {
final UserConnection connection = getUserConnection(player);
final EfficiencyAttributeStorage storage = getEfficiencyStorage(connection);
if (storage == null) {
return;
}
final PlayerInventory inventory = player.getInventory();
final ItemStack helmet = inventory.getHelmet();
final ItemStack leggings = swiftSneak != null ? inventory.getLeggings() : null;
final ItemStack boots = depthStrider != null ? inventory.getBoots() : null;
storage.setEnchants(player.getEntityId(), connection, storage.activeEnchants()
.aquaAffinity(helmet != null ? helmet.getEnchantmentLevel(aquaAffinity) : 0)
.swiftSneak(leggings != null ? leggings.getEnchantmentLevel(swiftSneak) : 0)
.depthStrider(boots != null ? boots.getEnchantmentLevel(depthStrider) : 0));
}
}
@@ -18,7 +18,6 @@
package com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
@@ -38,11 +37,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class PlayerChangeItemListener extends ViaBukkitListener {
// Use legacy function and names here to support all versions
private final Enchantment efficiency = getByName("efficiency", "DIG_SPEED");
private final Enchantment aquaAffinity = getByName("aqua_affinity", "WATER_WORKER");
private final Enchantment depthStrider = getByName("depth_strider", "DEPTH_STRIDER");
private final Enchantment soulSpeed = getByName("soul_speed", "SOUL_SPEED");
private final Enchantment swiftSneak = getByName("swift_sneak", "SWIFT_SNEAK");
protected final Enchantment efficiency = getByName("efficiency", "DIG_SPEED");
protected final Enchantment aquaAffinity = getByName("aqua_affinity", "WATER_WORKER");
protected final Enchantment depthStrider = getByName("depth_strider", "DEPTH_STRIDER");
protected final Enchantment soulSpeed = getByName("soul_speed", "SOUL_SPEED");
protected final Enchantment swiftSneak = getByName("swift_sneak", "SWIFT_SNEAK");
public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
super(plugin, Protocol1_20_5To1_21.class);
@@ -55,35 +54,26 @@ public class PlayerChangeItemListener extends ViaBukkitListener {
sendAttributeUpdate(player, item, Slot.HAND);
}
protected EfficiencyAttributeStorage getEfficiencyStorage(final UserConnection connection) {
return connection != null ? connection.get(EfficiencyAttributeStorage.class) : null;
}
void sendAttributeUpdate(final Player player, @Nullable final ItemStack item, final Slot slot) {
final UserConnection connection = Via.getAPI().getConnection(player.getUniqueId());
if (connection == null || !isOnPipe(player)) {
return;
}
final UserConnection connection = getUserConnection(player);
final EfficiencyAttributeStorage storage = getEfficiencyStorage(connection);
if (storage == null) return;
final EfficiencyAttributeStorage storage = connection.get(EfficiencyAttributeStorage.class);
if (storage == null) {
return;
}
final EfficiencyAttributeStorage.ActiveEnchants activeEnchants = storage.activeEnchants();
int efficiencyLevel = activeEnchants.efficiency().level();
int aquaAffinityLevel = activeEnchants.aquaAffinity().level();
int soulSpeedLevel = activeEnchants.soulSpeed().level();
int swiftSneakLevel = activeEnchants.swiftSneak().level();
int depthStriderLevel = activeEnchants.depthStrider().level();
switch (slot) {
case HAND -> efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
case HELMET -> aquaAffinityLevel = item != null ? item.getEnchantmentLevel(aquaAffinity) : 0;
case LEGGINGS -> swiftSneakLevel = item != null && swiftSneak != null ? item.getEnchantmentLevel(swiftSneak) : 0;
case BOOTS -> {
depthStriderLevel = item != null && depthStrider != null ? item.getEnchantmentLevel(depthStrider) : 0;
// TODO This needs continuous ticking for the supporting block as a conditional effect
// and is even more prone to desync from high ping than the other attributes
//soulSpeedLevel = item != null && soulSpeed != null ? item.getEnchantmentLevel(soulSpeed) : 0;
}
}
storage.setEnchants(player.getEntityId(), connection, efficiencyLevel, soulSpeedLevel, swiftSneakLevel, aquaAffinityLevel, depthStriderLevel);
EfficiencyAttributeStorage.ActiveEnchants enchants = storage.activeEnchants();
enchants = switch (slot) {
case HAND -> enchants.efficiency(item != null ? item.getEnchantmentLevel(efficiency) : 0);
case HELMET -> enchants.aquaAffinity(item != null ? item.getEnchantmentLevel(aquaAffinity) : 0);
case LEGGINGS -> enchants.swiftSneak(item != null && swiftSneak != null ? item.getEnchantmentLevel(swiftSneak) : 0);
case BOOTS -> enchants.depthStrider(item != null && depthStrider != null ? item.getEnchantmentLevel(depthStrider) : 0);
// TODO This needs continuous ticking for the supporting block as a conditional effect
// and is even more prone to desync from high ping than the other attributes
//soulSpeedLevel = item != null && soulSpeed != null ? item.getEnchantmentLevel(soulSpeed) : 0;
};
storage.setEnchants(player.getEntityId(), connection, enchants);
}
enum Slot {
@@ -26,23 +26,25 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.listeners.UpdateListener;
import com.viaversion.viaversion.bukkit.listeners.multiversion.PlayerSneakListener;
import com.viaversion.viaversion.bukkit.listeners.v1_14_4to1_15.EntityToggleGlideListener;
import com.viaversion.viaversion.bukkit.listeners.v1_19_3to1_19_4.ArmorToggleListener;
import com.viaversion.viaversion.bukkit.listeners.v1_18_2to1_19.BlockBreakListener;
import com.viaversion.viaversion.bukkit.listeners.v1_19_3to1_19_4.ArmorToggleListener;
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.LegacyChangeItemListener;
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PaperPlayerChangeItemListener;
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.ArmorListener;
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.BlockListener;
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.DeathListener;
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.HandItemCache;
import com.viaversion.viaversion.bukkit.listeners.v1_8to1_9.PaperPatch;
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PaperPlayerChangeItemListener;
import com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21.PlayerChangeItemListener;
import com.viaversion.viaversion.bukkit.providers.BukkitAckSequenceProvider;
import com.viaversion.viaversion.bukkit.providers.BukkitBlockConnectionProvider;
import com.viaversion.viaversion.bukkit.providers.BukkitInventoryQuickMoveProvider;
import com.viaversion.viaversion.bukkit.providers.BukkitPickItemProvider;
import com.viaversion.viaversion.bukkit.providers.BukkitViaMovementTransmitter;
import com.viaversion.viaversion.protocols.v1_11_1to1_12.provider.InventoryQuickMoveProvider;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.providers.BlockConnectionProvider;
import com.viaversion.viaversion.protocols.v1_18_2to1_19.provider.AckSequenceProvider;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
import com.viaversion.viaversion.protocols.v1_8to1_9.provider.HandItemProvider;
import com.viaversion.viaversion.protocols.v1_8to1_9.provider.MovementTransmitterProvider;
import java.util.HashSet;
@@ -184,7 +186,12 @@ public class BukkitViaLoader implements ViaPlatformLoader {
if (PaperViaInjector.hasClass("io.papermc.paper.event.player.PlayerInventorySlotChangeEvent")) {
new PaperPlayerChangeItemListener(plugin).register();
} else {
new PlayerChangeItemListener(plugin).register();
new LegacyChangeItemListener(plugin).register();
}
}
if (serverProtocolVersion.olderThan(ProtocolVersion.v1_21_4)) {
if (PaperViaInjector.hasMethod(Material.class, "isItem")) {
Via.getManager().getProviders().use(PickItemProvider.class, new BukkitPickItemProvider(plugin));
}
}
}
@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.platform;
import com.viaversion.viaversion.api.platform.PlatformTask;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
public record FoliaViaTask(ScheduledTask task) implements PlatformTask<ScheduledTask> {
@Override
public void cancel() {
task.cancel();
}
}
@@ -65,12 +65,7 @@ public final class PaperViaInjector {
}
private static boolean hasServerProtocolMethod() {
try {
Class.forName("org.bukkit.UnsafeValues").getDeclaredMethod("getProtocolVersion");
return true;
} catch (final ClassNotFoundException | NoSuchMethodException e) {
return false;
}
return hasMethod("org.bukkit.UnsafeValues", "getProtocolVersion");
}
private static boolean hasPaperInjectionMethod() {
@@ -78,12 +73,7 @@ public final class PaperViaInjector {
}
private static boolean hasIsStoppingMethod() {
try {
Bukkit.class.getDeclaredMethod("isStopping");
return true;
} catch (final NoSuchMethodException e) {
return false;
}
return hasMethod(Bukkit.class, "isStopping");
}
private static boolean hasPacketLimiter() {
@@ -98,4 +88,22 @@ public final class PaperViaInjector {
return false;
}
}
public static boolean hasMethod(final String className, final String method) {
try {
Class.forName(className).getDeclaredMethod(method);
return true;
} catch (final ClassNotFoundException | NoSuchMethodException e) {
return false;
}
}
public static boolean hasMethod(final Class<?> clazz, final String method) {
try {
clazz.getDeclaredMethod(method);
return true;
} catch (final NoSuchMethodException e) {
return false;
}
}
}
@@ -0,0 +1,182 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.bukkit.providers;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BukkitPickItemProvider extends PickItemProvider {
private static final boolean HAS_PLACEMENT_MATERIAL_METHOD = PaperViaInjector.hasMethod("org.bukkit.block.BlockData", "getPlacementMaterial");
private static final boolean HAS_SPAWN_EGG_METHOD = PaperViaInjector.hasMethod(ItemFactory.class, "getSpawnEgg");
private static final double BLOCK_RANGE = 4.5 + 1;
private static final double BLOCK_RANGE_SQUARED = BLOCK_RANGE * BLOCK_RANGE;
private static final double ENTITY_RANGE = 3 + 3;
private final ViaVersionPlugin plugin;
public BukkitPickItemProvider(final ViaVersionPlugin plugin) {
this.plugin = plugin;
}
@Override
public void pickItemFromBlock(final UserConnection connection, final BlockPosition blockPosition, final boolean includeData) {
final UUID uuid = connection.getProtocolInfo().getUuid();
plugin.getServer().getScheduler().runTask(plugin, () -> {
final Player player = plugin.getServer().getPlayer(uuid);
if (player == null) {
return;
}
final Location playerLocation = player.getLocation();
if (blockPosition.distanceFromCenterSquared(playerLocation.getX(), playerLocation.getY(), playerLocation.getZ()) > BLOCK_RANGE_SQUARED) {
return;
}
final Block block = player.getWorld().getBlockAt(blockPosition.x(), blockPosition.y(), blockPosition.z());
if (block.getType() == Material.AIR) {
return;
}
final ItemStack item = blockToItem(block, includeData && player.getGameMode() == GameMode.CREATIVE);
if (item != null) {
pickItem(player, item);
}
});
}
private @Nullable ItemStack blockToItem(final Block block, final boolean includeData) {
if (HAS_PLACEMENT_MATERIAL_METHOD) {
final ItemStack item = new ItemStack(block.getBlockData().getPlacementMaterial(), 1);
if (includeData && item.getItemMeta() instanceof final BlockStateMeta blockStateMeta) {
blockStateMeta.setBlockState(block.getState());
}
return item;
} else if (block.getType().isItem()) {
return new ItemStack(block.getType(), 1);
}
return null;
}
@Override
public void pickItemFromEntity(final UserConnection connection, final int entityId, final boolean includeData) {
if (!HAS_SPAWN_EGG_METHOD) {
return;
}
final UUID uuid = connection.getProtocolInfo().getUuid();
plugin.getServer().getScheduler().runTask(plugin, () -> {
final Player player = plugin.getServer().getPlayer(uuid);
if (player == null) {
return;
}
final Entity entity = player.getWorld().getNearbyEntities(player.getLocation(), ENTITY_RANGE, ENTITY_RANGE, ENTITY_RANGE).stream()
.filter(e -> e.getEntityId() == entityId)
.findAny()
.orElse(null);
if (entity == null) {
return;
}
final Material spawnEggType = Bukkit.getItemFactory().getSpawnEgg(entity.getType());
if (spawnEggType != null) {
pickItem(player, new ItemStack(spawnEggType, 1));
}
});
}
private void pickItem(final Player player, final ItemStack item) {
// Find matching item
final PlayerInventory inventory = player.getInventory();
final ItemStack[] contents = inventory.getStorageContents();
int sourceSlot = -1;
for (int i = 0; i < contents.length; i++) {
final ItemStack content = contents[i];
if (content == null || !content.isSimilar(item)) {
continue;
}
sourceSlot = i;
break;
}
if (sourceSlot != -1) {
moveToHotbar(inventory, sourceSlot, contents);
} else if (player.getGameMode() == GameMode.CREATIVE) {
spawnItem(item, inventory, contents);
}
}
private void spawnItem(final ItemStack item, final PlayerInventory inventory, final ItemStack[] contents) {
final int targetSlot = findEmptyHotbarSlot(inventory, inventory.getHeldItemSlot());
inventory.setHeldItemSlot(targetSlot);
final ItemStack heldItem = inventory.getItem(targetSlot);
int emptySlot = targetSlot;
if (heldItem != null && heldItem.getType() != Material.AIR) {
// Swap to the first free slot in the inventory, else add it to the current hotbar slot
for (int i = 0; i < contents.length; i++) {
if (contents[i] == null || contents[i].getType() == Material.AIR) {
emptySlot = i;
break;
}
}
}
inventory.setItem(emptySlot, heldItem);
inventory.setItemInMainHand(item);
}
private void moveToHotbar(final PlayerInventory inventory, final int sourceSlot, final ItemStack[] contents) {
if (sourceSlot <= 9) {
inventory.setHeldItemSlot(sourceSlot);
return;
}
final int heldSlot = inventory.getHeldItemSlot();
final int targetSlot = findEmptyHotbarSlot(inventory, heldSlot);
inventory.setHeldItemSlot(targetSlot);
final ItemStack heldItem = inventory.getItem(targetSlot);
inventory.setItemInMainHand(contents[sourceSlot]);
inventory.setItem(sourceSlot, heldItem);
}
private int findEmptyHotbarSlot(final PlayerInventory inventory, final int heldSlot) {
for (int i = 0; i < 9; i++) {
final ItemStack item = inventory.getItem(i);
if (item == null || item.getType() == Material.AIR) {
return i;
}
}
return heldSlot;
}
}
@@ -48,7 +48,10 @@ public abstract class ViaListener {
* @return True if on pipe
*/
protected boolean isOnPipe(UUID uuid) {
UserConnection userConnection = getUserConnection(uuid);
return isOnPipe(getUserConnection(uuid));
}
protected boolean isOnPipe(UserConnection userConnection) {
return userConnection != null &&
(requiredPipeline == null || userConnection.getProtocolInfo().getPipeline().contains(requiredPipeline));
}
@@ -244,9 +244,12 @@ public class ViaManagerImpl implements ViaManager {
}
if (version < 17) {
platform.getLogger().warning("You are running an outdated Java version, please update it to at least Java 17 (your version is " + javaVersion + ").");
platform.getLogger().warning("You are running an outdated Java version, please update it to at least Java 21 (your version is " + javaVersion + ").");
platform.getLogger().warning("ViaVersion no longer officially supports this version of Java, only offering unsupported compatibility builds.");
platform.getLogger().warning("See https://github.com/ViaVersion/ViaVersion/releases/tag/5.0.0 for more information.");
} else if (version < 21) {
platform.getLogger().warning("Please update your Java runtime to at least Java 21 (your version is " + javaVersion + ").");
platform.getLogger().warning("At some point in the future, ViaVersion will no longer be compatible with this version of Java.");
}
}
@@ -35,7 +35,7 @@ public class ReloadSubCmd implements ViaSubCommand {
@Override
public boolean execute(ViaCommandSender sender, String[] args) {
Via.getManager().getConfigurationProvider().reloadConfigs();
sendMessage(sender, "&6Configuration successfully reloaded! Some features may need a restart.");
sendMessage(sender, "&6Configuration successfully reloaded! Some config options may require a restart to take effect.");
return true;
}
}
@@ -96,6 +96,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
private boolean cancelBlockSounds;
private boolean hideScoreboardNumbers;
private boolean fix1_21PlacementRotation;
private boolean swordBlockingViaConsumable;
protected AbstractViaConfig(final File configFile, final Logger logger) {
super(configFile, logger);
@@ -164,7 +165,8 @@ 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);
fix1_21PlacementRotation = getBoolean("fix-1_21-placement-rotation", true);
swordBlockingViaConsumable = getBoolean("sword-blocking-via-consumable", true);
}
private BlockedProtocolVersions loadBlockedProtocolVersions() {
@@ -556,4 +558,9 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
public boolean fix1_21PlacementRotation() {
return fix1_21PlacementRotation;
}
@Override
public boolean swordBlockingViaConsumable() {
return swordBlockingViaConsumable;
}
}
@@ -29,8 +29,8 @@ import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.VarIntType;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.exception.InformativeException;
import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl;
@@ -55,6 +55,7 @@ import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;
public class UserConnectionImpl implements UserConnection {
private static final int PASSTHROUGH_DATA_BYTES = Long.BYTES * 2 + 2;
private static final AtomicLong IDS = new AtomicLong();
private final long id = IDS.incrementAndGet();
private final Map<Class<?>, StorableObject> storedObjects = new ConcurrentHashMap<>();
@@ -244,7 +245,8 @@ public class UserConnectionImpl implements UserConnection {
}
private void sendRawPacketToServerServerSide(final ByteBuf packet, final boolean currentThread) {
final ByteBuf buf = packet.alloc().buffer();
final int initialCapacity = active ? packet.readableBytes() + PASSTHROUGH_DATA_BYTES : packet.readableBytes();
final ByteBuf buf = packet.alloc().buffer(initialCapacity);
try {
// We'll use passing through because there are some encoder wrappers
ChannelHandlerContext context = PipelineUtil
@@ -333,7 +335,7 @@ public class UserConnectionImpl implements UserConnection {
return;
}
int id = Types.VAR_INT.readPrimitive(buf);
final int id = Types.VAR_INT.readPrimitive(buf);
if (id == PacketWrapper.PASSTHROUGH_ID) {
if (!passthroughTokens.remove(Types.UUID.read(buf))) {
throw new IllegalArgumentException("Invalid token");
@@ -341,20 +343,47 @@ public class UserConnectionImpl implements UserConnection {
return;
}
PacketWrapper wrapper = new PacketWrapperImpl(id, buf, this);
State state = protocolInfo.getState(direction);
final int valuesReaderIndex = buf.readerIndex();
final PacketWrapperImpl wrapper = new PacketWrapperImpl(id, buf, this);
try {
protocolInfo.getPipeline().transform(direction, state, wrapper);
} catch (CancelException ex) {
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(direction), wrapper);
} catch (final CancelException ex) {
throw cancelSupplier.apply(ex);
}
ByteBuf transformed = buf.alloc().buffer();
writeToBuffer(wrapper, buf, id, valuesReaderIndex);
}
private void writeToBuffer(final PacketWrapperImpl wrapper, final ByteBuf buf, final int originalId, final int originalReaderIndex) {
final int remainingBytes = buf.readableBytes();
if (buf.readerIndex() == originalReaderIndex && wrapper.areStoredPacketValuesEmpty()) {
if (wrapper.getId() == originalId) {
// No changes needed; just set the reader and writer indexes and we're done
buf.setIndex(0, originalReaderIndex + remainingBytes);
return;
}
if (VarIntType.varIntLength(wrapper.getId()) == VarIntType.varIntLength(originalId)) {
// If the var int encoded length is the same, simply replace the id at the head
buf.setIndex(0, 0);
Types.VAR_INT.writePrimitive(buf, wrapper.getId());
buf.writerIndex(originalReaderIndex + remainingBytes);
return;
}
}
// Instead of allocating a possible unnecessarily large buffer to write the wrapper contents to,
// only allocate the remaining bytes and write the rest to the original buf's head directly.
final ByteBuf remainingBuf = buf.alloc().buffer(remainingBytes, remainingBytes);
try {
wrapper.writeToBuffer(transformed);
buf.clear().writeBytes(transformed);
// Copy before modifying the buffer
remainingBuf.writeBytes(buf, remainingBytes);
// Reset indexes, write wrapper contents, then the unread bytes
buf.setIndex(0, 0);
wrapper.writeProcessedValues(buf);
buf.writeBytes(remainingBuf);
} finally {
transformed.release();
remainingBuf.release();
}
}
@@ -76,6 +76,7 @@ import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.Protocol1_20_2To1_20_
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.Protocol1_20_3To1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20to1_20_2.Protocol1_20To1_20_2;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.Protocol1_21To1_21_2;
import com.viaversion.viaversion.protocols.v1_8to1_9.Protocol1_8To1_9;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.Protocol1_9_1To1_9_3;
@@ -194,6 +195,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
registerProtocol(new Protocol1_20_5To1_21(), ProtocolVersion.v1_21, ProtocolVersion.v1_20_5);
registerProtocol(new Protocol1_21To1_21_2(), ProtocolVersion.v1_21_2, ProtocolVersion.v1_21);
registerProtocol(new Protocol1_21_2To1_21_4(), ProtocolVersion.v1_21_4, ProtocolVersion.v1_21_2);
}
@Override
@@ -222,6 +222,18 @@ public class PacketWrapperImpl implements PacketWrapper {
@Override
public void writeToBuffer(ByteBuf buffer) throws InformativeException {
writeProcessedValues(buffer);
if (inputBuffer != null) {
buffer.writeBytes(inputBuffer);
}
}
public boolean areStoredPacketValuesEmpty() {
// Check for read/added packet values, not the input buffer
return packetValues.isEmpty() && readableObjects.isEmpty();
}
public void writeProcessedValues(ByteBuf buffer) throws InformativeException {
if (id != -1) {
Types.VAR_INT.writePrimitive(buffer, id);
}
@@ -238,7 +250,6 @@ public class PacketWrapperImpl implements PacketWrapper {
throw createInformativeException(e, packetValue.type(), i);
}
}
writeRemaining(buffer);
}
private InformativeException createInformativeException(final Exception cause, final Type<?> type, final int index) {
@@ -264,12 +275,6 @@ public class PacketWrapperImpl implements PacketWrapper {
packetValues.clear();
}
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer);
}
}
@Override
public void send(Class<? extends Protocol> protocol, boolean skipCurrentPipeline) throws InformativeException {
send0(protocol, skipCurrentPipeline, true);
@@ -320,7 +325,7 @@ public class PacketWrapperImpl implements PacketWrapper {
final ProtocolInfo protocolInfo = user().getProtocolInfo();
final List<Protocol> protocols = protocolInfo.getPipeline().pipes(protocolClass, skipCurrentPipeline, direction);
apply(direction, protocolInfo.getState(direction), protocols);
final ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
final ByteBuf output = allocateOutputBuffer();
try {
writeToBuffer(output);
return output.retain();
@@ -354,7 +359,7 @@ public class PacketWrapperImpl implements PacketWrapper {
return cancelledFuture();
}
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
ByteBuf output = allocateOutputBuffer();
try {
writeToBuffer(output);
return user().sendRawPacketFuture(output.retain());
@@ -373,7 +378,7 @@ public class PacketWrapperImpl implements PacketWrapper {
return;
}
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
ByteBuf output = allocateOutputBuffer();
try {
writeToBuffer(output);
if (currentThread) {
@@ -386,6 +391,14 @@ public class PacketWrapperImpl implements PacketWrapper {
}
}
private ByteBuf allocateOutputBuffer() {
if (inputBuffer == null) {
return user().getChannel().alloc().buffer();
}
// May have already been partially or fully read
return inputBuffer.alloc().buffer(Math.max(inputBuffer.readableBytes(), 256));
}
private ChannelFuture cancelledFuture() {
return user().getChannel().newFailedFuture(new RuntimeException("Tried to send cancelled packet"));
}
@@ -454,7 +467,7 @@ public class PacketWrapperImpl implements PacketWrapper {
return;
}
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
ByteBuf output = allocateOutputBuffer();
try {
writeToBuffer(output);
if (currentThread) {
@@ -18,26 +18,26 @@
package com.viaversion.viaversion.protocols.template;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
// To replace if needed:
// ChunkType1_20_2
// RecipeRewriter1_20_3
// Types1_21, Types1_OLD
final class BlockItemPacketRewriter1_99 extends StructuredItemRewriter<ClientboundPacket1_21, ServerboundPacket1_20_5, Protocol1_99To_98> {
// RecipeDisplayRewriter
// Types1_21_4, Types1_OLD
final class BlockItemPacketRewriter1_99 extends StructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_4, Protocol1_99To_98> {
public BlockItemPacketRewriter1_99(final Protocol1_99To_98 protocol) {
super(protocol, Types1_21.ITEM, Types1_21.ITEM_ARRAY);
super(protocol, Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY);
/*super(protocol,
Types1_OLD.ITEM, Types1_OLD.ITEM_ARRAY, Types1_21.ITEM, Types1_21.ITEM_ARRAY,
Types1_OLD.ITEM_COST, Types1_OLD.OPTIONAL_ITEM_COST, Types1_21.ITEM_COST, Types1_21.OPTIONAL_ITEM_COST
Types1_OLD.ITEM, Types1_OLD.ITEM_ARRAY, Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY,
Types1_OLD.ITEM_COST, Types1_OLD.OPTIONAL_ITEM_COST, Types1_21_4.ITEM_COST, Types1_21_4.OPTIONAL_ITEM_COST
);*/
}
@@ -46,31 +46,32 @@ final class BlockItemPacketRewriter1_99 extends StructuredItemRewriter<Clientbou
// Register block and block state id changes
// Other places using block state id mappings: Spawn particle, entity data, entity spawn (falling blocks)
// Tags and statistics use block (!) ids
final BlockRewriter<ClientboundPacket1_21> blockRewriter = BlockRewriter.for1_20_2(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21.LEVEL_EVENT, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_21.BLOCK_ENTITY_DATA);
final BlockRewriter<ClientboundPacket1_21_2> blockRewriter = BlockRewriter.for1_20_2(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_2.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_2.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_2.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21_2.LEVEL_EVENT, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21_2.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_21_2.BLOCK_ENTITY_DATA);
// Registers item id changes
// Other places using item ids are: Entity data, tags, statistics, effect
// registerOpenScreen(ClientboundPackets1_21.OPEN_SCREEN); If a new container type was added; also remove from the component rewriter registration
//protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerCooldown1_21_2(ClientboundPackets1_21.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21.CONTAINER_SET_CONTENT);
registerSetSlot1_21_2(ClientboundPackets1_21.CONTAINER_SET_SLOT);
registerAdvancements1_20_3(ClientboundPackets1_21.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_21.SET_EQUIPMENT);
registerContainerClick1_21_2(ServerboundPackets1_20_5.CONTAINER_CLICK);
registerMerchantOffers1_20_5(ClientboundPackets1_21.MERCHANT_OFFERS);
registerSetCreativeModeSlot(ServerboundPackets1_20_5.SET_CREATIVE_MODE_SLOT);
// registerOpenScreen(ClientboundPackets1_21_2.OPEN_SCREEN); If a new container type was added; also remove from the component rewriter registration
protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerSetPlayerInventory(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_2.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21_2.CONTAINER_SET_CONTENT);
registerSetSlot1_21_2(ClientboundPackets1_21_2.CONTAINER_SET_SLOT);
registerAdvancements1_20_3(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_21_2.SET_EQUIPMENT);
registerMerchantOffers1_20_5(ClientboundPackets1_21_2.MERCHANT_OFFERS);
registerContainerClick1_21_2(ServerboundPackets1_21_4.CONTAINER_CLICK);
registerSetCreativeModeSlot(ServerboundPackets1_21_4.SET_CREATIVE_MODE_SLOT);
final RecipeDisplayRewriter<ClientboundPacket1_21> recipeRewriter = new RecipeDisplayRewriter<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21.UPDATE_RECIPES);
//recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21.PLACE_GHOST_RECIPE);
final RecipeDisplayRewriter<ClientboundPacket1_21_2> recipeRewriter = new RecipeDisplayRewriter<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21_2.UPDATE_RECIPES);
recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21_2.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_2.PLACE_GHOST_RECIPE);
// OR do this if serialization of recipes changed and override the relevant method
// Add new serializers to RecipeDisplayRewriter, or extend the last one for changes
}
@@ -19,11 +19,11 @@ package com.viaversion.viaversion.protocols.template;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.util.SerializerVersion;
public final class ComponentRewriter1_99 extends ComponentRewriter<ClientboundPacket1_21> {
final class ComponentRewriter1_99 extends ComponentRewriter<ClientboundPacket1_21_2> {
public ComponentRewriter1_99(final Protocol1_99To_98 protocol) {
super(protocol, ReadType.NBT);
@@ -41,6 +41,6 @@ public final class ComponentRewriter1_99 extends ComponentRewriter<ClientboundPa
@Override
protected SerializerVersion inputSerializerVersion() {
return SerializerVersion.V1_20_5;
return SerializerVersion.V1_21_4;
}
}
@@ -18,19 +18,19 @@
package com.viaversion.viaversion.protocols.template;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
// Replace if needed
// Types1_OLD
// Types1_21
final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_21, Protocol1_99To_98> {
// Types1_21_4
final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_99To_98> {
public EntityPacketRewriter1_99(final Protocol1_99To_98 protocol) {
super(protocol);
@@ -39,14 +39,14 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
@Override
public void registerPackets() {
// Tracks entities, applies entity data rewrites registered below, untracks entities
registerTrackerWithData1_19(ClientboundPackets1_21.ADD_ENTITY, EntityTypes1_20_5.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21.SET_ENTITY_DATA, /*Types1_OLD_ENTITY_DATA_LIST, */Types1_21.ENTITY_DATA_LIST); // Specify old and new entity data list if changed
registerRemoveEntities(ClientboundPackets1_21.REMOVE_ENTITIES);
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, /*Types1_OLD_ENTITY_DATA_LIST, */Types1_21_4.ENTITY_DATA_LIST); // Specify old and new entity data list if changed
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol);
protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle);
protocol.registerClientbound(ClientboundPackets1_21.LOGIN, wrapper -> {
protocol.registerClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
final int entityId = wrapper.passthrough(Types.INT); // Entity id
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
@@ -64,7 +64,7 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
trackPlayer(wrapper.user(), entityId);
});
protocol.registerClientbound(ClientboundPackets1_21.RESPAWN, wrapper -> {
protocol.registerClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); // Tracks world height and name for chunk data and entity (un)tracking
@@ -79,20 +79,20 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
if (id >= SomeAddedIndex) {
id++;
}
return Types1_21.ENTITY_DATA_TYPES.byId(id);
return Types1_21_4.ENTITY_DATA_TYPES.byId(id);
});*/
// Registers registry type id changes
registerEntityDataTypeHandler(
Types1_21.ENTITY_DATA_TYPES.itemType,
Types1_21.ENTITY_DATA_TYPES.blockStateType,
Types1_21.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21.ENTITY_DATA_TYPES.particleType,
Types1_21.ENTITY_DATA_TYPES.particlesType,
Types1_21.ENTITY_DATA_TYPES.componentType,
Types1_21.ENTITY_DATA_TYPES.optionalComponentType
Types1_21_4.ENTITY_DATA_TYPES.itemType,
Types1_21_4.ENTITY_DATA_TYPES.blockStateType,
Types1_21_4.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21_4.ENTITY_DATA_TYPES.particleType,
Types1_21_4.ENTITY_DATA_TYPES.particlesType,
Types1_21_4.ENTITY_DATA_TYPES.componentType,
Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType
);
registerBlockStateHandler(EntityTypes1_20_5.ABSTRACT_MINECART, 11);
registerBlockStateHandler(EntityTypes1_21_4.ABSTRACT_MINECART, 11);
}
@Override
@@ -103,6 +103,6 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
@Override
public EntityType typeFromId(final int type) {
return EntityTypes1_20_5.getTypeFromId(type);
return EntityTypes1_21_4.getTypeFromId(type);
}
}
@@ -20,20 +20,20 @@ package com.viaversion.viaversion.protocols.template;
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.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.AttributeRewriter;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.SoundRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
@@ -43,56 +43,56 @@ import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
// Placeholders to replace (in the entire package):
// Protocol1_99To_98, EntityPacketRewriter1_99, BlockItemPacketRewriter1_99 - move the latter two to a rewriter package
// ClientboundPacket1_21
// ServerboundPacket1_20_5
// EntityTypes1_20_5 (MAPPED type)
// Types1_21_2.PARTICLE
// ClientboundPacket1_21_2
// ServerboundPacket1_21_4
// EntityTypes1_21_4 (MAPPED type)
// Types1_21_4.PARTICLE
// 1.99, 1.98
final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21, ClientboundPacket1_21, ServerboundPacket1_20_5, ServerboundPacket1_20_5> {
final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_4> {
public static final MappingData MAPPINGS = new MappingDataBase("1.98", "1.99");
private final EntityPacketRewriter1_99 entityRewriter = new EntityPacketRewriter1_99(this);
private final BlockItemPacketRewriter1_99 itemRewriter = new BlockItemPacketRewriter1_99(this);
private final ParticleRewriter<ClientboundPacket1_21> particleRewriter = new ParticleRewriter<>(this, /*Types1_OLD.PARTICLE,*/ Types1_21_2.PARTICLE);
private final TagRewriter<ClientboundPacket1_21> tagRewriter = new TagRewriter<>(this);
private final ComponentRewriter1_99 componentRewriter = new ComponentRewriter1_99(this);
private final ParticleRewriter<ClientboundPacket1_21_2> particleRewriter = new ParticleRewriter<>(this, /*Types1_OLD.PARTICLE,*/ Types1_21_4.PARTICLE);
private final TagRewriter<ClientboundPacket1_21_2> tagRewriter = new TagRewriter<>(this);
private final ComponentRewriter<ClientboundPacket1_21_2> componentRewriter = new ComponentRewriter1_99(this);
public Protocol1_99To_98() {
// Passing the class types into the super constructor is needed for automatic packet type id remapping, but can otherwise be omitted
super(ClientboundPacket1_21.class, ClientboundPacket1_21.class, ServerboundPacket1_20_5.class, ServerboundPacket1_20_5.class);
super(ClientboundPacket1_21_2.class, ClientboundPacket1_21_2.class, ServerboundPacket1_21_4.class, ServerboundPacket1_21_4.class);
}
@Override
protected void registerPackets() {
super.registerPackets();
tagRewriter.registerGeneric(ClientboundPackets1_21.UPDATE_TAGS);
tagRewriter.registerGeneric(ClientboundPackets1_21_2.UPDATE_TAGS);
tagRewriter.registerGeneric(ClientboundConfigurationPackets1_21.UPDATE_TAGS);
// If needed for item or component changes
componentRewriter.registerOpenScreen(ClientboundPackets1_21.OPEN_SCREEN);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.SET_ACTION_BAR_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.SET_TITLE_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.SET_SUBTITLE_TEXT);
componentRewriter.registerBossEvent(ClientboundPackets1_21.BOSS_EVENT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.DISCONNECT);
componentRewriter.registerTabList(ClientboundPackets1_21.TAB_LIST);
componentRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21.PLAYER_COMBAT_KILL);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.SYSTEM_CHAT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21.DISGUISED_CHAT);
componentRewriter.registerPlayerInfoUpdate1_21_2(ClientboundPackets1_21.PLAYER_INFO_UPDATE);
componentRewriter.registerOpenScreen(ClientboundPackets1_21_2.OPEN_SCREEN);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_ACTION_BAR_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_TITLE_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_SUBTITLE_TEXT);
componentRewriter.registerBossEvent(ClientboundPackets1_21_2.BOSS_EVENT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.DISCONNECT);
componentRewriter.registerTabList(ClientboundPackets1_21_2.TAB_LIST);
componentRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_2.PLAYER_COMBAT_KILL);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SYSTEM_CHAT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.DISGUISED_CHAT);
componentRewriter.registerPlayerInfoUpdate1_21_4(ClientboundPackets1_21_2.PLAYER_INFO_UPDATE);
componentRewriter.registerPing();
// If needed for any particle, item, or block changes. Extend ParticleRewriter for particle serializer changes
particleRewriter.registerLevelParticles1_20_5(ClientboundPackets1_21.LEVEL_PARTICLES);
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21.EXPLODE); // Rewrites the included sound and particles
particleRewriter.registerLevelParticles1_20_5(ClientboundPackets1_21_2.LEVEL_PARTICLES);
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21_2.EXPLODE); // Rewrites the included sound and particles
final SoundRewriter<ClientboundPacket1_21> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21.SOUND);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21.SOUND_ENTITY);
final SoundRewriter<ClientboundPacket1_21_2> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_2.SOUND);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_2.SOUND_ENTITY);
new StatisticsRewriter<>(this).register(ClientboundPackets1_21.AWARD_STATS);
new AttributeRewriter<>(this).register1_21(ClientboundPackets1_21.UPDATE_ATTRIBUTES);
new StatisticsRewriter<>(this).register(ClientboundPackets1_21_2.AWARD_STATS);
new AttributeRewriter<>(this).register1_21(ClientboundPackets1_21_2.UPDATE_ATTRIBUTES);
// Uncomment if an existing type changed serialization format. Mappings for argument type keys can also be defined in mapping files
/*new CommandRewriter1_19_4<>(this) {
@@ -105,16 +105,16 @@ final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21, Cl
super.handleArgument(wrapper, argumentType);
}
}
}.registerDeclareCommands1_19(ClientboundPackets1_21.COMMANDS);*/
}.registerDeclareCommands1_19(ClientboundPackets1_21_2.COMMANDS);*/
}
@Override
protected void onMappingDataLoaded() {
// Uncomment this if the entity types enum has been newly added specifically for this Protocol
// EntityTypes1_20_5.initialize(this);
// EntityTypes1_21_4.initialize(this);
// Uncomment if a new particle was added = ids shifted; requires a new Types_ class copied from the last
/*Types1_21_2.PARTICLE.filler(this)
/*Types1_21_4.PARTICLE.filler(this)
.reader("block", ParticleType.Readers.BLOCK)
.reader("block_marker", ParticleType.Readers.BLOCK)
.reader("dust_pillar", ParticleType.Readers.BLOCK)
@@ -126,7 +126,7 @@ final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21, Cl
.reader("sculk_charge", ParticleType.Readers.SCULK_CHARGE)
.reader("shriek", ParticleType.Readers.SHRIEK)
.reader("entity_effect", ParticleType.Readers.COLOR)
.reader("trail", ParticleType.Readers.TRAIL)
.reader("trail", ParticleType.Readers.TRAIL1_21_4)
.reader("item", ParticleType.Readers.item(itemRewriter.mappedItemType()));*/
super.onMappingDataLoaded(); // Calls load methods on rewriters. Last in case the rewriters access the above filled data
@@ -135,7 +135,7 @@ final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21, Cl
@Override
public void init(final UserConnection connection) {
// Register the entity tracker - used for entity id/entity data rewriting AND for tracking world data sent to the client (then used for chunk data rewriting)
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_20_5.PLAYER));
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_4.PLAYER));
}
// Overriding these four methods is important as they are relied on various rewriter classes
@@ -156,27 +156,27 @@ final class Protocol1_99To_98 extends AbstractProtocol<ClientboundPacket1_21, Cl
}
@Override
public ParticleRewriter<ClientboundPacket1_21> getParticleRewriter() {
public ParticleRewriter<ClientboundPacket1_21_2> getParticleRewriter() {
return particleRewriter;
}
@Override
public TagRewriter<ClientboundPacket1_21> getTagRewriter() {
public TagRewriter<ClientboundPacket1_21_2> getTagRewriter() {
return tagRewriter;
}
@Override
public ComponentRewriter getComponentRewriter() {
public ComponentRewriter<ClientboundPacket1_21_2> getComponentRewriter() {
return componentRewriter;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21, ClientboundPacket1_21, ServerboundPacket1_20_5, ServerboundPacket1_20_5> createPacketTypesProvider() {
protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_4> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class),
packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class)
packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_4.class, ServerboundConfigurationPackets1_20_5.class),
packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_4.class, ServerboundConfigurationPackets1_20_5.class)
);
}
}
@@ -132,13 +132,13 @@ public class Protocol1_10To1_11 extends AbstractProtocol<ClientboundPackets1_9_3
if (effectID == 2002) {
int data = packetWrapper.get(Types.INT, 1);
boolean isInstant = false;
Pair<Integer, Boolean> newData = PotionColorMappings1_11.getNewData(data);
PotionColorMappings1_11.PotionData newData = PotionColorMappings1_11.getNewData(data);
if (newData == null) {
getLogger().warning("Received unknown potion data: " + data);
data = 0;
} else {
data = newData.key();
isInstant = newData.value();
data = newData.data();
isInstant = newData.instant();
}
if (isInstant) {
packetWrapper.set(Types.INT, 0, 2007);
@@ -17,14 +17,13 @@
*/
package com.viaversion.viaversion.protocols.v1_10to1_11.data;
import com.viaversion.viaversion.util.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public class PotionColorMappings1_11 {
//<oldData> to <newData, isInstant> mapping
private static final Int2ObjectMap<Pair<Integer, Boolean>> POTIONS = new Int2ObjectOpenHashMap<>(37);
private static final Int2ObjectMap<PotionData> POTIONS = new Int2ObjectOpenHashMap<>(37);
static {
addRewrite(0, 3694022, false);
@@ -66,12 +65,14 @@ public class PotionColorMappings1_11 {
addRewrite(36, 3381504, false);
}
public static Pair<Integer, Boolean> getNewData(int oldData) {
public static @Nullable PotionData getNewData(int oldData) {
return POTIONS.get(oldData);
}
private static void addRewrite(int oldData, int newData, boolean isInstant) {
POTIONS.put(oldData, new Pair<>(newData, isInstant));
POTIONS.put(oldData, new PotionData(newData, isInstant));
}
public record PotionData(int data, boolean instant) {
}
}
@@ -24,9 +24,9 @@ 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;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
@@ -37,6 +37,7 @@ import com.viaversion.viaversion.protocols.v1_10to1_11.data.EntityMappings1_11;
import com.viaversion.viaversion.protocols.v1_10to1_11.storage.EntityTracker1_11;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
@@ -91,13 +92,27 @@ public class EntityPacketRewriter1_11 extends EntityRewriter<ClientboundPackets1
map(Types.INT); // 8 - Data
// Track Entity
handler(objectTrackerHandler());
handler(wrapper -> {
byte type = wrapper.get(Types.BYTE, 0);
if (type == EntityTypes1_10.ObjectType.FISHIHNG_HOOK.getId()) {
tryFixFishingHookVelocity(wrapper);
} else if (type == EntityTypes1_10.ObjectType.ITEM.getId()) {
// Older clients used stone as fallback as long as the entity data was not set
wrapper.send(Protocol1_10To1_11.class);
wrapper.cancel();
final int entityId = wrapper.get(Types.VAR_INT, 0);
final List<EntityData> entityDataList = new ArrayList<>();
entityDataList.add(new EntityData(6, EntityDataTypes1_9.ITEM, new DataItem(1, (byte) 1, null)));
final PacketWrapper setItem = PacketWrapper.create(ClientboundPackets1_9_3.SET_ENTITY_DATA, wrapper.user());
setItem.write(Types.VAR_INT, entityId);
setItem.write(Types1_9.ENTITY_DATA_LIST, entityDataList);
setItem.send(Protocol1_10To1_11.class);
}
});
handler(objectTrackerHandler());
}
});
@@ -166,8 +166,8 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol<ClientboundPackets1_1
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, tag.getValue().clone());
}
});
if (w.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_20_2)) {
// Make sure it's included in the configuration packets
if (w.user().getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_20_5)) {
// Make sure it's included in the configuration packets as it may already be required for registry data
tagsPacket.send(Protocol1_12_2To1_13.class);
} else {
tagsPacket.scheduleSend(Protocol1_12_2To1_13.class);
@@ -775,7 +775,10 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol<ClientboundPackets1_1
for (Item[] ingredient : recipe.ingredients()) {
Item[] clone = new Item[ingredient.length];
for (int i = 0; i < ingredient.length; i++) {
if (clone[i] == null) continue;
if (ingredient[i] == null) {
continue;
}
clone[i] = ingredient[i].copy();
}
recipesPacket.write(Types.ITEM1_13_ARRAY, clone);
@@ -789,7 +792,10 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol<ClientboundPackets1_1
for (Item[] ingredient : recipe.ingredients()) {
Item[] clone = new Item[ingredient.length];
for (int i = 0; i < ingredient.length; i++) {
if (clone[i] == null) continue;
if (ingredient[i] == null) {
continue;
}
clone[i] = ingredient[i].copy();
}
recipesPacket.write(Types.ITEM1_13_ARRAY, clone);
@@ -800,7 +806,10 @@ public class Protocol1_12_2To1_13 extends AbstractProtocol<ClientboundPackets1_1
recipesPacket.write(Types.STRING, recipe.group());
Item[] ingredient = new Item[recipe.ingredient().length];
for (int i = 0; i < ingredient.length; i++) {
if (recipe.ingredient()[i] == null) continue;
if (recipe.ingredient()[i] == null) {
continue;
}
ingredient[i] = recipe.ingredient()[i].copy();
}
recipesPacket.write(Types.ITEM1_13_ARRAY, ingredient);
@@ -18,16 +18,19 @@
package com.viaversion.viaversion.protocols.v1_12_2to1_13.provider.blockentities;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.provider.BlockEntityProvider;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Pair;
import java.util.HashMap;
import java.util.Map;
public class FlowerPotHandler implements BlockEntityProvider.BlockEntityHandler {
// Object -> string (id without namespace) or byte (numeric id)
private static final Map<Pair<?, Byte>, Integer> flowers = new HashMap<>();
private static final int EMPTY_POT = 5265;
private static final Map<String, Byte> STRING_TO_BYTE_ID = new HashMap<>();
private static final Map<IntIdPair, Integer> FLOWERS = new HashMap<>();
static {
register("air", (byte) 0, (byte) 0, 5265);
@@ -55,37 +58,40 @@ public class FlowerPotHandler implements BlockEntityProvider.BlockEntityHandler
}
public static void register(String identifier, byte numbericBlockId, byte blockData, int newId) {
flowers.put(new Pair<>(identifier, blockData), newId);
flowers.put(new Pair<>(numbericBlockId, blockData), newId);
private static void register(String identifier, byte blockId, byte blockData, int newId) {
STRING_TO_BYTE_ID.put(identifier, blockId);
FLOWERS.put(new IntIdPair(blockId, blockData), newId);
}
@Override
public int transform(UserConnection user, CompoundTag tag) {
Object item = tag.contains("Item") ? tag.get("Item").getValue() : null;
Object data = tag.contains("Data") ? tag.get("Data").getValue() : null;
// Convert item to String without namespace or to Byte
if (item instanceof String) {
item = Key.stripMinecraftNamespace((String) item);
} else if (item instanceof Number) {
item = ((Number) item).byteValue();
} else {
item = (byte) 0;
Tag itemTag = tag.get("Item");
byte item = 0;
if (itemTag instanceof StringTag stringTag) {
item = STRING_TO_BYTE_ID.getOrDefault(Key.stripMinecraftNamespace(stringTag.getValue()), (byte) 0);
} else if (itemTag instanceof NumberTag numberTag) {
item = numberTag.asByte();
}
// Convert data to Byte
if (data instanceof Number) {
data = ((Number) data).byteValue();
} else {
data = (byte) 0;
byte data = 0;
if (tag.get("Data") instanceof NumberTag dataTag) {
data = dataTag.asByte();
}
Integer flower = flowers.get(new Pair<>(item, (byte) data));
if (flower != null) return flower;
flower = flowers.get(new Pair<>(item, (byte) 0));
if (flower != null) return flower;
Integer flower = FLOWERS.get(new IntIdPair(item, data));
if (flower != null) {
return flower;
}
return 5265; // Fallback to empty pot
flower = FLOWERS.get(new IntIdPair(item, (byte) 0));
if (flower != null) {
return flower;
}
return EMPTY_POT; // Fallback to empty pot
}
private record IntIdPair(int id, byte data) {
}
}
@@ -205,23 +205,23 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
String old = channel;
channel = getOldPluginChannelId(channel);
if (channel == null) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
if (Via.getManager().isDebug()) {
protocol.getLogger().warning("Ignoring serverbound plugin message with channel: " + old);
}
wrapper.cancel();
return;
} else if (channel.equals("REGISTER") || channel.equals("UNREGISTER")) {
String[] channels = new String(wrapper.read(Types.REMAINING_BYTES), StandardCharsets.UTF_8).split("\0");
String[] channels = new String(wrapper.read(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA), StandardCharsets.UTF_8).split("\0");
List<String> rewrittenChannels = new ArrayList<>();
for (String s : channels) {
String rewritten = getOldPluginChannelId(s);
if (rewritten != null) {
rewrittenChannels.add(rewritten);
} else if (!Via.getConfig().isSuppressConversionWarnings()) {
} else if (Via.getManager().isDebug()) {
protocol.getLogger().warning("Ignoring plugin channel in serverbound " + channel + ": " + s);
}
}
wrapper.write(Types.REMAINING_BYTES, Joiner.on('\0').join(rewrittenChannels).getBytes(StandardCharsets.UTF_8));
wrapper.write(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA, Joiner.on('\0').join(rewrittenChannels).getBytes(StandardCharsets.UTF_8));
}
wrapper.set(Types.STRING, 0, channel);
});
@@ -172,16 +172,16 @@ public class Protocol1_15_2To1_16 extends AbstractProtocol<ClientboundPackets1_1
final String channel = wrapper.get(Types.STRING, 0);
final String namespacedChannel = Key.namespaced(channel);
if (channel.length() > 32) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
if (Via.getManager().isDebug()) {
getLogger().warning("Ignoring serverbound plugin channel, as it is longer than 32 characters: " + channel);
}
wrapper.cancel();
} else if (namespacedChannel.equals("minecraft:register") || namespacedChannel.equals("minecraft:unregister")) {
String[] channels = new String(wrapper.read(Types.REMAINING_BYTES), StandardCharsets.UTF_8).split("\0");
String[] channels = new String(wrapper.read(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA), StandardCharsets.UTF_8).split("\0");
List<String> checkedChannels = new ArrayList<>(channels.length);
for (String registeredChannel : channels) {
if (registeredChannel.length() > 32) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
if (Via.getManager().isDebug()) {
getLogger().warning("Ignoring serverbound plugin channel register of '" + registeredChannel + "', as it is longer than 32 characters");
}
continue;
@@ -195,7 +195,7 @@ public class Protocol1_15_2To1_16 extends AbstractProtocol<ClientboundPackets1_1
return;
}
wrapper.write(Types.REMAINING_BYTES, Joiner.on('\0').join(checkedChannels).getBytes(StandardCharsets.UTF_8));
wrapper.write(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA, Joiner.on('\0').join(checkedChannels).getBytes(StandardCharsets.UTF_8));
}
});
}
@@ -168,6 +168,7 @@ public final class WorldPacketRewriter1_18 {
lightStorage.getLight(chunk.getX(), chunk.getZ()) : lightStorage.removeLight(chunk.getX(), chunk.getZ());
if (light == null) {
protocol.getLogger().warning("No light data found for chunk at " + chunk.getX() + ", " + chunk.getZ() + ". Chunk was already loaded: " + alreadyLoaded);
protocol.getLogger().warning("This means another plugin is sending chunk data in a bad order, or you need to re-enable the cache-1_17-light config option.");
final BitSet emptyLightMask = new BitSet();
emptyLightMask.set(0, tracker.currentWorldSectionHeight() + 2);
@@ -92,7 +92,9 @@ public final class Protocol1_17To1_17_1 extends AbstractProtocol<ClientboundPack
ListTag<StringTag> pagesTag = new ListTag<>(StringTag.class);
for (int i = 0; i < pages; i++) {
String page = wrapper.read(PAGE_STRING_TYPE);
pagesTag.add(new StringTag(page));
if (i < 200) { // Apply network limit as per game code
pagesTag.add(new StringTag(page));
}
}
// Legacy servers don't like an empty pages list
@@ -50,7 +50,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
wrapper.passthrough(Types.STRING); // Group
wrapper.passthrough(Types.VAR_INT); // Crafting book category
handleIngredients(wrapper);
wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType())));
handleResult(wrapper);
}
@Override
@@ -61,8 +61,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
for (int i = 0; i < ingredients; i++) {
handleIngredient(wrapper);
}
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
@Override
@@ -70,8 +69,7 @@ public class RecipeRewriter1_19_3<C extends ClientboundPacketType> extends Recip
wrapper.passthrough(Types.STRING); // Group
wrapper.passthrough(Types.VAR_INT); // Crafting book category
handleIngredient(wrapper);
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
wrapper.passthrough(Types.FLOAT); // EXP
wrapper.passthrough(Types.VAR_INT); // Cooking time
}
@@ -77,6 +77,7 @@ public final class Protocol1_20_2To1_20_3 extends AbstractProtocol<ClientboundPa
cancelServerbound(ServerboundPackets1_20_3.CONTAINER_SLOT_STATE_CHANGED);
tagRewriter.registerGeneric(ClientboundPackets1_20_2.UPDATE_TAGS);
tagRewriter.registerGeneric(ClientboundConfigurationPackets1_20_2.UPDATE_TAGS);
final SoundRewriter<ClientboundPacket1_20_2> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_20_2.SOUND);
@@ -307,7 +308,6 @@ public final class Protocol1_20_2To1_20_3 extends AbstractProtocol<ClientboundPa
registerServerbound(ServerboundConfigurationPackets1_20_2.RESOURCE_PACK, resourcePackStatusHandler());
registerClientbound(ClientboundConfigurationPackets1_20_2.RESOURCE_PACK, ClientboundConfigurationPackets1_20_3.RESOURCE_PACK_PUSH, resourcePackHandler(ClientboundConfigurationPackets1_20_3.RESOURCE_PACK_POP));
tagRewriter.registerGeneric(ClientboundConfigurationPackets1_20_2.UPDATE_TAGS);
}
private PacketHandler resourcePackStatusHandler() {
@@ -39,9 +39,7 @@ public class RecipeRewriter1_20_3<C extends ClientboundPacketType> extends Recip
for (int i = 0; i < ingredients; i++) {
handleIngredient(wrapper);
}
final Item item = rewrite(wrapper.user(), wrapper.read(itemType())); // Result
wrapper.write(mappedItemType(), item);
handleResult(wrapper);
wrapper.passthrough(Types.BOOLEAN); // Show notification
}
@@ -270,13 +270,13 @@ public final class Protocol1_20_3To1_20_5 extends AbstractProtocol<ClientboundPa
.add(StructuredDataKey.HIDE_TOOLTIP).add(StructuredDataKey.FOOD1_20_5).add(StructuredDataKey.FIRE_RESISTANT)
.add(StructuredDataKey.CUSTOM_NAME).add(StructuredDataKey.LORE).add(StructuredDataKey.ENCHANTMENTS)
.add(StructuredDataKey.CAN_PLACE_ON).add(StructuredDataKey.CAN_BREAK).add(StructuredDataKey.ATTRIBUTE_MODIFIERS1_20_5)
.add(StructuredDataKey.CUSTOM_MODEL_DATA).add(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP).add(StructuredDataKey.REPAIR_COST)
.add(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5).add(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP).add(StructuredDataKey.REPAIR_COST)
.add(StructuredDataKey.CREATIVE_SLOT_LOCK).add(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE).add(StructuredDataKey.INTANGIBLE_PROJECTILE)
.add(StructuredDataKey.STORED_ENCHANTMENTS).add(StructuredDataKey.DYED_COLOR).add(StructuredDataKey.MAP_COLOR)
.add(StructuredDataKey.MAP_ID).add(StructuredDataKey.MAP_DECORATIONS).add(StructuredDataKey.MAP_POST_PROCESSING)
.add(StructuredDataKey.CHARGED_PROJECTILES1_20_5).add(StructuredDataKey.BUNDLE_CONTENTS1_20_5).add(StructuredDataKey.POTION_CONTENTS1_20_5)
.add(StructuredDataKey.SUSPICIOUS_STEW_EFFECTS).add(StructuredDataKey.WRITABLE_BOOK_CONTENT).add(StructuredDataKey.WRITTEN_BOOK_CONTENT)
.add(StructuredDataKey.TRIM).add(StructuredDataKey.DEBUG_STICK_STATE).add(StructuredDataKey.ENTITY_DATA)
.add(StructuredDataKey.TRIM1_20_5).add(StructuredDataKey.DEBUG_STICK_STATE).add(StructuredDataKey.ENTITY_DATA)
.add(StructuredDataKey.BUCKET_ENTITY_DATA).add(StructuredDataKey.BLOCK_ENTITY_DATA).add(StructuredDataKey.INSTRUMENT1_20_5)
.add(StructuredDataKey.RECIPES).add(StructuredDataKey.LODESTONE_TRACKER).add(StructuredDataKey.FIREWORK_EXPLOSION)
.add(StructuredDataKey.FIREWORKS).add(StructuredDataKey.PROFILE).add(StructuredDataKey.NOTE_BLOCK_SOUND)
@@ -17,62 +17,62 @@
*/
package com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data;
import java.util.HashMap;
import java.util.Map;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
public final class MaxStackSize1_20_3 {
private static final Map<Integer, Integer> mapping = new HashMap<>();
private static final Int2IntMap MAPPING = new Int2IntOpenHashMap();
static {
fill(521, 537, 1);
fill(764, 790, 1);
mapping.put(793, 1);
mapping.put(795, 1);
mapping.put(797, 1);
MAPPING.put(793, 1);
MAPPING.put(795, 1);
MAPPING.put(797, 1);
fill(814, 843, 1);
mapping.put(846, 1);
mapping.put(853, 1);
MAPPING.put(846, 1);
MAPPING.put(853, 1);
fill(854, 876, 1);
fill(883, 905, 16);
fill(906, 908, 1);
mapping.put(909, 16);
MAPPING.put(909, 16);
fill(911, 917, 1);
mapping.put(924, 16);
mapping.put(927, 1);
mapping.put(928, 1);
mapping.put(930, 1);
MAPPING.put(924, 16);
MAPPING.put(927, 1);
MAPPING.put(928, 1);
MAPPING.put(930, 1);
fill(960, 976, 1);
mapping.put(980, 1);
mapping.put(990, 16);
mapping.put(995, 1);
mapping.put(1085, 1);
mapping.put(1086, 16);
mapping.put(1107, 1);
mapping.put(1113, 1);
mapping.put(1116, 16);
MAPPING.put(980, 1);
MAPPING.put(990, 16);
MAPPING.put(995, 1);
MAPPING.put(1085, 1);
MAPPING.put(1086, 16);
MAPPING.put(1107, 1);
MAPPING.put(1113, 1);
MAPPING.put(1116, 16);
fill(1117, 1120, 1);
mapping.put(1123, 1);
MAPPING.put(1123, 1);
fill(1126, 1141, 16);
mapping.put(1149, 1);
mapping.put(1151, 1);
MAPPING.put(1149, 1);
MAPPING.put(1151, 1);
fill(1154, 1156, 1);
fill(1159, 1176, 1);
mapping.put(1178, 1);
mapping.put(1182, 1);
mapping.put(1183, 1);
MAPPING.put(1178, 1);
MAPPING.put(1182, 1);
MAPPING.put(1183, 1);
fill(1185, 1191, 1);
mapping.put(1212, 16);
mapping.put(1256, 1);
MAPPING.put(1212, 16);
MAPPING.put(1256, 1);
}
public static int getMaxStackSize(final int identifier) {
return mapping.getOrDefault(identifier, 64);
return MAPPING.getOrDefault(identifier, 64);
}
private static void fill(final int start, final int end, final int value) {
for (int i = start; i <= end; i++) {
mapping.put(i, value);
MAPPING.put(i, value);
}
}
@@ -18,9 +18,10 @@
package com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;
public enum ServerboundConfigurationPackets1_20_5 implements ServerboundPacket1_20_5, ServerboundPacket1_21_2 {
public enum ServerboundConfigurationPackets1_20_5 implements ServerboundPacket1_20_5, ServerboundPacket1_21_2, ServerboundPacket1_21_4 {
CLIENT_INFORMATION, // 0x00
COOKIE_RESPONSE, // 0x01
@@ -105,8 +105,7 @@ import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.UUIDUtil;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -343,12 +342,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
}
});
final RecipeRewriter1_20_3<ClientboundPacket1_20_3> recipeRewriter = new RecipeRewriter1_20_3<>(protocol) {
@Override
protected Item rewrite(final UserConnection connection, @Nullable Item item) {
return handleNonEmptyItemToClient(connection, item);
}
};
final RecipeRewriter1_20_5<ClientboundPacket1_20_3> recipeRewriter = new RecipeRewriter1_20_5<>(protocol);
protocol.registerClientbound(ClientboundPackets1_20_3.UPDATE_RECIPES, wrapper -> {
final int size = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < size; i++) {
@@ -465,7 +459,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
final NumberTag customModelData = tag.getNumberTag("CustomModelData");
if (customModelData != null) {
data.set(StructuredDataKey.CUSTOM_MODEL_DATA, customModelData.asInt());
data.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, customModelData.asInt());
}
final CompoundTag blockState = tag.getCompoundTag("BlockStateTag");
@@ -625,7 +619,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
item.dataContainer().set(StructuredDataKey.MAX_DAMAGE, 326);
}
}
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_8)) {
if (Via.getConfig().swordBlockingViaConsumable() && serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_8)) {
if (item.identifier() == 814 || item.identifier() == 819 || item.identifier() == 824 || item.identifier() == 829 || item.identifier() == 834) { // swords
// Make sword "eatable" to enable clientside instant blocking on 1.8. Consume time is set really high, so the eating animation doesn't play
item.dataContainer().set(StructuredDataKey.FOOD1_20_5, new FoodProperties1_20_5(0, 0F, true, 3600, null, new FoodEffect[0]));
@@ -1000,17 +994,14 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
final CompoundTag overrideArmorMaterialsTag = materialCompoundTag.getCompoundTag("override_armor_materials");
final Tag descriptionTag = materialCompoundTag.get("description");
final Int2ObjectMap<String> overrideArmorMaterials = new Int2ObjectOpenHashMap<>();
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
if (overrideArmorMaterialsTag != null) {
for (final Map.Entry<String, Tag> entry : overrideArmorMaterialsTag.entrySet()) {
if (!(entry.getValue() instanceof StringTag valueTag)) {
continue;
}
try {
final int id = Integer.parseInt(entry.getKey());
overrideArmorMaterials.put(id, valueTag.getValue());
} catch (NumberFormatException ignored) {
}
overrideArmorMaterials.put(entry.getKey(), valueTag.getValue());
}
}
@@ -1054,7 +1045,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
));
} else return;
data.set(StructuredDataKey.TRIM, new ArmorTrim(materialHolder, patternHolder, showInTooltip));
data.set(StructuredDataKey.TRIM1_20_5, new ArmorTrim(materialHolder, patternHolder, showInTooltip));
}
private void updateMobTags(final StructuredDataContainer data, final CompoundTag tag) {
@@ -92,7 +92,6 @@ import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.UUIDUtil;
import com.viaversion.viaversion.util.Unit;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.List;
@@ -126,7 +125,7 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Co
register(StructuredDataKey.CAN_PLACE_ON, this::convertCanPlaceOn);
register(StructuredDataKey.CAN_BREAK, this::convertCanBreak);
register(StructuredDataKey.ATTRIBUTE_MODIFIERS1_20_5, this::convertAttributeModifiers);
register(StructuredDataKey.CUSTOM_MODEL_DATA, this::convertCustomModelData);
register(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, this::convertCustomModelData);
register(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP, this::convertHideAdditionalTooltip);
register(StructuredDataKey.HIDE_TOOLTIP, this::convertHideTooltip);
register(StructuredDataKey.REPAIR_COST, this::convertRepairCost);
@@ -148,7 +147,7 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Co
register(StructuredDataKey.SUSPICIOUS_STEW_EFFECTS, this::convertSuspiciousStewEffects);
register(StructuredDataKey.WRITABLE_BOOK_CONTENT, this::convertWritableBookContent);
register(StructuredDataKey.WRITTEN_BOOK_CONTENT, this::convertWrittenBookContent);
register(StructuredDataKey.TRIM, this::convertTrim);
register(StructuredDataKey.TRIM1_20_5, this::convertTrim);
register(StructuredDataKey.DEBUG_STICK_STATE, this::convertDebugStickRate);
register(StructuredDataKey.ENTITY_DATA, this::convertEntityData);
register(StructuredDataKey.BUCKET_ENTITY_DATA, this::convertBucketEntityData);
@@ -693,8 +692,8 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Co
}
final CompoundTag overrideArmorMaterialsTag = new CompoundTag();
for (final Int2ObjectMap.Entry<String> entry : armorTrimMaterial.overrideArmorMaterials().int2ObjectEntrySet()) {
final String materialKey = ArmorMaterials1_20_5.idToKey(entry.getIntKey());
for (final Map.Entry<String, String> entry : armorTrimMaterial.overrideArmorMaterials().entrySet()) {
final String materialKey = ArmorMaterials1_20_5.idToKey(Integer.parseInt(entry.getKey()));
if (materialKey != null) {
overrideArmorMaterialsTag.putString(materialKey, entry.getValue());
}
@@ -0,0 +1,54 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
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.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;
import java.util.ArrayList;
import java.util.List;
final class RecipeRewriter1_20_5 <C extends ClientboundPacketType> extends RecipeRewriter1_20_3<C> {
public RecipeRewriter1_20_5(final Protocol<C, ?, ?, ?> protocol) {
super(protocol);
}
@Override
protected void handleIngredient(final PacketWrapper wrapper) {
final Item[] items = wrapper.read(itemArrayType());
final List<Item> newItems = new ArrayList<>(items.length);
for (final Item item : items) {
if (item == null || item.isEmpty()) continue;
newItems.add(rewrite(wrapper.user(), item));
}
wrapper.write(mappedItemArrayType(), newItems.toArray(new Item[0]));
}
@Override
protected void handleResult(final PacketWrapper wrapper) {
Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
if (result == null || result.isEmpty()) {
result = new StructuredItem(1, 1);
}
wrapper.write(mappedItemType(), result);
}
}
@@ -65,7 +65,6 @@ import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.storage.BannerPattern
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.UUIDUtil;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.Arrays;
import java.util.Collections;
@@ -136,6 +135,7 @@ public final class StructuredDataConverter {
}
final CompoundTag modifierTag = new CompoundTag();
modifierTag.put("UUID", new IntArrayTag(UUIDUtil.toIntArray(modifier.modifier().uuid())));
modifierTag.putString("AttributeName", identifier.equals("generic.jump_strength") ? "horse.jump_strength" : identifier);
modifierTag.putString("Name", modifier.modifier().name());
modifierTag.putDouble("Amount", modifier.modifier().amount());
@@ -151,7 +151,7 @@ public final class StructuredDataConverter {
putHideFlag(tag, HIDE_ATTRIBUTES);
}
});
register(StructuredDataKey.CUSTOM_MODEL_DATA, (data, tag) -> tag.putInt("CustomModelData", data));
register(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, (data, tag) -> tag.putInt("CustomModelData", data));
register(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP, (data, tag) -> putHideFlag(tag, 0x20));
register(StructuredDataKey.REPAIR_COST, (data, tag) -> tag.putInt("RepairCost", data));
register(StructuredDataKey.DYED_COLOR, (data, tag) -> {
@@ -513,7 +513,7 @@ public final class StructuredDataConverter {
tag.putInt("map_scale_direction", 1);
}
});
register(StructuredDataKey.TRIM, (connection, data, tag) -> {
register(StructuredDataKey.TRIM1_20_5, (connection, data, tag) -> {
final CompoundTag trimTag = new CompoundTag();
final ArmorTrimStorage trimStorage = connection.get(ArmorTrimStorage.class);
if (data.material().isDirect()) {
@@ -530,8 +530,8 @@ public final class StructuredDataConverter {
final CompoundTag overrideArmorMaterials = new CompoundTag();
if (!material.overrideArmorMaterials().isEmpty()) {
for (final Int2ObjectMap.Entry<String> entry : material.overrideArmorMaterials().int2ObjectEntrySet()) {
overrideArmorMaterials.put(Integer.toString(entry.getIntKey()), new StringTag(entry.getValue()));
for (final Map.Entry<String, String> entry : material.overrideArmorMaterials().entrySet()) {
overrideArmorMaterials.put(entry.getKey(), new StringTag(entry.getValue()));
}
materialTag.put("override_armor_materials", overrideArmorMaterials);
}
@@ -46,7 +46,7 @@ import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.BlockItemPacke
import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.ComponentRewriter1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.EntityPacketRewriter1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.OnGroundTracker;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.PlayerPositionStorage;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.SoundRewriter;
@@ -203,13 +203,13 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
.add(StructuredDataKey.HIDE_TOOLTIP).add(StructuredDataKey.FIRE_RESISTANT)
.add(StructuredDataKey.CUSTOM_NAME).add(StructuredDataKey.LORE).add(StructuredDataKey.ENCHANTMENTS)
.add(StructuredDataKey.CAN_PLACE_ON).add(StructuredDataKey.CAN_BREAK)
.add(StructuredDataKey.CUSTOM_MODEL_DATA).add(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP).add(StructuredDataKey.REPAIR_COST)
.add(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5).add(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP).add(StructuredDataKey.REPAIR_COST)
.add(StructuredDataKey.CREATIVE_SLOT_LOCK).add(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE).add(StructuredDataKey.INTANGIBLE_PROJECTILE)
.add(StructuredDataKey.STORED_ENCHANTMENTS).add(StructuredDataKey.DYED_COLOR).add(StructuredDataKey.MAP_COLOR)
.add(StructuredDataKey.MAP_ID).add(StructuredDataKey.MAP_DECORATIONS).add(StructuredDataKey.MAP_POST_PROCESSING)
.add(StructuredDataKey.CHARGED_PROJECTILES1_21).add(StructuredDataKey.BUNDLE_CONTENTS1_21).add(StructuredDataKey.POTION_CONTENTS1_20_5)
.add(StructuredDataKey.SUSPICIOUS_STEW_EFFECTS).add(StructuredDataKey.WRITABLE_BOOK_CONTENT).add(StructuredDataKey.WRITTEN_BOOK_CONTENT)
.add(StructuredDataKey.TRIM).add(StructuredDataKey.DEBUG_STICK_STATE).add(StructuredDataKey.ENTITY_DATA)
.add(StructuredDataKey.TRIM1_20_5).add(StructuredDataKey.DEBUG_STICK_STATE).add(StructuredDataKey.ENTITY_DATA)
.add(StructuredDataKey.BUCKET_ENTITY_DATA).add(StructuredDataKey.BLOCK_ENTITY_DATA).add(StructuredDataKey.INSTRUMENT1_20_5)
.add(StructuredDataKey.RECIPES).add(StructuredDataKey.LODESTONE_TRACKER).add(StructuredDataKey.FIREWORK_EXPLOSION)
.add(StructuredDataKey.FIREWORKS).add(StructuredDataKey.PROFILE).add(StructuredDataKey.NOTE_BLOCK_SOUND)
@@ -234,7 +234,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_20_5.PLAYER));
connection.put(new EfficiencyAttributeStorage());
connection.put(new OnGroundTracker());
connection.put(new PlayerPositionStorage());
}
@Override
@@ -26,6 +26,7 @@ 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.minecraft.item.data.Enchantments;
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;
@@ -38,7 +39,8 @@ import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPac
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.data.AttributeModifierMappings1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.OnGroundTracker;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.PlayerPositionStorage;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import java.util.Arrays;
@@ -48,6 +50,14 @@ import java.util.Objects;
public final class BlockItemPacketRewriter1_21 extends StructuredItemRewriter<ClientboundPacket1_20_5, ServerboundPacket1_20_5, Protocol1_20_5To1_21> {
private static final List<String> DISCS = List.of("11", "13", "5", "blocks", "cat", "chirp", "far", "mall", "mellohi", "otherside", "pigstep", "relic", "stal", "strad", "wait", "ward");
private static final int HELMET_SLOT = 5;
private static final int CHESTPLATE_SLOT = 6;
private static final int LEGGINGS_SLOT = 7;
private static final int BOOTS_SLOT = 8;
private static final int AQUA_AFFINITY_ID = 6;
private static final int DEPTH_STRIDER_ID = 8;
private static final int SWIFT_SNEAK_ID = 12;
public BlockItemPacketRewriter1_21(final Protocol1_20_5To1_21 protocol) {
super(protocol,
@@ -67,7 +77,29 @@ public final class BlockItemPacketRewriter1_21 extends StructuredItemRewriter<Cl
registerCooldown(ClientboundPackets1_20_5.COOLDOWN);
registerSetContent1_17_1(ClientboundPackets1_20_5.CONTAINER_SET_CONTENT);
registerSetSlot1_17_1(ClientboundPackets1_20_5.CONTAINER_SET_SLOT);
protocol.registerClientbound(ClientboundPackets1_20_5.CONTAINER_SET_SLOT, wrapper -> {
final short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE); // Container id
wrapper.passthrough(Types.VAR_INT); // State id
final short slotId = wrapper.passthrough(Types.SHORT); // Slot id
final Item item = handleItemToClient(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), item);
// When a players' armor is set, update their attributes
if (containerId != 0 || slotId > BOOTS_SLOT || slotId < HELMET_SLOT || slotId == CHESTPLATE_SLOT) {
return;
}
final EfficiencyAttributeStorage storage = wrapper.user().get(EfficiencyAttributeStorage.class);
Enchantments enchants = item.dataContainer().get(StructuredDataKey.ENCHANTMENTS);
EfficiencyAttributeStorage.ActiveEnchants active = storage.activeEnchants();
active = switch (slotId) {
case HELMET_SLOT -> active.aquaAffinity(enchants == null ? 0 : enchants.getLevel(AQUA_AFFINITY_ID));
case LEGGINGS_SLOT -> active.swiftSneak(enchants == null ? 0 : enchants.getLevel(SWIFT_SNEAK_ID));
case BOOTS_SLOT -> active.depthStrider(enchants == null ? 0 : enchants.getLevel(DEPTH_STRIDER_ID));
default -> active;
};
storage.setEnchants(-1, wrapper.user(), active);
});
registerAdvancements1_20_3(ClientboundPackets1_20_5.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_20_5.SET_EQUIPMENT);
registerContainerClick1_17_1(ServerboundPackets1_20_5.CONTAINER_CLICK);
@@ -111,13 +143,16 @@ public final class BlockItemPacketRewriter1_21 extends StructuredItemRewriter<Cl
if (!Via.getConfig().fix1_21PlacementRotation()) {
return;
}
final OnGroundTracker tracker = wrapper.user().get(OnGroundTracker.class);
final PlayerPositionStorage storage = wrapper.user().get(PlayerPositionStorage.class);
// Not correct but *enough* for vanilla/normal servers to have block placement synchronized
final PacketWrapper playerRotation = wrapper.create(ServerboundPackets1_20_5.MOVE_PLAYER_ROT);
final PacketWrapper playerRotation = wrapper.create(ServerboundPackets1_20_5.MOVE_PLAYER_POS_ROT);
playerRotation.write(Types.DOUBLE, storage.x());
playerRotation.write(Types.DOUBLE, storage.y());
playerRotation.write(Types.DOUBLE, storage.z());
playerRotation.write(Types.FLOAT, yaw);
playerRotation.write(Types.FLOAT, pitch);
playerRotation.write(Types.BOOLEAN, tracker.onGround());
playerRotation.write(Types.BOOLEAN, storage.onGround());
playerRotation.sendToServer(Protocol1_20_5To1_21.class);
wrapper.sendToServer(Protocol1_20_5To1_21.class);
@@ -38,7 +38,7 @@ import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPac
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.data.Paintings1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.OnGroundTracker;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.PlayerPositionStorage;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
@@ -111,7 +111,8 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
map(Types.STRING); // World
handler(worldDataTrackerHandlerByKey1_20_5(3));
handler(playerTrackerHandler());
handler(wrapper -> wrapper.user().get(EfficiencyAttributeStorage.class).onLoginSent(wrapper.user()));
handler(wrapper -> wrapper.user().get(EfficiencyAttributeStorage.class)
.onLoginSent(wrapper.get(Types.INT, 0), wrapper.user()));
}
});
@@ -122,39 +123,51 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
// Resend attribute modifiers from items
wrapper.user().get(EfficiencyAttributeStorage.class).onRespawn(wrapper.user());
wrapper.user().put(new PlayerPositionStorage());
});
// Tracks on ground state for block interactions
if (!Via.getConfig().fix1_21PlacementRotation()) {
return;
}
// Tracking player position and on ground for block interactions, rotations is kept from the interaction packet
protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS, wrapper -> {
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.user().get(OnGroundTracker.class).setOnGround(wrapper.passthrough(Types.BOOLEAN));
if (Via.getConfig().fix1_21PlacementRotation()) {
storePosition(wrapper);
storeOnGround(wrapper);
}
});
protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_ROT, wrapper -> {
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
wrapper.user().get(OnGroundTracker.class).setOnGround(wrapper.passthrough(Types.BOOLEAN));
if (Via.getConfig().fix1_21PlacementRotation()) {
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
storeOnGround(wrapper);
}
});
protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS_ROT, wrapper -> {
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
wrapper.user().get(OnGroundTracker.class).setOnGround(wrapper.passthrough(Types.BOOLEAN));
if (Via.getConfig().fix1_21PlacementRotation()) {
storePosition(wrapper);
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
storeOnGround(wrapper);
}
});
protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_STATUS_ONLY, wrapper -> {
wrapper.user().get(OnGroundTracker.class).setOnGround(wrapper.passthrough(Types.BOOLEAN));
if (Via.getConfig().fix1_21PlacementRotation()) {
storeOnGround(wrapper);
}
});
}
private void storePosition(final PacketWrapper wrapper) {
final double x = wrapper.passthrough(Types.DOUBLE);
final double y = wrapper.passthrough(Types.DOUBLE);
final double z = wrapper.passthrough(Types.DOUBLE);
wrapper.user().get(PlayerPositionStorage.class).setPosition(x, y, z);
}
private void storeOnGround(final PacketWrapper wrapper) {
final boolean onGround = wrapper.passthrough(Types.BOOLEAN);
wrapper.user().get(PlayerPositionStorage.class).setOnGround(onGround);
}
@Override
protected void registerRewrites() {
filter().handler((event, data) -> {
@@ -45,25 +45,10 @@ public final class EfficiencyAttributeStorage implements StorableObject {
private volatile boolean loginSent;
private ActiveEnchants activeEnchants = DEFAULT;
public void setEnchants(final int entityId, final UserConnection connection, final int efficiency, final int soulSpeed,
final int swiftSneak, final int aquaAffinity, final int depthStrider) {
// Always called from the main thread
if (efficiency == activeEnchants.efficiency.level
&& soulSpeed == activeEnchants.soulSpeed.level
&& swiftSneak == activeEnchants.swiftSneak.level
&& aquaAffinity == activeEnchants.aquaAffinity.level
&& depthStrider == activeEnchants.depthStrider.level) {
return;
}
public void setEnchants(final int entityId, final UserConnection connection, final ActiveEnchants enchants) {
if (activeEnchants == enchants) return;
synchronized (lock) {
this.activeEnchants = new ActiveEnchants(entityId,
new ActiveEnchant(activeEnchants.efficiency, efficiency),
new ActiveEnchant(activeEnchants.soulSpeed, soulSpeed),
new ActiveEnchant(activeEnchants.swiftSneak, swiftSneak),
new ActiveEnchant(activeEnchants.aquaAffinity, aquaAffinity),
new ActiveEnchant(activeEnchants.depthStrider, depthStrider)
);
this.activeEnchants = entityId == -1 ? enchants : enchants.withEntityId(entityId);
this.attributesSent = false;
}
sendAttributesPacket(connection, false);
@@ -73,7 +58,10 @@ public final class EfficiencyAttributeStorage implements StorableObject {
return activeEnchants;
}
public void onLoginSent(final UserConnection connection) {
public void onLoginSent(final int entityId, final UserConnection connection) {
synchronized (lock) {
activeEnchants = activeEnchants.withEntityId(entityId);
}
// Always called from the netty thread
this.loginSent = true;
sendAttributesPacket(connection, false);
@@ -128,6 +116,59 @@ public final class EfficiencyAttributeStorage implements StorableObject {
public record ActiveEnchants(int entityId, ActiveEnchant efficiency, ActiveEnchant soulSpeed,
ActiveEnchant swiftSneak, ActiveEnchant aquaAffinity, ActiveEnchant depthStrider) {
private ActiveEnchants withEntityId(int entityId) {
return this.entityId == entityId ? this : new ActiveEnchants(entityId,
efficiency,
soulSpeed,
swiftSneak,
aquaAffinity,
depthStrider);
}
public ActiveEnchants efficiency(int level) {
return efficiency.level == level ? this : new ActiveEnchants(entityId,
new ActiveEnchant(efficiency, level),
soulSpeed,
swiftSneak,
aquaAffinity,
depthStrider);
}
public ActiveEnchants soulSpeed(int level) {
return soulSpeed.level == level ? this : new ActiveEnchants(entityId,
efficiency,
new ActiveEnchant(soulSpeed, level),
swiftSneak,
aquaAffinity,
depthStrider);
}
public ActiveEnchants swiftSneak(int level) {
return swiftSneak.level == level ? this : new ActiveEnchants(entityId,
efficiency,
soulSpeed,
new ActiveEnchant(swiftSneak, level),
aquaAffinity,
depthStrider);
}
public ActiveEnchants aquaAffinity(int level) {
return aquaAffinity.level == level ? this : new ActiveEnchants(entityId,
efficiency,
soulSpeed,
swiftSneak,
new ActiveEnchant(aquaAffinity, level),
depthStrider);
}
public ActiveEnchants depthStrider(int level) {
return depthStrider.level == level ? this : new ActiveEnchants(entityId,
efficiency,
soulSpeed,
swiftSneak,
aquaAffinity,
new ActiveEnchant(depthStrider, level));
}
}
public record ActiveEnchant(EnchantAttributeModifier modifier, int previousLevel, int level) {
@@ -19,10 +19,31 @@ package com.viaversion.viaversion.protocols.v1_20_5to1_21.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public final class OnGroundTracker implements StorableObject {
public final class PlayerPositionStorage implements StorableObject {
private double x;
private double y;
private double z;
private boolean onGround;
public double x() {
return x;
}
public double y() {
return y;
}
public double z() {
return z;
}
public void setPosition(final double x, final double y, final double z) {
this.x = x;
this.y = y;
this.z = z;
}
public boolean onGround() {
return onGround;
}
@@ -124,11 +124,7 @@ public final class Protocol1_20To1_20_2 extends AbstractProtocol<ClientboundPack
wrapper.resetReader();
wrapper.user().put(new LastTags(wrapper));
});
registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.UPDATE_TAGS, wrapper -> {
tagRewriter.handleGeneric(wrapper);
wrapper.resetReader();
wrapper.user().put(new LastTags(wrapper));
});
registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.UPDATE_TAGS, this::handleConfigTags);
registerClientbound(ClientboundPackets1_19_4.SET_DISPLAY_OBJECTIVE, wrapper -> {
final byte slot = wrapper.read(Types.BYTE);
@@ -229,6 +225,15 @@ public final class Protocol1_20To1_20_2 extends AbstractProtocol<ClientboundPack
});
}
private void handleConfigTags(final PacketWrapper wrapper) {
tagRewriter.handleGeneric(wrapper);
wrapper.resetReader();
final LastTags lastTags = new LastTags(wrapper);
lastTags.setSentDuringConfigPhase(true);
wrapper.user().put(lastTags);
}
private static void sanitizeCustomPayload(final PacketWrapper wrapper) {
final String channel = Key.namespaced(wrapper.get(Types.STRING, 0));
if (channel.equals("minecraft:brand")) {
@@ -299,6 +304,7 @@ public final class Protocol1_20To1_20_2 extends AbstractProtocol<ClientboundPack
} else if (unmappedId == ClientboundPackets1_19_4.UPDATE_ENABLED_FEATURES.getId()) {
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.UPDATE_ENABLED_FEATURES);
} else if (unmappedId == ClientboundPackets1_19_4.UPDATE_TAGS.getId()) {
handleConfigTags(packetWrapper); // Manually put through handler
packetWrapper.setPacketType(ClientboundConfigurationPackets1_20_2.UPDATE_TAGS);
} else {
// Not a packet that can be mapped to the configuration protocol
@@ -327,8 +333,12 @@ public final class Protocol1_20To1_20_2 extends AbstractProtocol<ClientboundPack
final LastTags lastTags = connection.get(LastTags.class);
if (lastTags != null) {
// The server might still follow up with a tags packet, but we wouldn't know
lastTags.sendLastTags(connection);
if (lastTags.sentDuringConfigPhase()) {
lastTags.setSentDuringConfigPhase(false);
} else {
// The server might still follow up with a tags packet, but we wouldn't know
lastTags.sendLastTags(connection);
}
}
if (lastResourcePack != null && connection.getProtocolInfo().protocolVersion() == ProtocolVersion.v1_20_2) {
@@ -31,6 +31,7 @@ import java.util.List;
public class LastTags implements StorableObject {
private final List<RegistryTags> registryTags = new ArrayList<>();
private boolean sentDuringConfigPhase;
public LastTags(final PacketWrapper wrapper) {
final int length = wrapper.passthrough(Types.VAR_INT);
@@ -65,6 +66,14 @@ public class LastTags implements StorableObject {
packet.send(Protocol1_20To1_20_2.class);
}
public void setSentDuringConfigPhase(final boolean sentDuringConfigPhase) {
this.sentDuringConfigPhase = sentDuringConfigPhase;
}
public boolean sentDuringConfigPhase() {
return sentDuringConfigPhase;
}
private record RegistryTags(String registryKey, List<TagData> tags) {
}
}
@@ -0,0 +1,244 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4;
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.Particle;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
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;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.BlockItemPacketRewriter1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.ComponentRewriter1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.EntityPacketRewriter1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.ParticleRewriter1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.AttributeRewriter;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.SoundRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import java.util.BitSet;
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
public final class Protocol1_21_2To1_21_4 extends AbstractProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_2, ServerboundPacket1_21_4> {
public static final MappingData MAPPINGS = new MappingDataBase("1.21.2", "1.21.4");
private final EntityPacketRewriter1_21_4 entityRewriter = new EntityPacketRewriter1_21_4(this);
private final BlockItemPacketRewriter1_21_4 itemRewriter = new BlockItemPacketRewriter1_21_4(this);
private final ParticleRewriter<ClientboundPacket1_21_2> particleRewriter = new ParticleRewriter1_21_4(this);
private final TagRewriter<ClientboundPacket1_21_2> tagRewriter = new TagRewriter<>(this);
private final ComponentRewriter<ClientboundPacket1_21_2> componentRewriter = new ComponentRewriter1_21_4(this);
public Protocol1_21_2To1_21_4() {
super(ClientboundPacket1_21_2.class, ClientboundPacket1_21_2.class, ServerboundPacket1_21_2.class, ServerboundPacket1_21_4.class);
}
@Override
protected void registerPackets() {
super.registerPackets();
tagRewriter.registerGeneric(ClientboundPackets1_21_2.UPDATE_TAGS);
tagRewriter.registerGeneric(ClientboundConfigurationPackets1_21.UPDATE_TAGS);
componentRewriter.registerOpenScreen(ClientboundPackets1_21_2.OPEN_SCREEN);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_ACTION_BAR_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_TITLE_TEXT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SET_SUBTITLE_TEXT);
componentRewriter.registerBossEvent(ClientboundPackets1_21_2.BOSS_EVENT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.DISCONNECT);
componentRewriter.registerTabList(ClientboundPackets1_21_2.TAB_LIST);
componentRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_2.PLAYER_COMBAT_KILL);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.SYSTEM_CHAT);
componentRewriter.registerComponentPacket(ClientboundPackets1_21_2.DISGUISED_CHAT);
componentRewriter.registerPing();
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21_2.EXPLODE);
registerClientbound(ClientboundPackets1_21_2.LEVEL_PARTICLES, wrapper -> {
wrapper.passthrough(Types.BOOLEAN); // Override limiter
wrapper.write(Types.BOOLEAN, false); // Always show
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.FLOAT); // Offset X
wrapper.passthrough(Types.FLOAT); // Offset Y
wrapper.passthrough(Types.FLOAT); // Offset Z
wrapper.passthrough(Types.FLOAT); // Particle Data
wrapper.passthrough(Types.INT); // Particle Count
final Particle particle = wrapper.passthroughAndMap(Types1_21_2.PARTICLE, Types1_21_4.PARTICLE);
particleRewriter.rewriteParticle(wrapper.user(), particle);
});
final SoundRewriter<ClientboundPacket1_21_2> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_2.SOUND);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_2.SOUND_ENTITY);
new StatisticsRewriter<>(this).register(ClientboundPackets1_21_2.AWARD_STATS);
new AttributeRewriter<>(this).register1_21(ClientboundPackets1_21_2.UPDATE_ATTRIBUTES);
registerClientbound(ClientboundPackets1_21_2.PLAYER_INFO_UPDATE, wrapper -> {
// Added "show hat" - true by default, keep it like that
final BitSet actions = wrapper.passthroughAndMap(Types.PROFILE_ACTIONS_ENUM1_21_2, Types.PROFILE_ACTIONS_ENUM1_21_4);
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
}
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
if (actions.get(6)) {
wrapper.passthrough(Types.VAR_INT); // List order
}
}
});
}
@Override
protected void onMappingDataLoaded() {
EntityTypes1_21_4.initialize(this);
Types1_21_4.PARTICLE.filler(this)
.reader("block", ParticleType.Readers.BLOCK)
.reader("block_marker", ParticleType.Readers.BLOCK)
.reader("dust_pillar", ParticleType.Readers.BLOCK)
.reader("falling_dust", ParticleType.Readers.BLOCK)
.reader("block_crumble", ParticleType.Readers.BLOCK)
.reader("dust", ParticleType.Readers.DUST1_21_2)
.reader("dust_color_transition", ParticleType.Readers.DUST_TRANSITION1_21_2)
.reader("vibration", ParticleType.Readers.VIBRATION1_20_3)
.reader("sculk_charge", ParticleType.Readers.SCULK_CHARGE)
.reader("shriek", ParticleType.Readers.SHRIEK)
.reader("entity_effect", ParticleType.Readers.COLOR)
.reader("trail", ParticleType.Readers.TRAIL1_21_4)
.reader("item", ParticleType.Readers.item(itemRewriter.mappedItemType()));
Types1_21_4.STRUCTURED_DATA.filler(this).add(StructuredDataKey.CUSTOM_DATA, StructuredDataKey.MAX_STACK_SIZE, StructuredDataKey.MAX_DAMAGE,
StructuredDataKey.UNBREAKABLE, StructuredDataKey.RARITY, StructuredDataKey.HIDE_TOOLTIP, StructuredDataKey.DAMAGE_RESISTANT,
StructuredDataKey.CUSTOM_NAME, StructuredDataKey.LORE, StructuredDataKey.ENCHANTMENTS, StructuredDataKey.CAN_PLACE_ON,
StructuredDataKey.CAN_BREAK, StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP,
StructuredDataKey.REPAIR_COST, StructuredDataKey.CREATIVE_SLOT_LOCK, StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE,
StructuredDataKey.INTANGIBLE_PROJECTILE, StructuredDataKey.STORED_ENCHANTMENTS, StructuredDataKey.DYED_COLOR,
StructuredDataKey.MAP_COLOR, StructuredDataKey.MAP_ID, StructuredDataKey.MAP_DECORATIONS, StructuredDataKey.MAP_POST_PROCESSING,
StructuredDataKey.POTION_CONTENTS1_21_2, StructuredDataKey.SUSPICIOUS_STEW_EFFECTS, StructuredDataKey.WRITABLE_BOOK_CONTENT,
StructuredDataKey.WRITTEN_BOOK_CONTENT, StructuredDataKey.TRIM1_21_4, StructuredDataKey.DEBUG_STICK_STATE, StructuredDataKey.ENTITY_DATA,
StructuredDataKey.BUCKET_ENTITY_DATA, StructuredDataKey.BLOCK_ENTITY_DATA, StructuredDataKey.INSTRUMENT1_21_2,
StructuredDataKey.RECIPES, StructuredDataKey.LODESTONE_TRACKER, StructuredDataKey.FIREWORK_EXPLOSION, StructuredDataKey.FIREWORKS,
StructuredDataKey.PROFILE, StructuredDataKey.NOTE_BLOCK_SOUND, StructuredDataKey.BANNER_PATTERNS, StructuredDataKey.BASE_COLOR,
StructuredDataKey.POT_DECORATIONS, StructuredDataKey.BLOCK_STATE, StructuredDataKey.BEES, StructuredDataKey.LOCK,
StructuredDataKey.CONTAINER_LOOT, StructuredDataKey.TOOL, StructuredDataKey.ITEM_NAME, StructuredDataKey.OMINOUS_BOTTLE_AMPLIFIER,
StructuredDataKey.FOOD1_21_2, StructuredDataKey.JUKEBOX_PLAYABLE, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21,
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_2,
StructuredDataKey.USE_COOLDOWN, StructuredDataKey.DAMAGE, StructuredDataKey.EQUIPPABLE, StructuredDataKey.ITEM_MODEL,
StructuredDataKey.GLIDER, StructuredDataKey.TOOLTIP_STYLE, StructuredDataKey.DEATH_PROTECTION,
// Volatile thanks to containing item
StructuredDataKey.CHARGED_PROJECTILES1_21_4, StructuredDataKey.BUNDLE_CONTENTS1_21_4, StructuredDataKey.CONTAINER1_21_4, StructuredDataKey.USE_REMAINDER1_21_4);
super.onMappingDataLoaded();
}
@Override
public void register(final ViaProviders providers) {
providers.register(PickItemProvider.class, new PickItemProvider());
}
@Override
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_4.PLAYER));
}
@Override
public MappingData getMappingData() {
return MAPPINGS;
}
@Override
public EntityPacketRewriter1_21_4 getEntityRewriter() {
return entityRewriter;
}
@Override
public BlockItemPacketRewriter1_21_4 getItemRewriter() {
return itemRewriter;
}
@Override
public ParticleRewriter<ClientboundPacket1_21_2> getParticleRewriter() {
return particleRewriter;
}
@Override
public TagRewriter<ClientboundPacket1_21_2> getTagRewriter() {
return tagRewriter;
}
@Override
public ComponentRewriter<ClientboundPacket1_21_2> getComponentRewriter() {
return componentRewriter;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_2, ServerboundPacket1_21_4> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_2.class, ServerboundConfigurationPackets1_20_5.class),
packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_4.class, ServerboundConfigurationPackets1_20_5.class)
);
}
}
@@ -0,0 +1,23 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
public interface ServerboundPacket1_21_4 extends ServerboundPacketType {
}
@@ -0,0 +1,94 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet;
public enum ServerboundPackets1_21_4 implements ServerboundPacket1_21_4 {
ACCEPT_TELEPORTATION, // 0x00
BLOCK_ENTITY_TAG_QUERY, // 0x01
BUNDLE_ITEM_SELECTED, // 0x02
CHANGE_DIFFICULTY, // 0x03
CHAT_ACK, // 0x04
CHAT_COMMAND, // 0x05
CHAT_COMMAND_SIGNED, // 0x06
CHAT, // 0x07
CHAT_SESSION_UPDATE, // 0x08
CHUNK_BATCH_RECEIVED, // 0x09
CLIENT_COMMAND, // 0x0A
CLIENT_TICK_END, // 0x0B
CLIENT_INFORMATION, // 0x0C
COMMAND_SUGGESTION, // 0x0D
CONFIGURATION_ACKNOWLEDGED, // 0x0E
CONTAINER_BUTTON_CLICK, // 0x0F
CONTAINER_CLICK, // 0x10
CONTAINER_CLOSE, // 0x11
CONTAINER_SLOT_STATE_CHANGED, // 0x12
COOKIE_RESPONSE, // 0x13
CUSTOM_PAYLOAD, // 0x14
DEBUG_SAMPLE_SUBSCRIPTION, // 0x15
EDIT_BOOK, // 0x16
ENTITY_TAG_QUERY, // 0x17
INTERACT, // 0x18
JIGSAW_GENERATE, // 0x19
KEEP_ALIVE, // 0x1A
LOCK_DIFFICULTY, // 0x1B
MOVE_PLAYER_POS, // 0x1C
MOVE_PLAYER_POS_ROT, // 0x1D
MOVE_PLAYER_ROT, // 0x1E
MOVE_PLAYER_STATUS_ONLY, // 0x1F
MOVE_VEHICLE, // 0x20
PADDLE_BOAT, // 0x21
PICK_ITEM_FROM_BLOCK, // 0x22
PICK_ITEM_FROM_ENTITY, // 0x23
PING_REQUEST, // 0x24
PLACE_RECIPE, // 0x25
PLAYER_ABILITIES, // 0x26
PLAYER_ACTION, // 0x27
PLAYER_COMMAND, // 0x28
PLAYER_INPUT, // 0x29
PLAYER_LOADED, // 0x2A
PONG, // 0x2B
RECIPE_BOOK_CHANGE_SETTINGS, // 0x2C
RECIPE_BOOK_SEEN_RECIPE, // 0x2D
RENAME_ITEM, // 0x2E
RESOURCE_PACK, // 0x2F
SEEN_ADVANCEMENTS, // 0x30
SELECT_TRADE, // 0x31
SET_BEACON, // 0x32
SET_CARRIED_ITEM, // 0x33
SET_COMMAND_BLOCK, // 0x34
SET_COMMAND_MINECART, // 0x35
SET_CREATIVE_MODE_SLOT, // 0x36
SET_JIGSAW_BLOCK, // 0x37
SET_STRUCTURE_BLOCK, // 0x38
SIGN_UPDATE, // 0x39
SWING, // 0x3A
TELEPORT_TO_ENTITY, // 0x3B
USE_ITEM_ON, // 0x3C
USE_ITEM; // 0x3D
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
}
@@ -0,0 +1,31 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.platform.providers.Provider;
public class PickItemProvider implements Provider {
public void pickItemFromBlock(final UserConnection connection, final BlockPosition blockPosition, final boolean includeData) {
}
public void pickItemFromEntity(final UserConnection connection, final int entityId, final boolean includeData) {
}
}
@@ -0,0 +1,153 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
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.CustomModelData1_21_4;
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_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.provider.PickItemProvider;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
public final class BlockItemPacketRewriter1_21_4 extends StructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_4, Protocol1_21_2To1_21_4> {
public BlockItemPacketRewriter1_21_4(final Protocol1_21_2To1_21_4 protocol) {
super(protocol,
Types1_21_2.ITEM, Types1_21_2.ITEM_ARRAY, Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY,
Types1_21_2.ITEM_COST, Types1_21_2.OPTIONAL_ITEM_COST, Types1_21_4.ITEM_COST, Types1_21_4.OPTIONAL_ITEM_COST
);
}
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPacket1_21_2> blockRewriter = BlockRewriter.for1_20_2(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_2.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_2.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_2.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21_2.LEVEL_EVENT, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21_2.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_21_2.BLOCK_ENTITY_DATA);
protocol.registerClientbound(ClientboundPackets1_21_2.SET_HELD_SLOT, wrapper -> {
final byte slot = wrapper.read(Types.BYTE);
wrapper.write(Types.VAR_INT, (int) slot);
});
protocol.registerServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_BLOCK, null, wrapper -> {
final BlockPosition blockPosition = wrapper.read(Types.BLOCK_POSITION1_14);
final boolean includeData = wrapper.read(Types.BOOLEAN);
Via.getManager().getProviders().get(PickItemProvider.class).pickItemFromBlock(wrapper.user(), blockPosition, includeData);
wrapper.cancel();
});
protocol.registerServerbound(ServerboundPackets1_21_4.PICK_ITEM_FROM_ENTITY, null, wrapper -> {
final int entityId = wrapper.read(Types.VAR_INT);
final boolean includeData = wrapper.read(Types.BOOLEAN);
Via.getManager().getProviders().get(PickItemProvider.class).pickItemFromEntity(wrapper.user(), entityId, includeData);
wrapper.cancel();
});
protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerSetPlayerInventory(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_2.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21_2.CONTAINER_SET_CONTENT);
registerSetSlot1_21_2(ClientboundPackets1_21_2.CONTAINER_SET_SLOT);
registerAdvancements1_20_3(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_21_2.SET_EQUIPMENT);
registerContainerClick1_21_2(ServerboundPackets1_21_4.CONTAINER_CLICK);
registerMerchantOffers1_20_5(ClientboundPackets1_21_2.MERCHANT_OFFERS);
registerSetCreativeModeSlot(ServerboundPackets1_21_4.SET_CREATIVE_MODE_SLOT);
final RecipeDisplayRewriter<ClientboundPacket1_21_2> recipeRewriter = new RecipeDisplayRewriter<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21_2.UPDATE_RECIPES);
recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21_2.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_2.PLACE_GHOST_RECIPE);
}
@Override
public Item handleItemToClient(final UserConnection connection, final Item item) {
super.handleItemToClient(connection, item);
final StructuredDataContainer dataContainer = item.dataContainer();
final Integer modelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5);
if (modelData != null) {
dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4(
new float[]{modelData.floatValue()},
new boolean[0],
new String[0],
new int[0]
));
saveTag(createCustomTag(item), new IntTag(modelData), "custom_model_data");
}
updateItemData(item);
return item;
}
@Override
public Item handleItemToServer(final UserConnection connection, final Item item) {
super.handleItemToServer(connection, item);
final StructuredDataContainer dataContainer = item.dataContainer();
final CompoundTag customData = dataContainer.get(StructuredDataKey.CUSTOM_DATA);
if (customData != null) {
if (customData.remove(nbtTagName("custom_model_data")) instanceof final IntTag customModelData) {
dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, customModelData.asInt());
removeCustomTag(dataContainer, customData);
}
}
downgradeItemData(item);
return item;
}
public static void updateItemData(final Item item) {
final StructuredDataContainer dataContainer = item.dataContainer();
dataContainer.replaceKey(StructuredDataKey.CHARGED_PROJECTILES1_21_2, StructuredDataKey.CHARGED_PROJECTILES1_21_4);
dataContainer.replaceKey(StructuredDataKey.BUNDLE_CONTENTS1_21_2, StructuredDataKey.BUNDLE_CONTENTS1_21_4);
dataContainer.replaceKey(StructuredDataKey.CONTAINER1_21_2, StructuredDataKey.CONTAINER1_21_4);
dataContainer.replaceKey(StructuredDataKey.USE_REMAINDER1_21_2, StructuredDataKey.USE_REMAINDER1_21_4);
dataContainer.replaceKey(StructuredDataKey.TRIM1_21_2, StructuredDataKey.TRIM1_21_4);
dataContainer.remove(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5);
}
public static void downgradeItemData(final Item item) {
final StructuredDataContainer dataContainer = item.dataContainer();
dataContainer.replaceKey(StructuredDataKey.CHARGED_PROJECTILES1_21_4, StructuredDataKey.CHARGED_PROJECTILES1_21_2);
dataContainer.replaceKey(StructuredDataKey.BUNDLE_CONTENTS1_21_4, StructuredDataKey.BUNDLE_CONTENTS1_21_2);
dataContainer.replaceKey(StructuredDataKey.CONTAINER1_21_4, StructuredDataKey.CONTAINER1_21_2);
dataContainer.replaceKey(StructuredDataKey.USE_REMAINDER1_21_4, StructuredDataKey.USE_REMAINDER1_21_2);
dataContainer.replaceKey(StructuredDataKey.TRIM1_21_4, StructuredDataKey.TRIM1_21_2);
dataContainer.remove(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4);
}
}
@@ -0,0 +1,48 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.TagUtil;
public final class ComponentRewriter1_21_4 extends ComponentRewriter<ClientboundPacket1_21_2> {
public ComponentRewriter1_21_4(final Protocol1_21_2To1_21_4 protocol) {
super(protocol, ReadType.NBT);
}
@Override
protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {
super.handleShowItem(connection, itemTag, componentsTag);
if (componentsTag == null) {
return;
}
TagUtil.removeNamespaced(componentsTag, "custom_model_data");
}
@Override
protected SerializerVersion inputSerializerVersion() {
return SerializerVersion.V1_20_5;
}
}
@@ -0,0 +1,151 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter;
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.RegistryEntry;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
import com.viaversion.viaversion.util.Key;
public final class EntityPacketRewriter1_21_4 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_21_2To1_21_4> {
public EntityPacketRewriter1_21_4(final Protocol1_21_2To1_21_4 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, Types1_21_2.ENTITY_DATA_LIST, Types1_21_4.ENTITY_DATA_LIST);
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol) {
@Override
public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {
if (Key.stripMinecraftNamespace(key).equals("worldgen/biome")) {
for (final RegistryEntry entry : entries) {
if (entry.tag() == null) {
continue;
}
final CompoundTag effectsTag = ((CompoundTag) entry.tag()).getCompoundTag("effects");
final CompoundTag musicTag = effectsTag.getCompoundTag("music");
if (musicTag == null) {
continue;
}
// Wrap music
final ListTag<CompoundTag> weightedMusicTags = new ListTag<>(CompoundTag.class);
final CompoundTag weightedMusicTag = new CompoundTag();
weightedMusicTag.put("data", musicTag);
weightedMusicTag.putInt("weight", 1);
weightedMusicTags.add(weightedMusicTag);
effectsTag.put("music", weightedMusicTags);
}
}
return super.handle(connection, key, entries);
}
};
protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle);
protocol.registerClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {
final int entityId = wrapper.passthrough(Types.INT); // Entity id
wrapper.passthrough(Types.BOOLEAN); // Hardcore
wrapper.passthrough(Types.STRING_ARRAY); // World List
wrapper.passthrough(Types.VAR_INT); // Max players
wrapper.passthrough(Types.VAR_INT); // View distance
wrapper.passthrough(Types.VAR_INT); // Simulation distance
wrapper.passthrough(Types.BOOLEAN); // Reduced debug info
wrapper.passthrough(Types.BOOLEAN); // Show death screen
wrapper.passthrough(Types.BOOLEAN); // Limited crafting
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
trackPlayer(wrapper.user(), entityId);
});
protocol.registerClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {
final int dimensionId = wrapper.passthrough(Types.VAR_INT);
final String world = wrapper.passthrough(Types.STRING);
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); // Tracks world height and name for chunk data and entity (un)tracking
});
protocol.registerServerbound(ServerboundPackets1_21_4.MOVE_VEHICLE, wrapper -> {
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.FLOAT); // Yaw
wrapper.passthrough(Types.FLOAT); // Pitch
wrapper.read(Types.BOOLEAN); // On ground
});
protocol.cancelServerbound(ServerboundPackets1_21_4.PLAYER_LOADED);
}
@Override
protected void registerRewrites() {
filter().mapDataType(Types1_21_4.ENTITY_DATA_TYPES::byId);
registerEntityDataTypeHandler(
Types1_21_4.ENTITY_DATA_TYPES.itemType,
Types1_21_4.ENTITY_DATA_TYPES.blockStateType,
Types1_21_4.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21_4.ENTITY_DATA_TYPES.particleType,
Types1_21_4.ENTITY_DATA_TYPES.particlesType,
Types1_21_4.ENTITY_DATA_TYPES.componentType,
Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType
);
registerBlockStateHandler(EntityTypes1_21_4.ABSTRACT_MINECART, 11);
filter().type(EntityTypes1_21_4.CREAKING).addIndex(18); // Is tearing down
filter().type(EntityTypes1_21_4.SALMON).index(17).handler((event, data) -> {
final String type = data.value();
final int typeId = switch (type) {
case "small" -> 0;
case "large" -> 2;
default -> 1; // medium
};
data.setTypeAndValue(Types1_21_4.ENTITY_DATA_TYPES.varIntType, typeId);
});
}
@Override
public void onMappingDataLoaded() {
mapTypes();
}
@Override
public EntityType typeFromId(final int type) {
return EntityTypes1_21_4.getTypeFromId(type);
}
}
@@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import java.util.concurrent.ThreadLocalRandom;
public final class ParticleRewriter1_21_4 extends ParticleRewriter<ClientboundPacket1_21_2> {
public ParticleRewriter1_21_4(final Protocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {
super(protocol, Types1_21_2.PARTICLE, Types1_21_4.PARTICLE);
}
@Override
public void rewriteParticle(final UserConnection connection, final Particle particle) {
super.rewriteParticle(connection, particle);
final String identifier = protocol.getMappingData().getParticleMappings().mappedIdentifier(particle.id());
if ("minecraft:trail".equals(identifier)) {
// Duration
particle.add(Types.VAR_INT, ThreadLocalRandom.current().nextInt(40) + 10);
}
}
}
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
@@ -171,7 +172,8 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol<ClientboundPack
registerClientbound(ClientboundPackets1_21.BUNDLE_DELIMITER, wrapper -> wrapper.user().get(BundleStateTracker.class).toggleBundling());
registerServerbound(ServerboundPackets1_21_2.PONG, wrapper -> {
final int id = wrapper.passthrough(Types.INT); // id
if (wrapper.user().get(PlayerPositionStorage.class).checkPong(id)) {
final PlayerPositionStorage playerPositionStorage = wrapper.user().get(PlayerPositionStorage.class);
if (playerPositionStorage != null && playerPositionStorage.checkPong(id)) {
wrapper.cancel();
}
});
@@ -204,17 +206,17 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol<ClientboundPack
.reader("sculk_charge", ParticleType.Readers.SCULK_CHARGE)
.reader("shriek", ParticleType.Readers.SHRIEK)
.reader("entity_effect", ParticleType.Readers.COLOR)
.reader("trail", ParticleType.Readers.TRAIL)
.reader("trail", ParticleType.Readers.TRAIL1_21_2)
.reader("item", ParticleType.Readers.item(Types1_21_2.ITEM));
Types1_21_2.STRUCTURED_DATA.filler(this).add(StructuredDataKey.CUSTOM_DATA, StructuredDataKey.MAX_STACK_SIZE, StructuredDataKey.MAX_DAMAGE,
StructuredDataKey.UNBREAKABLE, StructuredDataKey.RARITY, StructuredDataKey.HIDE_TOOLTIP, StructuredDataKey.DAMAGE_RESISTANT,
StructuredDataKey.CUSTOM_NAME, StructuredDataKey.LORE, StructuredDataKey.ENCHANTMENTS, StructuredDataKey.CAN_PLACE_ON,
StructuredDataKey.CAN_BREAK, StructuredDataKey.CUSTOM_MODEL_DATA, StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP,
StructuredDataKey.CAN_BREAK, StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP,
StructuredDataKey.REPAIR_COST, StructuredDataKey.CREATIVE_SLOT_LOCK, StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE,
StructuredDataKey.INTANGIBLE_PROJECTILE, StructuredDataKey.STORED_ENCHANTMENTS, StructuredDataKey.DYED_COLOR,
StructuredDataKey.MAP_COLOR, StructuredDataKey.MAP_ID, StructuredDataKey.MAP_DECORATIONS, StructuredDataKey.MAP_POST_PROCESSING,
StructuredDataKey.POTION_CONTENTS1_21_2, StructuredDataKey.SUSPICIOUS_STEW_EFFECTS, StructuredDataKey.WRITABLE_BOOK_CONTENT,
StructuredDataKey.WRITTEN_BOOK_CONTENT, StructuredDataKey.TRIM, StructuredDataKey.DEBUG_STICK_STATE, StructuredDataKey.ENTITY_DATA,
StructuredDataKey.WRITTEN_BOOK_CONTENT, StructuredDataKey.TRIM1_21_2, StructuredDataKey.DEBUG_STICK_STATE, StructuredDataKey.ENTITY_DATA,
StructuredDataKey.BUCKET_ENTITY_DATA, StructuredDataKey.BLOCK_ENTITY_DATA, StructuredDataKey.INSTRUMENT1_21_2,
StructuredDataKey.RECIPES, StructuredDataKey.LODESTONE_TRACKER, StructuredDataKey.FIREWORK_EXPLOSION, StructuredDataKey.FIREWORKS,
StructuredDataKey.PROFILE, StructuredDataKey.NOTE_BLOCK_SOUND, StructuredDataKey.BANNER_PATTERNS, StructuredDataKey.BASE_COLOR,
@@ -225,7 +227,7 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol<ClientboundPack
StructuredDataKey.USE_COOLDOWN, StructuredDataKey.DAMAGE, StructuredDataKey.EQUIPPABLE, StructuredDataKey.ITEM_MODEL,
StructuredDataKey.GLIDER, StructuredDataKey.TOOLTIP_STYLE, StructuredDataKey.DEATH_PROTECTION,
// Volatile thanks to containing item
StructuredDataKey.CHARGED_PROJECTILES1_21_2, StructuredDataKey.BUNDLE_CONTENTS1_21_2, StructuredDataKey.CONTAINER1_21_2, StructuredDataKey.USE_REMAINDER);
StructuredDataKey.CHARGED_PROJECTILES1_21_2, StructuredDataKey.BUNDLE_CONTENTS1_21_2, StructuredDataKey.CONTAINER1_21_2, StructuredDataKey.USE_REMAINDER1_21_2);
super.onMappingDataLoaded();
}
@@ -233,9 +235,19 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol<ClientboundPack
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTracker1_21_2(connection));
connection.put(new BundleStateTracker());
connection.put(new PlayerPositionStorage());
connection.put(new ChunkLoadTracker());
connection.put(new GroundFlagTracker());
final ProtocolVersion protocolVersion = connection.getProtocolInfo().protocolVersion();
if (protocolVersion.olderThan(ProtocolVersion.v1_21_4)) { // Only needed for 1.21.2/1.21.3
connection.put(new PlayerPositionStorage());
}
// <= 1.21.1 clients allowed loaded chunks to get replaced with new data without unloading them first.
// 1.21.2 introduced a graphical bug where it doesn't properly render the new data unless the chunk is unloaded beforehand.
// 1.21.4 fixed this bug, so the workaround is no longer needed.
if (protocolVersion.equals(ProtocolVersion.v1_21_2)) {
connection.put(new ChunkLoadTracker());
}
}
@Override
@@ -65,6 +65,7 @@ import com.viaversion.viaversion.rewriter.SoundRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Limit;
import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.TagUtil;
import com.viaversion.viaversion.util.Unit;
@@ -154,7 +155,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
wrapper.passthrough(Types.SHORT); // Slot
wrapper.passthrough(Types.BYTE); // Button
wrapper.passthrough(Types.VAR_INT); // Mode
final int length = wrapper.passthrough(Types.VAR_INT);
final int length = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Types.SHORT); // Slot
passthroughServerboundItem(wrapper);
@@ -375,6 +376,10 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
}
final ChunkLoadTracker chunkLoadTracker = wrapper.user().get(ChunkLoadTracker.class);
if (chunkLoadTracker == null) {
return;
}
if (chunkLoadTracker.isChunkLoaded(chunk.getX(), chunk.getZ())) {
// Unload the old chunk, so the new one can be loaded without graphical glitches
// Bundling it prevents the client from falling through the world during the chunk swap
@@ -400,7 +405,11 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
});
protocol.registerClientbound(ClientboundPackets1_21.FORGET_LEVEL_CHUNK, wrapper -> {
final ChunkPosition chunkPosition = wrapper.passthrough(Types.CHUNK_POSITION);
wrapper.user().get(ChunkLoadTracker.class).removeChunk(chunkPosition.chunkX(), chunkPosition.chunkZ());
final ChunkLoadTracker chunkLoadTracker = wrapper.user().get(ChunkLoadTracker.class);
if (chunkLoadTracker != null) {
chunkLoadTracker.removeChunk(chunkPosition.chunkX(), chunkPosition.chunkZ());
}
});
}
@@ -422,12 +431,20 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
}
super.handleItemToClient(connection, item);
// Handle food properties item manually here - the only protocol that has it
// The other way around it's handled by the super handleItemToServer method
final StructuredDataContainer data = item.dataContainer();
final FoodProperties1_20_5 food = data.get(StructuredDataKey.FOOD1_21);
if (food != null && food.usingConvertsTo() != null) {
this.handleItemToClient(connection, food.usingConvertsTo());
}
updateItemData(item);
// Add data components to fix issues in older protocols
appendItemDataFixComponents(connection, item);
final StructuredDataContainer data = item.dataContainer();
final Enchantments enchantments = data.get(StructuredDataKey.ENCHANTMENTS);
if (enchantments != null && enchantments.size() != 0) {
// Level 0 is no longer allowed
@@ -552,7 +569,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
dataContainer.set(StructuredDataKey.CONSUMABLE1_21_2, new Consumable1_21_2(food.eatSeconds(), 1 /* eat */, sound, true, consumeEffects));
if (food.usingConvertsTo() != null) {
dataContainer.set(StructuredDataKey.USE_REMAINDER, food.usingConvertsTo());
dataContainer.set(StructuredDataKey.USE_REMAINDER1_21_2, food.usingConvertsTo());
}
return new FoodProperties1_21_2(food.nutrition(), food.saturationModifier(), food.canAlwaysEat());
});
@@ -570,6 +587,13 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
itemComponentsTag.putString("custom_name", ComponentUtil.plainToJson(lock).toString());
return predicateTag;
});
dataContainer.replace(StructuredDataKey.TRIM1_20_5, StructuredDataKey.TRIM1_21_2, trim -> {
// TODO Rewrite from int to string id via sent registry
if (trim.material().isDirect()) {
trim.material().value().overrideArmorMaterials().clear();
}
return trim;
});
}
public static void downgradeItemData(final Item item) {
@@ -593,7 +617,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
});
dataContainer.replace(StructuredDataKey.FOOD1_21_2, StructuredDataKey.FOOD1_21, food -> {
final Consumable1_21_2 consumableData = dataContainer.get(StructuredDataKey.CONSUMABLE1_21_2);
final Item useRemainderData = dataContainer.get(StructuredDataKey.USE_REMAINDER);
final Item useRemainderData = dataContainer.get(StructuredDataKey.USE_REMAINDER1_21_2);
final float eatSeconds = consumableData != null ? consumableData.consumeSeconds() : 1.6F;
final List<FoodProperties1_20_5.FoodEffect> foodEffects = new ArrayList<>();
if (consumableData != null) {
@@ -607,6 +631,13 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
}
return new FoodProperties1_20_5(food.nutrition(), food.saturationModifier(), food.canAlwaysEat(), eatSeconds, useRemainderData, foodEffects.toArray(new FoodProperties1_20_5.FoodEffect[0]));
});
dataContainer.replace(StructuredDataKey.TRIM1_21_2, StructuredDataKey.TRIM1_20_5, trim -> {
// TODO
if (trim.material().isDirect()) {
trim.material().value().overrideArmorMaterials().clear();
}
return trim;
});
dataContainer.replaceKey(StructuredDataKey.CONTAINER1_21_2, StructuredDataKey.CONTAINER1_21);
dataContainer.replaceKey(StructuredDataKey.CHARGED_PROJECTILES1_21_2, StructuredDataKey.CHARGED_PROJECTILES1_21);
dataContainer.replaceKey(StructuredDataKey.BUNDLE_CONTENTS1_21_2, StructuredDataKey.BUNDLE_CONTENTS1_21);
@@ -620,7 +651,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
dataContainer.remove(StructuredDataKey.REPAIRABLE);
dataContainer.remove(StructuredDataKey.ENCHANTABLE);
dataContainer.remove(StructuredDataKey.CONSUMABLE1_21_2);
dataContainer.remove(StructuredDataKey.USE_REMAINDER);
dataContainer.remove(StructuredDataKey.USE_REMAINDER1_21_2);
dataContainer.remove(StructuredDataKey.USE_COOLDOWN);
dataContainer.remove(StructuredDataKey.ITEM_MODEL);
dataContainer.remove(StructuredDataKey.EQUIPPABLE);
@@ -159,7 +159,10 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
final EntityTracker entityTracker = tracker(wrapper.user());
if (entityTracker.currentWorld() != null && !entityTracker.currentWorld().equals(world)) {
wrapper.user().get(ChunkLoadTracker.class).clear();
final ChunkLoadTracker chunkLoadTracker = wrapper.user().get(ChunkLoadTracker.class);
if (chunkLoadTracker != null) {
chunkLoadTracker.clear();
}
}
trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);
@@ -197,6 +200,11 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
final int teleportId = wrapper.read(Types.VAR_INT);
wrapper.set(Types.VAR_INT, 0, teleportId);
final PlayerPositionStorage positionStorage = wrapper.user().get(PlayerPositionStorage.class);
if (positionStorage == null) {
return;
}
// Accept teleportation and player position were swapped.
// Send a ping first to then capture and send the player position the accept teleportation
final boolean isBundling = wrapper.user().get(BundleStateTracker.class).isBundling();
@@ -206,7 +214,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
}
final int pingId = ThreadLocalRandom.current().nextInt();
wrapper.user().get(PlayerPositionStorage.class).addPendingPong(pingId);
positionStorage.addPendingPong(pingId);
final PacketWrapper ping = wrapper.create(ClientboundPackets1_21_2.PING);
ping.write(Types.INT, pingId); // id
ping.send(Protocol1_21To1_21_2.class);
@@ -327,7 +335,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
handleOnGround(wrapper);
final PlayerPositionStorage playerPositionStorage = wrapper.user().get(PlayerPositionStorage.class);
if (playerPositionStorage.checkCaptureNextPlayerPositionPacket()) {
if (playerPositionStorage != null && playerPositionStorage.checkCaptureNextPlayerPositionPacket()) {
// Capture this packet and send it after accept teleportation
final boolean onGround = wrapper.get(Types.BOOLEAN, 0);
playerPositionStorage.setPlayerPosition(new PlayerPositionStorage.PlayerPosition(x, y, z, yaw, pitch, onGround));
@@ -352,7 +360,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
});
protocol.registerServerbound(ServerboundPackets1_21_2.ACCEPT_TELEPORTATION, wrapper -> {
final PlayerPositionStorage playerPositionStorage = wrapper.user().get(PlayerPositionStorage.class);
if (playerPositionStorage.checkHasPlayerPosition()) {
if (playerPositionStorage != null && playerPositionStorage.checkHasPlayerPosition()) {
// Send move player after accept teleportation
wrapper.sendToServer(Protocol1_21To1_21_2.class);
wrapper.cancel();
@@ -453,7 +461,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
// Remove old entity
final PacketWrapper removeEntityPacket = PacketWrapper.create(ClientboundPackets1_21_2.REMOVE_ENTITIES, event.user());
removeEntityPacket.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[] { event.entityId() });
removeEntityPacket.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{event.entityId()});
removeEntityPacket.send(Protocol1_21To1_21_2.class);
// Detect correct boat entity type from entity data
@@ -43,12 +43,12 @@ public final class ParticleRewriter1_21_2 extends ParticleRewriter<ClientboundPa
@Override
public void rewriteParticle(final UserConnection connection, final Particle particle) {
super.rewriteParticle(connection, particle);
final String identifier = protocol.getMappingData().getParticleMappings().mappedIdentifier(particle.id());
if (identifier.equals("minecraft:dust_color_transition")) {
if ("minecraft:dust_color_transition".equals(identifier)) {
floatsToARGB(particle, 0);
floatsToARGB(particle, 1);
} else if (identifier.equals("minecraft:dust")) {
} else if ("minecraft:dust".equals(identifier)) {
floatsToARGB(particle, 0);
}
}
@@ -327,6 +327,10 @@ final class RecipeRewriter1_21_2 extends RecipeRewriter1_20_3<ClientboundPacket1
wrapper.write(Types.VAR_INT, Recipe.SLOT_DISPLAY_EMPTY);
return;
}
if (ingredient.length == 1) {
writeItemDisplay(wrapper, ingredient[0]);
return;
}
wrapper.write(Types.VAR_INT, Recipe.SLOT_DISPLAY_COMPOSITE);
wrapper.write(Types.VAR_INT, ingredient.length);
@@ -21,53 +21,35 @@ import com.viaversion.nbt.tag.ByteTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.api.minecraft.ChunkPosition;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class CommandBlockStorage implements StorableObject {
private final Map<Pair<Integer, Integer>, Map<BlockPosition, CompoundTag>> storedCommandBlocks = new HashMap<>();
private final Map<Long, Map<BlockPosition, CompoundTag>> storedCommandBlocks = new HashMap<>();
private boolean permissions;
public void unloadChunk(int x, int z) {
Pair<Integer, Integer> chunkPos = new Pair<>(x, z);
storedCommandBlocks.remove(chunkPos);
storedCommandBlocks.remove(ChunkPosition.chunkKey(x, z));
}
public void addOrUpdateBlock(BlockPosition position, CompoundTag tag) {
Pair<Integer, Integer> chunkPos = getChunkCoords(position);
if (!storedCommandBlocks.containsKey(chunkPos)) {
storedCommandBlocks.put(chunkPos, new HashMap<>());
}
Map<BlockPosition, CompoundTag> blocks = storedCommandBlocks.get(chunkPos);
if (blocks.containsKey(position) && blocks.get(position).equals(tag)) {
return;
}
long chunkKey = ChunkPosition.chunkKeyForBlock(position.x(), position.z());
Map<BlockPosition, CompoundTag> blocks = storedCommandBlocks.computeIfAbsent(chunkKey, k -> new HashMap<>());
blocks.put(position, tag);
}
private Pair<Integer, Integer> getChunkCoords(BlockPosition position) {
int chunkX = Math.floorDiv(position.x(), 16);
int chunkZ = Math.floorDiv(position.z(), 16);
return new Pair<>(chunkX, chunkZ);
}
public Optional<CompoundTag> getCommandBlock(BlockPosition position) {
Pair<Integer, Integer> chunkCoords = getChunkCoords(position);
Map<BlockPosition, CompoundTag> blocks = storedCommandBlocks.get(chunkCoords);
if (blocks == null)
Map<BlockPosition, CompoundTag> blocks = storedCommandBlocks.get(ChunkPosition.chunkKeyForBlock(position.x(), position.z()));
if (blocks == null) {
return Optional.empty();
}
CompoundTag tag = blocks.get(position);
if (tag == null)
if (tag == null) {
return Optional.empty();
}
tag = tag.copy();
tag.put("powered", new ByteTag((byte) 0));
@@ -67,11 +67,11 @@ public class BlockRewriter<C extends ClientboundPacketType> {
}
public void registerBlockEvent(C packetType) {
if (protocol.getMappingData().getBlockMappings() == null) {
return;
}
protocol.registerClientbound(packetType, wrapper -> {
if (protocol.getMappingData().getBlockMappings() == null) {
return;
}
wrapper.passthrough(positionType); // Location
wrapper.passthrough(Types.UNSIGNED_BYTE); // Action id
wrapper.passthrough(Types.UNSIGNED_BYTE); // Action param
@@ -165,9 +165,9 @@ public class ComponentRewriter<C extends ClientboundPacketType> implements com.v
});
}
public void registerPlayerInfoUpdate1_21_2(final C packetType) {
public void registerPlayerInfoUpdate1_21_4(final C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
final BitSet actions = wrapper.passthrough(Types.PROFILE_ACTIONS_ENUM1_21_2);
final BitSet actions = wrapper.passthrough(Types.PROFILE_ACTIONS_ENUM1_21_4);
if (!actions.get(5)) { // Update display name
return;
}
@@ -204,6 +204,9 @@ public class ComponentRewriter<C extends ClientboundPacketType> implements com.v
if (actions.get(6)) {
wrapper.passthrough(Types.VAR_INT); // List order
}
if (actions.get(7)) {
wrapper.passthrough(Types.BOOLEAN); // Show hat
}
}
});
}
@@ -32,6 +32,7 @@ import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Limit;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
@@ -219,7 +220,7 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
wrapper.passthrough(Types.VAR_INT); // Mode
// Affected items
final int length = wrapper.passthrough(Types.VAR_INT);
final int length = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Types.SHORT); // Slot
passthroughServerboundItem(wrapper);
@@ -230,6 +231,13 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
});
}
public void registerSetPlayerInventory(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Slot
passthroughClientboundItem(wrapper);
});
}
public void registerCooldown(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
int itemId = wrapper.read(Types.VAR_INT);
@@ -239,11 +247,11 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
public void registerCooldown1_21_2(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
String itemIdentifier = wrapper.read(Types.OPTIONAL_STRING);
String itemIdentifier = wrapper.read(Types.STRING);
if (itemIdentifier != null) {
itemIdentifier = mappedIdentifier(protocol.getMappingData().getFullItemMappings(), itemIdentifier);
}
wrapper.write(Types.OPTIONAL_STRING, itemIdentifier);
wrapper.write(Types.STRING, itemIdentifier);
});
}
@@ -115,7 +115,25 @@ public class ParticleRewriter<C extends ClientboundPacketType> implements com.vi
public void registerLevelParticles1_20_5(final C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.BOOLEAN); // Long Distance
wrapper.passthrough(Types.BOOLEAN); // Override limiter
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.FLOAT); // Offset X
wrapper.passthrough(Types.FLOAT); // Offset Y
wrapper.passthrough(Types.FLOAT); // Offset Z
wrapper.passthrough(Types.FLOAT); // Particle Data
wrapper.passthrough(Types.INT); // Particle Count
final Particle particle = wrapper.passthroughAndMap(particleType, mappedParticleType);
rewriteParticle(wrapper.user(), particle);
});
}
public void registerLevelParticles1_21_4(final C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.BOOLEAN); // Override limiter
wrapper.passthrough(Types.BOOLEAN); // Always show
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
@@ -163,9 +181,11 @@ public class ParticleRewriter<C extends ClientboundPacketType> implements com.vi
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.DOUBLE); // Knockback X
wrapper.passthrough(Types.DOUBLE); // Knockback Y
wrapper.passthrough(Types.DOUBLE); // Knockback Z
if (wrapper.passthrough(Types.BOOLEAN)) {
wrapper.passthrough(Types.DOUBLE); // Knockback X
wrapper.passthrough(Types.DOUBLE); // Knockback Y
wrapper.passthrough(Types.DOUBLE); // Knockback Z
}
final Particle explosionParticle = wrapper.read(particleType);
wrapper.write(mappedParticleType, explosionParticle);
@@ -94,6 +94,8 @@ public class RecipeDisplayRewriter<C extends ClientboundPacketType> {
handleSlotDisplay(wrapper); // Fuel
handleSlotDisplay(wrapper); // Result
handleSlotDisplay(wrapper); // Crafting station
wrapper.passthrough(Types.VAR_INT); // Duration
wrapper.passthrough(Types.FLOAT); // Experience
}
protected void handleStoneCutter(final PacketWrapper wrapper) {
@@ -165,7 +167,7 @@ public class RecipeDisplayRewriter<C extends ClientboundPacketType> {
}
protected void handleItemId(final PacketWrapper wrapper) {
final int id = wrapper.passthrough(Types.VAR_INT);
final int id = wrapper.read(Types.VAR_INT);
wrapper.write(Types.VAR_INT, rewriteItemId(id));
}
@@ -108,23 +108,19 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
for (int i = 0; i < ingredientsNo; i++) {
handleIngredient(wrapper);
}
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
public void handleCraftingShapeless(PacketWrapper wrapper) {
wrapper.passthrough(Types.STRING); // Group
handleIngredients(wrapper);
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
public void handleSmelting(PacketWrapper wrapper) {
wrapper.passthrough(Types.STRING); // Group
handleIngredient(wrapper);
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
wrapper.passthrough(Types.FLOAT); // EXP
wrapper.passthrough(Types.VAR_INT); // Cooking time
}
@@ -132,15 +128,13 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
public void handleStonecutting(PacketWrapper wrapper) {
wrapper.passthrough(Types.STRING); // Group
handleIngredient(wrapper);
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
public void handleSmithing(PacketWrapper wrapper) {
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Addition
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
public void handleSimpleRecipe(final PacketWrapper wrapper) {
@@ -151,8 +145,7 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
handleIngredient(wrapper); // Template
handleIngredient(wrapper); // Base
handleIngredient(wrapper); // Additions
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
handleResult(wrapper);
}
public void handleSmithingTrim(final PacketWrapper wrapper) {
@@ -190,6 +183,11 @@ public class RecipeRewriter<C extends ClientboundPacketType> {
}
}
protected void handleResult(final PacketWrapper wrapper) {
final Item result = rewrite(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), result);
}
@FunctionalInterface
public interface RecipeConsumer {
@@ -59,6 +59,8 @@ public class RegistryDataRewriter {
key = Key.stripMinecraftNamespace(key);
if (key.equals("enchantment")) {
updateEnchantments(entries);
} else if (key.equals("trim_material")) {
updateTrimMaterials(entries);
}
final List<RegistryEntry> toAdd = this.toAdd.get(key);
@@ -126,6 +128,22 @@ public class RegistryDataRewriter {
}
}
public void updateTrimMaterials(final RegistryEntry[] entries) {
if (protocol.getMappingData().getFullItemMappings() == null) {
return;
}
for (final RegistryEntry entry : entries) {
if (entry.tag() == null) {
continue;
}
final StringTag ingredientTag = ((CompoundTag) entry.tag()).getStringTag("ingredient");
final String mappedIngredient = protocol.getMappingData().getFullItemMappings().mappedIdentifier(ingredientTag.getValue());
ingredientTag.setValue(mappedIngredient);
}
}
private void updateNestedEffect(final CompoundTag effectsTag) {
final CompoundTag effect = effectsTag.getCompoundTag("effect");
if (effect == null) {
@@ -117,7 +117,9 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
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.TRIM1_20_5, value -> value.rewrite(itemIdRewriter));
container.replace(StructuredDataKey.TRIM1_21_2, value -> value.rewrite(itemIdRewriter));
container.replace(StructuredDataKey.TRIM1_21_4, value -> value.rewrite(itemIdRewriter));
container.replace(StructuredDataKey.POT_DECORATIONS, value -> value.rewrite(itemIdRewriter));
container.replace(StructuredDataKey.REPAIRABLE, value -> value.rewrite(itemIdRewriter));
}
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff. Show more