mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2026-11-04 08:52:10 -05:00
Compare commits
66
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad24236c6b | ||
|
|
ab503f497a | ||
|
|
3afc3ee1e1 | ||
|
|
cb55edf220 | ||
|
|
9f4803b832 | ||
|
|
88419cdd71 | ||
|
|
2579ac2153 | ||
|
|
9d2f9f10e7 | ||
|
|
218fc88313 | ||
|
|
357cb229bc | ||
|
|
d89ea4c7be | ||
|
|
be08ce39f6 | ||
|
|
f71f4722e0 | ||
|
|
62ea10d662 | ||
|
|
ca49a1b4ad | ||
|
|
746740accf | ||
|
|
b90f379538 | ||
|
|
8b9a78aa02 | ||
|
|
3487e8ce5f | ||
|
|
73dc869595 | ||
|
|
66c064ad7d | ||
|
|
8d56c71974 | ||
|
|
28d16ec7d3 | ||
|
|
fc4b13f0a1 | ||
|
|
b712c0055d | ||
|
|
08776de832 | ||
|
|
77b7054629 | ||
|
|
63769b3661 | ||
|
|
12d75daf04 | ||
|
|
413268eb15 | ||
|
|
b27e2a5514 | ||
|
|
c59dfb4d93 | ||
|
|
f66cbd6941 | ||
|
|
1574f343f7 | ||
|
|
022fb96f83 | ||
|
|
7cbc751fea | ||
|
|
366f08d951 | ||
|
|
d628f09334 | ||
|
|
6fecd58773 | ||
|
|
da7619c82d | ||
|
|
b83268efa0 | ||
|
|
309c3e57c1 | ||
|
|
34a445efcd | ||
|
|
3a949d70eb | ||
|
|
99bfa389b4 | ||
|
|
327d4739f5 | ||
|
|
3a7a8e06a8 | ||
|
|
85db19200f | ||
|
|
226609b864 | ||
|
|
7049f7eaf1 | ||
|
|
a062d0905b | ||
|
|
7ac58c104b | ||
|
|
7a6e838eb1 | ||
|
|
57bc53346c | ||
|
|
3df7c76c8c | ||
|
|
3f35005051 | ||
|
|
f9d21e073b | ||
|
|
e813796d9a | ||
|
|
271b6e7ff7 | ||
|
|
7bf4a30075 | ||
|
|
414e978829 | ||
|
|
637a81e569 | ||
|
|
b4023127f6 | ||
|
|
46a61058dd | ||
|
|
66b90a5b2a | ||
|
|
8f03d6bcef |
No files matched your search
@@ -15,12 +15,12 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6.3.0
|
||||
uses: gradle/actions/setup-gradle@v6.4.0
|
||||
with:
|
||||
cache-disabled: false
|
||||
cache-provider: basic
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5.7.0
|
||||
uses: actions/setup-java@v6.1.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Upload Artifacts to GitHub
|
||||
# Only upload on the preview repository as there is no CI job for it.
|
||||
if: ${{ github.repository == 'ViaVersion/ViaVersionDev' && github.ref_name == 'preview' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: Artifacts
|
||||
path: build/libs/
|
||||
|
||||
@@ -20,12 +20,12 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6.3.0
|
||||
uses: gradle/actions/setup-gradle@v6.4.0
|
||||
with:
|
||||
cache-disabled: false
|
||||
cache-provider: basic
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5.7.0
|
||||
uses: actions/setup-java@v6.1.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
@@ -40,5 +40,5 @@ jobs:
|
||||
- name: Publish to Modrinth
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
run: ./gradlew modrinth --stacktrace
|
||||
run: ./gradlew publishMods --stacktrace
|
||||
continue-on-error: true
|
||||
@@ -14,18 +14,23 @@ or as [a standalone proxy](https://github.com/ViaVersion/ViaProxy) to join from
|
||||
any server version from the past decade. **See [HERE](https://viaversion.com) for an overview of the different Via\*
|
||||
projects.**
|
||||
|
||||
Note that ViaVersion will be able to **run best on either Paper servers or
|
||||
through [ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus?tab=readme-ov-file#viafabricplus)** due to having
|
||||
direct access to client/server state and more extensive API.
|
||||
**Note:** While ViaVersion works reliably across all listed platforms, it has the highest performance and state accuracy on **Paper** servers and through **[ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus)** on clients, since both offer more extensive APIs and direct access to client/server state.
|
||||
|
||||
Supported Versions:
|
||||
### Supported Versions
|
||||
|
||||

|
||||
| Players joining with… | Install |
|
||||
| :--- | :--- |
|
||||
| **Newer** versions than the server | ViaVersion |
|
||||
| **Older** versions than the server, down to 1.9 | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) |
|
||||
| **1.7.x – 1.8.x** | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) + [ViaRewind](https://github.com/ViaVersion/ViaRewind) |
|
||||
|
||||
**User Docs:** https://docs.viaversion.com
|
||||
If you're stuck, try [this tool](https://viaversion.com/setup) to work out what plugins you need.
|
||||
|
||||
**User Docs:** [docs.viaversion.com](https://docs.viaversion.com)
|
||||
|
||||
Snapshot Support
|
||||
----------
|
||||
|
||||
Snapshot support
|
||||
--------
|
||||
**ViaVersion will only be released a few days *after* a Minecraft update** unless the protocol changes of the update
|
||||
were trivial. If you want early-access, usually days or even weeks before the final release, you can subscribe to
|
||||
either:
|
||||
|
||||
@@ -500,6 +500,13 @@ public interface ViaVersionConfig extends Config {
|
||||
*/
|
||||
boolean handleInvalidItemCount();
|
||||
|
||||
/**
|
||||
* Backs up the original data of nested items (e.g. in containers or bundles) for 1.20.5+ clients on 1.20.4 and older servers.
|
||||
*
|
||||
* @return true if enabled
|
||||
*/
|
||||
boolean backupNestedItems();
|
||||
|
||||
/**
|
||||
* Tries to cancel block break/place sounds sent by 1.8 servers to 1.9+ clients to prevent them from playing twice
|
||||
*
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.viaversion.nbt.tag.ByteArrayTag;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.function.IntConsumer;
|
||||
@@ -49,6 +51,12 @@ public final class IdRanges {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static IntList decodeAsList(final ByteArrayTag rangesTag) {
|
||||
final IntList ids = new IntArrayList();
|
||||
forEachId(rangesTag, ids::add);
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static void forEachId(final ByteArrayTag rangesTag, final IntConsumer consumer) {
|
||||
final ByteBuf buf = Unpooled.wrappedBuffer(rangesTag.getValue());
|
||||
int prevEnd = 0;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteArrayTag;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
@@ -198,8 +199,14 @@ public class MappingDataBase implements MappingData {
|
||||
|
||||
final List<TagData> tagsList = new ArrayList<>(tags.size());
|
||||
for (final Map.Entry<String, Tag> entry : tag.entrySet()) {
|
||||
final IntArrayTag entries = (IntArrayTag) entry.getValue();
|
||||
tagsList.add(new TagData(entry.getKey(), entries.getValue()));
|
||||
final int[] entries;
|
||||
if (entry.getValue() instanceof final ByteArrayTag rangesTag) {
|
||||
entries = IdRanges.decodeAsList(rangesTag).toIntArray();
|
||||
} else {
|
||||
// Old format / if the values need to be sorted
|
||||
entries = ((IntArrayTag) entry.getValue()).getValue();
|
||||
}
|
||||
tagsList.add(new TagData(entry.getKey(), entries));
|
||||
}
|
||||
|
||||
this.tags.put(type, tagsList);
|
||||
|
||||
@@ -79,8 +79,8 @@ public record PaintingVariant(int width, int height, String assetId, @Nullable T
|
||||
.write("width", Types.INT, value.width())
|
||||
.write("height", Types.INT, value.height())
|
||||
.write("asset_id", Types.IDENTIFIER, Key.of(value.assetId()))
|
||||
.writeOptional("title", Types.TRUSTED_TAG, value.title())
|
||||
.writeOptional("author", Types.TRUSTED_TAG, value.author()));
|
||||
.writeOptional("title", Types.TEXT_COMPONENT_TAG, value.title())
|
||||
.writeOptional("author", Types.TEXT_COMPONENT_TAG, value.author()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,4 +39,11 @@ public record SoundEvent(String identifier, @Nullable Float fixedRange) {
|
||||
}
|
||||
return holder.updateId(soundRewriteFunction, () -> UNKNOWN);
|
||||
}
|
||||
|
||||
public static @Nullable Holder<SoundEvent> rewriteOptionalHolder(@Nullable final Holder<SoundEvent> holder, final Int2IntFunction soundRewriteFunction) {
|
||||
if (holder == null) {
|
||||
return null;
|
||||
}
|
||||
return holder.updateId(soundRewriteFunction, () -> null);
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -30,7 +30,6 @@ import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.PaintingVariant;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryKey;
|
||||
import com.viaversion.viaversion.api.minecraft.ResolvableProfile;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_20_5;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
|
||||
@@ -48,6 +47,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.Bee;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BlockEntityData;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BreakSound;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BrewingFuel;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.Compostable;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.consumable.Consumable1_21_2;
|
||||
@@ -55,7 +55,6 @@ import com.viaversion.viaversion.api.minecraft.item.data.CookingFuel;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant1_21_2;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant26_1;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageType;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DeathProtection;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DebugStickState;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DyedColor;
|
||||
@@ -125,7 +124,7 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
|
||||
public static final StructuredDataKey<Unit> UNBREAKABLE1_21_5 = new StructuredDataKey<>("unbreakable", Types.EMPTY);
|
||||
public static final StructuredDataKey<Tag> CUSTOM_NAME = new StructuredDataKey<>("custom_name", Types.TEXT_COMPONENT_TAG);
|
||||
public static final StructuredDataKey<Float> MINIMUM_ATTACK_CHARGE = new StructuredDataKey<>("minimum_attack_charge", Types.FLOAT);
|
||||
public static final StructuredDataKey<DamageType> DAMAGE_TYPE1_21_11 = new StructuredDataKey<>("damage_type", DamageType.TYPE1_21_11);
|
||||
public static final StructuredDataKey<Either<Integer, String>> DAMAGE_TYPE1_21_11 = new StructuredDataKey<>("damage_type", new SynchronizedRegistryEitherType(RegistryKey.of("damage_type")));
|
||||
public static final StructuredDataKey<Integer> DAMAGE_TYPE26_1 = new StructuredDataKey<>("damage_type", new SynchronizedRegistryValueType(RegistryKey.of("damage_type")));
|
||||
public static final StructuredDataKey<Tag> ITEM_NAME = new StructuredDataKey<>("item_name", Types.TEXT_COMPONENT_TAG);
|
||||
public static final StructuredDataKey<ItemModel> ITEM_MODEL = new StructuredDataKey<>("item_model", ItemModel.TYPE);
|
||||
@@ -153,6 +152,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.TYPE1_21_2);
|
||||
// 1.21.5-1.21.9 only, animation id 5 was still named spear
|
||||
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE1_21_5 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE1_21_5);
|
||||
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE26_3 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE26_3);
|
||||
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);
|
||||
@@ -238,7 +239,7 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
|
||||
public static final StructuredDataKey<Tag> LOCK1_20_5 = new StructuredDataKey<>("lock", Types.TAG);
|
||||
public static final StructuredDataKey<LockCode> LOCK1_21_2 = new StructuredDataKey<>("lock", LockCode.TYPE);
|
||||
public static final StructuredDataKey<CompoundTag> CONTAINER_LOOT = new StructuredDataKey<>("container_loot", Types.COMPOUND_TAG);
|
||||
public static final StructuredDataKey<Holder<SoundEvent>> BREAK_SOUND = new StructuredDataKey<>("break_sound", Types.SOUND_EVENT);
|
||||
public static final StructuredDataKey<BreakSound> BREAK_SOUND = new StructuredDataKey<>("break_sound", BreakSound.TYPE);
|
||||
public static final StructuredDataKey<Compostable> COMPOSTABLE = new StructuredDataKey<>("compostable", Compostable.TYPE);
|
||||
public static final StructuredDataKey<CookingFuel> COOKING_FUEL = new StructuredDataKey<>("cooking_fuel", CookingFuel.TYPE);
|
||||
public static final StructuredDataKey<BrewingFuel> BREWING_FUEL = new StructuredDataKey<>("brewing_fuel", BrewingFuel.TYPE);
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ public class StructuredDataKeys1_20_5 extends VersionedStructuredDataKeys {
|
||||
public final StructuredDataKey<Item[]> bundleContents;
|
||||
|
||||
public StructuredDataKeys1_20_5(final VersionedTypesHolder types) {
|
||||
this.container = add("container", types.itemArray());
|
||||
// Also includes later vanilla limits
|
||||
this.container = add("container", types.itemArray().withMaxLength(256));
|
||||
this.chargedProjectiles = add("charged_projectiles", types.itemArray().withMaxLength(1024));
|
||||
this.bundleContents = add("bundle_contents", types.itemArray().withMaxLength(1024));
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,6 +32,6 @@ public class StructuredDataKeys26_2 extends StructuredDataKeys1_21_11 {
|
||||
|
||||
public StructuredDataKeys26_2(final VersionedTypesHolder types) {
|
||||
super(types);
|
||||
this.sulfurCubeContent = add("sulfur_cube_content", types.item());
|
||||
this.sulfurCubeContent = add("sulfur_cube_content", types.itemTemplate());
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -27,7 +27,7 @@ import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public record BlockStateProperties(Map<String, String> properties) implements Copyable {
|
||||
@@ -36,7 +36,7 @@ public record BlockStateProperties(Map<String, String> properties) implements Co
|
||||
@Override
|
||||
public BlockStateProperties read(final ByteBuf buffer) {
|
||||
final int size = Types.VAR_INT.readPrimitive(buffer);
|
||||
final Map<String, String> properties = new Object2ObjectOpenHashMap<>();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
properties.put(Types.STRING.read(buffer), Types.STRING.read(buffer));
|
||||
}
|
||||
@@ -64,6 +64,6 @@ public record BlockStateProperties(Map<String, String> properties) implements Co
|
||||
|
||||
@Override
|
||||
public BlockStateProperties copy() {
|
||||
return new BlockStateProperties(new Object2ObjectOpenHashMap<>(properties));
|
||||
return new BlockStateProperties(new HashMap<>(properties));
|
||||
}
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData.MappingType;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.ArrayType;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
// TODO move block state provider rewriting to registry data rewriter
|
||||
public record BlockTransformData(CompoundTag blockStateProvider, Holder<SoundEvent> sound, int transformParticle,
|
||||
int[] disallowedFaces, @Nullable String loot, int dropStrategy,
|
||||
boolean updateFromNeighbors, int transformType,
|
||||
boolean consumeOnUse, int itemDamagePerUse) implements Rewritable, Copyable {
|
||||
|
||||
public static final Type<BlockTransformData> TYPE = new Type<>(BlockTransformData.class) {
|
||||
@Override
|
||||
public BlockTransformData read(final ByteBuf buffer) {
|
||||
final CompoundTag blockStateProvider = Types.COMPOUND_TAG.read(buffer);
|
||||
final Holder<SoundEvent> sound = Types.SOUND_EVENT.read(buffer);
|
||||
final int transformParticle = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int[] disallowedFaces = Types.VAR_INT_ARRAY_PRIMITIVE.read(buffer);
|
||||
final String loot = Types.OPTIONAL_STRING.read(buffer);
|
||||
final int dropStrategy = Types.VAR_INT.readPrimitive(buffer);
|
||||
final boolean updateFromNeighbors = Types.BOOLEAN.read(buffer);
|
||||
final int transformType = Types.VAR_INT.readPrimitive(buffer);
|
||||
final boolean consumeOnUse = Types.BOOLEAN.read(buffer);
|
||||
final int itemDamagePerUse = Types.VAR_INT.readPrimitive(buffer);
|
||||
return new BlockTransformData(blockStateProvider, sound, transformParticle, disallowedFaces, loot, dropStrategy, updateFromNeighbors, transformType, consumeOnUse, itemDamagePerUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final ByteBuf buffer, final BlockTransformData value) {
|
||||
Types.COMPOUND_TAG.write(buffer, value.blockStateProvider);
|
||||
Types.SOUND_EVENT.write(buffer, value.sound);
|
||||
Types.VAR_INT.writePrimitive(buffer, value.transformParticle);
|
||||
Types.VAR_INT_ARRAY_PRIMITIVE.write(buffer, value.disallowedFaces);
|
||||
Types.OPTIONAL_STRING.write(buffer, value.loot);
|
||||
Types.VAR_INT.writePrimitive(buffer, value.dropStrategy);
|
||||
Types.BOOLEAN.write(buffer, value.updateFromNeighbors);
|
||||
Types.VAR_INT.writePrimitive(buffer, value.transformType);
|
||||
Types.BOOLEAN.write(buffer, value.consumeOnUse);
|
||||
Types.VAR_INT.writePrimitive(buffer, value.itemDamagePerUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final BlockTransformData data) {
|
||||
final Holder<SoundEvent> defaultSound = Holder.of(ops.context().registryAccess().id(MappingType.SOUND, "intentionally_empty"));
|
||||
ops.writeMap(map -> map
|
||||
.write("block_state_provider", Types.COMPOUND_TAG, data.blockStateProvider)
|
||||
.writeOptional("sound", Types.SOUND_EVENT, data.sound, defaultSound)
|
||||
.writeOptional("particle", EnumTypes.TRANSFORM_PARTICLE, data.transformParticle, 0)
|
||||
.writeOptional("disallowed_faces", EnumTypes.DIRECTION.new EnumArrayType(), data.disallowedFaces, new int[0])
|
||||
.writeOptional("loot", Types.STRING, data.loot)
|
||||
.writeOptional("drop_strategy", Types.VAR_INT, data.dropStrategy, 1)
|
||||
.writeOptional("update_from_neighbors", Types.BOOLEAN, data.updateFromNeighbors, true)
|
||||
.writeOptional("transform_type", Types.VAR_INT, data.transformType, 0)
|
||||
.writeOptional("consume_on_use", Types.BOOLEAN, data.consumeOnUse, true)
|
||||
.writeOptional("item_damage_per_use", Types.VAR_INT, data.itemDamagePerUse, 0)
|
||||
);
|
||||
}
|
||||
};
|
||||
public static final ArrayType<BlockTransformData> ARRAY_TYPE = new ArrayType<>(TYPE);
|
||||
|
||||
@Override
|
||||
public BlockTransformData rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
CompoundTag updatedTag = blockStateProvider().copy();
|
||||
if (protocol.getRegistryDataRewriter().updateBlockStateProvider(updatedTag)) {
|
||||
// Back it up clientbound so server data doesn't disappear; remove it for newly spawned items serverbound.
|
||||
// Given it's not even encoded properly over the network and would otherwise require keeping full block state mappings
|
||||
// and is not needed on the client, we only do the bare minimum to guarantee compatibility.
|
||||
if (clientbound) {
|
||||
updatedTag.put("backup", this.blockStateProvider);
|
||||
} else {
|
||||
final CompoundTag backupTag = updatedTag.getCompoundTag("backup");
|
||||
updatedTag = backupTag != null ? backupTag : dummyBlockStateProvider();
|
||||
}
|
||||
}
|
||||
return new BlockTransformData(updatedTag, sound, transformParticle, disallowedFaces, loot, dropStrategy, updateFromNeighbors, transformType, consumeOnUse, itemDamagePerUse);
|
||||
}
|
||||
|
||||
private CompoundTag dummyBlockStateProvider() {
|
||||
final CompoundTag state = new CompoundTag();
|
||||
state.putString("Name", "dirt");
|
||||
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString("type", "simple");
|
||||
tag.put("state", state);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockTransformData copy() {
|
||||
return new BlockTransformData(blockStateProvider.copy(), sound, transformParticle, disallowedFaces, loot, dropStrategy, updateFromNeighbors, transformType, consumeOnUse, itemDamagePerUse);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -130,8 +130,8 @@ public record BlocksAttacks(
|
||||
|
||||
@Override
|
||||
public BlocksAttacks rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final Holder<SoundEvent> blockSound = SoundEvent.rewriteHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
final Holder<SoundEvent> disableSound = SoundEvent.rewriteHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
final Holder<SoundEvent> blockSound = SoundEvent.rewriteOptionalHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
final Holder<SoundEvent> disableSound = SoundEvent.rewriteOptionalHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return new BlocksAttacks(this.blockDelaySeconds, this.disableCooldownScale, this.damageReductions, this.itemDamage, this.bypassedBy, blockSound, disableSound);
|
||||
}
|
||||
|
||||
|
||||
+13
-9
@@ -22,18 +22,22 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.TransformingType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.EitherType;
|
||||
import com.viaversion.viaversion.util.Either;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
|
||||
/**
|
||||
* 1.21.11-only damage type.
|
||||
*
|
||||
* @param id registry int or string id
|
||||
*/
|
||||
public record DamageType(Either<Integer, String> id) {
|
||||
public record BreakSound(Holder<SoundEvent> sound) implements Rewritable {
|
||||
|
||||
public static final Type<DamageType> TYPE1_21_11 = TransformingType.of(new EitherType<>(Types.VAR_INT, Types.STRING), DamageType.class, DamageType::new, DamageType::id);
|
||||
public static final Type<BreakSound> TYPE = TransformingType.of(Types.SOUND_EVENT, BreakSound.class, BreakSound::new, BreakSound::sound);
|
||||
|
||||
@Override
|
||||
public BreakSound rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final Holder<SoundEvent> sound = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return this.sound == sound ? this : new BreakSound(sound);
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -22,27 +22,36 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.consumable.ConsumeEffect;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.TransformingType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
|
||||
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable {
|
||||
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable, Rewritable {
|
||||
|
||||
public static final Type<DeathProtection> TYPE1_21_2 = new TransformingType<>(ConsumeEffect.ARRAY_TYPE1_21_2, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
|
||||
@Override
|
||||
public void write(final Ops ops, final DeathProtection value) {
|
||||
ops.writeMap(map -> map.write("death_effects", ConsumeEffect.ARRAY_TYPE1_21_2, value.deathEffects));
|
||||
ops.writeMap(map -> map.writeOptional("death_effects", ConsumeEffect.ARRAY_TYPE1_21_2, value.deathEffects, new ConsumeEffect<?>[0]));
|
||||
}
|
||||
};
|
||||
public static final Type<DeathProtection> TYPE26_3 = new TransformingType<>(ConsumeEffect.ARRAY_TYPE26_3, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
|
||||
@Override
|
||||
public void write(final Ops ops, final DeathProtection value) {
|
||||
ops.writeMap(map -> map.write("death_effects", ConsumeEffect.ARRAY_TYPE26_3, value.deathEffects));
|
||||
ops.writeMap(map -> map.writeOptional("death_effects", ConsumeEffect.ARRAY_TYPE26_3, value.deathEffects, new ConsumeEffect<?>[0]));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public DeathProtection rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final ConsumeEffect<?>[] deathEffects = ConsumeEffect.rewrite(this.deathEffects, connection, protocol, clientbound);
|
||||
return deathEffects == this.deathEffects ? this : new DeathProtection(deathEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeathProtection copy() {
|
||||
return new DeathProtection(Copyable.copy(deathEffects));
|
||||
|
||||
+3
-2
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
@@ -41,7 +42,7 @@ public record Enchantments(Int2IntMap enchantments, boolean showInTooltip) imple
|
||||
@Override
|
||||
public Enchantments read(final ByteBuf buffer) {
|
||||
final Int2IntMap enchantments = new Int2IntOpenHashMap();
|
||||
final int size = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int id = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int level = Types.VAR_INT.readPrimitive(buffer);
|
||||
@@ -65,7 +66,7 @@ public record Enchantments(Int2IntMap enchantments, boolean showInTooltip) imple
|
||||
@Override
|
||||
public Enchantments read(final ByteBuf buffer) {
|
||||
final Int2IntMap enchantments = new Int2IntOpenHashMap();
|
||||
final int size = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int id = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int level = Types.VAR_INT.readPrimitive(buffer);
|
||||
|
||||
@@ -47,11 +47,8 @@ public final class EnumTypes {
|
||||
public static final EnumType AXOLOTL_VARIANT = new EnumType("lucy", "wild", "gold", "cyan", "blue");
|
||||
public static final EnumType EQUIPMENT_SLOT = new EnumType("mainhand", "feet", "legs", "chest", "head", "offhand", "body", "saddle");
|
||||
public static final EnumType SWING_ANIMATION1_21_11 = new EnumType("none", "whack", "stab");
|
||||
public static final EnumType ITEM_USE_ANIMATION1_21_5 = new EnumType("none", "eat", "drink", "block", "bow", "spear", "crossbow", "spyglass", "toot_horn", "brush", "bundle");
|
||||
public static final EnumType ITEM_USE_ANIMATION = new EnumType("none", "eat", "drink", "block", "bow", "trident", "crossbow", "spyglass", "toot_horn", "brush", "bundle", "spear");
|
||||
public static final EnumType DIRECTION = new EnumType("down", "up", "north", "south", "west", "east");
|
||||
public static final EnumType TRANSFORM_PARTICLE = new EnumType("none", "scrape", "wax_on", "wax_off");
|
||||
public static final EnumType TRAMNSFORM_DROP_STRATEGY = new EnumType("clicked_face", "from_middle");
|
||||
public static final EnumType TRAMNSFORM_TYPE = new EnumType("single_block", "copper_chest");
|
||||
// Enums with non-ordinal ids
|
||||
public static final FakeEnumType RABBIT_VARIANT = new FakeEnumType(List.of("brown", "white", "black", "white_splotched", "gold", "salt"), of(99, "evil"));
|
||||
// Hardcoded registries
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class FilterableComponent extends Filterable<Tag> implements Copyable, Rewritable {
|
||||
|
||||
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
|
||||
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TEXT_COMPONENT_TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
|
||||
@Override
|
||||
protected FilterableComponent create(final Tag raw, final Tag filtered) {
|
||||
return new FilterableComponent(raw, filtered);
|
||||
|
||||
+3
-3
@@ -67,7 +67,7 @@ public record Instrument1_21_2(Holder<SoundEvent> soundEvent, float useDuration,
|
||||
.write("sound_event", Types.SOUND_EVENT, value.soundEvent())
|
||||
.write("use_duration", Types.FLOAT, value.useDuration())
|
||||
.write("range", Types.FLOAT, value.range())
|
||||
.write("description", Types.TAG, value.description()));
|
||||
.write("description", Types.TEXT_COMPONENT_TAG, value.description()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,8 +103,8 @@ public record Instrument1_21_2(Holder<SoundEvent> soundEvent, float useDuration,
|
||||
.write("sound_event", Types.SOUND_EVENT, value.soundEvent())
|
||||
.write("use_duration", Types.FLOAT, value.useDuration())
|
||||
.write("range", Types.FLOAT, value.range())
|
||||
.write("durability_damage", Types.VAR_INT, value.durabilityDamage())
|
||||
.write("description", Types.TAG, value.description()));
|
||||
.writeOptional("durability_damage", Types.VAR_INT, value.durabilityDamage(), 0)
|
||||
.write("description", Types.TEXT_COMPONENT_TAG, value.description()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-1
@@ -22,19 +22,22 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.OptionalType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable Condition dismountConditions,
|
||||
@Nullable Condition knockbackConditions, @Nullable Condition damageConditions,
|
||||
float forwardMovement, float damageMultiplier,
|
||||
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
|
||||
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
|
||||
|
||||
public static final Type<KineticWeapon> TYPE = new Type<>(KineticWeapon.class) {
|
||||
@Override
|
||||
@@ -80,6 +83,13 @@ public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public KineticWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return sound == this.sound && hitSound == this.hitSound ? this : new KineticWeapon(contactCooldownTicks, delayTicks, dismountConditions, knockbackConditions, damageConditions, forwardMovement, damageMultiplier, sound, hitSound);
|
||||
}
|
||||
|
||||
public record Condition(int maxDurationTicks, float minSpeed, float minRelativeSpeed) {
|
||||
|
||||
public static final Type<Condition> TYPE = new Type<>(Condition.class) {
|
||||
|
||||
+11
-1
@@ -22,16 +22,19 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
|
||||
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
|
||||
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
|
||||
|
||||
public static final Type<PiercingWeapon> TYPE = new Type<>(PiercingWeapon.class) {
|
||||
@Override
|
||||
@@ -61,4 +64,11 @@ public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public PiercingWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return sound == this.sound && hitSound == this.hitSound ? this : new PiercingWeapon(dealsKnockback, dismounts, sound, hitSound);
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -29,16 +29,19 @@ import com.viaversion.viaversion.api.type.TransformingType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
|
||||
public record PotDecorations1_20_5(int[] itemIds) implements Copyable, Rewritable {
|
||||
|
||||
private static final Key BRICK = Key.ofPath("brick");
|
||||
public static final Type<PotDecorations1_20_5> TYPE = new TransformingType<>(Types.VAR_INT_ARRAY_PRIMITIVE, PotDecorations1_20_5.class, PotDecorations1_20_5::new, PotDecorations1_20_5::itemIds) {
|
||||
@Override
|
||||
public void write(final Ops ops, final PotDecorations1_20_5 value) {
|
||||
// Vanilla always encodes all 4 sides, filling missing ones with bricks
|
||||
ops.writeList(list -> {
|
||||
for (final int itemId : value.itemIds) {
|
||||
list.write(Types.IDENTIFIER, ops.context().registryAccess().item(itemId));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
list.write(Types.IDENTIFIER, i < value.itemIds.length ? ops.context().registryAccess().item(value.itemIds[i]) : BRICK);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public record PotionEffectData(int amplifier, int duration, boolean ambient, boo
|
||||
final boolean ambient = buffer.readBoolean();
|
||||
final boolean showParticles = buffer.readBoolean();
|
||||
final boolean showIcon = buffer.readBoolean();
|
||||
final PotionEffectData hiddenEffect = OPTIONAL_TYPE.read(buffer);
|
||||
final PotionEffectData hiddenEffect = OPTIONAL_TYPE.readTrackingDepth(buffer);
|
||||
return new PotionEffectData(amplifier, duration, ambient, showParticles, showIcon, hiddenEffect);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,10 +52,10 @@ public record SignText(Tag[] messages, Tag @Nullable [] filteredMessages, int co
|
||||
@Override
|
||||
public void write(final Ops ops, final SignText value) {
|
||||
ops.writeMap(map -> map
|
||||
.write("messages", Types.SIGN_TEXT, value.messages)
|
||||
.writeOptional("filtered_messages", Types.SIGN_TEXT, value.filteredMessages)
|
||||
.write("color", Types.VAR_INT, value.color, 15)
|
||||
.write("has_glowing_text", Types.BOOLEAN, value.hasGlowingText, false));
|
||||
.write("messages", Types.TEXT_COMPONENT_TAG_ARRAY, value.messages)
|
||||
.writeOptional("filtered_messages", Types.TEXT_COMPONENT_TAG_ARRAY, value.filteredMessages)
|
||||
.write("color", EnumTypes.DYE_COLOR, value.color)
|
||||
.write("has_glowing_text", Types.BOOLEAN, value.hasGlowingText));
|
||||
}
|
||||
};
|
||||
}
|
||||
+2
-1
@@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
||||
@@ -42,7 +43,7 @@ public record TooltipDisplay(boolean hideTooltip,
|
||||
public TooltipDisplay read(final ByteBuf buffer) {
|
||||
final boolean hideTooltip = buffer.readBoolean();
|
||||
final IntSortedSet hiddenComponents = new IntLinkedOpenHashSet();
|
||||
final int size = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
|
||||
for (int i = 0; i < size; i++) {
|
||||
hiddenComponents.add(Types.VAR_INT.readPrimitive(buffer));
|
||||
}
|
||||
|
||||
+11
-5
@@ -31,6 +31,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.EnumTypes;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.EnumType;
|
||||
import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -39,16 +40,19 @@ public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<S
|
||||
boolean hasConsumeParticles,
|
||||
ConsumeEffect<?>[] consumeEffects) implements Copyable, Rewritable {
|
||||
|
||||
public static final Type<Consumable1_21_2> TYPE1_21_2 = new ConsumableType(ConsumeEffect.ARRAY_TYPE1_21_2);
|
||||
public static final Type<Consumable1_21_2> TYPE26_3 = new ConsumableType(ConsumeEffect.ARRAY_TYPE26_3);
|
||||
public static final Type<Consumable1_21_2> TYPE1_21_2 = new ConsumableType(ConsumeEffect.ARRAY_TYPE1_21_2, EnumTypes.ITEM_USE_ANIMATION);
|
||||
public static final Type<Consumable1_21_2> TYPE1_21_5 = new ConsumableType(ConsumeEffect.ARRAY_TYPE1_21_2, EnumTypes.ITEM_USE_ANIMATION1_21_5);
|
||||
public static final Type<Consumable1_21_2> TYPE26_3 = new ConsumableType(ConsumeEffect.ARRAY_TYPE26_3, EnumTypes.ITEM_USE_ANIMATION);
|
||||
|
||||
private static final class ConsumableType extends Type<Consumable1_21_2> {
|
||||
|
||||
private final Type<ConsumeEffect<?>[]> consumeEffectType;
|
||||
private final EnumType animationType;
|
||||
|
||||
private ConsumableType(final Type<ConsumeEffect<?>[]> consumeEffectType) {
|
||||
private ConsumableType(final Type<ConsumeEffect<?>[]> consumeEffectType, final EnumType animationType) {
|
||||
super(Consumable1_21_2.class);
|
||||
this.consumeEffectType = consumeEffectType;
|
||||
this.animationType = animationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +79,7 @@ public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<S
|
||||
final Holder<SoundEvent> defaultSound = Holder.of(ops.context().registryAccess().id(MappingData.MappingType.SOUND, "entity.generic.eat"));
|
||||
ops.writeMap(map -> map
|
||||
.writeOptional("consume_seconds", Types.FLOAT, value.consumeSeconds, 1.6F)
|
||||
.writeOptional("animation", EnumTypes.ITEM_USE_ANIMATION, value.animationType, 1)
|
||||
.writeOptional("animation", animationType, value.animationType, 1)
|
||||
.writeOptional("sound", Types.SOUND_EVENT, value.sound, defaultSound)
|
||||
.writeOptional("has_consume_particles", Types.BOOLEAN, value.hasConsumeParticles, true)
|
||||
.writeOptional("on_consume_effects", consumeEffectType, value.consumeEffects, new ConsumeEffect<?>[0]));
|
||||
@@ -85,7 +89,9 @@ public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<S
|
||||
@Override
|
||||
public Consumable1_21_2 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
final Holder<SoundEvent> soundHolder = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return soundHolder == this.sound ? this : new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
|
||||
final ConsumeEffect<?>[] consumeEffects = ConsumeEffect.rewrite(this.consumeEffects, connection, protocol, clientbound);
|
||||
return soundHolder == this.sound && consumeEffects == this.consumeEffects ? this
|
||||
: new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+33
-4
@@ -22,20 +22,23 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.HolderSet;
|
||||
import com.viaversion.viaversion.api.minecraft.SoundEvent;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.EnumTypes;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.ArrayType;
|
||||
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
|
||||
import com.viaversion.viaversion.api.type.types.misc.HolderType;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public record ConsumeEffect<T>(int id, Type<T> type, T value) {
|
||||
public record ConsumeEffect<T>(int id, Type<T> type, T value) implements Rewritable {
|
||||
|
||||
private static final HolderSetType REMOVE_EFFECTS_TYPE = new HolderSetType() {
|
||||
@Override
|
||||
@@ -56,7 +59,7 @@ public record ConsumeEffect<T>(int id, Type<T> type, T value) {
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final Float value) {
|
||||
ops.writeMap(map -> map.write("diameter", Types.FLOAT, value, 16F));
|
||||
ops.writeMap(map -> map.writeOptional("diameter", Types.FLOAT, value, 16F));
|
||||
}
|
||||
};
|
||||
public static final Type<TeleportRandomlyConsumeEffect> TELEPORT_RANDOMLY_TYPE26_3 = new Type<>(TeleportRandomlyConsumeEffect.class) {
|
||||
@@ -76,8 +79,8 @@ public record ConsumeEffect<T>(int id, Type<T> type, T value) {
|
||||
@Override
|
||||
public void write(final Ops ops, final TeleportRandomlyConsumeEffect value) {
|
||||
ops.writeMap(map -> map
|
||||
.write("diameter", Types.FLOAT, value.diameter(), 16F)
|
||||
.write("directional_particles", Types.BOOLEAN, value.directionalParticles(), true)
|
||||
.writeOptional("diameter", Types.FLOAT, value.diameter(), 16F)
|
||||
.writeOptional("directional_particles", Types.BOOLEAN, value.directionalParticles(), true)
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -123,6 +126,32 @@ public record ConsumeEffect<T>(int id, Type<T> type, T value) {
|
||||
return new ConsumeEffect<>(id, type, (T) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeEffect<T> rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
if (this.type != PLAY_SOUND_TYPE) {
|
||||
return this;
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
final Holder<SoundEvent> sound = (Holder<SoundEvent>) this.value;
|
||||
final Holder<SoundEvent> rewrittenSound = SoundEvent.rewriteHolder(sound, Rewritable.soundRewriteFunction(protocol, clientbound));
|
||||
return rewrittenSound == sound ? this : ConsumeEffect.of(this.id, this.type, rewrittenSound);
|
||||
}
|
||||
|
||||
public static ConsumeEffect<?>[] rewrite(final ConsumeEffect<?>[] effects, final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
ConsumeEffect<?>[] rewrittenEffects = effects;
|
||||
for (int i = 0; i < effects.length; i++) {
|
||||
final ConsumeEffect<?> rewrittenEffect = effects[i].rewrite(connection, protocol, clientbound);
|
||||
if (rewrittenEffect != effects[i]) {
|
||||
if (rewrittenEffects == effects) {
|
||||
rewrittenEffects = effects.clone();
|
||||
}
|
||||
rewrittenEffects[i] = rewrittenEffect;
|
||||
}
|
||||
}
|
||||
return rewrittenEffects;
|
||||
}
|
||||
|
||||
void writeValue(final ByteBuf buf) {
|
||||
this.type.write(buf, this.value);
|
||||
}
|
||||
|
||||
+6
-6
@@ -32,7 +32,7 @@ import com.viaversion.viaversion.util.Copyable;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.Rewritable;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemModelIndex,
|
||||
@@ -57,7 +57,7 @@ public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemMo
|
||||
final float itemModelIndex = buffer.readFloat();
|
||||
|
||||
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
|
||||
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
|
||||
final Map<String, String> overrideArmorMaterials = new HashMap<>();
|
||||
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
|
||||
final int key = Types.VAR_INT.readPrimitive(buffer);
|
||||
final String value = Types.STRING.read(buffer);
|
||||
@@ -91,7 +91,7 @@ public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemMo
|
||||
final float itemModelIndex = buffer.readFloat();
|
||||
|
||||
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
|
||||
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
|
||||
final Map<String, String> overrideArmorMaterials = new HashMap<>();
|
||||
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
|
||||
final String key = Types.STRING.read(buffer);
|
||||
final String value = Types.STRING.read(buffer);
|
||||
@@ -124,7 +124,7 @@ public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemMo
|
||||
final int item = Types.VAR_INT.readPrimitive(buffer);
|
||||
|
||||
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
|
||||
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
|
||||
final Map<String, String> overrideArmorMaterials = new HashMap<>();
|
||||
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
|
||||
final String key = Types.STRING.read(buffer);
|
||||
final String value = Types.STRING.read(buffer);
|
||||
@@ -155,7 +155,7 @@ public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemMo
|
||||
final String assetName = Types.STRING.read(buffer);
|
||||
|
||||
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
|
||||
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
|
||||
final Map<String, String> overrideArmorMaterials = new HashMap<>();
|
||||
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
|
||||
final String key = Types.STRING.read(buffer);
|
||||
final String value = Types.STRING.read(buffer);
|
||||
@@ -207,6 +207,6 @@ public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemMo
|
||||
|
||||
@Override
|
||||
public ArmorTrimMaterial1_20_5 copy() {
|
||||
return new ArmorTrimMaterial1_20_5(assetName, itemId, itemModelIndex, new Object2ObjectArrayMap<>(overrideArmorMaterials), description);
|
||||
return new ArmorTrimMaterial1_20_5(assetName, itemId, itemModelIndex, new HashMap<>(overrideArmorMaterials), description);
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public class PacketTracker {
|
||||
|
||||
// Warning system
|
||||
private long warningPeriodStart = System.nanoTime();
|
||||
private long lastWarning = Long.MIN_VALUE;
|
||||
private long lastWarning = warningPeriodStart - SECOND_NANOS;
|
||||
private int warnings;
|
||||
|
||||
public RateTracker(int windowSize) {
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
package com.viaversion.viaversion.api.type;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.util.NestingLimiter;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* Type for buffer reading and writing.
|
||||
@@ -63,6 +65,22 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T>, Cod
|
||||
return typeName != null && !typeName.isEmpty() ? typeName : this.getClass().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a value and counts it towards the nesting depth limit of the current thread, to be used by recursive types.
|
||||
*
|
||||
* @param buffer buffer to read from
|
||||
* @return read value
|
||||
* @see NestingLimiter
|
||||
*/
|
||||
public T readTrackingDepth(final ByteBuf buffer) {
|
||||
NestingLimiter.increment();
|
||||
try {
|
||||
return read(buffer);
|
||||
} finally {
|
||||
NestingLimiter.decrement();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Ops ops, T value) {
|
||||
throw new UnsupportedOperationException("Write operation not supported for type: " + getTypeName());
|
||||
|
||||
@@ -227,6 +227,7 @@ public final class Types {
|
||||
public static final Type<Tag[]> TAG_ARRAY = new ArrayType<>(TAG);
|
||||
public static final Type<Tag> OPTIONAL_TAG = TagType.OptionalTagType.type();
|
||||
public static final Type<Tag> TEXT_COMPONENT_TAG = new TextComponentTagType(); // only strictly needed for hashing
|
||||
public static final Type<Tag[]> TEXT_COMPONENT_TAG_ARRAY = new ArrayType<>(Types.TEXT_COMPONENT_TAG);
|
||||
|
||||
public static final Type<Tag> CUSTOM_CLICK_ACTION_TAG = new LengthPrefixedTagType(65536);
|
||||
public static final FixedLengthArrayType<Tag> SIGN_TEXT = new FixedLengthArrayType<>(TAG, 4);
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ArrayType<T> extends Type<T[]> {
|
||||
private static final int MAX_PRESIZED_LENGTH = 128;
|
||||
protected final Type<T> elementType;
|
||||
private final int maxLength;
|
||||
|
||||
@@ -63,7 +64,7 @@ public class ArrayType<T> extends Type<T[]> {
|
||||
throw new IllegalArgumentException("Array length " + amount + " is longer than maximum " + maxLength);
|
||||
}
|
||||
|
||||
return amount < Short.MAX_VALUE ? readArray(buffer, amount) : readList(buffer, amount);
|
||||
return amount <= MAX_PRESIZED_LENGTH ? readArray(buffer, amount) : readList(buffer, amount);
|
||||
}
|
||||
|
||||
protected T[] readArray(ByteBuf buffer, int length) {
|
||||
@@ -111,4 +112,14 @@ public class ArrayType<T> extends Type<T[]> {
|
||||
public ArrayType<T> withMaxLength(final int maxLength) {
|
||||
return new ArrayType<>(this.getOutputClass(), this.elementType, maxLength);
|
||||
}
|
||||
|
||||
static int checkLength(final int length, final int elementBytes, final int readable) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length " + length + " is negative");
|
||||
}
|
||||
if ((long) length * elementBytes > readable) {
|
||||
throw new IllegalArgumentException("Length " + length + " is larger than readable bytes");
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class BooleanArrayType extends Type<boolean[]> {
|
||||
@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");
|
||||
Preconditions.checkArgument(buffer.isReadable(length), "Length is larger than readable bytes");
|
||||
final boolean[] array = new boolean[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = buffer.readBoolean();
|
||||
|
||||
@@ -32,15 +32,30 @@ import io.netty.buffer.ByteBuf;
|
||||
public class ByteArrayType extends Type<byte[]> {
|
||||
|
||||
private final int length;
|
||||
private final int maxLength;
|
||||
|
||||
public ByteArrayType(final int length) {
|
||||
super(byte[].class);
|
||||
this.length = length;
|
||||
this(length, -1);
|
||||
}
|
||||
|
||||
public ByteArrayType() {
|
||||
this(-1, -1);
|
||||
}
|
||||
|
||||
private ByteArrayType(final int length, final int maxLength) {
|
||||
super(byte[].class);
|
||||
this.length = -1;
|
||||
this.length = length;
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a length-prefixed byte array type that rejects arrays longer than the given maximum.
|
||||
*
|
||||
* @param maxLength maximum array length
|
||||
* @return length-prefixed byte array type with the given maximum length
|
||||
*/
|
||||
public static ByteArrayType withMaxLength(final int maxLength) {
|
||||
return new ByteArrayType(-1, maxLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,6 +63,7 @@ public class ByteArrayType extends Type<byte[]> {
|
||||
if (this.length != -1) {
|
||||
Preconditions.checkArgument(length == object.length, "Length does not match expected length");
|
||||
} else {
|
||||
checkMaxLength(object.length);
|
||||
Types.VAR_INT.writePrimitive(buffer, object.length);
|
||||
}
|
||||
buffer.writeBytes(object);
|
||||
@@ -55,13 +71,20 @@ public class ByteArrayType extends Type<byte[]> {
|
||||
|
||||
@Override
|
||||
public byte[] 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 int length = this.length == -1 ? checkMaxLength(Types.VAR_INT.readPrimitive(buffer)) : this.length;
|
||||
Preconditions.checkArgument(buffer.isReadable(length), "Length is larger than readable bytes");
|
||||
final byte[] array = new byte[length];
|
||||
buffer.readBytes(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
private int checkMaxLength(final int length) {
|
||||
if (maxLength != -1 && length > maxLength) {
|
||||
throw new IllegalArgumentException("Byte array length " + length + " is longer than maximum " + maxLength);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final byte[] value) {
|
||||
ops.writeBytes(value);
|
||||
|
||||
@@ -27,6 +27,8 @@ import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
|
||||
|
||||
public class FloatArrayType extends Type<float[]> {
|
||||
|
||||
private final int length;
|
||||
@@ -55,8 +57,7 @@ public class FloatArrayType extends Type<float[]> {
|
||||
|
||||
@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 int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Float.BYTES, buffer.readableBytes());
|
||||
final float[] array = new float[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = buffer.readFloat();
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
|
||||
|
||||
public class IntArrayType extends Type<int[]> {
|
||||
|
||||
private final int length;
|
||||
@@ -56,8 +58,7 @@ public class IntArrayType extends Type<int[]> {
|
||||
|
||||
@Override
|
||||
public int[] 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 int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Integer.BYTES, buffer.readableBytes());
|
||||
final int[] array = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = buffer.readInt();
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
|
||||
|
||||
public class LongArrayType extends Type<long[]> {
|
||||
|
||||
private final int length;
|
||||
@@ -43,8 +45,7 @@ public class LongArrayType extends Type<long[]> {
|
||||
|
||||
@Override
|
||||
public long[] 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 int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Long.BYTES, buffer.readableBytes());
|
||||
return readFixedLength(buffer, length);
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -34,6 +34,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
public class HashedItemType1_21_5 extends Type<HashedItem> {
|
||||
private static final int MAX_DATA_TYPE_ID = 255; // Needs adjusting if we ever get beyond this
|
||||
|
||||
public HashedItemType1_21_5() {
|
||||
super(HashedItem.class);
|
||||
@@ -51,7 +52,7 @@ public class HashedItemType1_21_5 extends Type<HashedItem> {
|
||||
final int addedComponentsSize = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
|
||||
final Int2IntMap dataHashes = new Int2IntOpenHashMap(addedComponentsSize);
|
||||
for (int i = 0; i < addedComponentsSize; i++) {
|
||||
final int dataType = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int dataType = readDataType(buffer);
|
||||
final int hash = Types.INT.readPrimitive(buffer);
|
||||
dataHashes.put(dataType, hash);
|
||||
}
|
||||
@@ -59,12 +60,20 @@ public class HashedItemType1_21_5 extends Type<HashedItem> {
|
||||
final int removedComponentsSize = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
|
||||
final IntSet removedData = new IntOpenHashSet(removedComponentsSize);
|
||||
for (int i = 0; i < removedComponentsSize; i++) {
|
||||
final int dataType = Types.VAR_INT.readPrimitive(buffer);
|
||||
final int dataType = readDataType(buffer);
|
||||
removedData.add(dataType);
|
||||
}
|
||||
return new HashedStructuredItem(id, amount, dataHashes, removedData);
|
||||
}
|
||||
|
||||
private static int readDataType(final ByteBuf buffer) {
|
||||
final int dataType = Types.VAR_INT.readPrimitive(buffer);
|
||||
if (dataType < 0 || dataType > MAX_DATA_TYPE_ID) {
|
||||
throw new IllegalArgumentException("Invalid data component type " + dataType);
|
||||
}
|
||||
return dataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final ByteBuf buffer, final HashedItem value) {
|
||||
if (value.isEmpty()) {
|
||||
|
||||
+2
-2
@@ -34,7 +34,6 @@ import io.netty.buffer.ByteBuf;
|
||||
// Very similar to normal items (and just results in an item), except it allows non-positive amounts and has id/amount swapped because ???
|
||||
public final class ItemCostType1_20_5 extends Type<Item> {
|
||||
|
||||
private static final StructuredData<?>[] EMPTY_DATA_ARRAY = new StructuredData[0];
|
||||
private final Type<StructuredData<?>[]> dataArrayType;
|
||||
|
||||
public ItemCostType1_20_5(final Type<StructuredData<?>[]> dataArrayType) {
|
||||
@@ -54,7 +53,8 @@ public final class ItemCostType1_20_5 extends Type<Item> {
|
||||
public void write(final ByteBuf buffer, final Item object) {
|
||||
Types.VAR_INT.writePrimitive(buffer, object.identifier());
|
||||
Types.VAR_INT.writePrimitive(buffer, object.amount());
|
||||
dataArrayType.write(buffer, object.dataContainer().data().values().toArray(EMPTY_DATA_ARRAY));
|
||||
// Item costs are exact-match predicates without removal markers, so empty data can't be written
|
||||
dataArrayType.write(buffer, object.dataContainer().data().values().stream().filter(StructuredData::isPresent).toArray(StructuredData[]::new));
|
||||
}
|
||||
|
||||
public static final class OptionalItemCostType extends OptionalType<Item> {
|
||||
|
||||
+11
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.type.types.item;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
@@ -55,6 +56,16 @@ public class ItemTemplateType26_1 extends ItemType1_20_5 {
|
||||
writeData(buffer, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final Item item) {
|
||||
// Unlike item stacks, templates omit the default count
|
||||
ops.writeMap(map -> {
|
||||
map.write("id", Types.IDENTIFIER, ops.context().registryAccess().item(item.identifier()))
|
||||
.writeOptional("count", Types.VAR_INT, item.amount(), 1);
|
||||
writeComponents(map, item);
|
||||
});
|
||||
}
|
||||
|
||||
public static final class OptionalItemTemplateType extends OptionalType<Item> {
|
||||
|
||||
public OptionalItemTemplateType(final Type<Item> type) {
|
||||
|
||||
+16
-13
@@ -127,22 +127,25 @@ public class ItemType1_20_5 extends Type<Item> {
|
||||
ops.writeMap(map -> {
|
||||
map.write("id", Types.IDENTIFIER, ops.context().registryAccess().item(item.identifier()))
|
||||
.write("count", Types.VAR_INT, item.amount());
|
||||
writeComponents(map, item);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.dataContainer().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
protected void writeComponents(final Ops.MapSerializer map, final Item item) {
|
||||
if (item.dataContainer().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
map.writeMap("components", components -> {
|
||||
for (final StructuredData<?> data : item.dataContainer().data().values()) {
|
||||
String key = Key.namespaced(data.key().identifier());
|
||||
if (data.isEmpty()) {
|
||||
key = "!" + key;
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
components.write(key, (Type<StructuredData<?>>) dataType, data);
|
||||
map.writeMap("components", components -> {
|
||||
for (final StructuredData<?> data : item.dataContainer().data().values()) {
|
||||
String key = Key.namespaced(data.key().identifier());
|
||||
if (data.isEmpty()) {
|
||||
key = "!" + key;
|
||||
}
|
||||
});
|
||||
|
||||
//noinspection unchecked
|
||||
components.write(key, (Type<StructuredData<?>>) dataType, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class StructuredDataType extends Type<StructuredData<?>> implements Struc
|
||||
}
|
||||
|
||||
<T> StructuredData<T> readData(final ByteBuf buffer, final StructuredDataKey<T> key, final int id) {
|
||||
return StructuredData.of(key, key.type().read(buffer), id);
|
||||
return StructuredData.of(key, key.type().readTrackingDepth(buffer), id);
|
||||
}
|
||||
|
||||
public DataFiller filler(final Protocol<?, ?, ?, ?> protocol) {
|
||||
|
||||
@@ -26,9 +26,12 @@ import com.viaversion.viaversion.api.minecraft.ProfileKey;
|
||||
import com.viaversion.viaversion.api.type.OptionalType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.ByteArrayType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class ProfileKeyType extends Type<ProfileKey> {
|
||||
private static final ByteArrayType PUBLIC_KEY_TYPE = ByteArrayType.withMaxLength(512);
|
||||
private static final ByteArrayType KEY_SIGNATURE_TYPE = ByteArrayType.withMaxLength(4096);
|
||||
|
||||
public ProfileKeyType() {
|
||||
super(ProfileKey.class);
|
||||
@@ -36,14 +39,14 @@ public class ProfileKeyType extends Type<ProfileKey> {
|
||||
|
||||
@Override
|
||||
public ProfileKey read(final ByteBuf buffer) {
|
||||
return new ProfileKey(buffer.readLong(), Types.BYTE_ARRAY_PRIMITIVE.read(buffer), Types.BYTE_ARRAY_PRIMITIVE.read(buffer));
|
||||
return new ProfileKey(buffer.readLong(), PUBLIC_KEY_TYPE.read(buffer), KEY_SIGNATURE_TYPE.read(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final ByteBuf buffer, final ProfileKey object) {
|
||||
buffer.writeLong(object.expiresAt());
|
||||
Types.BYTE_ARRAY_PRIMITIVE.write(buffer, object.publicKey());
|
||||
Types.BYTE_ARRAY_PRIMITIVE.write(buffer, object.keySignature());
|
||||
PUBLIC_KEY_TYPE.write(buffer, object.publicKey());
|
||||
KEY_SIGNATURE_TYPE.write(buffer, object.keySignature());
|
||||
}
|
||||
|
||||
public static final class OptionalProfileKeyType extends OptionalType<ProfileKey> {
|
||||
|
||||
@@ -39,9 +39,7 @@ public interface Copyable {
|
||||
final int length = Array.getLength(object);
|
||||
final Object copy = Array.newInstance(componentType, length);
|
||||
if (componentType.isPrimitive()) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
Array.set(copy, i, Array.get(object, i));
|
||||
}
|
||||
System.arraycopy(object, 0, copy, 0, length);
|
||||
} else {
|
||||
// See if we need to copy elements, too
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.util;
|
||||
|
||||
public final class NestingLimiter {
|
||||
|
||||
private static final ThreadLocal<int[]> STATE = ThreadLocal.withInitial(() -> new int[]{0, -1}); // Current depth, max depth
|
||||
|
||||
/**
|
||||
* Sets the max depth for reads on the current thread.
|
||||
*
|
||||
* @param maxDepth max depth, or -1 for no limit
|
||||
* @return the previous max depth, to be set again once done
|
||||
*/
|
||||
public static int setMaxDepth(final int maxDepth) {
|
||||
final int[] state = STATE.get();
|
||||
final int previous = state[1];
|
||||
state[1] = maxDepth;
|
||||
return previous;
|
||||
}
|
||||
|
||||
public static void increment() {
|
||||
final int[] state = STATE.get();
|
||||
if (state[1] != -1 && state[0] >= state[1]) {
|
||||
throw new IllegalArgumentException("Data is nested too deeply");
|
||||
}
|
||||
state[0]++;
|
||||
}
|
||||
|
||||
public static void decrement() {
|
||||
STATE.get()[0]--;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public interface Rewritable {
|
||||
|
||||
static Int2IntFunction soundRewriteFunction(final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
return protocol.getMappingData().getSoundMappings() == null ? Int2IntFunction.identity()
|
||||
: (clientbound ? protocol.getMappingData().getSoundMappings()::getNewId : protocol.getMappingData()::getOldSoundId);
|
||||
: (clientbound ? protocol.getMappingData().getSoundMappings()::getNewId : protocol.getMappingData().getSoundMappings().inverse()::getNewId);
|
||||
}
|
||||
|
||||
static Int2IntFunction entityRewriteFunction(final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
|
||||
|
||||
@@ -44,11 +44,22 @@ public final class UUIDUtil {
|
||||
}
|
||||
|
||||
public static @Nullable UUID parseUUID(final String uuidString) {
|
||||
try {
|
||||
return UUID.fromString(uuidString);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// Check the format beforehand instead of catching the exception, exceptions are expensive
|
||||
if (uuidString.length() != 36) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 36; i++) {
|
||||
final char c = uuidString.charAt(i);
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
if (c != '-') {
|
||||
return null;
|
||||
}
|
||||
} else if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return UUID.fromString(uuidString);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.viaversion.viaversion.commands.ViaCommandHandler;
|
||||
import com.viaversion.viaversion.configuration.ConfigurationProviderImpl;
|
||||
import com.viaversion.viaversion.connection.ConnectionManagerImpl;
|
||||
import com.viaversion.viaversion.debug.DebugHandlerImpl;
|
||||
import com.viaversion.viaversion.platform.UserConnectionViaVersionPlatform;
|
||||
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
|
||||
import com.viaversion.viaversion.protocol.ServerProtocolVersionRange;
|
||||
import com.viaversion.viaversion.protocol.ServerProtocolVersionSingleton;
|
||||
@@ -203,6 +204,10 @@ public class ViaManagerImpl implements ViaManager {
|
||||
// Refresh Versions
|
||||
protocolManager.refreshVersions();
|
||||
|
||||
if (protocolVersion.isKnown() && !(platform instanceof UserConnectionViaVersionPlatform)) {
|
||||
exploitableVersionsWarning(protocolVersion);
|
||||
}
|
||||
|
||||
for (final Runnable listener : postEnableListeners) {
|
||||
listener.run();
|
||||
}
|
||||
@@ -243,6 +248,28 @@ public class ViaManagerImpl implements ViaManager {
|
||||
platform.getLogger().info("ViaVersion has been disabled; uninjected the platform and shut down the scheduler.");
|
||||
}
|
||||
|
||||
private void exploitableVersionsWarning(final ServerProtocolVersion serverProtocolVersion) {
|
||||
if (!platform.isProxy() && isExploitableVersion(serverProtocolVersion.highestSupportedProtocolVersion())) {
|
||||
platform.getLogger().warning("The Vanilla protocol of Minecraft 1.20.5-1.21.4 is exploitable easily. Please consider updating your server to at least 1.21.5.");
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> versions = new ArrayList<>();
|
||||
for (final ProtocolVersion version : protocolManager.getSupportedVersions()) {
|
||||
if (isExploitableVersion(version) && !platform.getConf().blockedProtocolVersions().contains(version)) {
|
||||
versions.add('"' + version.getName() + '"');
|
||||
}
|
||||
}
|
||||
if (!versions.isEmpty()) {
|
||||
platform.getLogger().warning("The Vanilla protocol of Minecraft 1.20.5-1.21.4 is easily exploitable. "
|
||||
+ "If you don't need to support these versions, consider adding them to block-versions in the ViaVersion config: " + String.join(", ", versions));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isExploitableVersion(final ProtocolVersion version) {
|
||||
return version.betweenInclusive(ProtocolVersion.v1_20_5, ProtocolVersion.v1_21_4);
|
||||
}
|
||||
|
||||
private void checkJavaVersion() {
|
||||
int javaVersion = Runtime.version().feature();
|
||||
if (javaVersion < 17) {
|
||||
|
||||
@@ -233,6 +233,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
|
||||
callOnMappingDataLoaded(getEntityRewriter());
|
||||
callOnMappingDataLoaded(getItemRewriter());
|
||||
callOnMappingDataLoaded(getTagRewriter());
|
||||
callOnMappingDataLoaded(getBlockRewriter());
|
||||
}
|
||||
|
||||
private void callRegister(@Nullable Rewriter<?> rewriter) {
|
||||
|
||||
@@ -31,7 +31,6 @@ public record CodecRegistryContext(Protocol<?, ?, ?, ?> protocol, RegistryAccess
|
||||
|
||||
// Generally from hardcoded, but highly variable client data
|
||||
private static final Set<StructuredDataKey<?>> NOT_IMPLEMENTED = new ReferenceOpenHashSet<>(List.of(
|
||||
StructuredDataKey.DAMAGE_TYPE1_21_11
|
||||
));
|
||||
|
||||
public CodecRegistryContext(final Protocol<?, ?, ?, ?> protocol, final RegistryAccess registryAccess, final boolean mapped) {
|
||||
|
||||
@@ -95,6 +95,7 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
private boolean translateOcelotToCat;
|
||||
private boolean enforceSecureChat;
|
||||
private boolean handleInvalidItemCount;
|
||||
private boolean backupNestedItems;
|
||||
private boolean cancelBlockSounds;
|
||||
private boolean hideScoreboardNumbers;
|
||||
private boolean fix1_21PlacementRotation;
|
||||
@@ -172,6 +173,7 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
translateOcelotToCat = getBoolean("translate-ocelot-to-cat", true);
|
||||
enforceSecureChat = getBoolean("enforce-secure-chat", false);
|
||||
handleInvalidItemCount = getBoolean("handle-invalid-item-count", false);
|
||||
backupNestedItems = getBoolean("backup-nested-items", false);
|
||||
cancelBlockSounds = getBoolean("cancel-block-sounds", true);
|
||||
hideScoreboardNumbers = getBoolean("hide-scoreboard-numbers", false);
|
||||
fix1_21PlacementRotation = getBoolean("fix-1_21-placement-rotation", true);
|
||||
@@ -622,6 +624,11 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
return handleInvalidItemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean backupNestedItems() {
|
||||
return backupNestedItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancelBlockSounds() {
|
||||
return cancelBlockSounds;
|
||||
|
||||
@@ -36,15 +36,16 @@ import com.viaversion.viaversion.exception.CancelException;
|
||||
import com.viaversion.viaversion.exception.InformativeException;
|
||||
import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl;
|
||||
import com.viaversion.viaversion.util.ChatColorUtil;
|
||||
import com.viaversion.viaversion.util.NestingLimiter;
|
||||
import com.viaversion.viaversion.util.PipelineUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.CodecException;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -58,6 +59,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class UserConnectionImpl implements UserConnection {
|
||||
private static final int PASSTHROUGH_DATA_BYTES = Long.BYTES + 2;
|
||||
private static final int MAX_SERVERBOUND_NESTING_DEPTH = 128;
|
||||
private static final AtomicLong IDS = new AtomicLong();
|
||||
private final long id = IDS.incrementAndGet();
|
||||
private final SplittableRandom random = new SplittableRandom();
|
||||
@@ -225,10 +227,10 @@ public class UserConnectionImpl implements UserConnection {
|
||||
|
||||
private void sendRawPacket(final ByteBuf packet, final boolean currentThread) {
|
||||
if (currentThread) {
|
||||
sendRawPacketNow(packet);
|
||||
sendRawPacketFuture(packet);
|
||||
} else {
|
||||
try {
|
||||
channel.eventLoop().execute(() -> sendRawPacketNow(packet));
|
||||
channel.eventLoop().execute(() -> sendRawPacketFuture(packet));
|
||||
} catch (Throwable e) {
|
||||
packet.release(); // Couldn't schedule
|
||||
e.printStackTrace();
|
||||
@@ -236,25 +238,22 @@ public class UserConnectionImpl implements UserConnection {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendRawPacketNow(final ByteBuf buf) {
|
||||
final ChannelPipeline pipeline = getChannel().pipeline();
|
||||
final ViaInjector injector = Via.getManager().getInjector();
|
||||
if (clientSide) {
|
||||
// We'll just assume that Via decoder isn't wrapping the original decoder
|
||||
pipeline.context(injector.getDecoderName()).fireChannelRead(buf);
|
||||
} else {
|
||||
pipeline.context(injector.getEncoderName()).writeAndFlush(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
|
||||
final ViaInjector injector = Via.getManager().getInjector();
|
||||
final ChannelHandlerContext context = channel.pipeline().context(clientSide ? injector.getDecoderName() : injector.getEncoderName());
|
||||
if (context == null) {
|
||||
// Handlers are removed once the channel is closed
|
||||
packet.release();
|
||||
return channel.newFailedFuture(new ClosedChannelException());
|
||||
}
|
||||
|
||||
if (clientSide) {
|
||||
// Assume that decoder isn't wrapping
|
||||
getChannel().pipeline().context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet);
|
||||
return getChannel().newSucceededFuture();
|
||||
context.fireChannelRead(packet);
|
||||
return channel.newSucceededFuture();
|
||||
} else {
|
||||
return channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet);
|
||||
return context.writeAndFlush(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +342,7 @@ public class UserConnectionImpl implements UserConnection {
|
||||
writeAndFlush(packet);
|
||||
} else {
|
||||
try {
|
||||
getChannel().eventLoop().execute(() -> writeAndFlush(packet));
|
||||
channel.eventLoop().execute(() -> writeAndFlush(packet));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
packet.release(); // Couldn't schedule
|
||||
@@ -352,7 +351,12 @@ public class UserConnectionImpl implements UserConnection {
|
||||
}
|
||||
|
||||
private void writeAndFlush(final ByteBuf buf) {
|
||||
getChannel().pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(buf);
|
||||
final ChannelHandlerContext context = channel.pipeline().context(Via.getManager().getInjector().getEncoderName());
|
||||
if (context == null) {
|
||||
buf.release();
|
||||
return;
|
||||
}
|
||||
context.writeAndFlush(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -406,10 +410,13 @@ public class UserConnectionImpl implements UserConnection {
|
||||
|
||||
final int valuesReaderIndex = buf.readerIndex();
|
||||
final PacketWrapperImpl wrapper = new PacketWrapperImpl(id, buf, this);
|
||||
final int previousMaxDepth = NestingLimiter.setMaxDepth(direction == Direction.SERVERBOUND ? MAX_SERVERBOUND_NESTING_DEPTH : -1);
|
||||
try {
|
||||
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(direction), wrapper);
|
||||
} catch (final CancelException ex) {
|
||||
throw cancelSupplier.apply(ex);
|
||||
} finally {
|
||||
NestingLimiter.setMaxDepth(previousMaxDepth);
|
||||
}
|
||||
|
||||
writeToBuffer(wrapper, buf, id, valuesReaderIndex);
|
||||
|
||||
@@ -329,7 +329,9 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
List<ProtocolPathEntry> path = calculateProtocolPath(clientVersion, serverVersion);
|
||||
if (path == null) {
|
||||
// Also cache that there is no path
|
||||
pathCache.put(protocolKey, List.of());
|
||||
if (clientVersion.isKnown() && serverVersion.isKnown()) {
|
||||
pathCache.put(protocolKey, List.of());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+46
-1
@@ -21,6 +21,7 @@ import com.google.common.base.Preconditions;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.ProtocolInfo;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.Direction;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketType;
|
||||
@@ -42,6 +43,7 @@ import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class PacketWrapperImpl implements PacketWrapper {
|
||||
@@ -271,7 +273,7 @@ public class PacketWrapperImpl implements PacketWrapper {
|
||||
.set("Server Protocol", protocolInfo.serverProtocolVersion().getName())
|
||||
.set("Index", index)
|
||||
.set("Type", type.getTypeName())
|
||||
.set("Data", this.packetValues)
|
||||
.set("Data", new PacketValuesSummary(this.packetValues))
|
||||
.set("Packet ID", this.id);
|
||||
}
|
||||
|
||||
@@ -621,6 +623,49 @@ public class PacketWrapperImpl implements PacketWrapper {
|
||||
'}';
|
||||
}
|
||||
|
||||
private static final class PacketValuesSummary {
|
||||
private final List<PacketValue<?>> values;
|
||||
private String s;
|
||||
|
||||
private PacketValuesSummary(final List<PacketValue<?>> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.s == null) {
|
||||
this.s = Via.getManager().isDebug() ? this.values.toString() : summarize();
|
||||
}
|
||||
return this.s;
|
||||
}
|
||||
|
||||
private String summarize() {
|
||||
final int maxLength = Math.max(0, Via.getConfig().maxErrorLength());
|
||||
final StringBuilder builder = new StringBuilder("[");
|
||||
for (final PacketValue<?> packetValue : this.values) {
|
||||
if (builder.length() > 1) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append('{').append(packetValue.type()).append(": ").append(stringToLog(packetValue.value())).append('}');
|
||||
if (builder.length() >= maxLength) {
|
||||
builder.setLength(maxLength);
|
||||
return builder.append("...").toString();
|
||||
}
|
||||
}
|
||||
return builder.append(']').toString();
|
||||
}
|
||||
|
||||
private static String stringToLog(@Nullable final Object value) {
|
||||
if (value == null || value instanceof Number || value instanceof Boolean || value instanceof Character
|
||||
|| value instanceof CharSequence || value instanceof UUID || value instanceof Enum<?>) {
|
||||
return String.valueOf(value);
|
||||
} else if (value instanceof Item item) {
|
||||
return "Item{id=" + item.identifier() + ", amount=" + item.amount() + "}";
|
||||
}
|
||||
return "<" + value.getClass().getSimpleName() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
public static final class PacketValue<T> {
|
||||
private final Type<T> type;
|
||||
private T value;
|
||||
|
||||
@@ -154,6 +154,9 @@ public class InitialBaseProtocol extends AbstractProtocol<BaseClientboundPacket,
|
||||
info.setState(State.STATUS);
|
||||
} else if (state == LOGIN_INTENT || state == TRANSFER_INTENT) {
|
||||
info.setState(State.LOGIN);
|
||||
} else {
|
||||
// Unknown intent, don't handle further packets as handshakes
|
||||
wrapper.user().setActive(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+3
-1
@@ -48,7 +48,9 @@ public class ServerboundBaseProtocol1_7 extends AbstractProtocol<BaseClientbound
|
||||
// State tracking
|
||||
registerServerbound(ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, wrapper -> {
|
||||
final ProtocolInfo info = wrapper.user().getProtocolInfo();
|
||||
info.setState(State.CONFIGURATION);
|
||||
if (info.getUuid() != null) {
|
||||
info.setState(State.CONFIGURATION);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle blocked version disconnect
|
||||
|
||||
+5
-26
@@ -24,8 +24,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WallConnectionHandler extends AbstractFenceConnectionHandler {
|
||||
private static final BlockFace[] BLOCK_FACES = {BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST};
|
||||
private static final int[] OPPOSITES = {3, 2, 1, 0};
|
||||
|
||||
static List<ConnectionData.ConnectorInitAction> init() {
|
||||
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(2);
|
||||
@@ -49,7 +47,7 @@ public class WallConnectionHandler extends AbstractFenceConnectionHandler {
|
||||
@Override
|
||||
protected byte getStates(UserConnection user, BlockPosition position) {
|
||||
byte states = super.getStates(user, position);
|
||||
if (up(user, position)) states |= 16;
|
||||
if (up(user, position, states)) states |= 16;
|
||||
return states;
|
||||
}
|
||||
|
||||
@@ -58,28 +56,9 @@ public class WallConnectionHandler extends AbstractFenceConnectionHandler {
|
||||
return 32;
|
||||
}
|
||||
|
||||
public boolean up(UserConnection user, BlockPosition position) {
|
||||
if (isWall(getBlockData(user, position.getRelative(BlockFace.BOTTOM))) || isWall(getBlockData(user, position.getRelative(BlockFace.TOP))))
|
||||
return true;
|
||||
int blockFaces = getBlockFaces(user, position);
|
||||
if (blockFaces == 0 || blockFaces == 0xF) return true;
|
||||
for (int i = 0; i < BLOCK_FACES.length; i++) {
|
||||
if ((blockFaces & (1 << i)) != 0 && (blockFaces & (1 << OPPOSITES[i])) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getBlockFaces(UserConnection user, BlockPosition position) {
|
||||
int blockFaces = 0;
|
||||
for (int i = 0; i < BLOCK_FACES.length; i++) {
|
||||
if (isWall(getBlockData(user, position.getRelative(BLOCK_FACES[i])))) {
|
||||
blockFaces |= 1 << i;
|
||||
}
|
||||
}
|
||||
return blockFaces;
|
||||
}
|
||||
|
||||
private boolean isWall(int id) {
|
||||
return getBlockStates().contains(id);
|
||||
private boolean up(UserConnection user, BlockPosition position, byte states) {
|
||||
// Only straight walls with air above have no post, see BlockWall#getActualState
|
||||
boolean straight = states == (2 | 4) || states == (1 | 8);
|
||||
return !straight || getBlockData(user, position.getRelative(BlockFace.TOP)) != 0;
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -494,9 +494,12 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
|
||||
if (tag == null)
|
||||
item.setTag(tag = new CompoundTag());
|
||||
if (!tag.contains("EntityTag")) {
|
||||
CompoundTag entityTag = new CompoundTag();
|
||||
entityTag.put("id", new StringTag(eggEntityId.get()));
|
||||
tag.put("EntityTag", entityTag);
|
||||
tag.put("EntityTag", new CompoundTag());
|
||||
}
|
||||
// The id is removed from a remaining EntityTag in handleItemToClient
|
||||
CompoundTag entityTag = tag.getCompoundTag("EntityTag");
|
||||
if (entityTag != null && !entityTag.contains("id")) {
|
||||
entityTag.putString("id", eggEntityId.get());
|
||||
}
|
||||
} else {
|
||||
rawId = IdAndData.getId(oldId) << 16 | oldId & 0xF;
|
||||
@@ -548,6 +551,10 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
|
||||
if (patternsTag != null) {
|
||||
for (CompoundTag pattern : patternsTag) {
|
||||
NumberTag colorTag = pattern.getNumberTag("Color");
|
||||
if (colorTag == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.putInt("Color", 15 - colorTag.asInt()); // Invert color id
|
||||
}
|
||||
}
|
||||
@@ -659,6 +666,11 @@ public class ItemPacketRewriter1_13 extends ItemRewriter<ClientboundPackets1_12_
|
||||
}
|
||||
tag.put("CanDestroy", newCanDestroy);
|
||||
}
|
||||
|
||||
// Damage, map and the original id are stored in a tag created in handleItemToClient
|
||||
if (tag.isEmpty()) {
|
||||
item.setTag(null);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
+19
-1
@@ -31,6 +31,7 @@ import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPacke
|
||||
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;
|
||||
import com.viaversion.viaversion.rewriter.ItemRewriter;
|
||||
import com.viaversion.viaversion.rewriter.RecipeRewriter;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
|
||||
public final class ItemPacketRewriter1_17 extends ItemRewriter<ClientboundPackets1_16_2, ServerboundPackets1_17, Protocol1_16_4To1_17> {
|
||||
|
||||
@@ -55,7 +56,7 @@ public final class ItemPacketRewriter1_17 extends ItemRewriter<ClientboundPacket
|
||||
|
||||
handler(wrapper -> {
|
||||
// Affected items - throw them away!
|
||||
int length = wrapper.read(Types.VAR_INT);
|
||||
int length = Limit.max(wrapper.read(Types.VAR_INT), 128);
|
||||
for (int i = 0; i < length; i++) {
|
||||
wrapper.read(Types.SHORT); // Slot
|
||||
wrapper.read(Types.ITEM1_13_2);
|
||||
@@ -125,6 +126,7 @@ public final class ItemPacketRewriter1_17 extends ItemRewriter<ClientboundPacket
|
||||
}
|
||||
if (tag.getNumberTag("map") == null) {
|
||||
tag.put("map", new IntTag(0));
|
||||
tag.putBoolean(nbtTagName("map"), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,4 +134,20 @@ public final class ItemPacketRewriter1_17 extends ItemRewriter<ClientboundPacket
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item handleItemToServer(UserConnection connection, Item item) {
|
||||
if (item == null) return null;
|
||||
|
||||
super.handleItemToServer(connection, item);
|
||||
|
||||
CompoundTag tag = item.tag();
|
||||
if (item.identifier() == 733 && tag != null && tag.remove(nbtTagName("map")) != null) {
|
||||
tag.remove("map");
|
||||
if (tag.isEmpty()) {
|
||||
item.setTag(null);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -55,6 +55,7 @@ import com.viaversion.viaversion.rewriter.SoundRewriter;
|
||||
import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.util.CipherUtil;
|
||||
import com.viaversion.viaversion.util.ComponentUtil;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public final class Protocol1_18_2To1_19 extends AbstractProtocol<ClientboundPackets1_18, ClientboundPackets1_19, ServerboundPackets1_17, ServerboundPackets1_19> {
|
||||
@@ -243,7 +244,7 @@ public final class Protocol1_18_2To1_19 extends AbstractProtocol<ClientboundPack
|
||||
final String command = wrapper.get(Types.STRING, 0);
|
||||
wrapper.set(Types.STRING, 0, "/" + command);
|
||||
|
||||
final int signatures = wrapper.read(Types.VAR_INT);
|
||||
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
|
||||
for (int i = 0; i < signatures; i++) {
|
||||
wrapper.read(Types.STRING); // Argument name
|
||||
wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature
|
||||
|
||||
+4
@@ -174,6 +174,10 @@ public final class ItemPacketRewriter1_19 extends ItemRewriter<ClientboundPacket
|
||||
private PacketHandler sequenceHandler() {
|
||||
return wrapper -> {
|
||||
final int sequence = wrapper.read(Types.VAR_INT);
|
||||
if (sequence < 0) {
|
||||
throw new IllegalArgumentException("Expected packet sequence nr >= 0");
|
||||
}
|
||||
|
||||
final AckSequenceProvider provider = Via.getManager().getProviders().get(AckSequenceProvider.class);
|
||||
provider.handleSequence(wrapper.user(), sequence);
|
||||
};
|
||||
|
||||
+4
-1
@@ -51,6 +51,7 @@ import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.ComponentRew
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.EntityPacketRewriter1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.ItemPacketRewriter1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.NonceStorage1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.PlayerHealthStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.ReceivedMessagesStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;
|
||||
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPackets1_19_1;
|
||||
@@ -59,6 +60,7 @@ import com.viaversion.viaversion.rewriter.CommandRewriter;
|
||||
import com.viaversion.viaversion.rewriter.ParticleRewriter;
|
||||
import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.util.ComponentUtil;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import java.security.SignatureException;
|
||||
import java.util.List;
|
||||
@@ -220,7 +222,7 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
|
||||
final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class);
|
||||
final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);
|
||||
|
||||
final int signatures = wrapper.read(Types.VAR_INT);
|
||||
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
|
||||
for (int i = 0; i < signatures; i++) {
|
||||
wrapper.read(Types.STRING); // Argument name
|
||||
wrapper.read(Types.SIGNATURE_BYTES); // Signature
|
||||
@@ -380,6 +382,7 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
|
||||
@Override
|
||||
public void init(final UserConnection user) {
|
||||
user.put(new ReceivedMessagesStorage());
|
||||
user.put(new PlayerHealthStorage());
|
||||
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19_3.PLAYER));
|
||||
}
|
||||
|
||||
|
||||
+20
@@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.type.types.version.Types1_19;
|
||||
import com.viaversion.viaversion.api.type.types.version.Types1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.Protocol1_19_1To1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;
|
||||
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage.PlayerHealthStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;
|
||||
import com.viaversion.viaversion.rewriter.EntityRewriter;
|
||||
import java.util.BitSet;
|
||||
@@ -75,6 +76,13 @@ public final class EntityPacketRewriter1_19_3 extends EntityRewriter<Clientbound
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_19_1.SET_HEALTH, wrapper -> {
|
||||
final float health = wrapper.passthrough(Types.FLOAT);
|
||||
final int food = wrapper.passthrough(Types.VAR_INT);
|
||||
final float saturation = wrapper.passthrough(Types.FLOAT);
|
||||
wrapper.user().get(PlayerHealthStorage.class).update(health, food, saturation);
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_19_1.RESPAWN, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -94,6 +102,18 @@ public final class EntityPacketRewriter1_19_3 extends EntityRewriter<Clientbound
|
||||
}
|
||||
wrapper.write(Types.BYTE, keepDataMask);
|
||||
});
|
||||
handler(wrapper -> {
|
||||
final PlayerHealthStorage healthStorage = wrapper.user().get(PlayerHealthStorage.class);
|
||||
if (healthStorage.dead()) {
|
||||
// The client takes over the entity data of the dead player, its health of 0 would instantly re-open the death screen
|
||||
final PacketWrapper setHealth = wrapper.create(ClientboundPackets1_19_3.SET_HEALTH);
|
||||
setHealth.write(Types.FLOAT, 20F); // Overwritten by the server right after the respawn
|
||||
setHealth.write(Types.VAR_INT, healthStorage.food());
|
||||
setHealth.write(Types.FLOAT, healthStorage.saturation());
|
||||
setHealth.send(Protocol1_19_1To1_19_3.class);
|
||||
healthStorage.setDead(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_19_1to1_19_3.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
|
||||
public final class PlayerHealthStorage implements StorableObject {
|
||||
|
||||
private boolean dead;
|
||||
private int food;
|
||||
private float saturation;
|
||||
|
||||
public void update(final float health, final int food, final float saturation) {
|
||||
this.dead = health <= 0;
|
||||
this.food = food;
|
||||
this.saturation = saturation;
|
||||
}
|
||||
|
||||
public boolean dead() {
|
||||
return this.dead;
|
||||
}
|
||||
|
||||
public void setDead(final boolean dead) {
|
||||
this.dead = dead;
|
||||
}
|
||||
|
||||
public int food() {
|
||||
return this.food;
|
||||
}
|
||||
|
||||
public float saturation() {
|
||||
return this.saturation;
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -26,6 +26,7 @@ import com.viaversion.nbt.tag.NumberTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;
|
||||
import com.viaversion.viaversion.api.minecraft.ProfileKey;
|
||||
import com.viaversion.viaversion.api.minecraft.signature.SignableCommandArgumentsProvider;
|
||||
import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage;
|
||||
@@ -34,7 +35,9 @@ import com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19
|
||||
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.State;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
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.libs.mcstructs.text.Style;
|
||||
import com.viaversion.viaversion.libs.mcstructs.text.TextComponent;
|
||||
import com.viaversion.viaversion.libs.mcstructs.text.TextFormatting;
|
||||
@@ -50,6 +53,7 @@ import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPacke
|
||||
import com.viaversion.viaversion.protocols.v1_19to1_19_1.storage.ChatTypeStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_19to1_19_1.storage.NonceStorage1_19_1;
|
||||
import com.viaversion.viaversion.util.CipherUtil;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import com.viaversion.viaversion.util.ProtocolLogger;
|
||||
import com.viaversion.viaversion.util.SerializerVersion;
|
||||
@@ -64,6 +68,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
public final class Protocol1_19To1_19_1 extends AbstractProtocol<ClientboundPackets1_19, ClientboundPackets1_19_1, ServerboundPackets1_19, ServerboundPackets1_19_1> {
|
||||
|
||||
public static final ProtocolLogger LOGGER = new ProtocolLogger(Protocol1_19To1_19_1.class);
|
||||
private static final Type<PlayerMessageSignature[]> LAST_SEEN_MESSAGES_TYPE = new ArrayType<>(Types.PLAYER_MESSAGE_SIGNATURE, 5);
|
||||
|
||||
public Protocol1_19To1_19_1() {
|
||||
super(ClientboundPackets1_19.class, ClientboundPackets1_19_1.class, ServerboundPackets1_19.class, ServerboundPackets1_19_1.class);
|
||||
@@ -139,7 +144,7 @@ public final class Protocol1_19To1_19_1 extends AbstractProtocol<ClientboundPack
|
||||
wrapper.set(Types.BOOLEAN, 0, decoratableMessage.isDecorated()); // Signed preview
|
||||
}
|
||||
});
|
||||
read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages
|
||||
read(LAST_SEEN_MESSAGES_TYPE); // Last seen messages
|
||||
read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message
|
||||
}
|
||||
});
|
||||
@@ -153,7 +158,7 @@ public final class Protocol1_19To1_19_1 extends AbstractProtocol<ClientboundPack
|
||||
final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class);
|
||||
final SignableCommandArgumentsProvider argumentsProvider = Via.getManager().getProviders().get(SignableCommandArgumentsProvider.class);
|
||||
|
||||
final int signatures = wrapper.read(Types.VAR_INT);
|
||||
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
|
||||
for (int i = 0; i < signatures; i++) {
|
||||
wrapper.read(Types.STRING); // Argument name
|
||||
wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature
|
||||
@@ -187,7 +192,7 @@ public final class Protocol1_19To1_19_1 extends AbstractProtocol<ClientboundPack
|
||||
}
|
||||
});
|
||||
map(Types.BOOLEAN); // Signed preview
|
||||
read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages
|
||||
read(LAST_SEEN_MESSAGES_TYPE); // Last seen messages
|
||||
read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message
|
||||
}
|
||||
});
|
||||
|
||||
+4
-1
@@ -137,7 +137,10 @@ public final class BlockItemPacketRewriter1_20_3 extends ItemRewriter<Clientboun
|
||||
final CompoundTag filteredPages = tag.getCompoundTag("filtered_pages");
|
||||
if (filteredPages != null) {
|
||||
for (final String string : filteredPages.keySet()) {
|
||||
updatePageTag(filteredPages.getStringTag(string));
|
||||
final StringTag pageTag = filteredPages.getStringTag(string);
|
||||
if (pageTag != null) {
|
||||
updatePageTag(pageTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -59,6 +59,7 @@ import com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundConfi
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.rewriter.text.JsonNBTComponentRewriter;
|
||||
import com.viaversion.viaversion.util.Limit;
|
||||
import com.viaversion.viaversion.util.ProtocolLogger;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
@@ -148,7 +149,7 @@ public final class Protocol1_20_3To1_20_5 extends AbstractProtocol<ClientboundPa
|
||||
final ProtocolStorables1_20_5 storables = wrapper.user().storables(this);
|
||||
final AcknowledgedMessagesStorage storage = storables.acknowledgedMessages();
|
||||
final long salt = wrapper.read(Types.LONG);
|
||||
final int signatures = wrapper.read(Types.VAR_INT);
|
||||
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
|
||||
if (storage.isSecureChatEnforced()) {
|
||||
wrapper.write(Types.LONG, salt);
|
||||
wrapper.write(Types.VAR_INT, signatures);
|
||||
|
||||
+91
-43
@@ -379,7 +379,10 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
tag.putBoolean(nbtTagName(), true);
|
||||
structuredItem.dataContainer().set(StructuredDataKey.CUSTOM_DATA, tag);
|
||||
}
|
||||
return handleStructuredItemToClient(connection, structuredItem);
|
||||
}
|
||||
|
||||
private Item handleStructuredItemToClient(final UserConnection connection, final Item structuredItem) {
|
||||
// Add data components to fix issues in older protocols
|
||||
appendItemDataFixComponents(connection, structuredItem);
|
||||
|
||||
@@ -429,6 +432,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
}
|
||||
|
||||
public Item toStructuredItem(final UserConnection connection, final Item old) {
|
||||
return toStructuredItem(connection, old, false);
|
||||
}
|
||||
|
||||
// Nested items are converted from a copy made by the root item, no need to copy again
|
||||
private Item toStructuredItem(final UserConnection connection, final Item old, final boolean copied) {
|
||||
final CompoundTag tag = old.tag();
|
||||
final StructuredItem item = new StructuredItem(old.identifier(), (byte) old.amount(), new StructuredDataContainer());
|
||||
final StructuredDataContainer data = item.dataContainer();
|
||||
@@ -482,7 +490,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
final CompoundTag blockEntityTag = tag.getCompoundTag("BlockEntityTag");
|
||||
if (blockEntityTag != null) {
|
||||
final CompoundTag clonedTag = blockEntityTag.copy();
|
||||
final CompoundTag clonedTag = copied ? blockEntityTag : blockEntityTag.copy();
|
||||
// Same as above
|
||||
updateBlockEntityTag(connection, data, clonedTag);
|
||||
CompoundTag spawnData = clonedTag.getCompoundTag("SpawnData");
|
||||
@@ -571,9 +579,9 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
updateMobTags(data, tag);
|
||||
|
||||
updateItemList(connection, data, tag, "ChargedProjectiles", StructuredDataKey.V1_20_5.chargedProjectiles);
|
||||
updateItemList(connection, data, tag, "ChargedProjectiles", StructuredDataKey.V1_20_5.chargedProjectiles, copied);
|
||||
if (old.identifier() == 927) {
|
||||
updateItemList(connection, data, tag, "Items", StructuredDataKey.V1_20_5.bundleContents);
|
||||
updateItemList(connection, data, tag, "Items", StructuredDataKey.V1_20_5.bundleContents, copied);
|
||||
}
|
||||
|
||||
updateEnchantments(data, tag, "Enchantments", StructuredDataKey.ENCHANTMENTS1_20_5, (hideFlagsValue & StructuredDataConverter.HIDE_ENCHANTMENTS) == 0);
|
||||
@@ -692,6 +700,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
if (bannerPatterns != null) {
|
||||
restoreBannerPatternsFromBackup(bannerPatterns, data);
|
||||
}
|
||||
|
||||
final CompoundTag trim = backupTag.getCompoundTag("trim");
|
||||
if (trim != null) {
|
||||
restoreArmorTrimFromBackup(trim, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreInstrumentFromBackup(final CompoundTag instrument, final StructuredDataContainer data) {
|
||||
@@ -799,6 +812,25 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
data.set(StructuredDataKey.BANNER_PATTERNS, patternLayer.toArray(new BannerPatternLayer[0]));
|
||||
}
|
||||
|
||||
private void restoreArmorTrimFromBackup(final CompoundTag trim, final StructuredDataContainer data) {
|
||||
final Tag materialTag = trim.get("material");
|
||||
final Holder<ArmorTrimMaterial1_20_5> materialHolder;
|
||||
if (materialTag instanceof IntTag materialIntTag) {
|
||||
materialHolder = Holder.of(materialIntTag.asInt());
|
||||
} else if (materialTag instanceof CompoundTag materialCompoundTag) {
|
||||
ArmorTrimMaterial1_20_5 material = readTrimMaterial(materialCompoundTag);
|
||||
if (material == null) {
|
||||
return;
|
||||
}
|
||||
materialHolder = Holder.of(material);
|
||||
} else return;
|
||||
|
||||
final Holder<ArmorTrimPattern> patternHolder = Holder.of(trim.getInt("pattern"));
|
||||
final boolean showInTooltip = trim.getBoolean("show_in_tooltip");
|
||||
|
||||
data.set(StructuredDataKey.TRIM1_20_5, new ArmorTrim1_20_5(materialHolder, patternHolder, showInTooltip));
|
||||
}
|
||||
|
||||
private AdventureModePredicate updateBlockPredicates(final UserConnection connection, final ListTag<StringTag> tag, final boolean showInTooltip) {
|
||||
final BlockPredicate[] predicates = tag.stream()
|
||||
.map(StringTag::getValue)
|
||||
@@ -969,8 +1001,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
private void updateArmorTrim(final UserConnection connection, final StructuredDataContainer data, final CompoundTag trimTag, final boolean showInTooltip) {
|
||||
final Tag materialTag = trimTag.get("material");
|
||||
final Holder<ArmorTrimMaterial1_20_5> materialHolder;
|
||||
final ProtocolStorables1_20_5 storables = connection.storables(protocol);
|
||||
final ArmorTrimStorage trimStorage = storables.armorTrims();
|
||||
final ArmorTrimStorage trimStorage = connection.get(ArmorTrimStorage.class);
|
||||
if (materialTag instanceof StringTag materialStringTag) {
|
||||
final int id = trimStorage.trimMaterials().keyToId(materialStringTag.getValue());
|
||||
if (id == -1) {
|
||||
@@ -979,39 +1010,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
materialHolder = Holder.of(id);
|
||||
} else if (materialTag instanceof CompoundTag materialCompoundTag) {
|
||||
final StringTag assetNameTag = materialCompoundTag.getStringTag("asset_name");
|
||||
final StringTag ingredientTag = materialCompoundTag.getStringTag("ingredient");
|
||||
if (assetNameTag == null || ingredientTag == null) {
|
||||
ArmorTrimMaterial1_20_5 material = readTrimMaterial(materialCompoundTag);
|
||||
if (material == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int ingredientId = StructuredDataConverter.removeItemBackupTag(materialCompoundTag, toMappedItemId(ingredientTag.getValue()));
|
||||
if (ingredientId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final NumberTag itemModelIndexTag = materialCompoundTag.getNumberTag("item_model_index");
|
||||
final CompoundTag overrideArmorMaterialsTag = materialCompoundTag.getCompoundTag("override_armor_materials");
|
||||
final Tag descriptionTag = materialCompoundTag.get("description");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
overrideArmorMaterials.put(entry.getKey(), valueTag.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
materialHolder = Holder.of(new ArmorTrimMaterial1_20_5(
|
||||
assetNameTag.getValue(),
|
||||
ingredientId,
|
||||
itemModelIndexTag != null ? itemModelIndexTag.asFloat() : 0,
|
||||
overrideArmorMaterials,
|
||||
descriptionTag
|
||||
));
|
||||
materialHolder = Holder.of(material);
|
||||
} else return;
|
||||
|
||||
final Tag patternTag = trimTag.get("pattern");
|
||||
@@ -1048,6 +1051,42 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
data.set(StructuredDataKey.TRIM1_20_5, new ArmorTrim1_20_5(materialHolder, patternHolder, showInTooltip));
|
||||
}
|
||||
|
||||
private ArmorTrimMaterial1_20_5 readTrimMaterial(final CompoundTag tag) {
|
||||
final StringTag assetNameTag = tag.getStringTag("asset_name");
|
||||
final StringTag ingredientTag = tag.getStringTag("ingredient");
|
||||
if (assetNameTag == null || ingredientTag == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int ingredientId = StructuredDataConverter.removeItemBackupTag(tag, toMappedItemId(ingredientTag.getValue()));
|
||||
if (ingredientId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final NumberTag itemModelIndexTag = tag.getNumberTag("item_model_index");
|
||||
final CompoundTag overrideArmorMaterialsTag = tag.getCompoundTag("override_armor_materials");
|
||||
final Tag descriptionTag = tag.get("description");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
overrideArmorMaterials.put(entry.getKey(), valueTag.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return new ArmorTrimMaterial1_20_5(
|
||||
assetNameTag.getValue(),
|
||||
ingredientId,
|
||||
itemModelIndexTag != null ? itemModelIndexTag.asFloat() : 0,
|
||||
overrideArmorMaterials,
|
||||
descriptionTag
|
||||
);
|
||||
}
|
||||
|
||||
private void updateMobTags(final StructuredDataContainer data, final CompoundTag tag) {
|
||||
final CompoundTag bucketEntityData = new CompoundTag();
|
||||
for (final String mobTagKey : MOB_TAGS) {
|
||||
@@ -1230,12 +1269,12 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
}
|
||||
|
||||
private void updateItemList(final UserConnection connection, final StructuredDataContainer data, final CompoundTag tag,
|
||||
final String key, final StructuredDataKey<Item[]> dataKey) {
|
||||
final String key, final StructuredDataKey<Item[]> dataKey, final boolean copied) {
|
||||
final ListTag<CompoundTag> itemsTag = tag.getListTag(key, CompoundTag.class);
|
||||
if (itemsTag != null) {
|
||||
final Item[] items = itemsTag.stream()
|
||||
.limit(256)
|
||||
.map(item -> itemFromTag(connection, item))
|
||||
.map(item -> itemFromTag(connection, copied ? item : item.copy()))
|
||||
.filter(item -> !item.isEmpty())
|
||||
.toArray(Item[]::new);
|
||||
data.set(dataKey, items);
|
||||
@@ -1255,7 +1294,11 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
final byte count = item.getByte("Count", (byte) 1);
|
||||
final CompoundTag tag = item.getCompoundTag("tag");
|
||||
return handleItemToClient(connection, new DataItem(itemId, count, tag));
|
||||
final DataItem dataItem = new DataItem(itemId, count, tag);
|
||||
if (Via.getConfig().backupNestedItems()) {
|
||||
return handleItemToClient(connection, dataItem);
|
||||
}
|
||||
return handleStructuredItemToClient(connection, toStructuredItem(connection, dataItem, true));
|
||||
}
|
||||
|
||||
private void updateEnchantments(final StructuredDataContainer data, final CompoundTag tag, final String key,
|
||||
@@ -1494,11 +1537,15 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
final ListTag<CompoundTag> itemsTag = tag.getListTag("Items", CompoundTag.class);
|
||||
if (itemsTag != null) {
|
||||
// Only convert once, items can contain further containers
|
||||
final int size = Math.min(itemsTag.size(), 256);
|
||||
final Item[] items = new Item[size];
|
||||
int highestSlot = 0;
|
||||
|
||||
for (int i = 0, size = Math.min(itemsTag.size(), 256); i < size; i++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
final CompoundTag itemTag = itemsTag.get(i);
|
||||
final Item item = itemFromTag(connection, itemTag);
|
||||
items[i] = item;
|
||||
if (item.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1509,18 +1556,19 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
|
||||
|
||||
final Item[] filteredItems = new Item[highestSlot + 1];
|
||||
Arrays.fill(filteredItems, StructuredItem.empty());
|
||||
for (final CompoundTag itemTag : itemsTag) {
|
||||
final Item item = itemFromTag(connection, itemTag);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Item item = items[i];
|
||||
if (item.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int slot = itemTag.getByte("Slot");
|
||||
final int slot = itemsTag.get(i).getByte("Slot");
|
||||
if (slot >= 0 && slot < filteredItems.length) {
|
||||
filteredItems[slot] = item;
|
||||
}
|
||||
}
|
||||
|
||||
tag.remove("Items");
|
||||
data.set(StructuredDataKey.V1_20_5.container, filteredItems);
|
||||
addBlockEntityId(tag, "shulker_box");
|
||||
}
|
||||
|
||||
+14
-2
@@ -551,10 +551,22 @@ public final class StructuredDataConverter {
|
||||
} else {
|
||||
final String oldKey = trimStorage.trimPatterns().idToKey(data.pattern().id());
|
||||
if (oldKey != null) {
|
||||
trimTag.putString("pattern", oldKey);
|
||||
if (!"flow".equals(oldKey) && !"bolt".equals(oldKey)) {
|
||||
trimTag.putString("pattern", oldKey);
|
||||
} else if (backupInconvertibleData) {
|
||||
final CompoundTag backupTrim = trimTag.copy();
|
||||
if (!data.material().isDirect()) {
|
||||
backupTrim.putInt("material", data.material().id());
|
||||
}
|
||||
backupTrim.putInt("pattern", data.pattern().id());
|
||||
backupTrim.putBoolean("show_in_tooltip", data.showInTooltip());
|
||||
getBackupTag(tag).put("trim", backupTrim);
|
||||
}
|
||||
}
|
||||
}
|
||||
tag.put("Trim", trimTag);
|
||||
if (trimTag.contains("material") && trimTag.contains("pattern")) {
|
||||
tag.put("Trim", trimTag);
|
||||
}
|
||||
if (!data.showInTooltip()) {
|
||||
putHideFlag(tag, HIDE_ARMOR_TRIM);
|
||||
}
|
||||
|
||||
+4
-2
@@ -55,6 +55,7 @@ import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.rewriter.text.JsonNBTComponentRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.UUIDUtil;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -183,7 +184,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
|
||||
}
|
||||
|
||||
uuid = UUIDUtil.parseUUID(Key.stripNamespace(id).toUpperCase(Locale.ROOT));
|
||||
return uuid != null ? uuid : UUIDUtil.randomUUID();
|
||||
return uuid != null ? uuid : UUID.nameUUIDFromBytes(id.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,9 +216,10 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
|
||||
tagRewriter.addEmptyTags(RegistryType.ENTITY, "minecraft:can_turn_in_boats", "minecraft:deflects_projectiles", "minecraft:immune_to_infested",
|
||||
"minecraft:immune_to_oozing", "minecraft:no_anger_from_wind_charge");
|
||||
tagRewriter.addTag(RegistryType.ENCHANTMENT, "minecraft:curse", 10, 41); // Binding and vanishing curse
|
||||
tagRewriter.addTag(RegistryType.ENCHANTMENT, "minecraft:prevents_decorated_pot_shattering", 21); // Silk touch
|
||||
// Add other enchantment tags empty
|
||||
tagRewriter.addEmptyTags(RegistryType.ENCHANTMENT, "double_trade_price", "in_enchanting_table", "non_treasure", "on_mob_spawn_equipment", "on_random_loot",
|
||||
"on_traded_equipment", "prevents_bee_spawns_when_mining", "prevents_decorated_pot_shattering", "prevents_ice_melting", "prevents_infested_spawns", "smelts_loot",
|
||||
"on_traded_equipment", "prevents_bee_spawns_when_mining", "prevents_ice_melting", "prevents_infested_spawns", "smelts_loot",
|
||||
"tooltip_order", "tradeable", "treasure", "exclusive_set/armor", "exclusive_set/boots", "exclusive_set/bow", "exclusive_set/crossbow", "exclusive_set/damage",
|
||||
"exclusive_set/mining", "exclusive_set/riptide");
|
||||
}
|
||||
|
||||
+6
-2
@@ -151,11 +151,15 @@ public final class Protocol1_20To1_20_2 extends AbstractProtocol<ClientboundPack
|
||||
registerServerbound(State.LOGIN, ServerboundLoginPackets.LOGIN_ACKNOWLEDGED.getId(), -1, wrapper -> {
|
||||
wrapper.cancel();
|
||||
|
||||
final ProtocolStorables1_20_2 storables = wrapper.user().storables(this);
|
||||
final ConfigurationState configurationState = storables.configurationState();
|
||||
if (configurationState.bridgePhase() != BridgePhase.PROFILE_SENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Overwrite the state set in the base protocol to what the server actually keeps sending
|
||||
wrapper.user().getProtocolInfo().setServerState(State.PLAY);
|
||||
|
||||
final ProtocolStorables1_20_2 storables = wrapper.user().storables(this);
|
||||
final ConfigurationState configurationState = storables.configurationState();
|
||||
configurationState.setBridgePhase(BridgePhase.CONFIGURATION);
|
||||
configurationState.sendQueuedPackets(wrapper.user());
|
||||
});
|
||||
|
||||
+3
-4
@@ -33,7 +33,6 @@ import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant1_21_2;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant26_1;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.DamageType;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.JukeboxPlayable;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.ProvidesBannerPatterns;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.trim.ProvidesTrimMaterial1_20_5;
|
||||
@@ -86,7 +85,7 @@ public final class BlockItemPacketRewriter26_1 extends StructuredItemRewriter<Cl
|
||||
container.replace(StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5, StructuredDataKey.PROVIDES_TRIM_MATERIAL26_1, providesTrimMaterial -> upgradeHolder(protocol, connection, providesTrimMaterial.material(), "trim_material"));
|
||||
container.replace(StructuredDataKey.CHICKEN_VARIANT1_21_5, StructuredDataKey.CHICKEN_VARIANT26_1, chickenVariant -> upgradeEitherVariant(protocol, connection, chickenVariant, "chicken_variant"));
|
||||
container.replace(StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11, StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT26_1, nautilusVariant -> upgradeEitherVariant(protocol, connection, nautilusVariant, "zombie_nautilus_variant"));
|
||||
container.replace(StructuredDataKey.DAMAGE_TYPE1_21_11, StructuredDataKey.DAMAGE_TYPE26_1, damageType -> upgradeEitherVariant(protocol, connection, damageType.id(), "damage_type"));
|
||||
container.replace(StructuredDataKey.DAMAGE_TYPE1_21_11, StructuredDataKey.DAMAGE_TYPE26_1, damageType -> upgradeEitherVariant(protocol, connection, damageType, "damage_type"));
|
||||
container.replace(StructuredDataKey.PROVIDES_BANNER_PATTERNS1_21_5, StructuredDataKey.PROVIDES_BANNER_PATTERNS26_1, key -> new ProvidesBannerPatterns(tagKeyToHolderSet(protocol, connection, "banner_pattern", key.original())));
|
||||
container.replace(StructuredDataKey.DAMAGE_RESISTANT1_21_2, StructuredDataKey.DAMAGE_RESISTANT26_1, damageResistant -> new DamageResistant26_1(tagKeyToHolderSet(protocol, connection, "damage_type", damageResistant.typesTagKey().original())));
|
||||
container.replaceKey(StructuredDataKey.BLOCKS_ATTACKS1_21_5, StructuredDataKey.BLOCKS_ATTACKS26_1);
|
||||
@@ -137,14 +136,14 @@ public final class BlockItemPacketRewriter26_1 extends StructuredItemRewriter<Cl
|
||||
container.replace(StructuredDataKey.PROVIDES_TRIM_MATERIAL26_1, StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5, providesTrimMaterial -> new ProvidesTrimMaterial1_20_5(EitherHolder.of(providesTrimMaterial)));
|
||||
container.replace(StructuredDataKey.CHICKEN_VARIANT26_1, StructuredDataKey.CHICKEN_VARIANT1_21_5, Either::left);
|
||||
container.replace(StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT26_1, StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11, Either::left);
|
||||
container.replace(StructuredDataKey.DAMAGE_TYPE26_1, StructuredDataKey.DAMAGE_TYPE1_21_11, damageType -> new DamageType(Either.left(damageType)));
|
||||
container.replace(StructuredDataKey.DAMAGE_TYPE26_1, StructuredDataKey.DAMAGE_TYPE1_21_11, Either::left);
|
||||
container.replace(StructuredDataKey.PROVIDES_BANNER_PATTERNS26_1, StructuredDataKey.PROVIDES_BANNER_PATTERNS1_21_5, patterns -> tagOrNull(patterns.patterns()));
|
||||
container.replace(StructuredDataKey.DAMAGE_RESISTANT26_1, StructuredDataKey.DAMAGE_RESISTANT1_21_2, damageResistant -> {
|
||||
final Key key = tagOrNull(damageResistant.types());
|
||||
return key != null ? new DamageResistant1_21_2(key) : null;
|
||||
});
|
||||
container.replace(StructuredDataKey.BLOCKS_ATTACKS26_1, StructuredDataKey.BLOCKS_ATTACKS1_21_5, blocksAttacks -> {
|
||||
if (blocksAttacks.bypassedBy() == null) {
|
||||
if (blocksAttacks.bypassedBy() == null || blocksAttacks.bypassedBy().hasTagKey()) {
|
||||
return blocksAttacks;
|
||||
}
|
||||
// Remove bypassed by ids
|
||||
|
||||
+5
-1
@@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryType;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;
|
||||
@@ -181,7 +182,7 @@ public final class Protocol1_21_4To1_21_5 extends AbstractProtocol<ClientboundPa
|
||||
StructuredDataKey.POT_DECORATIONS1_20_5, StructuredDataKey.BLOCK_STATE, StructuredDataKey.BEES1_20_5, StructuredDataKey.LOCK1_21_2,
|
||||
StructuredDataKey.CONTAINER_LOOT, StructuredDataKey.TOOL1_21_5, StructuredDataKey.ITEM_NAME, StructuredDataKey.OMINOUS_BOTTLE_AMPLIFIER,
|
||||
StructuredDataKey.FOOD1_21_2, StructuredDataKey.JUKEBOX_PLAYABLE1_21_5, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_5,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_2,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_5,
|
||||
StructuredDataKey.USE_COOLDOWN, StructuredDataKey.DAMAGE, StructuredDataKey.EQUIPPABLE1_21_5, StructuredDataKey.ITEM_MODEL,
|
||||
StructuredDataKey.GLIDER, StructuredDataKey.TOOLTIP_STYLE, StructuredDataKey.DEATH_PROTECTION1_21_2, StructuredDataKey.WEAPON,
|
||||
StructuredDataKey.POTION_DURATION_SCALE, StructuredDataKey.VILLAGER_VARIANT, StructuredDataKey.WOLF_VARIANT, StructuredDataKey.WOLF_COLLAR,
|
||||
@@ -191,6 +192,9 @@ public final class Protocol1_21_4To1_21_5 extends AbstractProtocol<ClientboundPa
|
||||
StructuredDataKey.PAINTING_VARIANT, StructuredDataKey.LLAMA_VARIANT, StructuredDataKey.AXOLOTL_VARIANT, StructuredDataKey.CAT_VARIANT,
|
||||
StructuredDataKey.CAT_COLLAR, StructuredDataKey.SHEEP_COLOR, StructuredDataKey.SHULKER_COLOR, StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5,
|
||||
StructuredDataKey.BREAK_SOUND, StructuredDataKey.COW_VARIANT, StructuredDataKey.CHICKEN_VARIANT1_21_5, StructuredDataKey.WOLF_SOUND_VARIANT);
|
||||
|
||||
tagRewriter.renameTag(RegistryType.BLOCK, "dead_bush_may_place_on", "dry_vegetation_may_place_on");
|
||||
|
||||
super.onMappingDataLoaded();
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -302,6 +302,7 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
|
||||
final StructuredDataContainer dataContainer = item.dataContainer();
|
||||
dataContainer.replaceKey(StructuredDataKey.TOOL1_20_5, StructuredDataKey.TOOL1_21_5);
|
||||
dataContainer.replaceKey(StructuredDataKey.EQUIPPABLE1_21_2, StructuredDataKey.EQUIPPABLE1_21_5);
|
||||
dataContainer.replaceKey(StructuredDataKey.CONSUMABLE1_21_2, StructuredDataKey.CONSUMABLE1_21_5);
|
||||
dataContainer.replace(StructuredDataKey.INSTRUMENT1_21_2, StructuredDataKey.INSTRUMENT1_21_5, EitherHolder::of);
|
||||
|
||||
// Collect hidden tooltips
|
||||
@@ -408,6 +409,7 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
|
||||
final StructuredDataContainer dataContainer = item.dataContainer();
|
||||
dataContainer.replaceKey(StructuredDataKey.TOOL1_21_5, StructuredDataKey.TOOL1_20_5);
|
||||
dataContainer.replaceKey(StructuredDataKey.EQUIPPABLE1_21_5, StructuredDataKey.EQUIPPABLE1_21_2);
|
||||
dataContainer.replaceKey(StructuredDataKey.CONSUMABLE1_21_5, StructuredDataKey.CONSUMABLE1_21_2);
|
||||
dataContainer.replace(StructuredDataKey.INSTRUMENT1_21_5, StructuredDataKey.INSTRUMENT1_21_2, instrument -> instrument.hasHolder() ? instrument.holder() : null);
|
||||
|
||||
final TooltipDisplay tooltipDisplay = dataContainer.get(StructuredDataKey.TOOLTIP_DISPLAY);
|
||||
@@ -417,7 +419,8 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
|
||||
}
|
||||
|
||||
final FullMappings mappings = Protocol1_21_4To1_21_5.MAPPINGS.getDataComponentSerializerMappings();
|
||||
if (tooltipDisplay.hiddenComponents().containsAll(HIDE_ADDITIONAL_KEYS.stream().map(key -> mappings.id(key.identifier())).toList())) {
|
||||
// Components added in 1.21.5 don't have an id here and have already been dropped from the hidden set
|
||||
if (tooltipDisplay.hiddenComponents().containsAll(HIDE_ADDITIONAL_KEYS.stream().map(key -> mappings.id(key.identifier())).filter(id -> id != -1).toList())) {
|
||||
dataContainer.set(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP);
|
||||
}
|
||||
}
|
||||
@@ -478,7 +481,7 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
|
||||
final ProtocolVersion serverVersion = connection.getProtocolInfo().serverProtocolVersion();
|
||||
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_8)) {
|
||||
if (item.identifier() == 858 || item.identifier() == 863 || item.identifier() == 873 || item.identifier() == 868 || item.identifier() == 878) { // swords
|
||||
item.dataContainer().remove(StructuredDataKey.CONSUMABLE1_21_2);
|
||||
item.dataContainer().remove(StructuredDataKey.CONSUMABLE1_21_5);
|
||||
item.dataContainer().set(StructuredDataKey.BLOCKS_ATTACKS1_21_5,
|
||||
new BlocksAttacks(
|
||||
0F,
|
||||
|
||||
+9
-2
@@ -31,10 +31,12 @@ import com.viaversion.viaversion.codec.hash.HashOps;
|
||||
import com.viaversion.viaversion.data.item.ItemHasherBase;
|
||||
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.Protocol1_21_4To1_21_5;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class ItemHashStorage1_21_5 implements ItemHasher {
|
||||
|
||||
private final Cache<Long, StructuredData<?>> hashToStructuredData = CacheBuilder.newBuilder().concurrencyLevel(1).maximumSize(512).build();
|
||||
private final long keyMultiplier = ThreadLocalRandom.current().nextLong() | 1; // Odd to keep keys unique, random to not have predictable buckets
|
||||
private boolean processingClientboundInventoryPacket;
|
||||
private final CodecContext context;
|
||||
|
||||
@@ -54,7 +56,7 @@ public class ItemHashStorage1_21_5 implements ItemHasher {
|
||||
return;
|
||||
}
|
||||
|
||||
final long key = (long) structuredData.id() << 32 | hash;
|
||||
final long key = key(structuredData.id(), hash);
|
||||
try {
|
||||
this.hashToStructuredData.get(key, structuredData::copy);
|
||||
} catch (final ExecutionException e) {
|
||||
@@ -63,11 +65,16 @@ public class ItemHashStorage1_21_5 implements ItemHasher {
|
||||
}
|
||||
|
||||
public StructuredData<?> dataFromHash(final int dataComponentId, final int hash) {
|
||||
final long key = (long) dataComponentId << 32 | hash;
|
||||
final long key = key(dataComponentId, hash);
|
||||
final StructuredData<?> data = this.hashToStructuredData.getIfPresent(key);
|
||||
return data != null ? data.copy() : null;
|
||||
}
|
||||
|
||||
private long key(final int dataComponentId, final int hash) {
|
||||
// Add a key multiplier to avoid collisions from Guava's cache unseeded hashing
|
||||
return ((long) dataComponentId << 32 | (hash & 0xFFFFFFFFL)) * this.keyMultiplier;
|
||||
}
|
||||
|
||||
private static <T> int hash(final Hasher hasher, final StructuredData<T> data) {
|
||||
hasher.write(data.key().type(), data.value());
|
||||
return hasher.hash();
|
||||
|
||||
+5
-1
@@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.minecraft.GameMode;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryType;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
|
||||
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
|
||||
@@ -131,7 +132,7 @@ public final class Protocol1_21_5To1_21_6 extends AbstractProtocol<ClientboundPa
|
||||
StructuredDataKey.POT_DECORATIONS1_20_5, StructuredDataKey.BLOCK_STATE, StructuredDataKey.BEES1_20_5, StructuredDataKey.LOCK1_21_2,
|
||||
StructuredDataKey.CONTAINER_LOOT, StructuredDataKey.TOOL1_21_5, StructuredDataKey.ITEM_NAME, StructuredDataKey.OMINOUS_BOTTLE_AMPLIFIER,
|
||||
StructuredDataKey.FOOD1_21_2, StructuredDataKey.JUKEBOX_PLAYABLE1_21_5, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_2,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_5,
|
||||
StructuredDataKey.USE_COOLDOWN, StructuredDataKey.DAMAGE, StructuredDataKey.EQUIPPABLE1_21_6, StructuredDataKey.ITEM_MODEL,
|
||||
StructuredDataKey.GLIDER, StructuredDataKey.TOOLTIP_STYLE, StructuredDataKey.DEATH_PROTECTION1_21_2, StructuredDataKey.WEAPON,
|
||||
StructuredDataKey.POTION_DURATION_SCALE, StructuredDataKey.VILLAGER_VARIANT, StructuredDataKey.WOLF_VARIANT, StructuredDataKey.WOLF_COLLAR,
|
||||
@@ -141,6 +142,9 @@ public final class Protocol1_21_5To1_21_6 extends AbstractProtocol<ClientboundPa
|
||||
StructuredDataKey.PAINTING_VARIANT, StructuredDataKey.LLAMA_VARIANT, StructuredDataKey.AXOLOTL_VARIANT, StructuredDataKey.CAT_VARIANT,
|
||||
StructuredDataKey.CAT_COLLAR, StructuredDataKey.SHEEP_COLOR, StructuredDataKey.SHULKER_COLOR, StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5,
|
||||
StructuredDataKey.BREAK_SOUND, StructuredDataKey.COW_VARIANT, StructuredDataKey.CHICKEN_VARIANT1_21_5, StructuredDataKey.WOLF_SOUND_VARIANT);
|
||||
|
||||
tagRewriter.renameTag(RegistryType.BLOCK, "plays_ambient_desert_block_sounds", "triggers_ambient_desert_dry_vegetation_block_sounds");
|
||||
|
||||
super.onMappingDataLoaded();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ public final class Protocol1_21_7To1_21_9 extends AbstractProtocol<ClientboundPa
|
||||
StructuredDataKey.POT_DECORATIONS1_20_5, StructuredDataKey.BLOCK_STATE, StructuredDataKey.BEES1_21_9, StructuredDataKey.LOCK1_21_2,
|
||||
StructuredDataKey.CONTAINER_LOOT, StructuredDataKey.TOOL1_21_5, StructuredDataKey.ITEM_NAME, StructuredDataKey.OMINOUS_BOTTLE_AMPLIFIER,
|
||||
StructuredDataKey.FOOD1_21_2, StructuredDataKey.JUKEBOX_PLAYABLE1_21_5, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_2,
|
||||
StructuredDataKey.REPAIRABLE, StructuredDataKey.ENCHANTABLE, StructuredDataKey.CONSUMABLE1_21_5,
|
||||
StructuredDataKey.USE_COOLDOWN, StructuredDataKey.DAMAGE, StructuredDataKey.EQUIPPABLE1_21_6, StructuredDataKey.ITEM_MODEL,
|
||||
StructuredDataKey.GLIDER, StructuredDataKey.TOOLTIP_STYLE, StructuredDataKey.DEATH_PROTECTION1_21_2, StructuredDataKey.WEAPON,
|
||||
StructuredDataKey.POTION_DURATION_SCALE, StructuredDataKey.VILLAGER_VARIANT, StructuredDataKey.WOLF_VARIANT, StructuredDataKey.WOLF_COLLAR,
|
||||
|
||||
+7
-1
@@ -81,7 +81,13 @@ public final class BlockItemPacketRewriter1_21_9 extends StructuredItemRewriter<
|
||||
}
|
||||
|
||||
public static void downgradeData(final Item item, final StructuredDataContainer container) {
|
||||
container.replaceKey(StructuredDataKey.BEES1_21_9, StructuredDataKey.BEES1_20_5);
|
||||
container.replace(StructuredDataKey.BEES1_21_9, StructuredDataKey.BEES1_20_5, bees -> {
|
||||
for (final Bee bee : bees) {
|
||||
// Stripped from the tag since 1.21.9 but required to load the entity before
|
||||
bee.entityData().tag().putString("id", "minecraft:bee");
|
||||
}
|
||||
return bees;
|
||||
});
|
||||
container.replace(StructuredDataKey.ENTITY_DATA1_21_9, StructuredDataKey.ENTITY_DATA1_20_5, entityData -> {
|
||||
final String id = Protocol1_21_7To1_21_9.MAPPINGS.getEntityMappings().identifier(entityData.type());
|
||||
entityData.tag().putString("id", id);
|
||||
|
||||
+2
@@ -89,9 +89,11 @@ public final class BlockItemPacketRewriter1_21_11 extends StructuredItemRewriter
|
||||
}
|
||||
|
||||
public static void upgradeData(final Item item, final StructuredDataContainer container) {
|
||||
container.replaceKey(StructuredDataKey.CONSUMABLE1_21_5, StructuredDataKey.CONSUMABLE1_21_2);
|
||||
}
|
||||
|
||||
public static void downgradeData(final Item item, final StructuredDataContainer container) {
|
||||
container.replaceKey(StructuredDataKey.CONSUMABLE1_21_2, StructuredDataKey.CONSUMABLE1_21_5);
|
||||
container.remove(StructuredDataKey.SWING_ANIMATION);
|
||||
container.remove(StructuredDataKey.KINETIC_WEAPON);
|
||||
container.remove(StructuredDataKey.PIERCING_WEAPON);
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public final class ComponentRewriter1_21_11 extends NBTComponentRewriter<Clientb
|
||||
final StringTag sprite = tag.getStringTag("sprite");
|
||||
if (sprite != null) {
|
||||
final String strippedSprite = Key.stripNamespace(sprite.getValue());
|
||||
if (strippedSprite.startsWith("item/")) {
|
||||
if (strippedSprite.startsWith("item/") || strippedSprite.startsWith("trims/items/")) {
|
||||
tag.putString("atlas", "items");
|
||||
} else if (strippedSprite.startsWith("block/")) {
|
||||
tag.putString("atlas", "blocks");
|
||||
|
||||
+1
@@ -502,6 +502,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
|
||||
|
||||
final IntArrayTag emptyEnchantments = customData.getIntArrayTag(nbtTagName("0_enchants"));
|
||||
if (emptyEnchantments != null) {
|
||||
Limit.max(emptyEnchantments.length(), 256);
|
||||
Enchantments enchantments = dataContainer.get(StructuredDataKey.ENCHANTMENTS1_20_5);
|
||||
if (enchantments == null) {
|
||||
enchantments = new Enchantments(true);
|
||||
|
||||
+17
-2
@@ -409,7 +409,10 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag();
|
||||
}
|
||||
CompoundTag entityTag = new CompoundTag();
|
||||
CompoundTag entityTag = tag.getCompoundTag("EntityTag");
|
||||
if (entityTag == null) {
|
||||
entityTag = new CompoundTag();
|
||||
}
|
||||
String entityName = EntityIds1_8.ENTITY_ID_TO_NAME.get((int) item.data());
|
||||
if (entityName != null) {
|
||||
StringTag id = new StringTag(entityName);
|
||||
@@ -493,7 +496,13 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
|
||||
data = EntityIds1_8.ENTITY_NAME_TO_ID.get(id.getValue());
|
||||
}
|
||||
}
|
||||
tag.remove("EntityTag");
|
||||
entityTag.remove("id");
|
||||
if (entityTag.isEmpty()) {
|
||||
tag.remove("EntityTag");
|
||||
}
|
||||
}
|
||||
if (tag != null && tag.isEmpty()) {
|
||||
tag = null;
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
@@ -509,6 +518,9 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
|
||||
}
|
||||
tag.remove("Potion");
|
||||
}
|
||||
if (tag != null && tag.isEmpty()) {
|
||||
tag = null;
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
}
|
||||
@@ -525,6 +537,9 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
|
||||
}
|
||||
tag.remove("Potion");
|
||||
}
|
||||
if (tag != null && tag.isEmpty()) {
|
||||
tag = null;
|
||||
}
|
||||
item.setTag(tag);
|
||||
item.setData((short) data);
|
||||
}
|
||||
|
||||
+5
@@ -216,6 +216,11 @@ public class SpawnPacketRewriter1_9 {
|
||||
int entityID = wrapper.get(Types.VAR_INT, 0);
|
||||
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.handleEntityData(entityID, entityDataList);
|
||||
|
||||
// dragon default phase on 1.9+ is 10 (hovering), so we set it to match the only phase 1.8 had, which was 0 (circling)
|
||||
if (tracker.entityType(entityID) == EntityTypes1_9.EntityType.ENDER_DRAGON) {
|
||||
entityDataList.add(new EntityData(11, EntityDataTypes1_9.VAR_INT, 0)); // 11 = dragon phase id
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+73
-1
@@ -17,10 +17,14 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v26_2to26_3;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.HolderSet;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryType;
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys26_2;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys26_3;
|
||||
@@ -36,6 +40,7 @@ import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
|
||||
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
|
||||
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
|
||||
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPacket26_1;
|
||||
@@ -54,6 +59,7 @@ import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.ComponentRewrite
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.EntityPacketRewriter26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.RecipeDisplayRewriter26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.RegistryDataRewriter26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.storage.ProtocolStorables26_3;
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
import com.viaversion.viaversion.rewriter.ParticleRewriter;
|
||||
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
|
||||
@@ -67,6 +73,36 @@ import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
|
||||
public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket26_1, ClientboundPacket26_3, ServerboundPacket26_1, ServerboundPacket26_3> {
|
||||
|
||||
public static final MappingData26_3 MAPPINGS = new MappingData26_3();
|
||||
private static final int SPLASH_POTION_SOUND = 1053;
|
||||
private static final int INSTANT_SPLASH_POTION_SOUND = 1054;
|
||||
private static final int SPLASH_POTION_PARTICLES = 2002;
|
||||
private static final int INSTANT_SPLASH_POTION_PARTICLES = 2007;
|
||||
private static final String[] POT_PATTERNS = {
|
||||
"angler",
|
||||
"archer",
|
||||
"arms_up",
|
||||
"blade",
|
||||
"brewer",
|
||||
"burn",
|
||||
"danger",
|
||||
"explorer",
|
||||
"flow",
|
||||
"friend",
|
||||
"guster",
|
||||
"heart",
|
||||
"heartbreak",
|
||||
"howl",
|
||||
"miner",
|
||||
"mourner",
|
||||
"plenty",
|
||||
"prize",
|
||||
"scrape",
|
||||
"sheaf",
|
||||
"shelter",
|
||||
"skull",
|
||||
"snort"
|
||||
|
||||
};
|
||||
private final EntityPacketRewriter26_3 entityRewriter = new EntityPacketRewriter26_3(this);
|
||||
private final BlockItemPacketRewriter26_3 itemRewriter = new BlockItemPacketRewriter26_3(this);
|
||||
private final BlockRewriter<ClientboundPacket26_1> blockRewriter = new BlockPacketRewriter26_3(this);
|
||||
@@ -98,7 +134,14 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
|
||||
appendClientbound(ClientboundConfigurationPackets1_21_9.FINISH_CONFIGURATION, wrapper -> {
|
||||
final PacketWrapper clocksPacket = wrapper.create(ClientboundConfigurationPackets26_3.REGISTRY_DATA);
|
||||
clocksPacket.write(Types.STRING, "decorated_pot_pattern");
|
||||
clocksPacket.write(Types.REGISTRY_ENTRY_ARRAY, MAPPINGS.potPatterns());
|
||||
final RegistryEntry[] entries = new RegistryEntry[POT_PATTERNS.length];
|
||||
for (int i = 0; i < POT_PATTERNS.length; i++) {
|
||||
final String key = POT_PATTERNS[i];
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString("asset_id", key + "_pottery_pattern");
|
||||
entries[i] = new RegistryEntry(key, tag);
|
||||
}
|
||||
clocksPacket.write(Types.REGISTRY_ENTRY_ARRAY, entries);
|
||||
clocksPacket.send(Protocol26_2To26_3.class);
|
||||
|
||||
final PacketWrapper blockTransformersPacket = wrapper.create(ClientboundConfigurationPackets26_3.REGISTRY_DATA);
|
||||
@@ -137,6 +180,27 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
|
||||
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
|
||||
});
|
||||
|
||||
// 26.3 plays the potion splash sound through its own level event, older clients played it together with the particles
|
||||
appendClientbound(ClientboundPackets26_1.LEVEL_EVENT, wrapper -> {
|
||||
final int id = wrapper.get(Types.INT, 0);
|
||||
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_14, 0);
|
||||
final int soundId;
|
||||
if (id == SPLASH_POTION_PARTICLES) {
|
||||
soundId = SPLASH_POTION_SOUND;
|
||||
} else if (id == INSTANT_SPLASH_POTION_PARTICLES) {
|
||||
soundId = INSTANT_SPLASH_POTION_SOUND;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
final PacketWrapper soundPacket = wrapper.create(ClientboundPackets26_3.LEVEL_EVENT);
|
||||
soundPacket.write(Types.INT, soundId);
|
||||
soundPacket.write(Types.BLOCK_POSITION1_14, position);
|
||||
soundPacket.write(Types.INT, 0); // Data
|
||||
soundPacket.write(Types.BOOLEAN, false); // Global
|
||||
soundPacket.scheduleSend(Protocol26_2To26_3.class);
|
||||
});
|
||||
|
||||
// 26.3 reads the items the brewing stand slots accept from the server, older clients have them hardcoded
|
||||
appendClientbound(ClientboundPackets26_1.UPDATE_RECIPES, wrapper -> {
|
||||
wrapper.resetReader();
|
||||
@@ -186,9 +250,17 @@ public final class Protocol26_2To26_3 extends AbstractProtocol<ClientboundPacket
|
||||
StructuredDataKey.PROVIDES_POTTERY_PATTERN, StructuredDataKey.BLOCK_TRANSFORMER, StructuredDataKey.COMPOSTABLE, StructuredDataKey.VILLAGER_FOOD,
|
||||
StructuredDataKey.COOKING_FUEL, StructuredDataKey.BREWING_FUEL, StructuredDataKey.MOB_VISIBILITY, StructuredDataKey.SIGN_TEXT_BACK, StructuredDataKey.SIGN_TEXT_FRONT,
|
||||
StructuredDataKey.WAXED, StructuredDataKey.CUSHION_COLOR, StructuredDataKey.ATTACK_ANIMATION, StructuredDataKey.INTERACT_ANIMATION);
|
||||
|
||||
tagRewriter.renameTag(RegistryType.BLOCK, "convertable_to_mud", "convertible_to_mud");
|
||||
|
||||
super.onMappingDataLoaded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolStorablesBase createStorables() {
|
||||
return new ProtocolStorables26_3();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final UserConnection connection) {
|
||||
addEntityTracker(connection);
|
||||
|
||||
+17
-41
@@ -20,15 +20,16 @@ package com.viaversion.viaversion.protocols.v26_2to26_3.data;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
|
||||
import java.util.Arrays;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public final class MappingData26_3 extends MappingDataBase {
|
||||
|
||||
private static final String[] BREWING_INPUTS = {
|
||||
"potion",
|
||||
"splash_potion",
|
||||
"lingering_potion"
|
||||
"lingering_potion",
|
||||
"glass_bottle"
|
||||
};
|
||||
private static final String[] BREWING_REAGENTS = {
|
||||
"blaze_powder",
|
||||
@@ -53,31 +54,6 @@ public final class MappingData26_3 extends MappingDataBase {
|
||||
"sugar",
|
||||
"turtle_helmet"
|
||||
};
|
||||
private static final String[] POT_PATTERNS = {
|
||||
"angler",
|
||||
"archer",
|
||||
"arms_up",
|
||||
"blade",
|
||||
"brewer",
|
||||
"burn",
|
||||
"danger",
|
||||
"explorer",
|
||||
"flow",
|
||||
"friend",
|
||||
"guster",
|
||||
"heart",
|
||||
"heartbreak",
|
||||
"howl",
|
||||
"miner",
|
||||
"mourner",
|
||||
"plenty",
|
||||
"prize",
|
||||
"scrape",
|
||||
"sheaf",
|
||||
"shelter",
|
||||
"skull",
|
||||
"snort"
|
||||
};
|
||||
|
||||
private CompoundTag blockTransformerRegistry;
|
||||
private int[] brewingInputIds;
|
||||
@@ -91,25 +67,25 @@ public final class MappingData26_3 extends MappingDataBase {
|
||||
protected void loadExtras(final CompoundTag data) {
|
||||
blockTransformerRegistry = MappingDataLoader.INSTANCE.loadNBTFromFile("block-transformer-registry-26.3.nbt");
|
||||
|
||||
brewingInputIds = Arrays.stream(BREWING_INPUTS).mapToInt(identifier -> this.getFullItemMappings().mappedId(identifier)).toArray();
|
||||
brewingReagentIds = Arrays.stream(BREWING_REAGENTS).mapToInt(identifier -> this.getFullItemMappings().mappedId(identifier)).toArray();
|
||||
brewingInputIds = itemIds(BREWING_INPUTS);
|
||||
brewingReagentIds = itemIds(BREWING_REAGENTS);
|
||||
}
|
||||
|
||||
private int[] itemIds(final String[] identifiers) {
|
||||
final IntList ids = new IntArrayList(identifiers.length);
|
||||
for (final String identifier : identifiers) {
|
||||
final int id = getFullItemMappings().mappedId(identifier);
|
||||
if (id != -1) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
return ids.toIntArray();
|
||||
}
|
||||
|
||||
public CompoundTag blockTransformerRegistry() {
|
||||
return blockTransformerRegistry;
|
||||
}
|
||||
|
||||
public RegistryEntry[] potPatterns() {
|
||||
final RegistryEntry[] entries = new RegistryEntry[POT_PATTERNS.length];
|
||||
for (int i = 0; i < POT_PATTERNS.length; i++) {
|
||||
final String key = POT_PATTERNS[i];
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString("asset_id", key + "_pottery_pattern");
|
||||
entries[i] = new RegistryEntry(key, tag);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
public int[] brewingInputIds() {
|
||||
return brewingInputIds;
|
||||
}
|
||||
|
||||
+38
-1
@@ -18,6 +18,7 @@
|
||||
package com.viaversion.viaversion.protocols.v26_2to26_3.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
@@ -36,6 +37,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrim26_3;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial1_20_5;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial26_3;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
|
||||
@@ -201,6 +203,10 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
|
||||
@Override
|
||||
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {
|
||||
upgradeData(container);
|
||||
|
||||
// Add data components to fix issues in older protocols
|
||||
appendItemDataFixComponents(connection, item, container);
|
||||
|
||||
super.handleItemDataComponentsToClient(connection, item, container);
|
||||
}
|
||||
|
||||
@@ -210,10 +216,31 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
|
||||
super.handleItemDataComponentsToServer(connection, item, container);
|
||||
}
|
||||
|
||||
private void appendItemDataFixComponents(final UserConnection connection, final Item item, final StructuredDataContainer container) {
|
||||
final ProtocolVersion serverVersion = connection.getProtocolInfo().serverProtocolVersion();
|
||||
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_12_2)) {
|
||||
// Axes only started stripping logs in 1.13
|
||||
if (item.identifier() == 1043 || item.identifier() == 1033 || item.identifier() == 1053 || item.identifier() == 1058 || item.identifier() == 1048 || item.identifier() == 1038 || item.identifier() == 1028) {
|
||||
container.setEmpty(StructuredDataKey.BLOCK_TRANSFORMER);
|
||||
}
|
||||
}
|
||||
if (serverVersion.olderThanOrEqualTo(ProtocolVersion.v1_8)) {
|
||||
// Shovels only started creating dirt paths in 1.9
|
||||
if (item.identifier() == 1036 || item.identifier() == 1046 || item.identifier() == 1041 || item.identifier() == 1026 || item.identifier() == 1056 || item.identifier() == 1031 || item.identifier() == 1051) {
|
||||
container.setEmpty(StructuredDataKey.BLOCK_TRANSFORMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void upgradeData(final StructuredDataContainer container) {
|
||||
container.remove(StructuredDataKey.MAP_COLOR);
|
||||
|
||||
container.replaceKey(StructuredDataKey.INSTRUMENT26_1, StructuredDataKey.INSTRUMENT26_3);
|
||||
if (container.hasEmpty(StructuredDataKey.SWING_ANIMATION)) {
|
||||
container.remove(StructuredDataKey.SWING_ANIMATION);
|
||||
container.setEmpty(StructuredDataKey.ATTACK_ANIMATION);
|
||||
container.setEmpty(StructuredDataKey.INTERACT_ANIMATION);
|
||||
}
|
||||
container.replace(StructuredDataKey.SWING_ANIMATION, animation -> {
|
||||
container.set(StructuredDataKey.ATTACK_ANIMATION, animation);
|
||||
container.set(StructuredDataKey.INTERACT_ANIMATION, animation);
|
||||
@@ -272,7 +299,7 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
|
||||
for (final Map.Entry<String, Tag> entry : mapDecorations.entrySet()) {
|
||||
final StringTag typeTag = ((CompoundTag) entry.getValue()).getStringTag("type");
|
||||
if (NEW_MAP_DECORATION_TYPES.contains(Key.stripMinecraftNamespace(typeTag.getValue()))) {
|
||||
typeTag.setValue("village_plains");
|
||||
typeTag.setValue("target_x");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,6 +379,11 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
|
||||
}
|
||||
|
||||
backupTrimMaterial(backupTag, "provides_trim_material", container.get(StructuredDataKey.PROVIDES_TRIM_MATERIAL26_1));
|
||||
|
||||
final Integer mapColor = container.get(StructuredDataKey.MAP_COLOR);
|
||||
if (mapColor != null) {
|
||||
backupTag.putInt("map_color", mapColor);
|
||||
}
|
||||
}
|
||||
|
||||
private void backupTrimMaterial(final CompoundTag tag, final String key, @Nullable final Holder<ArmorTrimMaterial1_20_5> materialHolder) {
|
||||
@@ -386,6 +418,11 @@ public final class BlockItemPacketRewriter26_3 extends StructuredItemRewriter<Cl
|
||||
if (providesTrimMaterialTag != null) {
|
||||
container.replace(StructuredDataKey.PROVIDES_TRIM_MATERIAL26_3, StructuredDataKey.PROVIDES_TRIM_MATERIAL26_1, material -> restoreTrimMaterial(material, providesTrimMaterialTag));
|
||||
}
|
||||
|
||||
final IntTag mapColorTag = backupTag.getIntTag("map_color");
|
||||
if (mapColorTag != null) {
|
||||
container.set(StructuredDataKey.MAP_COLOR, mapColorTag.asInt());
|
||||
}
|
||||
}
|
||||
|
||||
private Holder<ArmorTrimMaterial1_20_5> restoreTrimMaterial(final Holder<ArmorTrimMaterial26_3> holder, final CompoundTag tag) {
|
||||
|
||||
+36
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v26_2to26_3.rewriter;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes26_3;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_3;
|
||||
@@ -30,6 +31,7 @@ import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPack
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.Protocol26_2To26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ClientboundPackets26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.packet.ServerboundPackets26_3;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.storage.ProtocolStorables26_3;
|
||||
import com.viaversion.viaversion.rewriter.EntityRewriter;
|
||||
|
||||
public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPacket26_1, Protocol26_2To26_3> {
|
||||
@@ -40,7 +42,12 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
|
||||
private static final int INTERPOLATION_STEP_TICKS = 3;
|
||||
private static final int MAIN_HAND = 0;
|
||||
private static final int OFF_HAND = 1;
|
||||
private static final int START_DESTROY_BLOCK_ACTION = 0;
|
||||
private static final int CHANGE_DESTROY_DIRECTION_ACTION = 1;
|
||||
private static final int ABORT_DESTROY_BLOCK_ACTION = 2;
|
||||
private static final int STOP_DESTROY_BLOCK_ACTION = 3;
|
||||
private static final int DESTROY_PROGRESS_PARTICLES = 2019;
|
||||
private static final int DESTROY_PROGRESS_PARTICLES_AND_SOUND = 2020;
|
||||
private static final SwingAnimation DEFAULT_SWING_ANIMATION = new SwingAnimation(EnumTypes.SWING_ANIMATION1_21_11.idFromName("whack"), 6);
|
||||
|
||||
public EntityPacketRewriter26_3(final Protocol26_2To26_3 protocol) {
|
||||
@@ -139,16 +146,45 @@ public final class EntityPacketRewriter26_3 extends EntityRewriter<ClientboundPa
|
||||
|
||||
protocol.registerServerbound(ServerboundPackets26_3.PUNCH, ServerboundPackets26_1.SWING, wrapper -> {
|
||||
wrapper.write(Types.VAR_INT, 0); // Main hand
|
||||
|
||||
// Sent every tick while destroying a block
|
||||
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
|
||||
storables.setPunched();
|
||||
});
|
||||
|
||||
protocol.registerServerbound(ServerboundPackets26_3.CLIENT_TICK_END, wrapper -> {
|
||||
// Needs to be sent by the server every tick after starting now
|
||||
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
|
||||
final int ticks = storables.endDestroyTick();
|
||||
if (ticks <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PacketWrapper levelEvent = wrapper.create(ClientboundPackets26_3.LEVEL_EVENT);
|
||||
levelEvent.write(Types.INT, ticks % 4 == 0 ? DESTROY_PROGRESS_PARTICLES_AND_SOUND : DESTROY_PROGRESS_PARTICLES);
|
||||
levelEvent.write(Types.BLOCK_POSITION1_14, storables.destroyPosition());
|
||||
levelEvent.write(Types.INT, storables.destroyDirection());
|
||||
levelEvent.write(Types.BOOLEAN, false); // Global
|
||||
levelEvent.scheduleSend(Protocol26_2To26_3.class);
|
||||
});
|
||||
|
||||
protocol.registerServerbound(ServerboundPackets26_3.PLAYER_ACTION, wrapper -> {
|
||||
final int action = wrapper.read(Types.VAR_INT);
|
||||
final ProtocolStorables26_3 storables = wrapper.user().storables(protocol);
|
||||
if (action == CHANGE_DESTROY_DIRECTION_ACTION) {
|
||||
wrapper.read(Types.BLOCK_POSITION1_14); // Position
|
||||
storables.setDestroyDirection(wrapper.read(Types.UNSIGNED_BYTE));
|
||||
wrapper.cancel(); // 26.2 has no equivalent and never sends anything here
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.write(Types.VAR_INT, action > CHANGE_DESTROY_DIRECTION_ACTION ? action - 1 : action);
|
||||
if (action == START_DESTROY_BLOCK_ACTION) {
|
||||
final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);
|
||||
storables.startDestroying(position, wrapper.passthrough(Types.UNSIGNED_BYTE));
|
||||
} else if (action == ABORT_DESTROY_BLOCK_ACTION || action == STOP_DESTROY_BLOCK_ACTION) {
|
||||
storables.stopDestroying();
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets26_1.ANIMATE, wrapper -> {
|
||||
|
||||
+117
-2
@@ -18,12 +18,15 @@
|
||||
package com.viaversion.viaversion.protocols.v26_2to26_3.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.NumberTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viaversion.protocols.v26_2to26_3.Protocol26_2To26_3;
|
||||
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public final class RegistryDataRewriter26_3 extends RegistryDataRewriter {
|
||||
|
||||
@@ -36,9 +39,10 @@ public final class RegistryDataRewriter26_3 extends RegistryDataRewriter {
|
||||
final StringTag condition = term.removeUnchecked("condition");
|
||||
if (condition != null) {
|
||||
term.put("type", condition);
|
||||
updateNumberProviders(term, condition);
|
||||
}
|
||||
|
||||
if (Key.equals(condition.getValue(), "damage_source_properties")) {
|
||||
if (condition != null && Key.equals(condition.getValue(), "damage_source_properties")) {
|
||||
final CompoundTag predicate = term.getCompoundTag("predicate");
|
||||
if (predicate != null) {
|
||||
final ListTag<CompoundTag> tags = predicate.getListTag("tags", CompoundTag.class);
|
||||
@@ -51,7 +55,7 @@ public final class RegistryDataRewriter26_3 extends RegistryDataRewriter {
|
||||
super.updateEnchantmentTerm(term);
|
||||
|
||||
// has to run after super, which maps the block id under the old key
|
||||
if (Key.equals(condition.getValue(), "block_state_property")) {
|
||||
if (condition != null && Key.equals(condition.getValue(), "block_state_property")) {
|
||||
condition.setValue("minecraft:match_block");
|
||||
term.put("blocks", term.remove("block"));
|
||||
|
||||
@@ -62,6 +66,117 @@ public final class RegistryDataRewriter26_3 extends RegistryDataRewriter {
|
||||
}
|
||||
}
|
||||
|
||||
// Number providers have been split into int and float providers
|
||||
private static void updateNumberProviders(final CompoundTag term, final StringTag condition) {
|
||||
if (Key.equals(condition.getValue(), "value_check")) {
|
||||
condition.setValue("int_value_check");
|
||||
convert(term, "value", "value", RegistryDataRewriter26_3::toIntProvider);
|
||||
convert(term, "range", "test", RegistryDataRewriter26_3::updateIntRange);
|
||||
} else if (Key.equals(condition.getValue(), "time_check")) {
|
||||
convert(term, "value", "value", RegistryDataRewriter26_3::updateIntRange);
|
||||
} else if (Key.equals(condition.getValue(), "random_chance")) {
|
||||
convert(term, "chance", "chance", RegistryDataRewriter26_3::toFloatProvider);
|
||||
}
|
||||
}
|
||||
|
||||
private static Tag updateIntRange(final Tag range) {
|
||||
if (range instanceof CompoundTag rangeTag) {
|
||||
convert(rangeTag, "min", "min", RegistryDataRewriter26_3::toIntProvider);
|
||||
convert(rangeTag, "max", "max", RegistryDataRewriter26_3::toIntProvider);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
// Old providers were floats, with getInt rounding the value
|
||||
private static Tag toIntProvider(final Tag tag) {
|
||||
if (tag instanceof NumberTag numberTag) {
|
||||
return new IntTag(Math.round(numberTag.asFloat()));
|
||||
} else if (tag instanceof CompoundTag provider) {
|
||||
final String type = provider.getString("type");
|
||||
if (Key.equals(type, "binomial")) {
|
||||
convert(provider, "n", "n", RegistryDataRewriter26_3::toIntProvider);
|
||||
convert(provider, "p", "p", RegistryDataRewriter26_3::toFloatProvider);
|
||||
return provider;
|
||||
} else if (Key.equals(type, "score") && provider.getFloat("scale", 1) == 1) {
|
||||
provider.remove("scale");
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return unary("from_float", unary("round", toFloatProvider(tag)));
|
||||
}
|
||||
|
||||
private static Tag toFloatProvider(final Tag tag) {
|
||||
if (!(tag instanceof CompoundTag provider)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
// Untyped providers used to fall back to uniform
|
||||
switch (Key.stripMinecraftNamespace(provider.getString("type", "uniform"))) {
|
||||
case "uniform" -> {
|
||||
provider.putString("type", "minecraft:uniform");
|
||||
convert(provider, "min", "min", RegistryDataRewriter26_3::toFloatProvider);
|
||||
convert(provider, "max", "max", RegistryDataRewriter26_3::toFloatProvider);
|
||||
}
|
||||
case "sum" -> {
|
||||
provider.putString("type", "minecraft:add");
|
||||
convert(provider, "summands", "inputs", summands -> {
|
||||
final ListTag<CompoundTag> inputs = new ListTag<>(CompoundTag.class);
|
||||
if (summands instanceof ListTag<?> summandsList) {
|
||||
for (final Tag summand : summandsList) {
|
||||
inputs.add(toFloatProviderCompound(summand));
|
||||
}
|
||||
}
|
||||
return inputs;
|
||||
});
|
||||
}
|
||||
case "binomial" -> {
|
||||
return unary("from_int", toIntProvider(provider));
|
||||
}
|
||||
case "score" -> {
|
||||
final Tag scale = provider.remove("scale");
|
||||
final CompoundTag score = unary("from_int", provider);
|
||||
if (scale == null) {
|
||||
return score;
|
||||
}
|
||||
|
||||
final ListTag<CompoundTag> inputs = new ListTag<>(CompoundTag.class);
|
||||
inputs.add(score);
|
||||
inputs.add(toFloatProviderCompound(scale));
|
||||
final CompoundTag product = new CompoundTag();
|
||||
product.putString("type", "minecraft:mul");
|
||||
product.put("inputs", inputs);
|
||||
return product;
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static CompoundTag toFloatProviderCompound(final Tag tag) {
|
||||
final Tag provider = toFloatProvider(tag);
|
||||
if (provider instanceof CompoundTag compoundTag) {
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
final CompoundTag constant = new CompoundTag();
|
||||
constant.putString("type", "minecraft:constant");
|
||||
constant.put("value", provider);
|
||||
return constant;
|
||||
}
|
||||
|
||||
private static CompoundTag unary(final String type, final Tag input) {
|
||||
final CompoundTag provider = new CompoundTag();
|
||||
provider.putString("type", "minecraft:" + type);
|
||||
provider.put("input", input);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static void convert(final CompoundTag tag, final String key, final String newKey, final UnaryOperator<Tag> converter) {
|
||||
final Tag value = tag.remove(key);
|
||||
if (value != null) {
|
||||
tag.put(newKey, converter.apply(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateBlockStateProvider(final CompoundTag tag) {
|
||||
final StringTag typeTag = tag.getStringTag("type");
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v26_2to26_3.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class ProtocolStorables26_3 extends ProtocolStorablesBase {
|
||||
|
||||
private BlockPosition destroyPosition;
|
||||
private short destroyDirection;
|
||||
private int destroyTicks;
|
||||
private boolean punched;
|
||||
|
||||
public @Nullable BlockPosition destroyPosition() {
|
||||
return destroyPosition;
|
||||
}
|
||||
|
||||
public int destroyDirection() {
|
||||
return destroyDirection;
|
||||
}
|
||||
|
||||
public void setDestroyDirection(final short destroyDirection) {
|
||||
this.destroyDirection = destroyDirection;
|
||||
}
|
||||
|
||||
public void startDestroying(final BlockPosition position, final short direction) {
|
||||
this.destroyPosition = position;
|
||||
this.destroyDirection = direction;
|
||||
this.destroyTicks = 0;
|
||||
this.punched = false;
|
||||
}
|
||||
|
||||
public void stopDestroying() {
|
||||
this.destroyPosition = null;
|
||||
}
|
||||
|
||||
public void setPunched() {
|
||||
this.punched = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the destroy tick that just ended, or -1 if the client did not continue destroying in it.
|
||||
*
|
||||
* @return destroy tick that just ended, or -1
|
||||
*/
|
||||
public int endDestroyTick() {
|
||||
if (destroyPosition == null) {
|
||||
return -1;
|
||||
}
|
||||
if (!punched) {
|
||||
destroyPosition = null;
|
||||
return -1;
|
||||
}
|
||||
|
||||
punched = false;
|
||||
return destroyTicks++;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.rewriter.MappingDataListener;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.MathUtil;
|
||||
@@ -44,7 +45,7 @@ import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class BlockRewriter<C extends ClientboundPacketType> {
|
||||
public class BlockRewriter<C extends ClientboundPacketType> implements MappingDataListener {
|
||||
protected final Protocol<C, ?, ?, ?> protocol;
|
||||
private final Type<BlockPosition> positionType;
|
||||
private final Type<CompoundTag> compoundTagType;
|
||||
|
||||
@@ -86,8 +86,10 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
|
||||
switch (key) {
|
||||
case "enchantment" -> updateEnchantments(connection, entries);
|
||||
case "trim_material" -> updateTrimMaterials(entries);
|
||||
case "jukebox_song" -> updateJukeboxSongs(entries);
|
||||
case "trim_material" -> updateTrimMaterials(connection, entries);
|
||||
case "jukebox_song" -> updateJukeboxSongs(connection, entries);
|
||||
case "trim_pattern", "instrument" -> updateTextComponents(connection, entries, "description");
|
||||
case "painting_variant" -> updateTextComponents(connection, entries, "title", "author");
|
||||
case "worldgen/biome" -> updateBiomes(entries);
|
||||
case "worldgen/block_state_provider" -> updateBlockStateProviders(entries);
|
||||
case "dimension_type" -> updateDimensionTypes(entries);
|
||||
@@ -291,6 +293,18 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
updateTextComponent(connection, tag, "tooltip");
|
||||
}
|
||||
|
||||
protected void updateTextComponents(final UserConnection connection, final RegistryEntry[] entries, final String... keys) {
|
||||
for (final RegistryEntry entry : entries) {
|
||||
if (!(entry.tag() instanceof final CompoundTag tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final String key : keys) {
|
||||
updateTextComponent(connection, tag, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateTextComponent(final UserConnection connection, @Nullable final CompoundTag tag, final String key) {
|
||||
if (tag != null && protocol.getComponentRewriter() != null) {
|
||||
protocol.getComponentRewriter().processTag(connection, tag.get(key));
|
||||
@@ -333,6 +347,7 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
}
|
||||
|
||||
final CompoundTag tag = (CompoundTag) entry.tag();
|
||||
updateTextComponent(connection, tag, "description");
|
||||
if (!Mappings.isFullIdentity(protocol.getMappingData().getFullItemMappings())) {
|
||||
updateItemList(tag.getListTag("supported_items", StringTag.class));
|
||||
updateItemList(tag.getListTag("primary_items", StringTag.class));
|
||||
@@ -365,7 +380,8 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTrimMaterials(final RegistryEntry[] entries) {
|
||||
public void updateTrimMaterials(final UserConnection connection, final RegistryEntry[] entries) {
|
||||
updateTextComponents(connection, entries, "description");
|
||||
if (Mappings.isFullIdentity(protocol.getMappingData().getFullItemMappings())) {
|
||||
return;
|
||||
}
|
||||
@@ -384,7 +400,8 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
}
|
||||
}
|
||||
|
||||
public void updateJukeboxSongs(final RegistryEntry[] entries) {
|
||||
public void updateJukeboxSongs(final UserConnection connection, final RegistryEntry[] entries) {
|
||||
updateTextComponents(connection, entries, "description");
|
||||
// can be overridden
|
||||
}
|
||||
|
||||
@@ -454,6 +471,14 @@ public class RegistryDataRewriter implements com.viaversion.viaversion.api.rewri
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tag dripstoneParticle = TagUtil.getNamespacedTag(tag, "visual/default_dripstone_particle");
|
||||
if (dripstoneParticle instanceof CompoundTag modifierTag && modifierTag.contains("argument")) {
|
||||
dripstoneParticle = modifierTag.get("argument");
|
||||
}
|
||||
if (dripstoneParticle instanceof CompoundTag particle) {
|
||||
handleParticleData(particle);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNestedEffect(final CompoundTag effectsTag) {
|
||||
|
||||
+19
-5
@@ -212,13 +212,24 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
|
||||
return null;
|
||||
}
|
||||
|
||||
final MappingData mappingData = protocol.getMappingData();
|
||||
final FullMappings dataComponentMappings = mappingData != null ? mappingData.getDataComponentSerializerMappings() : null;
|
||||
if (dataComponentMappings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int dataComponentTypes = Math.max(dataComponentMappings.size(), dataComponentMappings.mappedSize());
|
||||
final OriginalHashedItem hashedItem = new OriginalHashedItem(idTag.asInt(), item.amount(), backupTagName);
|
||||
try {
|
||||
for (final Map.Entry<String, Tag> entry : originalHashes.entrySet()) {
|
||||
if (entry.getValue() instanceof IntTag hashTag) {
|
||||
final String key = entry.getKey();
|
||||
if (!key.equals("id")) {
|
||||
hashedItem.dataHashesById().put(Integer.parseInt(key), hashTag.asInt());
|
||||
final int id = Integer.parseInt(key);
|
||||
if (id < 0 || id >= dataComponentTypes) {
|
||||
return null;
|
||||
}
|
||||
hashedItem.dataHashesById().put(id, hashTag.asInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,6 +240,9 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
|
||||
final IntArrayTag removedTag = originalHashes.getIntArrayTag("removed");
|
||||
if (removedTag != null) {
|
||||
for (final int id : removedTag.getValue()) {
|
||||
if (id < 0 || id >= dataComponentTypes) {
|
||||
return null;
|
||||
}
|
||||
hashedItem.removedDataIds().add(id);
|
||||
}
|
||||
}
|
||||
@@ -423,11 +437,11 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
|
||||
if (customName != null) {
|
||||
container.set(StructuredDataKey.CUSTOM_NAME, customName);
|
||||
}
|
||||
}
|
||||
|
||||
final Tag itemName = removeBackupTag(customData, "item_name");
|
||||
if (itemName != null) {
|
||||
container.set(StructuredDataKey.ITEM_NAME, itemName);
|
||||
}
|
||||
final Tag itemName = removeBackupTag(customData, "item_name");
|
||||
if (itemName != null) {
|
||||
container.set(StructuredDataKey.ITEM_NAME, itemName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+40
-6
@@ -29,6 +29,11 @@ import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class BlockRewriter1_21_5<C extends ClientboundPacketType> extends BlockRewriter<C> {
|
||||
private int signId = -1;
|
||||
private int hangingSignId = -1;
|
||||
private int shelfId = -1;
|
||||
private int campfireId = -1;
|
||||
private int vaultId = -1;
|
||||
|
||||
public BlockRewriter1_21_5(final Protocol<C, ?, ?, ?> protocol, final ChunkTypeSupplier chunkTypeSupplier, @Nullable final ChunkTypeSupplier mappedChunkTypeSupplier) {
|
||||
super(protocol, Types.BLOCK_POSITION1_14, Types.TRUSTED_COMPOUND_TAG, chunkTypeSupplier, mappedChunkTypeSupplier);
|
||||
@@ -41,17 +46,46 @@ public class BlockRewriter1_21_5<C extends ClientboundPacketType> extends BlockR
|
||||
@Override
|
||||
public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {
|
||||
final CompoundTag tag = blockEntity.tag();
|
||||
if (tag == null) {
|
||||
if (tag == null || protocol.getComponentRewriter() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final FullMappings blockEntityMappings = protocol.getMappingData().getBlockEntityMappings();
|
||||
if (blockEntityMappings != null && protocol.getComponentRewriter() != null) {
|
||||
final int typeId = blockEntity.typeId();
|
||||
if (typeId == signId || typeId == hangingSignId) {
|
||||
// Update sign text components, relevant as of 1.21.5 given they are properly parsed and should not error on the client
|
||||
if (blockEntity.typeId() == blockEntityMappings.mappedId("sign") || blockEntity.typeId() == blockEntityMappings.mappedId("hanging_sign")) {
|
||||
updateSignMessages(connection, tag.getCompoundTag("front_text"));
|
||||
updateSignMessages(connection, tag.getCompoundTag("back_text"));
|
||||
updateSignMessages(connection, tag.getCompoundTag("front_text"));
|
||||
updateSignMessages(connection, tag.getCompoundTag("back_text"));
|
||||
} else if (typeId == shelfId || typeId == campfireId) {
|
||||
// Update items that are actually visible to the client
|
||||
final ListTag<CompoundTag> items = tag.getListTag("Items", CompoundTag.class);
|
||||
if (items != null) {
|
||||
for (final CompoundTag item : items) {
|
||||
updateItem(connection, item);
|
||||
}
|
||||
}
|
||||
} else if (typeId == vaultId) {
|
||||
final CompoundTag sharedData = tag.getCompoundTag("shared_data");
|
||||
if (sharedData != null) {
|
||||
updateItem(connection, sharedData.getCompoundTag("display_item"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMappingDataLoaded() {
|
||||
final FullMappings blockEntityMappings = protocol.getMappingData() != null ? protocol.getMappingData().getBlockEntityMappings() : null;
|
||||
if (blockEntityMappings != null) {
|
||||
signId = blockEntityMappings.mappedId("sign");
|
||||
hangingSignId = blockEntityMappings.mappedId("hanging_sign");
|
||||
shelfId = blockEntityMappings.mappedId("shelf");
|
||||
campfireId = blockEntityMappings.mappedId("campfire");
|
||||
vaultId = blockEntityMappings.mappedId("vault");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateItem(final UserConnection connection, @Nullable final CompoundTag item) {
|
||||
if (item != null && item.getStringTag("id") != null) {
|
||||
protocol.getComponentRewriter().handleShowItem(connection, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
@@ -418,6 +418,35 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
|
||||
// Entity / block entity types
|
||||
handleTypedEntityData(componentsTag, "entity_data", protocol.getMappingData().getEntityMappings());
|
||||
handleTypedEntityData(componentsTag, "block_entity_data", protocol.getMappingData().getBlockEntityMappings());
|
||||
|
||||
handleTooltipDisplay(componentsTag);
|
||||
}
|
||||
|
||||
private void handleTooltipDisplay(final CompoundTag componentsTag) {
|
||||
final FullMappings mappings = protocol.getMappingData().getDataComponentSerializerMappings();
|
||||
if (Mappings.isFullIdentity(mappings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final CompoundTag tooltipDisplay = TagUtil.getNamespacedCompoundTag(componentsTag, "tooltip_display");
|
||||
if (tooltipDisplay == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ListTag<StringTag> hiddenComponents = tooltipDisplay.getListTag("hidden_components", StringTag.class);
|
||||
if (hiddenComponents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remap or remove data component types that have been renamed or removed
|
||||
hiddenComponents.getValue().removeIf(componentTag -> {
|
||||
final String mappedId = mappings.mappedIdentifier(componentTag.getValue());
|
||||
if (mappedId != null) {
|
||||
componentTag.setValue(mappedId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSoundEventTag(@Nullable final Tag soundEventTag, final FullMappings soundMappings) {
|
||||
|
||||
@@ -194,6 +194,10 @@ enforce-secure-chat: false
|
||||
# Handles items with invalid count values (higher than max stack size) on 1.20.3 servers.
|
||||
handle-invalid-item-count: false
|
||||
#
|
||||
# Backs up the original data of nested items (e.g. in shulker boxes or bundles) for 1.20.5+ clients on 1.20.4 and older servers.
|
||||
# This keeps their data intact when taken out individually in creative mode, but greatly increases the size of deeply nested items.
|
||||
backup-nested-items: false
|
||||
#
|
||||
# Hides sidebar scoreboard numbers for 1.20.3+ clients on older server versions.
|
||||
hide-scoreboard-numbers: false
|
||||
#
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user