Compare commits

...
33 Commits
Author SHA1 Message Date
Nassim Jahnke cd45a6e7ce 5.7.2 Release 2026-03-01 16:28:28 +01:00
Nassim Jahnke 271362da78 Update dependencies 2026-03-01 16:28:16 +01:00
Nassim Jahnke 05562c27c1 Fix nether biome sky colors, add missing dimension ambient sounds 2026-02-22 13:47:24 +01:00
Nassim Jahnke 5b4ed02748 Handle number format in set_score packet 2026-02-21 16:03:13 +01:00
Nassim Jahnke fe4e407520 Add gradle to dependabot config 2026-02-20 22:17:25 +01:00
Nassim Jahnke f003b6b84a Only do compression handler reorder on old servers after compression is enabled 2026-02-20 21:58:25 +01:00
Nassim Jahnke 207e085120 Add breath_of_the_nautilus to mob effects enum type 2026-02-20 19:18:19 +01:00
Nassim Jahnke 0a155651a1 Handle 1.21.9->1.21.11 biome sounds and particles 2026-02-20 13:56:19 +01:00
Florian Reuth d061083a8a Handle nested lore components in item_hover handling again (#4830) 2026-02-12 21:31:33 +01:00
Nassim Jahnke ad4c8e42f2 Add system property to disable legacy compression reorder 2026-02-09 17:56:19 +01:00
Florian Reuth b8b13f5831 [ci skip] Update my donation URL (#4826) 2026-02-05 17:09:38 +01:00
FlorianMichael 4dc533506f Don't register a cooldown rewriter in 1.17->1.17.1 (#4824) 2026-02-02 16:16:20 +01:00
dependabot[bot] a166b70996 [ci skip] Bump actions/checkout from 6.0.1 to 6.0.2 (#4822)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 14:07:37 +01:00
dependabot[bot] 639e8c454d [ci skip] Bump actions/setup-java from 5.1.0 to 5.2.0 (#4823)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/f2beeb24e141e01a676f977032f5a29d81c9e27e...be666c2fcd27ec809703dec50e508c2fdc7f6654)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 14:07:23 +01:00
FlorianMichael 61fc96ee11 Allow platforms to change compression names (#4821) 2026-01-31 21:08:01 +01:00
Nassim Jahnke 56259c8318 Update supported Velocity versions 2026-01-31 12:02:24 +01:00
FlorianMichael a8637d9a06 Fix play state compression when already set during login in 1.8->1.9 (#4820) 2026-01-30 22:02:40 +01:00
FlorianMichael ce607b5cc7 Use compression handler names instead for utility method (#4818) 2026-01-30 21:28:37 +01:00
FlorianMichael aa9a615c6b [ci skip] Small code cleanups (#4819) 2026-01-30 21:25:40 +01:00
FlorianMichael 8febef07c3 Add ViaChannelInitializer#reorderPipeline (#4817) 2026-01-29 17:09:42 +01:00
Nassim Jahnke 13fcce6059 Use right method for sub 1.21.5 item_name/custom_name item_hover handling 2026-01-28 17:12:34 +01:00
FlorianMichael cf6427325c Call component rewriter for component item components (#4812) 2026-01-28 12:10:47 +01:00
ValapheeandFlorianMichael 2c30a2748c Display out of bounds enchantments in item lore in 1.17->1.17.1 (#4743)
Co-authored-by: FlorianMichael <git@florianmichael.de>
2026-01-25 20:29:38 +01:00
Nassim Jahnke 1ca5b33304 Fix trusted tag limiting 2026-01-25 13:13:55 +01:00
Nassim Jahnke 265859bdae Re-use byte array in hash builder 2026-01-25 10:59:39 +01:00
FlorianMichael 93e29ac832 Use BlockRewriter1_21_5 in 1.21.7->1.21.9 & 1.21.9->1.21.11 (#4807) 2026-01-24 19:35:27 +01:00
FlorianMichael 4735795323 [ci skip] Remove Gradle wrapper update action (#4805) 2026-01-24 19:25:39 +01:00
FlorianMichael fe5732b616 Make Config#originalRootSection consistent (#4798) 2026-01-21 17:18:27 +01:00
RK_01 b0d9b747de Add ProtocolVersionRange#getMin and ProtocolVersionRange#getMax (#4797) 2026-01-19 21:08:30 +01:00
Nassim Jahnke 3f0f973fac Use synchronized map for config to preserve correct order on late saves 2026-01-19 19:33:00 +01:00
RK_01 1fa3f15d91 Handle 1.21->1.21.2 empty food component split fully (#4796) 2026-01-18 21:39:40 +01:00
FlorianMichael 2979b9d63d Also send version type; always send player details (#4795) 2026-01-18 21:00:52 +01:00
FlorianMichael a734a4a9d1 Make UserConnectionViaVersionPlatform a proxy platform by default (#4794) 2026-01-18 21:00:25 +01:00
62 changed files with 682 additions and 315 deletions

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
github: kennytv
patreon: kennytv
custom: ['https://florianmichael.de/donate']
custom: ['https://florianreuth.de/donate']
+6
View File
@@ -6,3 +6,9 @@ updates:
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "gradle"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "monthly"
+3 -3
View File
@@ -11,13 +11,13 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # 5.0.0
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1
- name: Set up JDK 17
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # 5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
with:
distribution: 'temurin'
java-version: 17
+3 -3
View File
@@ -16,13 +16,13 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # 5.0.0
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1
- name: Set up JDK 17
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # 5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
with:
distribution: 'temurin'
java-version: 17
@@ -1,22 +0,0 @@
name: Update Gradle Wrapper
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 0"
permissions:
contents: write
pull-requests: write
jobs:
update-gradle-wrapper:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@512b1875f3b6270828abfe77b247d5895a2da1e5 # 2.1.0
@@ -134,6 +134,15 @@ public interface ProtocolInfo {
void setUuid(UUID uuid);
/**
* Returns whether compression is enabled for this connection.
*
* @return whether compression is enabled
*/
boolean compressionEnabled();
void setCompressionEnabled(boolean compressionEnabled);
/**
* Returns the user's pipeline.
*
@@ -58,8 +58,8 @@ public record PaintingVariant(int width, int height, String assetId, @Nullable T
final int width = Types.VAR_INT.readPrimitive(buffer);
final int height = Types.VAR_INT.readPrimitive(buffer);
final String assetId = Types.STRING.read(buffer);
final Tag title = Types.OPTIONAL_TAG.read(buffer);
final Tag author = Types.OPTIONAL_TAG.read(buffer);
final Tag title = Types.TRUSTED_OPTIONAL_TAG.read(buffer);
final Tag author = Types.TRUSTED_OPTIONAL_TAG.read(buffer);
return new PaintingVariant(width, height, assetId, title, author);
}
@@ -68,8 +68,8 @@ public record PaintingVariant(int width, int height, String assetId, @Nullable T
Types.VAR_INT.writePrimitive(buffer, variant.width());
Types.VAR_INT.writePrimitive(buffer, variant.height());
Types.STRING.write(buffer, variant.assetId());
Types.OPTIONAL_TAG.write(buffer, variant.title());
Types.OPTIONAL_TAG.write(buffer, variant.author());
Types.TRUSTED_OPTIONAL_TAG.write(buffer, variant.title());
Types.TRUSTED_OPTIONAL_TAG.write(buffer, variant.author());
}
@Override
@@ -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.TAG, value.title())
.writeOptional("author", Types.TAG, value.author()));
.writeOptional("title", Types.TRUSTED_TAG, value.title())
.writeOptional("author", Types.TRUSTED_TAG, value.author()));
}
@Override
@@ -160,6 +160,10 @@ public final class StructuredDataContainer implements Copyable {
}
public <T, V> void replace(final StructuredDataKey<T> key, final StructuredDataKey<V> toKey, final Function<T, @Nullable V> valueMapper) {
replace(key, toKey, valueMapper, null);
}
public <T, V> void replace(final StructuredDataKey<T> key, final StructuredDataKey<V> toKey, final Function<T, @Nullable V> valueMapper, final Runnable emptyValueHandler) {
final StructuredData<?> data = this.data.remove(key);
if (data == null) {
return;
@@ -175,6 +179,9 @@ public final class StructuredDataContainer implements Copyable {
} else {
// Also replace the key for empty data
setEmpty(toKey);
if (emptyValueHandler != null) {
emptyValueHandler.run();
}
}
}
@@ -33,8 +33,8 @@ public final class EntityDataTypes1_20_3 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType = add(7, Types.ITEM1_20_2);
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -33,8 +33,8 @@ public final class EntityDataTypes1_20_5 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -35,8 +35,8 @@ public final class EntityDataTypes1_21 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -34,8 +34,8 @@ public final class EntityDataTypes1_21_11 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -35,8 +35,8 @@ public final class EntityDataTypes1_21_2 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -34,8 +34,8 @@ public final class EntityDataTypes1_21_5 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -34,8 +34,8 @@ public final class EntityDataTypes1_21_9 extends AbstractEntityDataTypes {
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TAG);
public final EntityDataType optionalComponentType = add(6, Types.OPTIONAL_TAG);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
@@ -59,7 +59,7 @@ public record ChatType(ChatTypeDecoration chatDecoration, ChatTypeDecoration nar
public ChatTypeDecoration read(final ByteBuf buffer) {
final String translationKey = Types.STRING.read(buffer);
final int[] parameters = Types.INT_ARRAY_PRIMITIVE.read(buffer);
final Tag style = Types.TAG.read(buffer);
final Tag style = Types.TRUSTED_TAG.read(buffer);
return new ChatTypeDecoration(translationKey, parameters, style);
}
@@ -67,7 +67,7 @@ public record ChatType(ChatTypeDecoration chatDecoration, ChatTypeDecoration nar
public void write(final ByteBuf buffer, final ChatTypeDecoration value) {
Types.STRING.write(buffer, value.translationKey());
Types.INT_ARRAY_PRIMITIVE.write(buffer, value.parameters());
Types.TAG.write(buffer, value.style());
Types.TRUSTED_TAG.write(buffer, value.style());
}
};
@@ -53,6 +53,6 @@ public final class EnumTypes {
// Hardcoded registries
public static final RegistryValueType VILLAGER_TYPE = new RegistryValueType(Key.of("villager_type"), "desert", "jungle", "plains", "savanna", "snow", "swamp", "taiga");
public static final RegistryValueType POTION = new RegistryValueType(Key.of("potion"), "water", "mundane", "thick", "awkward", "night_vision", "long_night_vision", "invisibility", "long_invisibility", "leaping", "long_leaping", "strong_leaping", "fire_resistance", "long_fire_resistance", "swiftness", "long_swiftness", "strong_swiftness", "slowness", "long_slowness", "strong_slowness", "turtle_master", "long_turtle_master", "strong_turtle_master", "water_breathing", "long_water_breathing", "healing", "strong_healing", "harming", "strong_harming", "poison", "long_poison", "strong_poison", "regeneration", "long_regeneration", "strong_regeneration", "strength", "long_strength", "strong_strength", "weakness", "long_weakness", "luck", "slow_falling", "long_slow_falling", "wind_charged", "weaving", "oozing", "infested");
public static final RegistryValueType MOB_EFFECT = new RegistryValueType(Key.of("mob_effect"), "speed", "slowness", "haste", "mining_fatigue", "strength", "instant_health", "instant_damage", "jump_boost", "nausea", "regeneration", "resistance", "fire_resistance", "water_breathing", "invisibility", "blindness", "night_vision", "hunger", "weakness", "poison", "wither", "health_boost", "absorption", "saturation", "glowing", "levitation", "luck", "unluck", "slow_falling", "conduit_power", "dolphins_grace", "bad_omen", "hero_of_the_village", "darkness", "trial_omen", "raid_omen", "wind_charged", "weaving", "oozing", "infested");
public static final RegistryValueType MOB_EFFECT = new RegistryValueType(Key.of("mob_effect"), "speed", "slowness", "haste", "mining_fatigue", "strength", "instant_health", "instant_damage", "jump_boost", "nausea", "regeneration", "resistance", "fire_resistance", "water_breathing", "invisibility", "blindness", "night_vision", "hunger", "weakness", "poison", "wither", "health_boost", "absorption", "saturation", "glowing", "levitation", "luck", "unluck", "slow_falling", "conduit_power", "dolphins_grace", "bad_omen", "hero_of_the_village", "darkness", "trial_omen", "raid_omen", "wind_charged", "weaving", "oozing", "infested", "breath_of_the_nautilus");
public static final RegistryValueType CONSUME_EFFECT = new RegistryValueType(Key.of("consume_effect_type"), "apply_effects", "remove_effects", "clear_all_effects", "teleport_randomly", "play_sound");
}
@@ -79,7 +79,7 @@ public interface ViaInjector {
}
/**
* Get the name of the encoder for then netty pipeline for this platform.
* Get the name of the encoder for the netty pipeline for this platform.
*
* @return The name
*/
@@ -88,7 +88,7 @@ public interface ViaInjector {
}
/**
* Get the name of the decoder for then netty pipeline for this platform.
* Get the name of the decoder for the netty pipeline for this platform.
*
* @return The name
*/
@@ -27,9 +27,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Wrapper class file for {@link com.google.common.collect.Range} to support multiple ranges. This class is used to
* Wrapper class file for {@link Range} to support multiple ranges. This class is used to
* compare {@link ProtocolVersion} objects.
*/
public class ProtocolVersionRange {
@@ -43,7 +44,7 @@ public class ProtocolVersionRange {
}
/**
* Returns a range that contains all versions.
* Returns a range that contains all versions. Adding new ranges to this range is not supported.
*
* @return the range
*/
@@ -159,6 +160,46 @@ public class ProtocolVersionRange {
return false;
}
/**
* Gets the minimum version in this range. If there are multiple ranges, the lowest minimum is returned.
*
* @return the minimum version, or null if there is no minimum
*/
public @Nullable ProtocolVersion getMin() {
ProtocolVersion min = null;
if (this.ranges != null) {
for (Range<ProtocolVersion> range : this.ranges) {
if (range.hasLowerBound()) {
ProtocolVersion rangeMin = range.lowerEndpoint();
if (min == null || rangeMin.compareTo(min) < 0) {
min = rangeMin;
}
}
}
}
return min;
}
/**
* Gets the maximum version in this range. If there are multiple ranges, the highest maximum is returned.
*
* @return the maximum version, or null if there is no maximum
*/
public @Nullable ProtocolVersion getMax() {
ProtocolVersion max = null;
if (this.ranges != null) {
for (Range<ProtocolVersion> range : this.ranges) {
if (range.hasUpperBound()) {
ProtocolVersion rangeMax = range.upperEndpoint();
if (max == null || rangeMax.compareTo(max) > 0) {
max = rangeMax;
}
}
}
}
return max;
}
@Override
public String toString() {
if (this.ranges != null) {
@@ -197,7 +238,7 @@ public class ProtocolVersionRange {
}
/**
* Parses a range from a string.
* Parses a range from a string generated by {@link #toString()}.
*
* @param str the string
* @return the range
@@ -117,8 +117,8 @@ import com.viaversion.viaversion.api.type.types.misc.NamedCompoundTagType;
import com.viaversion.viaversion.api.type.types.misc.PlayerMessageSignatureType;
import com.viaversion.viaversion.api.type.types.misc.ProfileKeyType;
import com.viaversion.viaversion.api.type.types.misc.ProfilePropertyType;
import com.viaversion.viaversion.api.type.types.misc.SoundEventType;
import com.viaversion.viaversion.api.type.types.misc.ResolvableProfileType;
import com.viaversion.viaversion.api.type.types.misc.SoundEventType;
import com.viaversion.viaversion.api.type.types.misc.TagKeyType;
import com.viaversion.viaversion.api.type.types.misc.TagType;
import com.viaversion.viaversion.api.type.types.misc.TextComponentTagType;
@@ -204,13 +204,19 @@ public final class Types {
public static final Type<CompoundTag> OPTIONAL_NAMED_COMPOUND_TAG = new NamedCompoundTagType.OptionalNamedCompoundTagType();
public static final Type<CompoundTag[]> NAMED_COMPOUND_TAG_ARRAY = new ArrayType<>(Types.NAMED_COMPOUND_TAG);
public static final Type<CompoundTag> COMPOUND_TAG = new CompoundTagType();
public static final Type<CompoundTag> OPTIONAL_COMPOUND_TAG = new CompoundTagType.OptionalCompoundTagType();
public static final Type<CompoundTag> OPTIONAL_COMPOUND_TAG = CompoundTagType.OptionalCompoundTagType.type();
public static final Type<Tag> TAG = new TagType();
public static final Type<Tag[]> TAG_ARRAY = new ArrayType<>(TAG);
public static final Type<Tag> OPTIONAL_TAG = new TagType.OptionalTagType();
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> CUSTOM_CLICK_ACTION_TAG = new LengthPrefixedTagType(65536);
public static final Type<Tag> TRUSTED_TAG = new TagType(false);
public static final Type<Tag> TRUSTED_OPTIONAL_TAG = TagType.OptionalTagType.trustedType();
public static final Type<CompoundTag> TRUSTED_COMPOUND_TAG = new CompoundTagType(false);
public static final Type<CompoundTag> TRUSTED_OPTIONAL_COMPOUND_TAG = CompoundTagType.OptionalCompoundTagType.trustedType();
public static final Type<GlobalBlockPosition> GLOBAL_POSITION = new GlobalBlockPositionType();
public static final Type<GlobalBlockPosition> OPTIONAL_GLOBAL_POSITION = new GlobalBlockPositionType.OptionalGlobalPositionType();
public static final Type<ChunkPosition> CHUNK_POSITION = new ChunkPositionType();
@@ -41,14 +41,21 @@ import java.util.Map;
*/
public class CompoundTagType extends Type<CompoundTag> {
private final int maxBytes;
public CompoundTagType() {
this(true);
}
public CompoundTagType(final boolean limitMaxBytes) {
super(CompoundTag.class);
this.maxBytes = limitMaxBytes ? NamedCompoundTagType.MAX_NBT_BYTES : Integer.MAX_VALUE;
}
@Override
public CompoundTag read(final ByteBuf buffer) {
try {
return NamedCompoundTagType.read(buffer, false);
return NamedCompoundTagType.read(buffer, this.maxBytes, false);
} catch (final IOException e) {
throw new RuntimeException(e);
}
@@ -74,8 +81,16 @@ public class CompoundTagType extends Type<CompoundTag> {
public static final class OptionalCompoundTagType extends OptionalType<CompoundTag> {
public OptionalCompoundTagType() {
super(Types.COMPOUND_TAG);
private OptionalCompoundTagType(final Type<CompoundTag> tagType) {
super(tagType);
}
public static OptionalCompoundTagType type() {
return new OptionalCompoundTagType(Types.COMPOUND_TAG);
}
public static OptionalCompoundTagType trustedType() {
return new OptionalCompoundTagType(Types.TRUSTED_COMPOUND_TAG);
}
}
}
@@ -46,7 +46,7 @@ public class NamedCompoundTagType extends Type<CompoundTag> {
@Override
public CompoundTag read(final ByteBuf buffer) {
try {
return read(buffer, true);
return read(buffer, MAX_NBT_BYTES, true);
} catch (final IOException e) {
throw new RuntimeException(e);
}
@@ -61,7 +61,7 @@ public class NamedCompoundTagType extends Type<CompoundTag> {
}
}
public static CompoundTag read(final ByteBuf buffer, final boolean readName) throws IOException {
public static CompoundTag read(final ByteBuf buffer, final int maxBytes, final boolean readName) throws IOException {
final byte id = buffer.readByte();
if (id == 0) {
return null;
@@ -74,7 +74,7 @@ public class NamedCompoundTagType extends Type<CompoundTag> {
buffer.skipBytes(buffer.readUnsignedShort());
}
final TagLimiter tagLimiter = TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL);
final TagLimiter tagLimiter = TagLimiter.create(maxBytes, MAX_NESTING_LEVEL);
return CompoundTag.read(new ByteBufInputStream(buffer), tagLimiter, 0);
}
@@ -50,8 +50,15 @@ import java.util.Map;
public class TagType extends Type<Tag> {
private final int maxBytes;
public TagType() {
this(true);
}
public TagType(final boolean limitBytes) {
super(Tag.class);
this.maxBytes = limitBytes ? NamedCompoundTagType.MAX_NBT_BYTES : Integer.MAX_VALUE;
}
@Override
@@ -61,7 +68,7 @@ public class TagType extends Type<Tag> {
return null;
}
final TagLimiter tagLimiter = TagLimiter.create(NamedCompoundTagType.MAX_NBT_BYTES, NamedCompoundTagType.MAX_NESTING_LEVEL);
final TagLimiter tagLimiter = TagLimiter.create(this.maxBytes, NamedCompoundTagType.MAX_NESTING_LEVEL);
try {
return TagRegistry.read(id, new ByteBufInputStream(buffer), tagLimiter, 0);
} catch (final IOException e) {
@@ -127,8 +134,16 @@ public class TagType extends Type<Tag> {
public static final class OptionalTagType extends OptionalType<Tag> {
public OptionalTagType() {
super(Types.TAG);
private OptionalTagType(final Type<Tag> tagType) {
super(tagType);
}
public static OptionalTagType type() {
return new OptionalTagType(Types.TAG);
}
public static OptionalTagType trustedType() {
return new OptionalTagType(Types.TRUSTED_TAG);
}
}
}
@@ -37,7 +37,7 @@ import java.util.List;
@ChannelHandler.Sharable
public final class BukkitEncodeHandler extends ViaEncodeHandler {
private boolean handledCompression = BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT != null;
private boolean handledCompression = BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT != null || Boolean.getBoolean("com.viaversion.disableLegacyCompressionReorder");
public BukkitEncodeHandler(final UserConnection connection) {
super(connection);
@@ -56,7 +56,7 @@ public final class BukkitEncodeHandler extends ViaEncodeHandler {
final ByteBuf transformedBuf = ByteBufUtil.copy(ctx.alloc(), bytebuf);
try {
final boolean needsCompression = !handledCompression && handleCompressionOrder(ctx, transformedBuf);
final boolean needsCompression = !handledCompression && connection.getProtocolInfo().compressionEnabled() && handleCompressionOrder(ctx, transformedBuf);
connection.transformClientbound(transformedBuf, CancelEncoderException::generate);
if (needsCompression) {
recompress(ctx, transformedBuf);
@@ -1,44 +0,0 @@
/*
* 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.classgenerator.generated;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
public interface HandlerSupplier {
MessageToMessageEncoder<ByteBuf> newEncodeHandler(UserConnection connection);
MessageToMessageDecoder<ByteBuf> newDecodeHandler(UserConnection connection);
final class DefaultHandlerSupplier implements HandlerSupplier {
@Override
public MessageToMessageEncoder<ByteBuf> newEncodeHandler(final UserConnection connection) {
return new BukkitEncodeHandler(connection);
}
@Override
public MessageToMessageDecoder<ByteBuf> newDecodeHandler(final UserConnection connection) {
return new BukkitDecodeHandler(connection);
}
}
}
@@ -22,9 +22,11 @@ import java.util.Arrays;
public final class HashBuilder {
private static final byte[] EMPTY_BYTES = new byte[0];
private static final int MAX_RETAINED_CAPACITY = 4096;
private final HashFunction hashFunction;
private byte[] bytes = EMPTY_BYTES;
private int index;
private boolean direct;
public HashBuilder(final HashFunction hashFunction) {
this.hashFunction = hashFunction;
@@ -43,11 +45,17 @@ public final class HashBuilder {
return this;
}
/**
* Writes the given bytes directly, avoiding a copy if currently empty.
*
* @param bytes the bytes to write directly
*/
public void writeBytesDirect(final byte[] bytes) {
if (this.bytes.length == 0) {
// Set bytes directly if still empty
this.bytes = bytes;
this.index = bytes.length;
this.direct = true;
} else {
writeBytes(bytes);
}
@@ -75,9 +83,11 @@ public final class HashBuilder {
public HashBuilder writeString(final CharSequence sequence) {
final int length = sequence.length();
this.ensureSize(length);
this.ensureSize(length * 2);
for (int i = 0; i < length; i++) {
this.writeChar(sequence.charAt(i));
final char c = sequence.charAt(i);
this.bytes[this.index++] = (byte) c;
this.bytes[this.index++] = (byte) (c >> 8);
}
return this;
}
@@ -130,9 +140,11 @@ public final class HashBuilder {
private void ensureSize(final int bytes) {
final int length = this.bytes.length;
if (this.index + bytes > length) {
final int newLength = Math.max(length * 2, this.index + bytes);
final int required = this.index + bytes;
if (required > length) {
final int newLength = Math.max(length * 2, required);
this.bytes = Arrays.copyOf(this.bytes, newLength);
this.direct = false;
}
}
@@ -142,7 +154,11 @@ public final class HashBuilder {
public void reset() {
this.index = 0;
this.bytes = EMPTY_BYTES;
// Discard the buffer if it was direct (externally owned) or exceeds max retained capacity
if (this.direct || this.bytes.length > MAX_RETAINED_CAPACITY) {
this.bytes = EMPTY_BYTES;
this.direct = false;
}
}
public HashFunction function() {
@@ -64,24 +64,20 @@ public final class ConnectionDetails {
return;
}
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
final ProtocolVersion nativeVersion = protocolInfo.protocolVersion();
final ProtocolVersion serverVersion = protocolInfo.serverProtocolVersion();
if (serverVersion.equals(nativeVersion)) {
// No need to send details if the native version is the same as the server version
return;
}
final JsonObject payload = new JsonObject();
payload.addProperty("specVersion", VERSION);
final String platformName = Via.getPlatform().getPlatformName();
final String platformVersion = Via.getPlatform().getPlatformVersion();
final JsonObject payload = new JsonObject();
payload.addProperty("specVersion", VERSION);
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
final ProtocolVersion clientVersion = protocolInfo.protocolVersion();
payload.addProperty("platformName", platformName);
payload.addProperty("platformVersion", platformVersion);
payload.addProperty("version", nativeVersion.getOriginalVersion());
payload.addProperty("versionName", nativeVersion.getName());
payload.addProperty("version", clientVersion.getOriginalVersion());
payload.addProperty("versionName", clientVersion.getName());
payload.addProperty("versionType", clientVersion.getVersionType().name());
Via.getPlatform().sendCustomPayload(connection, channel, payload.toString().getBytes(StandardCharsets.UTF_8));
}
@@ -98,13 +94,14 @@ public final class ConnectionDetails {
return;
}
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
final ProtocolVersion serverVersion = protocolInfo.serverProtocolVersion();
final JsonObject payload = new JsonObject();
payload.addProperty("specVersion", VERSION);
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
final ProtocolVersion serverVersion = protocolInfo.serverProtocolVersion();
payload.addProperty("version", serverVersion.getOriginalVersion());
payload.addProperty("versionName", serverVersion.getName());
payload.addProperty("versionType", serverVersion.getVersionType().name());
Via.getPlatform().sendCustomPayloadToClient(connection, channel, payload.toString().getBytes(StandardCharsets.UTF_8));
}
@@ -32,6 +32,7 @@ public class ProtocolInfoImpl implements ProtocolInfo {
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
private boolean compressionEnabled;
@Override
public State getClientState() {
@@ -99,6 +100,16 @@ public class ProtocolInfoImpl implements ProtocolInfo {
this.uuid = uuid;
}
@Override
public boolean compressionEnabled() {
return compressionEnabled;
}
@Override
public void setCompressionEnabled(final boolean compressionEnabled) {
this.compressionEnabled = compressionEnabled;
}
@Override
public ProtocolPipeline getPipeline() {
return pipeline;
@@ -49,6 +49,11 @@ public abstract class UserConnectionViaVersionPlatform implements ViaPlatform<Us
@Override
public abstract Logger createLogger(String name);
@Override
public boolean isProxy() {
return true;
}
@Override
public Logger getLogger() {
return logger;
@@ -17,10 +17,13 @@
*/
package com.viaversion.viaversion.platform;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import java.lang.reflect.Method;
@@ -59,6 +62,30 @@ public abstract class ViaChannelInitializer extends ChannelInitializer<Channel>
return connection;
}
/**
* Reorders the ViaVersion handlers in the pipeline to be after the specified handlers. This is needed in platforms
* where enabling the compression breaks the order of Via handlers to be: encoder -> compressor -> via encoder.
*
* @param pipeline The channel pipeline
* @param compress The name of the compress handler where Via's encoder was initially placed after
* @param decompress The name of the decompress handler where Via's decoder was initially placed after
*/
public static void reorderPipeline(final ChannelPipeline pipeline, final String compress, final String decompress) {
final ViaInjector injector = Via.getManager().getInjector();
final int decompressIndex = pipeline.names().indexOf(decompress);
if (decompressIndex == -1) {
return;
}
if (decompressIndex > pipeline.names().indexOf(injector.getDecoderName())) {
final ChannelHandler encoderHandler = pipeline.remove(injector.getEncoderName());
final ChannelHandler decoderHandler = pipeline.remove(injector.getDecoderName());
pipeline.addAfter(compress, injector.getEncoderName(), encoderHandler);
pipeline.addAfter(decompress, injector.getDecoderName(), decoderHandler);
}
}
protected abstract void injectPipeline(ChannelPipeline pipeline, UserConnection connection);
@Override
@@ -128,6 +128,11 @@ public class ClientboundBaseProtocol1_7 extends AbstractProtocol<BaseClientbound
}
});
registerClientbound(ClientboundLoginPackets.LOGIN_COMPRESSION, wrapper -> {
final int threshold = wrapper.passthrough(Types.VAR_INT);
wrapper.user().getProtocolInfo().setCompressionEnabled(threshold >= 0);
});
// Track player name/uuid and setup connection + track state
registerClientbound(ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {
final ProtocolInfo info = wrapper.user().getProtocolInfo();
@@ -67,10 +67,7 @@ public class ItemPacketRewriter1_11 extends ItemRewriter<ClientboundPackets1_9_3
if (enchTag != null && enchTag.isEmpty()) {
tag.putBoolean(nbtTagName("clearEnch"), true);
CompoundTag dummyTag = new CompoundTag();
dummyTag.putShort("id", Short.MAX_VALUE);
enchTag.add(dummyTag);
enchTag.add(new CompoundTag());
}
return item;
}
@@ -23,17 +23,18 @@ import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.StringType;
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;
import com.viaversion.viaversion.protocols.v1_17to1_17_1.packet.ClientboundPackets1_17_1;
import com.viaversion.viaversion.protocols.v1_17to1_17_1.rewriter.ItemPacketRewriter1_17_1;
public final class Protocol1_17To1_17_1 extends AbstractProtocol<ClientboundPackets1_17, ClientboundPackets1_17_1, ServerboundPackets1_17, ServerboundPackets1_17> {
private static final StringType PAGE_STRING_TYPE = new StringType(8192);
private static final StringType TITLE_STRING_TYPE = new StringType(128);
private final ItemPacketRewriter1_17_1 itemRewriter = new ItemPacketRewriter1_17_1(this);
public Protocol1_17To1_17_1() {
super(ClientboundPackets1_17.class, ClientboundPackets1_17_1.class, ServerboundPackets1_17.class, ServerboundPackets1_17.class);
@@ -41,43 +42,14 @@ public final class Protocol1_17To1_17_1 extends AbstractProtocol<ClientboundPack
@Override
protected void registerPackets() {
super.registerPackets();
registerClientbound(ClientboundPackets1_17.REMOVE_ENTITY, ClientboundPackets1_17_1.REMOVE_ENTITIES, wrapper -> {
// Aaaaand back to an array again!
int entityId = wrapper.read(Types.VAR_INT);
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{entityId});
});
registerClientbound(ClientboundPackets1_17.CONTAINER_SET_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Container id
create(Types.VAR_INT, 0); // Add arbitrary state id
}
});
registerClientbound(ClientboundPackets1_17.CONTAINER_SET_CONTENT, new PacketHandlers() {
@Override
public void register() {
map(Types.UNSIGNED_BYTE); // Container id
create(Types.VAR_INT, 0); // Add arbitrary state id
handler(wrapper -> {
// Length encoded as var int now
wrapper.write(Types.ITEM1_13_2_ARRAY, wrapper.read(Types.ITEM1_13_2_SHORT_ARRAY));
// Carried item - should work like this
wrapper.write(Types.ITEM1_13_2, null);
});
}
});
registerServerbound(ServerboundPackets1_17.CONTAINER_CLICK, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Container id
read(Types.VAR_INT); // Remove state id
}
});
registerServerbound(ServerboundPackets1_17.EDIT_BOOK, wrapper -> {
CompoundTag tag = new CompoundTag();
Item item = new DataItem(942, (byte) 1, tag); // Magic value for writable books
@@ -122,4 +94,9 @@ public final class Protocol1_17To1_17_1 extends AbstractProtocol<ClientboundPack
wrapper.write(Types.VAR_INT, slot);
});
}
@Override
public ItemPacketRewriter1_17_1 getItemRewriter() {
return itemRewriter;
}
}
@@ -0,0 +1,203 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2025 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_17to1_17_1.rewriter;
import com.viaversion.nbt.tag.ByteTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;
import com.viaversion.viaversion.protocols.v1_17to1_17_1.Protocol1_17To1_17_1;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import com.viaversion.viaversion.rewriter.RecipeRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Limit;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class ItemPacketRewriter1_17_1 extends ItemRewriter<ClientboundPackets1_17, ServerboundPackets1_17, Protocol1_17To1_17_1> {
public ItemPacketRewriter1_17_1(Protocol1_17To1_17_1 protocol) {
super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);
}
@Override
public void registerPackets() {
registerMerchantOffers(ClientboundPackets1_17.MERCHANT_OFFERS);
registerSetEquipment(ClientboundPackets1_17.SET_EQUIPMENT);
registerAdvancements(ClientboundPackets1_17.UPDATE_ADVANCEMENTS);
new RecipeRewriter<>(protocol).register(ClientboundPackets1_17.UPDATE_RECIPES);
registerSetCreativeModeSlot(ServerboundPackets1_17.SET_CREATIVE_MODE_SLOT);
protocol.registerClientbound(ClientboundPackets1_17.CONTAINER_SET_SLOT, wrapper -> {
wrapper.passthrough(Types.BYTE); // Container id
wrapper.write(Types.VAR_INT, 0); // Add arbitrary state id
wrapper.passthrough(Types.SHORT); // Slot id
passthroughClientboundItem(wrapper);
});
protocol.registerClientbound(ClientboundPackets1_17.CONTAINER_SET_CONTENT, wrapper -> {
wrapper.passthrough(Types.UNSIGNED_BYTE); // Container id
wrapper.write(Types.VAR_INT, 0); // Add arbitrary state id
// Length encoded as var int now
Item[] items = wrapper.passthroughAndMap(Types.ITEM1_13_2_SHORT_ARRAY, Types.ITEM1_13_2_ARRAY);
for (int i = 0; i < items.length; i++) {
items[i] = handleItemToClient(wrapper.user(), items[i]);
}
// Carried item - should work like this
wrapper.write(Types.ITEM1_13_2, null);
});
protocol.registerServerbound(ServerboundPackets1_17.CONTAINER_CLICK, wrapper -> {
wrapper.passthrough(Types.BYTE); // Container id
wrapper.read(Types.VAR_INT); // Remove state id
wrapper.passthrough(Types.SHORT); // Slot
wrapper.passthrough(Types.BYTE); // Button
wrapper.passthrough(Types.VAR_INT); // Mode
// Affected items
final int length = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Types.SHORT); // Slot
wrapper.write(Types.ITEM1_13_2, handleItemToServer(wrapper.user(), wrapper.read(Types.ITEM1_13_2)));
}
// Carried item
wrapper.write(Types.ITEM1_13_2, handleItemToServer(wrapper.user(), wrapper.read(Types.ITEM1_13_2)));
});
}
@Override
public Item handleItemToClient(UserConnection connection, Item item) {
if (item == null) return null;
final CompoundTag tag = item.tag();
if (tag == null) {
return item;
}
int hideFlags = tag.getInt("HideFlags");
if ((hideFlags & 1) == 0) {
replaceInvalidEnchantments(tag, "Enchantments");
}
if ((hideFlags & 1 << 5) == 0) {
replaceInvalidEnchantments(tag, "StoredEnchantments");
}
return item;
}
@Override
public @Nullable Item handleItemToServer(final UserConnection connection, @Nullable final Item item) {
if (item == null) return null;
final CompoundTag tag = item.tag();
if (tag == null) {
return item;
}
restoreInvalidEnchantments(tag, "Enchantments");
restoreInvalidEnchantments(tag, "StoredEnchantments");
return item;
}
private void replaceInvalidEnchantments(CompoundTag tag, String tagName) {
final ListTag<CompoundTag> enchantments = tag.getListTag(tagName, CompoundTag.class);
if (enchantments == null) {
return;
}
// Check if any enchantment has an invalid level
boolean hasInvalidLevel = false;
for (final CompoundTag enchantment : enchantments.getValue()) {
final short lvl = enchantment.getShort("lvl");
if (lvl < 0 || lvl > 255) {
hasInvalidLevel = true;
break;
}
}
if (!hasInvalidLevel) {
return;
}
tag.put(nbtTagName(tagName), new ByteTag(true));
final int originalHideFlags = tag.getInt("HideFlags");
tag.put(nbtTagName("HideFlags"), new ByteTag((byte) originalHideFlags));
final int hideBit = tagName.equals("Enchantments") ? 1 : (1 << 5);
tag.put("HideFlags", new ByteTag((byte) (originalHideFlags | hideBit)));
CompoundTag display = tag.getCompoundTag("display");
if (display != null) {
tag.put(nbtTagName("display"), display.copy());
} else {
tag.put("display", display = new CompoundTag());
}
ListTag<StringTag> lore = display.getListTag("Lore", StringTag.class);
if (lore == null) {
display.put("Lore", lore = new ListTag<>(StringTag.class));
}
// Add all enchantments to lore in reverse order (so first enchantment ends up at top)
for (int i = enchantments.size() - 1; i >= 0; i--) {
final CompoundTag enchantment = enchantments.get(i);
final String id = enchantment.getString("id");
if (id == null) {
continue;
}
final short lvl = enchantment.getShort("lvl");
final Key key = Key.of(id);
lore.getValue().add(0, new StringTag("{\"italic\":false,\"color\":\"gray\",\"translate\":\"enchantment." + key.namespace() + "." + key.path() + "\",\"extra\":[\" \",{\"translate\":\"enchantment.level." + lvl + "\"}]}"));
}
}
private void restoreInvalidEnchantments(final CompoundTag tag, final String tagName) {
final Tag marker = tag.remove(nbtTagName(tagName));
if (marker == null) {
return;
}
final Tag hideFlags = tag.remove(nbtTagName("HideFlags"));
if (hideFlags != null) {
if (((ByteTag) hideFlags).asByte() == 0) {
tag.remove("HideFlags");
} else {
tag.put("HideFlags", hideFlags);
}
}
final Tag display = tag.remove(nbtTagName("display"));
if (display != null) {
tag.put("display", display);
} else {
tag.remove("display");
}
}
}
@@ -102,7 +102,7 @@ public final class Protocol1_20_2To1_20_3 extends AbstractProtocol<ClientboundPa
wrapper.passthrough(Types.VAR_INT); // Score
// Null display and number format
wrapper.write(Types.OPTIONAL_TAG, null);
wrapper.write(Types.TRUSTED_OPTIONAL_TAG, null);
wrapper.write(Types.BOOLEAN, false);
});
registerClientbound(ClientboundPackets1_20_2.SET_OBJECTIVE, wrapper -> {
@@ -338,11 +338,11 @@ public final class Protocol1_20_2To1_20_3 extends AbstractProtocol<ClientboundPa
}
private void convertComponent(final PacketWrapper wrapper) {
wrapper.write(Types.TAG, ComponentUtil.jsonToTag(wrapper.read(Types.COMPONENT)));
wrapper.write(Types.TRUSTED_TAG, ComponentUtil.jsonToTag(wrapper.read(Types.COMPONENT)));
}
private void convertOptionalComponent(final PacketWrapper wrapper) {
wrapper.write(Types.OPTIONAL_TAG, ComponentUtil.jsonToTag(wrapper.read(Types.OPTIONAL_COMPONENT)));
wrapper.write(Types.TRUSTED_OPTIONAL_TAG, ComponentUtil.jsonToTag(wrapper.read(Types.OPTIONAL_COMPONENT)));
}
@Override
@@ -125,7 +125,7 @@ public final class Protocol1_20_3To1_20_5 extends AbstractProtocol<ClientboundPa
});
registerClientbound(ClientboundPackets1_20_3.SERVER_DATA, wrapper -> {
wrapper.passthrough(Types.TAG); // MOTD
wrapper.passthrough(Types.TRUSTED_TAG); // MOTD
wrapper.passthrough(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE); // Icon
// Moved to join game
@@ -163,14 +163,14 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
return;
}
CompoundTag tag = wrapper.read(Types.COMPOUND_TAG);
CompoundTag tag = wrapper.read(Types.TRUSTED_COMPOUND_TAG);
if (tag != null) {
updateBlockEntityTag(wrapper.user(), null, tag);
} else {
// No longer nullable
tag = new CompoundTag();
}
wrapper.write(Types.COMPOUND_TAG, tag);
wrapper.write(Types.TRUSTED_COMPOUND_TAG, tag);
});
registerCooldown(ClientboundPackets1_20_3.COOLDOWN);
@@ -196,8 +196,8 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
// Display data
if (wrapper.passthrough(Types.BOOLEAN)) {
wrapper.passthrough(Types.TAG); // Title
wrapper.passthrough(Types.TAG); // Description
wrapper.passthrough(Types.TRUSTED_TAG); // Title
wrapper.passthrough(Types.TRUSTED_TAG); // Description
Item item = handleNonEmptyItemToClient(wrapper.user(), wrapper.read(itemType()));
wrapper.write(mappedItemType(), item);
@@ -99,7 +99,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
particleRewriter.registerExplode1_20_5(ClientboundPackets1_20_5.EXPLODE); // Rewrites the included sound and particles
registerClientbound(ClientboundPackets1_20_5.DISGUISED_CHAT, wrapper -> {
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Message
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Message
// Holder time
final int chatType = wrapper.read(Types.VAR_INT);
@@ -121,7 +121,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
}
}
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Unsigned content
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Unsigned content
final int filterMaskType = wrapper.passthrough(Types.VAR_INT);
if (filterMaskType == 2) {
@@ -130,8 +130,8 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
final int chatType = wrapper.read(Types.VAR_INT);
wrapper.write(ChatType.TYPE, Holder.of(chatType));
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Target Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
});
registerClientbound(ClientboundPackets1_20_5.UPDATE_ATTRIBUTES, wrapper -> {
@@ -90,7 +90,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
protocol.registerClientbound(ClientboundPackets1_19_4.BLOCK_ENTITY_DATA, wrapper -> {
wrapper.passthrough(Types.BLOCK_POSITION1_14); // Position
wrapper.passthrough(Types.VAR_INT); // Type
wrapper.write(Types.COMPOUND_TAG, handleBlockEntity(wrapper.read(Types.NAMED_COMPOUND_TAG)));
wrapper.write(Types.TRUSTED_COMPOUND_TAG, handleBlockEntity(wrapper.read(Types.NAMED_COMPOUND_TAG)));
});
protocol.registerClientbound(ClientboundPackets1_19_4.LEVEL_CHUNK_WITH_LIGHT, wrapper -> {
@@ -144,7 +144,7 @@ public final class Protocol1_21_2To1_21_4 extends AbstractProtocol<ClientboundPa
if (actions.get(4)) {
wrapper.passthrough(Types.VAR_INT); // Latency
}
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));
if (actions.get(6)) {
wrapper.passthrough(Types.VAR_INT); // List order
}
@@ -140,7 +140,7 @@ public final class Protocol1_21_4To1_21_5 extends AbstractProtocol<ClientboundPa
}
}
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Unsigned content
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Unsigned content
final int filterMaskType = wrapper.passthrough(Types.VAR_INT);
if (filterMaskType == 2) { // Partially filtered
@@ -148,8 +148,8 @@ public final class Protocol1_21_4To1_21_5 extends AbstractProtocol<ClientboundPa
}
wrapper.passthrough(ChatType.TYPE); // Chat Type
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Target Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
});
registerServerbound(ServerboundPackets1_21_5.CHAT_COMMAND_SIGNED, wrapper -> {
wrapper.passthrough(Types.STRING); // Command
@@ -211,8 +211,8 @@ public final class BlockItemPacketRewriter1_21_5 extends StructuredItemRewriter<
// Display data
if (wrapper.passthrough(Types.BOOLEAN)) {
final Tag title = wrapper.passthrough(Types.TAG);
final Tag description = wrapper.passthrough(Types.TAG);
final Tag title = wrapper.passthrough(Types.TRUSTED_TAG);
final Tag description = wrapper.passthrough(Types.TRUSTED_TAG);
final ComponentRewriter componentRewriter = protocol.getComponentRewriter();
if (componentRewriter != null) {
componentRewriter.processTag(wrapper.user(), title);
@@ -46,7 +46,6 @@ import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundCon
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.BlockItemPacketRewriter1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.ComponentRewriter1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.EntityPacketRewriter1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.storage.SneakStorage;
import com.viaversion.viaversion.rewriter.AttributeRewriter;
@@ -68,7 +67,7 @@ public final class Protocol1_21_5To1_21_6 extends AbstractProtocol<ClientboundPa
private final BlockItemPacketRewriter1_21_6 itemRewriter = new BlockItemPacketRewriter1_21_6(this);
private final ParticleRewriter<ClientboundPacket1_21_5> particleRewriter = new ParticleRewriter<>(this);
private final TagRewriter<ClientboundPacket1_21_5> tagRewriter = new TagRewriter<>(this);
private final NBTComponentRewriter<ClientboundPacket1_21_5> componentRewriter = new ComponentRewriter1_21_6(this);
private final NBTComponentRewriter<ClientboundPacket1_21_5> componentRewriter = new NBTComponentRewriter<>(this);
private final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(this);
public Protocol1_21_5To1_21_6() {
@@ -1,41 +0,0 @@
/*
* 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_21_5to1_21_6.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.Protocol1_21_5To1_21_6;
import com.viaversion.viaversion.rewriter.text.NBTComponentRewriter;
public final class ComponentRewriter1_21_6 extends NBTComponentRewriter<ClientboundPacket1_21_5> {
public ComponentRewriter1_21_6(final Protocol1_21_5To1_21_6 protocol) {
super(protocol);
}
@Override
protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {
super.handleShowItem(connection, itemTag, componentsTag);
if (componentsTag == null) {
return;
}
// Remove or update data from componentsTag
}
}
@@ -40,6 +40,7 @@ import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.storage.DimensionScal
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;
public final class BlockItemPacketRewriter1_21_9 extends StructuredItemRewriter<ClientboundPacket1_21_6, ServerboundPacket1_21_9, Protocol1_21_7To1_21_9> {
@@ -49,7 +50,7 @@ public final class BlockItemPacketRewriter1_21_9 extends StructuredItemRewriter<
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = BlockRewriter.for1_20_2(protocol);
final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = new BlockRewriter1_21_5<>(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_6.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_6.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_6.SECTION_BLOCKS_UPDATE);
@@ -67,6 +67,7 @@ import com.viaversion.viaversion.rewriter.text.NBTComponentRewriter;
import com.viaversion.viaversion.util.Key;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -75,6 +76,7 @@ import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundPacket1_21_9, ClientboundPacket1_21_11, ServerboundPacket1_21_9, ServerboundPacket1_21_9> {
public static final MappingData1_21_11 MAPPINGS = new MappingData1_21_11();
private static final Set<String> REMOVE_SKY_COLOR_FROM_BIOMES = Set.of("warped_forest", "basalt_deltas", "nether_wastes", "soul_sand_valley", "crimson_forest"); // rendered via the already defined fog instead
private final EntityPacketRewriter1_21_11 entityRewriter = new EntityPacketRewriter1_21_11(this);
private final BlockItemPacketRewriter1_21_11 itemRewriter = new BlockItemPacketRewriter1_21_11(this);
private final ParticleRewriter<ClientboundPacket1_21_9> particleRewriter = new ParticleRewriter<>(this);
@@ -115,8 +117,9 @@ public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundP
final CompoundTag attributes = new CompoundTag();
tag.put("attributes", attributes);
// Fill in the new defaults
if (Key.equals(key, "the_nether")) {
tag.put("timelines", new ListTag<>(List.of(new StringTag("villager_schedule"))));
tag.put("timelines", new ListTag<>(List.of(new StringTag("villager_schedule")))); // inline the actual tag values
tag.putString("skybox", "none");
tag.putString("cardinal_light", "nether");
attributes.putString("visual/sky_light_color", "#7a7aff");
@@ -129,11 +132,29 @@ public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundP
attributes.putString("visual/fog_color", "#181318");
attributes.putString("visual/sky_color", "#000000");
attributes.putString("visual/sky_light_color", "#e580ff");
addAmbientCaveSound(attributes);
final CompoundTag backgroundMusic = new CompoundTag();
backgroundMusic.put("default", backgroundMusicEntry("music.game", false, 12000));
backgroundMusic.put("creative", backgroundMusicEntry("music.creative", false, 12000));
attributes.put("audio/background_music", backgroundMusic);
} else {
final ListTag<StringTag> timelines = new ListTag<>(List.of(new StringTag("day"), new StringTag("moon"), new StringTag("early_game")));
tag.put("timelines", timelines);
attributes.putString("visual/fog_color", "#c0d8ff");
attributes.putString("visual/sky_color", "#78a7ff");
addAmbientCaveSound(attributes);
final CompoundTag backgroundMusic = new CompoundTag();
backgroundMusic.put("default", backgroundMusicEntry("music.end", true, 6000));
attributes.put("audio/background_music", backgroundMusic);
}
// Migrate existing values
final Tag fixedTime = tag.remove("fixed_time");
if (fixedTime != null) {
tag.putBoolean("has_fixed_time", true);
}
if (!tag.getBoolean("natural")) {
attributes.putFloat("visual/sky_light_factor", 0F);
}
@@ -144,7 +165,7 @@ public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundP
}
moveAttribute(tag, attributes, "cloud_height", "visual/cloud_height", cloudHeight -> {
if (cloudHeight instanceof NumberTag numberTag) {
if (cloudHeight instanceof final NumberTag numberTag) {
attributes.putString("visual/cloud_color", "#ccffffff");
return new FloatTag(numberTag.asFloat());
}
@@ -160,11 +181,69 @@ public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundP
final CompoundTag effects = tag.getCompoundTag("effects");
final CompoundTag attributes = new CompoundTag();
tag.put("attributes", attributes);
moveAttribute(effects, attributes, "sky_color", "visual/sky_color", Function.identity(), new IntTag(0));
// Move color attributes
if (!REMOVE_SKY_COLOR_FROM_BIOMES.contains(Key.stripMinecraftNamespace(key))) {
moveAttribute(effects, attributes, "sky_color", "visual/sky_color", Function.identity(), new IntTag(0));
}
if (!Key.equals(key, "the_end")) {
moveAttribute(effects, attributes, "water_fog_color", "visual/water_fog_color", Function.identity(), new IntTag(-16448205));
moveAttribute(effects, attributes, "fog_color", "visual/fog_color", Function.identity(), new IntTag(0));
}
// Move music and sound attributes
final ListTag<CompoundTag> musicTag = effects.getListTag("music", CompoundTag.class);
if (musicTag != null && !musicTag.isEmpty()) {
final CompoundTag data = musicTag.get(0).getCompoundTag("data");
if (data != null) {
final CompoundTag defaultMusic = new CompoundTag();
final Tag maxDelay = data.get("max_delay");
final Tag minDelay = data.get("min_delay");
final Tag sound = data.get("sound");
if (maxDelay != null) {
defaultMusic.put("max_delay", maxDelay);
}
if (minDelay != null) {
defaultMusic.put("min_delay", minDelay);
}
if (sound != null) {
defaultMusic.put("sound", sound);
}
final CompoundTag backgroundMusic = new CompoundTag();
backgroundMusic.put("default", defaultMusic);
attributes.put("audio/background_music", backgroundMusic);
}
}
final CompoundTag ambientSounds = new CompoundTag();
final Tag moodSound = effects.get("mood_sound");
if (moodSound != null) {
ambientSounds.put("mood", moodSound);
}
final Tag additionsSound = effects.get("additions_sound");
if (additionsSound != null) {
ambientSounds.put("additions", additionsSound);
}
final Tag loopSound = effects.get("ambient_sound");
if (loopSound != null) {
ambientSounds.put("loop", loopSound);
}
if (!ambientSounds.isEmpty()) {
attributes.put("audio/ambient_sounds", ambientSounds);
}
// Move particles
final CompoundTag particleTag = effects.getCompoundTag("particle");
if (particleTag != null) {
final CompoundTag entry = new CompoundTag();
entry.put("probability", particleTag.get("probability"));
entry.put("particle", particleTag.get("options"));
final ListTag<CompoundTag> ambientParticles = new ListTag<>(CompoundTag.class);
ambientParticles.add(entry);
attributes.put("visual/ambient_particles", ambientParticles);
}
});
registerClientbound(ClientboundConfigurationPackets1_21_9.REGISTRY_DATA, registryDataRewriter::handle);
@@ -199,6 +278,28 @@ public final class Protocol1_21_9To1_21_11 extends AbstractProtocol<ClientboundP
new AttributeRewriter<>(this).register1_21(ClientboundPackets1_21_9.UPDATE_ATTRIBUTES);
}
private void addAmbientCaveSound(final CompoundTag attributes) {
final CompoundTag ambientSounds = new CompoundTag();
final CompoundTag moodSound = new CompoundTag();
moodSound.putInt("tick_delay", 6000);
moodSound.putFloat("offset", 2);
moodSound.putString("sound", "ambient.cave");
moodSound.putInt("block_search_extent", 8);
ambientSounds.put("mood", moodSound);
attributes.put("audio/ambient_sounds", ambientSounds);
}
private CompoundTag backgroundMusicEntry(final String soundKey, final boolean replaceCurrentMusic, final int minDelay) {
final CompoundTag sound = new CompoundTag();
if (replaceCurrentMusic) {
sound.putBoolean("replace_current_music", true);
}
sound.putInt("max_delay", 24000);
sound.putString("sound", soundKey);
sound.putInt("min_delay", minDelay);
return sound;
}
@Override
protected void onMappingDataLoaded() {
EntityTypes1_21_11.initialize(this);
@@ -36,6 +36,7 @@ import com.viaversion.viaversion.protocols.v1_21_9to1_21_11.storage.GameTimeStor
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;
public final class BlockItemPacketRewriter1_21_11 extends StructuredItemRewriter<ClientboundPacket1_21_9, ServerboundPacket1_21_9, Protocol1_21_9To1_21_11> {
@@ -45,7 +46,7 @@ public final class BlockItemPacketRewriter1_21_11 extends StructuredItemRewriter
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPacket1_21_9> blockRewriter = BlockRewriter.for1_20_2(protocol);
final BlockRewriter<ClientboundPacket1_21_9> blockRewriter = new BlockRewriter1_21_5<>(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_9.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_9.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_9.SECTION_BLOCKS_UPDATE);
@@ -166,7 +166,7 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol<ClientboundPack
if (actions.get(4)) {
wrapper.passthrough(Types.VAR_INT); // Latency
}
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));
}
});
@@ -567,6 +567,9 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter<
dataContainer.set(StructuredDataKey.V1_21_2.useRemainder, food.usingConvertsTo());
}
return new FoodProperties1_21_2(food.nutrition(), food.saturationModifier(), food.canAlwaysEat());
}, () -> {
dataContainer.setEmpty(StructuredDataKey.CONSUMABLE1_21_2);
dataContainer.setEmpty(StructuredDataKey.V1_21_2.useRemainder);
});
dataContainer.replaceKey(StructuredDataKey.POTION_CONTENTS1_20_5, StructuredDataKey.POTION_CONTENTS1_21_2);
dataContainer.replace(StructuredDataKey.FIRE_RESISTANT, StructuredDataKey.DAMAGE_RESISTANT, fireResistant -> new DamageResistant(Key.of("minecraft:is_fire")));
@@ -34,23 +34,31 @@ import java.util.zip.Inflater;
public class CompressionProvider implements Provider {
public void handlePlayCompression(UserConnection user, int threshold) {
if (!user.isClientSide()) throw new IllegalStateException("PLAY state Compression packet is unsupported");
if (!user.isClientSide()) {
throw new IllegalStateException("PLAY state Compression packet is unsupported");
}
ChannelPipeline pipe = user.getChannel().pipeline();
if (threshold < 0) {
if (pipe.get("compress") != null) {
pipe.remove("compress");
pipe.remove("decompress");
}
} else {
if (pipe.get("compress") == null) {
pipe.addBefore(Via.getManager().getInjector().getEncoderName(), "compress", getEncoder(threshold));
pipe.addBefore(Via.getManager().getInjector().getDecoderName(), "decompress", getDecoder(threshold));
} else {
((CompressionHandler) pipe.get("compress")).setCompressionThreshold(threshold);
((CompressionHandler) pipe.get("decompress")).setCompressionThreshold(threshold);
}
removeHandlers(pipe);
return;
}
if (pipe.get(getCompressName()) instanceof CompressionHandler compressionHandler) {
compressionHandler.setCompressionThreshold(threshold);
((CompressionHandler) pipe.get(getDecompressName())).setCompressionThreshold(threshold);
return;
}
removeHandlers(pipe);
pipe.addBefore(Via.getManager().getInjector().getEncoderName(), getCompressName(), getEncoder(threshold));
pipe.addBefore(Via.getManager().getInjector().getDecoderName(), getDecompressName(), getDecoder(threshold));
}
private void removeHandlers(ChannelPipeline pipeline) {
if (pipeline.get(getCompressName()) != null) {
pipeline.remove(getCompressName());
pipeline.remove(getDecompressName());
}
}
@@ -62,12 +70,20 @@ public class CompressionProvider implements Provider {
return new Decompressor(threshold);
}
protected String getCompressName() {
return "compress";
}
protected String getDecompressName() {
return "decompress";
}
public interface CompressionHandler extends ChannelHandler {
void setCompressionThreshold(int threshold);
}
private static class Decompressor extends MessageToMessageDecoder<ByteBuf> implements CompressionHandler {
// https://github.com/Gerrygames/ClientViaVersion/blob/master/src/main/java/de/gerrygames/the5zig/clientviaversion/netty/CompressionEncoder.java
private final Inflater inflater;
private int threshold;
@@ -118,7 +134,7 @@ public class CompressionProvider implements Provider {
}
private static class Compressor extends MessageToByteEncoder<ByteBuf> implements CompressionHandler {
// https://github.com/Gerrygames/ClientViaVersion/blob/master/src/main/java/de/gerrygames/the5zig/clientviaversion/netty/CompressionEncoder.java
private final Deflater deflater;
private int threshold;
@@ -331,9 +331,12 @@ public class PlayerPacketRewriter1_9 {
/* Removed packets */
protocol.registerClientbound(ClientboundPackets1_8.SET_COMPRESSION, null, wrapper -> {
wrapper.cancel();
CompressionProvider provider = Via.getManager().getProviders().get(CompressionProvider.class);
provider.handlePlayCompression(wrapper.user(), wrapper.read(Types.VAR_INT));
int threshold = wrapper.read(Types.VAR_INT);
wrapper.user().getProtocolInfo().setCompressionEnabled(threshold >= 0);
CompressionProvider provider = Via.getManager().getProviders().get(CompressionProvider.class);
provider.handlePlayCompression(wrapper.user(), threshold);
});
@@ -472,8 +472,8 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
// Display data
if (wrapper.passthrough(Types.BOOLEAN)) {
final Tag title = wrapper.passthrough(Types.TAG);
final Tag description = wrapper.passthrough(Types.TAG);
final Tag title = wrapper.passthrough(Types.TRUSTED_TAG);
final Tag description = wrapper.passthrough(Types.TRUSTED_TAG);
final ComponentRewriter componentRewriter = protocol.getComponentRewriter();
if (componentRewriter != null) {
componentRewriter.processTag(wrapper.user(), title);
@@ -116,6 +116,14 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
wrapper.passthrough(Types.STRING); // Objective name
wrapper.passthrough(Types.VAR_INT); // Score
passthroughAndProcessOptional(wrapper);
if (wrapper.passthrough(Types.BOOLEAN)) {
final int numberFormatType = wrapper.passthrough(Types.VAR_INT);
if (numberFormatType == 1) { // styled
passthroughAndProcess(wrapper); // Only contains the style
} else if (numberFormatType == 2) { // fixed
passthroughAndProcess(wrapper);
}
}
});
}
@@ -179,7 +187,7 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
wrapper.passthrough(Types.VAR_INT); // Latency
}
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));
if (actions.get(6)) {
wrapper.passthrough(Types.VAR_INT); // List order
@@ -210,14 +218,14 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
public void passthroughAndProcess(final PacketWrapper wrapper) {
switch (type) {
case JSON -> processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT));
case NBT -> processTag(wrapper.user(), wrapper.passthrough(Types.TAG));
case NBT -> processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG));
}
}
public void passthroughAndProcessOptional(final PacketWrapper wrapper) {
switch (type) {
case JSON -> processText(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_COMPONENT));
case NBT -> processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
case NBT -> processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));
}
}
@@ -352,8 +360,12 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
return;
}
handleAttributeModifiers(componentsTag);
handleNestedComponent(connection, TagUtil.getNamespacedTag(componentsTag, "item_name"));
handleNestedComponent(connection, TagUtil.getNamespacedTag(componentsTag, "custom_name"));
handleLore(connection, componentsTag);
handleWrittenBookContents(connection, componentsTag);
handleAttributeModifiers(componentsTag);
handleContainerContents(connection, componentsTag);
handleItemArrayContents(connection, componentsTag, "bundle_contents");
handleItemArrayContents(connection, componentsTag, "charged_projectiles");
@@ -401,6 +413,17 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
}
}
protected void handleLore(final UserConnection connection, final CompoundTag tag) {
final ListTag<? extends Tag> loreTag = TagUtil.getNamespacedTagList(tag, "lore");
if (loreTag == null) {
return;
}
for (final Tag lore : loreTag) {
handleNestedComponent(connection, lore);
}
}
protected void handleWrittenBookContents(final UserConnection connection, final CompoundTag tag) {
final CompoundTag book = TagUtil.getNamespacedCompoundTag(tag, "written_book_content");
if (book == null) {
@@ -413,8 +436,8 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
}
for (final CompoundTag compoundTag : pagesTag) {
handleNestedComponent(connection, compoundTag, "raw");
handleNestedComponent(connection, compoundTag, "filtered");
handleNestedComponent(connection, compoundTag.get("raw"));
handleNestedComponent(connection, compoundTag.get("filtered"));
}
}
@@ -455,7 +478,13 @@ public abstract class ComponentRewriterBase<C extends ClientboundPacketType> imp
|| tag.remove("!" + Key.stripMinecraftNamespace(key)) != null;
}
protected abstract void handleNestedComponent(UserConnection connection, CompoundTag parent, String key);
/**
* Rewrites a nested text component. Stored as a regular tag in 1.21.5+, but is nested via snbt in string components before that.
*
* @param connection user connection
* @param tag nested tag to handle
*/
protected abstract void handleNestedComponent(UserConnection connection, @Nullable Tag tag);
public enum ReadType {
@@ -112,7 +112,7 @@ public class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends C
if (actions.get(4)) {
wrapper.passthrough(Types.VAR_INT); // Latency
}
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG));
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));
}
});
}
@@ -134,7 +134,7 @@ public class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends C
}
}
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Unsigned content
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Unsigned content
final int filterMaskType = wrapper.passthrough(Types.VAR_INT);
if (filterMaskType == 2) { // Partially filtered
@@ -142,8 +142,8 @@ public class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends C
}
wrapper.passthrough(chatType); // Chat Type
processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Name
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Target Name
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
});
}
@@ -203,9 +203,8 @@ public class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends C
}
@Override
protected void handleNestedComponent(final UserConnection connection, final CompoundTag parent, final String key) {
final StringTag tag = parent.getStringTag(key);
if (tag == null) {
protected void handleNestedComponent(final UserConnection connection, @Nullable final Tag tag) {
if (!(tag instanceof StringTag stringTag)) {
return;
}
@@ -213,10 +212,10 @@ public class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends C
final var input = inputSerializerVersion();
final var output = outputSerializerVersion();
final Tag asTag = input.toTag(input.toComponent(tag.getValue()));
final Tag asTag = input.toTag(input.toComponent(stringTag.getValue()));
processTag(connection, asTag);
tag.setValue(output.toString(output.toComponent(asTag)));
stringTag.setValue(output.toString(output.toComponent(asTag)));
}
@Override
@@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.type.Types;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Rewrites nbt serialized components from 1.21.5 upwards. Can also handle json components outside of hover events.
@@ -68,11 +69,8 @@ public class NBTComponentRewriter<C extends ClientboundPacketType> extends Compo
}
@Override
protected void handleNestedComponent(final UserConnection connection, final CompoundTag parent, final String key) {
final Tag tag = parent.get(key);
if (tag != null) {
processTag(connection, tag);
}
protected void handleNestedComponent(final UserConnection connection, @Nullable final Tag tag) {
processTag(connection, tag);
}
@Override
@@ -98,7 +96,7 @@ public class NBTComponentRewriter<C extends ClientboundPacketType> extends Compo
}
}
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Unsigned content
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Unsigned content
final int filterMaskType = wrapper.passthrough(Types.VAR_INT);
if (filterMaskType == 2) { // Partially filtered
@@ -106,8 +104,8 @@ public class NBTComponentRewriter<C extends ClientboundPacketType> extends Compo
}
wrapper.passthrough(ChatType.TYPE); // Chat Type
processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Name
processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Target Name
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name
processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name
});
}
}
@@ -22,10 +22,10 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.yaml.snakeyaml.DumperOptions;
@@ -139,9 +139,9 @@ public abstract class Config extends ConfigSection {
handleConfig(mergedConfig);
if (!mergedConfig.equals(existingConfig)) {
originalRoot = existingConfig != null ? new ConfigSection(this, "", existingConfig) : null;
originalRoot = existingConfig != null ? new ConfigSection(this, "", existingConfig) : null;
if (!mergedConfig.equals(existingConfig)) {
// Also updates comments once values need to be saved
save(location, mergedConfig);
}
@@ -212,7 +212,7 @@ public abstract class Config extends ConfigSection {
if (this.configFile.getParentFile() != null) {
this.configFile.getParentFile().mkdirs();
}
this.values = new ConcurrentSkipListMap<>(loadConfig(this.configFile));
this.values = Collections.synchronizedMap(loadConfig(this.configFile));
}
@Override
@@ -228,7 +228,7 @@ public abstract class Config extends ConfigSection {
/**
* Returns the pre-modification root section of the config if present.
*
* @return pre-modification root section, or null if no config existed or it did not require changes on load
* @return pre-modification root section, or null if no config existed
*/
public @Nullable ConfigSection originalRootSection() {
return originalRoot;
@@ -75,6 +75,11 @@ public final class TagUtil {
return compoundTag != null ? compoundTag : tag.getCompoundTag(Key.stripMinecraftNamespace(key));
}
public static @Nullable ListTag<? extends Tag> getNamespacedTagList(final CompoundTag tag, final String key) {
final ListTag<? extends Tag> listTag = tag.getListTag(Key.namespaced(key));
return listTag != null ? listTag : tag.getListTag(Key.stripMinecraftNamespace(key));
}
public static @Nullable ListTag<CompoundTag> getNamespacedCompoundTagList(final CompoundTag tag, final String key) {
final ListTag<CompoundTag> listTag = tag.getListTag(Key.namespaced(key), CompoundTag.class);
return listTag != null ? listTag : tag.getListTag(Key.stripMinecraftNamespace(key), CompoundTag.class);
+2 -2
View File
@@ -1,10 +1,10 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=5.7.1
projectVersion=5.7.2
# Smile emoji (note that modrinth may not have added the version on release yet)
mcVersions=1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9
mcVersionRange=1.8-1.21.11
velocityVersion=3.4
velocityVersion=3.4-3.5
# Gradle properties
org.gradle.daemon=true
+3 -3
View File
@@ -2,7 +2,7 @@ metadata.format.version = "1.1"
[versions]
gson = "2.13.1"
gson = "2.13.2"
snakeYaml = "2.5"
fastutil = "8.5.15"
vianbt = "5.1.2"
@@ -12,14 +12,14 @@ mcstructs = "5-3.2.2-SNAPSHOT"
netty = "4.0.20.Final"
guava = "17.0"
junit = "6.0.1"
junit = "6.0.2"
platformLauncher = "6.0.1"
checkerQual = "3.52.1"
# Platforms
paper = "1.20.4-R0.1-SNAPSHOT"
legacyBukkit = "1.8.8-R0.1-SNAPSHOT"
velocity = "3.1.1"
velocity = "3.4.0"
viaProxy = "[3.0.0,4.0.0)"
[libraries]
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=72f44c9f8ebcb1af43838f45ee5c4aa9c5444898b3468ab3f4af7b6076c5bc3f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -19,6 +19,7 @@ package com.viaversion.viaversion;
import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
@@ -80,21 +81,10 @@ public class VelocityPlugin implements ViaServerProxyPlatform<Player> {
@Subscribe
public void onProxyInit(ProxyInitializeEvent e) {
if (!hasConnectionEvent()) {
// No way to disable the plugin :(
Logger logger = this.loggerslf4j;
logger.error(" / \\");
logger.error(" / \\");
logger.error(" / | \\");
logger.error(" / | \\ VELOCITY 3.0.0 IS REQUIRED");
logger.error(" / \\ VIAVERSION WILL NOT WORK AS INTENDED");
logger.error(" / o \\");
logger.error("/_____________\\");
}
PROXY = proxy;
VelocityCommandHandler commandHandler = new VelocityCommandHandler();
PROXY.getCommandManager().register("viaver", commandHandler, "vvvelocity", "viaversion");
CommandManager commandManager = PROXY.getCommandManager();
commandManager.register(commandManager.metaBuilder("viaver").aliases("vvvelocity", "viaversion").plugin(this).build(), commandHandler);
api = new VelocityViaAPI();
logger = new LoggerWrapper(loggerslf4j);
conf = new VelocityViaConfig(configDir.toFile(), logger);
@@ -106,6 +96,7 @@ public class VelocityPlugin implements ViaServerProxyPlatform<Player> {
conf.reload();
}
@SuppressWarnings("deprecation")
@Subscribe(order = PostOrder.LAST)
public void onProxyLateInit(ProxyInitializeEvent e) {
final ViaManagerImpl manager = (ViaManagerImpl) Via.getManager();
@@ -240,13 +231,4 @@ public class VelocityPlugin implements ViaServerProxyPlatform<Player> {
public ProtocolDetectorService protocolDetectorService() {
return protocolDetectorService;
}
private boolean hasConnectionEvent() {
try {
Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent");
return true;
} catch (ClassNotFoundException ignored) {
return false;
}
}
}