Compare commits

..
55 Commits
Author SHA1 Message Date
Nassim Jahnke b90b573f1d 5.4.1 Release 2025-06-30 16:04:55 +02:00
Nassim Jahnke 74aeadd688 1.21.7-rc1/2 2025-06-30 16:04:53 +02:00
riku6460 6f27988cf6 Ensure run_command click events start with '/' in 1.21.5->1.21.4 (#1051) 2025-06-30 16:03:42 +02:00
Nassim Jahnke 81aad86d9f Map waterlogged light to water
Fixes #1038
2025-06-30 12:32:05 +02:00
FlorianMichael 1e7dbf38fd [ci skip] Add SECURITY.md (#1031) 2025-06-29 15:37:05 +02:00
riku6460andFlorianMichael 0404184e15 Use correct tag type to write custom click actions in 1.21.6->.5 (#1050)
Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-06-29 09:45:13 +02:00
FlorianMichael 64a7d9f8bd Strip default namespace when reading types (#1049) 2025-06-28 13:06:10 +02:00
FlorianMichael 924346882c Fix style and remap translations for item description in 1.21.6->.5 dialogs (#1048) 2025-06-28 12:34:32 +02:00
FlorianMichael 1d7332101f Fix run_command and custom click events (#1046) 2025-06-25 09:08:45 +02:00
FlorianMichael 30e63ebc3b Improve multiple text widgets follow one another in dialogs (#1044) 2025-06-24 13:36:19 +02:00
FlorianMichael 217c9459f5 Emulate dialog screens using chest inventories (#1041) 2025-06-23 22:53:59 +02:00
Nassim Jahnke ead2c6f330 Send sneaking command on vehicle exit in 1.21.2->1.21.1
Fixes #1001
2025-06-23 12:26:04 +02:00
Nassim Jahnke 1a972da613 Fix horse saddle handling in 1.21.5->1.21.4 2025-06-23 11:10:47 +02:00
Nassim Jahnke 44dbbf37e2 Remove saddle slot group from enchantments in 1.21.5->1.21.4
Fixes #1033
2025-06-22 22:14:47 +02:00
RK_01 def010c2bf Remap difficulty data type (#1040) 2025-06-21 10:08:55 +02:00
Nassim Jahnke 75744d5525 Bump version to 5.4.1-SNAPSHOT 2025-06-21 09:46:31 +02:00
Nassim Jahnke 7f66786b76 5.4.0 Release 2025-06-20 19:07:34 +02:00
Nassim Jahnke 6993308019 Remove hanging entity direction 2025-06-17 17:28:56 +02:00
Nassim Jahnke a69b0b499c Add translations 2025-06-17 02:12:47 +02:00
Nassim Jahnke a48731ded2 1.21.6-pre1 2025-06-17 02:11:22 +02:00
Nassim Jahnke 11857f1abe 25w21a 2025-06-17 02:11:22 +02:00
Nassim Jahnke fdf2d5e87b Use updated versioned types 2025-06-17 02:11:22 +02:00
Nassim Jahnke 069c537dbb 25w20a 2025-06-17 02:11:22 +02:00
Nassim Jahnke 22e86fb127 Add hashed item handling 2025-06-17 02:11:22 +02:00
Nassim Jahnke d08da420b2 Fix modifier display backup handling 2025-06-17 02:11:22 +02:00
Nassim Jahnke 5bddf285e3 25w19a 2025-06-17 02:11:22 +02:00
Nassim Jahnke 3ba77829fe Shift player_command actions 2025-06-17 02:11:22 +02:00
Nassim Jahnke 3c3cd07fd7 25w16a 2025-06-17 02:11:21 +02:00
Nassim Jahnke 5a2d3b6b31 25w15a 2025-06-17 02:11:21 +02:00
FlorianMichael 0c21ddfe40 Add missing dimension change checks for entity tracking (#1034) 2025-06-17 02:11:05 +02:00
FlorianMichael 9b65b59411 Push block entity custom name through component conversion in 1.20.3->.2 (#1035) 2025-06-15 04:44:23 +02:00
riku6460 d3248037d8 Don't show enchantment level if level == 1 && max_level == 1 (#1027) 2025-06-12 15:10:20 +02:00
FlorianMichael e0f3a98d79 Don't add block mappings for alternative mappings (#1024) 2025-06-12 15:10:11 +02:00
Nassim Jahnke 39a4d4e973 Make publish action manually runnable 2025-06-11 10:38:45 +02:00
FlorianMichael 8a9bf708f2 Add option to disable display entity emulation in 1.19.4->.3 (#1022) 2025-05-31 02:43:19 +02:00
Nassim Jahnke 5070e51b04 Update VV usage 2025-05-27 14:28:31 +02:00
riku6460 12baac5cbd Fix login finished packet in 1.19->1.18.2 (#1018) 2025-05-27 04:41:00 +02:00
Nassim Jahnke 8669c17512 Update VV usage 2025-05-26 11:57:18 +02:00
github-actions[bot]andgradle-update-robot fe993fc2a7 Update Gradle Wrapper from 8.14 to 8.14.1 (#1016)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2025-05-22 17:35:16 +02:00
AdrianandFlorianMichael 54af45da27 Add alternative scaffolding mapping in 1.14->1.13.2 (#1015)
Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-05-22 15:18:40 +02:00
Nassim Jahnke 175daef7da Refactor 1.20.5+ versioned types 2025-05-17 14:30:10 +02:00
Nassim Jahnke 986ecaaf8d Fix 1.20.5 item handler
Fixes #1011
2025-05-15 15:10:50 +02:00
Nassim Jahnke b42c19752c Remove no longer needed container handling, now handled in the super call 2025-05-15 11:10:23 +02:00
Nassim Jahnke d98de382fc Update VV usage 2025-05-14 14:00:00 +02:00
Nassim Jahnke 13bc18b496 Update VV usage 2025-05-13 22:56:18 +02:00
Nassim Jahnke 4160b37f13 Update VV usage 2025-05-12 19:08:05 +02:00
FlorianMichael f9f7a24dda [ci skip] Make the runViaProxy task work with Gradle's configuration cache (#1005) 2025-05-12 17:54:17 +02:00
FlorianMichael 21fd034c02 Remove enchantments with level 0 if sent by the client in 1.21.2->1.21 (#1004) 2025-05-12 15:31:37 +02:00
Nassim Jahnke 6a59e887c8 Attempt to convert serverbound clicks to hashed items in 1.21.5
This will fix desyncs on item drags due to the server missing the individual data hashes. Some are not implemented, in which case it will still cause those sames issues, but generally this will fix its handling for most items
2025-05-11 23:14:51 +02:00
FlorianMichael 32488cf2ff [ci skip] Allow build if Git is not installed or the project is downloaded as code zip (#1002) 2025-05-11 22:26:49 +02:00
FlorianMichael 774a35db17 [ci skip] Don't publish viaversion-fabric to the repository server (#1000) 2025-05-05 21:07:22 +02:00
FlorianMichael 32f41382fa Add missing PLAYER_CHAT handlers in 1.20/1.21 versions (#999) 2025-04-28 22:08:25 +02:00
Nassim Jahnke c9573a1cfd Move data handling 1.21.5 text component rewriting to the right place
Fixes #997
2025-04-28 16:20:06 +02:00
github-actions[bot]andgradle-update-robot def9df84b4 Update Gradle Wrapper from 8.13 to 8.14 (#996)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2025-04-26 20:51:11 +02:00
FlorianMichael fd4bc59058 Fix potion entity hover event in 1.21.5->1.21.4 (#995) 2025-04-26 20:50:21 +02:00
95 changed files with 4475 additions and 554 deletions

No files matched your search

+1
View File
@@ -4,6 +4,7 @@ on:
branches:
- master
- dev
workflow_dispatch:
jobs:
publish:
+10
View File
@@ -0,0 +1,10 @@
# Security Policy
## Reporting a Vulnerability
If you discover a **security vulnerability**, please **do not** open a public issue. Instead, use one of the following **confidential contact methods**:
* **Email:** [security@viaversion.com](mailto:security@viaversion.com)
* **Discord:** Join our [Discord server](https://discord.gg/viaversion) and visit the [#exploit-report](https://discord.com/channels/316206679014244363/1388847764137316485) channel for further instructions.
If your issue is **not security-related** (e.g., bug reports, feature requests, or usage questions), please use the [GitHub Issues page](https://github.com/ViaVersion/ViaBackwards/issues) or contact us via the [Discord server](https://discord.gg/viaversion).
+34 -3
View File
@@ -1,6 +1,12 @@
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.ValueSource
import org.gradle.api.provider.ValueSourceParameters
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.process.ExecOperations
import java.io.ByteArrayOutputStream
import javax.inject.Inject
fun Project.latestCommitHash(): String {
return runGitCommand(listOf("rev-parse", "--short", "HEAD"))
@@ -15,11 +21,36 @@ fun Project.branchName(): String {
}
fun Project.runGitCommand(args: List<String>): String {
return providers.exec {
commandLine = listOf("git") + args
}.standardOutput.asBytes.get().toString(Charsets.UTF_8).trim()
return providers.of(GitCommand::class.java) { parameters.args.set(args) }.getOrNull() ?: "unknown"
}
abstract class GitCommand : ValueSource<String, GitCommand.GitCommandParameters> {
@get:Inject
abstract val execOperations: ExecOperations
interface GitCommandParameters : ValueSourceParameters {
val args: ListProperty<String>
}
override fun obtain(): String? {
try {
val command = listOf("git") + parameters.args.get()
val output = ByteArrayOutputStream()
execOperations.exec {
commandLine = command
standardOutput = output
isIgnoreExitValue = true
}
return output.toString(Charsets.UTF_8).trim().takeIf { it.isNotBlank() }
} catch (e: Exception) {
return null
}
}
}
fun Project.parseMinecraftSnapshotVersion(version: String): String? {
val separatorIndex = version.indexOf('-')
val lastSeparatorIndex = version.lastIndexOf('-')
+1 -2
View File
@@ -13,8 +13,7 @@ val main = setOf(
projects.viabackwards,
projects.viabackwardsCommon,
projects.viabackwardsBukkit,
projects.viabackwardsVelocity,
projects.viabackwardsFabric
projects.viabackwardsVelocity
).map { it.path }
subprojects {
+24 -16
View File
@@ -27,29 +27,37 @@ java {
// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy
// For further instructions see the ViaProxy repository README
tasks.register<JavaExec>("runViaProxy") {
dependsOn(tasks.shadowJar)
val prepareViaProxyFiles by tasks.registering(Copy::class) {
dependsOn(project.tasks.shadowJar)
val viaProxyConfiguration = configurations.create("viaProxy")
viaProxyConfiguration.dependencies.add(dependencies.create(rootProject.libs.viaProxy.get().copy().setTransitive(false)))
from(project.tasks.shadowJar.get().archiveFile.get().asFile)
into(layout.projectDirectory.dir("run/jars"))
val projectName = project.name
rename { "${projectName}.jar" }
}
val cleanupViaProxyFiles by tasks.registering(Delete::class) {
delete(
layout.projectDirectory.file("run/jars/${project.name}.jar"),
layout.projectDirectory.dir("run/logs")
)
}
val viaProxyConfiguration: Configuration by configurations.creating {
dependencies.add(rootProject.libs.viaProxy.get().copy().setTransitive(false))
}
tasks.register<JavaExec>("runViaProxy") {
dependsOn(prepareViaProxyFiles)
finalizedBy(cleanupViaProxyFiles)
mainClass.set("net.raphimc.viaproxy.ViaProxy")
classpath = viaProxyConfiguration
workingDir = file("run")
workingDir = layout.projectDirectory.dir("run").asFile
jvmArgs = listOf("-DskipUpdateCheck")
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
}
doFirst {
val jarsDir = file("$workingDir/jars")
jarsDir.mkdirs()
file("$jarsDir/${project.name}.jar").writeBytes(tasks.shadowJar.get().archiveFile.get().asFile.readBytes())
}
doLast {
file("$workingDir/jars/${project.name}.jar").delete()
file("$workingDir/logs").deleteRecursively()
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2023 ViaVersion and contributors
* 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
@@ -36,8 +36,10 @@ public class ViaBackwardsConfig extends Config implements com.viaversion.viaback
private boolean handlePingsAsInvAcknowledgements;
private boolean bedrockAtY0;
private boolean sculkShriekersToCryingObsidian;
private boolean scaffoldingToWater;
private boolean mapDarknessEffect;
private boolean mapCustomModelData;
private boolean mapDisplayEntities;
private boolean suppressEmulationWarnings;
public ViaBackwardsConfig(File configFile, Logger logger) {
@@ -59,8 +61,10 @@ public class ViaBackwardsConfig extends Config implements com.viaversion.viaback
handlePingsAsInvAcknowledgements = getBoolean("handle-pings-as-inv-acknowledgements", false);
bedrockAtY0 = getBoolean("bedrock-at-y-0", false);
sculkShriekersToCryingObsidian = getBoolean("sculk-shriekers-to-crying-obsidian", false);
scaffoldingToWater = getBoolean("scaffolding-to-water", false);
mapDarknessEffect = getBoolean("map-darkness-effect", true);
mapCustomModelData = getBoolean("map-custom-model-data", true);
mapDisplayEntities = getBoolean("map-display-entities", true);
suppressEmulationWarnings = getBoolean("suppress-emulation-warnings", false);
}
@@ -104,6 +108,11 @@ public class ViaBackwardsConfig extends Config implements com.viaversion.viaback
return sculkShriekersToCryingObsidian;
}
@Override
public boolean scaffoldingToWater() {
return scaffoldingToWater;
}
@Override
public boolean mapDarknessEffect() {
return mapDarknessEffect;
@@ -114,6 +123,11 @@ public class ViaBackwardsConfig extends Config implements com.viaversion.viaback
return mapCustomModelData;
}
@Override
public boolean mapDisplayEntities() {
return mapDisplayEntities;
}
@Override
public boolean suppressEmulationWarnings() {
return suppressEmulationWarnings;
@@ -78,6 +78,13 @@ public interface ViaBackwardsConfig extends Config {
*/
boolean sculkShriekerToCryingObsidian();
/**
* Shows scaffolding as water for 1.13.2 clients on 1.14+ servers. This fixes collision issues.
*
* @return true if enabled
*/
boolean scaffoldingToWater();
/**
* Maps the darkness effect to blindness for 1.18.2 clients on 1.19+ servers.
*
@@ -92,6 +99,13 @@ public interface ViaBackwardsConfig extends Config {
*/
boolean mapCustomModelData();
/**
* If enabled, 1.19.3 clients will receive display entities as armor stands with custom entity data on 1.19.4+ servers.
*
* @return true if enabled
*/
boolean mapDisplayEntities();
/**
* Suppresses warnings of missing emulations for certain features that are not supported (e.g. world height in 1.17+).
*
@@ -21,47 +21,50 @@ package com.viaversion.viabackwards.api;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.ViaBackwardsConfig;
import com.viaversion.viabackwards.api.data.TranslatableMappings;
import com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;
import com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;
import com.viaversion.viabackwards.protocol.v1_10to1_9_3.Protocol1_10To1_9_3;
import com.viaversion.viabackwards.protocol.v1_11_1to1_11.Protocol1_11_1To1_11;
import com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.Protocol1_12_2To1_12_1;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;
import com.viaversion.viabackwards.protocol.v1_12_1to1_12.Protocol1_12_1To1_12;
import com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.Protocol1_12_2To1_12_1;
import com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;
import com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;
import com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_14_1to1_14.Protocol1_14_1To1_14;
import com.viaversion.viabackwards.protocol.v1_14_2to1_14_1.Protocol1_14_2To1_14_1;
import com.viaversion.viabackwards.protocol.v1_14_3to1_14_2.Protocol1_14_3To1_14_2;
import com.viaversion.viabackwards.protocol.v1_14_4to1_14_3.Protocol1_14_4To1_14_3;
import com.viaversion.viabackwards.protocol.v1_15to1_14_4.Protocol1_15To1_14_4;
import com.viaversion.viabackwards.protocol.v1_14_1to1_14.Protocol1_14_1To1_14;
import com.viaversion.viabackwards.protocol.v1_15_2to1_15_1.Protocol1_15_2To1_15_1;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_15_1to1_15.Protocol1_15_1To1_15;
import com.viaversion.viabackwards.protocol.v1_15_2to1_15_1.Protocol1_15_2To1_15_1;
import com.viaversion.viabackwards.protocol.v1_15to1_14_4.Protocol1_15To1_14_4;
import com.viaversion.viabackwards.protocol.v1_16_1to1_16.Protocol1_16_1To1_16;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;
import com.viaversion.viabackwards.protocol.v1_16_3to1_16_2.Protocol1_16_3To1_16_2;
import com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.Protocol1_16_4To1_16_3;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;
import com.viaversion.viabackwards.protocol.v1_16_1to1_16.Protocol1_16_1To1_16;
import com.viaversion.viabackwards.protocol.v1_18to1_17_1.Protocol1_18To1_17_1;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.Protocol1_17_1To1_17;
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;
import com.viaversion.viabackwards.protocol.v1_18_2to1_18.Protocol1_18_2To1_18;
import com.viaversion.viabackwards.protocol.v1_18to1_17_1.Protocol1_18To1_17_1;
import com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;
import com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.Protocol1_19_3To1_19_1;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;
import com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.Protocol1_21_4To1_21_2;
import com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;
import com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;
import com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;
import com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;
import com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;
import com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;
import com.viaversion.viabackwards.protocol.v1_21_2to1_21.task.PlayerPacketsTickTask;
import com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.Protocol1_21_4To1_21_2;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.task.ChestDialogViewTask;
import com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;
import com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;
import com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.Protocol1_9_3To1_9_1;
import com.viaversion.viabackwards.protocol.v1_10to1_9_3.Protocol1_10To1_9_3;
import com.viaversion.viabackwards.protocol.v1_9_1to1_9.Protocol1_9_1To1_9;
import com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.Protocol1_9_3To1_9_1;
import com.viaversion.viabackwards.utils.VersionInfo;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
@@ -73,7 +76,7 @@ import java.util.logging.Logger;
public interface ViaBackwardsPlatform {
String MINIMUM_VV_VERSION = "5.3.2";
String MINIMUM_VV_VERSION = "5.4.0";
/**
* Initialize ViaBackwards.
@@ -148,6 +151,9 @@ public interface ViaBackwardsPlatform {
protocolManager.registerProtocol(new Protocol1_21_2To1_21(), ProtocolVersion.v1_21, ProtocolVersion.v1_21_2);
protocolManager.registerProtocol(new Protocol1_21_4To1_21_2(), ProtocolVersion.v1_21_2, ProtocolVersion.v1_21_4);
protocolManager.registerProtocol(new Protocol1_21_5To1_21_4(), ProtocolVersion.v1_21_4, ProtocolVersion.v1_21_5);
protocolManager.registerProtocol(new Protocol1_21_6To1_21_5(), ProtocolVersion.v1_21_5, ProtocolVersion.v1_21_6);
protocolManager.registerProtocol(new Protocol1_21_7To1_21_6(), ProtocolVersion.v1_21_6, ProtocolVersion.v1_21_7);
}
default void enable() {
@@ -155,6 +161,10 @@ public interface ViaBackwardsPlatform {
if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_21_2)) {
Via.getPlatform().runRepeatingSync(new PlayerPacketsTickTask(), 1L);
}
if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_21_6)) {
Via.getPlatform().runRepeatingSync(new ChestDialogViewTask(), 20L);
}
}
/**
@@ -0,0 +1,51 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.api.rewriters;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
public class BackwardsRegistryRewriter extends RegistryDataRewriter {
private final BackwardsProtocol<?, ?, ?, ?> protocol;
public BackwardsRegistryRewriter(final BackwardsProtocol<?, ?, ?, ?> protocol) {
super(protocol);
this.protocol = protocol;
}
@Override
public void updateJukeboxSongs(final RegistryEntry[] entries) {
for (final RegistryEntry entry : entries) {
if (entry.tag() == null) {
continue;
}
final StringTag soundEvent = ((CompoundTag) entry.tag()).getStringTag("sound_event");
if (soundEvent != null) {
final String mappedNamedSound = protocol.getMappingData().getMappedNamedSound(soundEvent.getValue());
if (mappedNamedSound != null) {
soundEvent.setValue(mappedNamedSound);
}
}
}
}
}
@@ -31,6 +31,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.EitherHolder;
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.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
@@ -38,8 +39,8 @@ import com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.rewriter.StructuredItemRewriter;
import com.viaversion.viaversion.util.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
@@ -51,47 +52,22 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
private static final int[] EMPTY_INT_ARRAY = new int[0];
public BackwardsStructuredItemRewriter(
T protocol,
Type<Item> itemType, Type<Item[]> itemArrayType, Type<Item> mappedItemType, Type<Item[]> mappedItemArrayType,
Type<Item> itemCostType, Type<Item> optionalItemCostType, Type<Item> mappedItemCostType, Type<Item> mappedOptionalItemCostType
) {
super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType, itemCostType, optionalItemCostType, mappedItemCostType, mappedOptionalItemCostType);
}
public BackwardsStructuredItemRewriter(final T protocol, final Type<Item> itemType, final Type<Item[]> itemArrayType, final Type<Item> mappedItemType, final Type<Item[]> mappedItemArrayType) {
super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType);
}
public BackwardsStructuredItemRewriter(final T protocol, final Type<Item> itemType, final Type<Item[]> itemArrayType) {
super(protocol, itemType, itemArrayType);
public BackwardsStructuredItemRewriter(T protocol) {
super(protocol);
}
@Override
public Item handleItemToClient(final UserConnection connection, final Item item) {
if (item.isEmpty()) {
return item;
}
final StructuredDataContainer dataContainer = item.dataContainer();
updateItemDataComponentTypeIds(dataContainer, true);
protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {
super.backupInconvertibleData(connection, item, dataContainer, backupTag);
final BackwardsMappingData mappingData = protocol.getMappingData();
final MappedItem mappedItem = mappingData != null ? mappingData.getMappedItem(item.identifier()) : null;
if (mappedItem == null) {
// Just rewrite the id
if (mappingData != null && mappingData.getItemMappings() != null) {
item.setIdentifier(mappingData.getNewItemId(item.identifier()));
}
updateItemDataComponents(connection, item, true);
return item;
return;
}
// Save original id, set remapped id
final CompoundTag tag = createCustomTag(item);
tag.putInt(nbtTagName("id"), item.identifier());
item.setIdentifier(mappedItem.id());
final CompoundTag customTag = createCustomTag(item);
customTag.putInt(nbtTagName("id"), item.identifier()); // Save original id
// Add custom model data
if (mappedItem.customModelData() != null) {
@@ -112,38 +88,17 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
// Set custom name - only done if there is no original one
if (!dataContainer.has(StructuredDataKey.CUSTOM_NAME)) {
dataContainer.set(StructuredDataKey.CUSTOM_NAME, mappedItem.tagName());
tag.putBoolean(nbtTagName("added_custom_name"), true);
customTag.putBoolean(nbtTagName("added_custom_name"), true);
}
updateItemDataComponents(connection, item, true);
return item;
}
@Override
public Item handleItemToServer(final UserConnection connection, final Item item) {
if (item.isEmpty()) {
return item;
protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {
super.restoreBackupData(item, container, customData);
if (removeBackupTag(customData, "id") instanceof final IntTag originalTag) {
item.setIdentifier(originalTag.asInt());
removeCustomTag(container, customData);
}
final StructuredDataContainer dataContainer = item.dataContainer();
updateItemDataComponentTypeIds(dataContainer, false);
final BackwardsMappingData mappingData = protocol.getMappingData();
if (mappingData != null && mappingData.getItemMappings() != null) {
item.setIdentifier(mappingData.getOldItemId(item.identifier()));
}
final CompoundTag customData = dataContainer.get(StructuredDataKey.CUSTOM_DATA);
if (customData != null) {
if (customData.remove(nbtTagName("id")) instanceof final IntTag originalTag) {
item.setIdentifier(originalTag.asInt());
removeCustomTag(dataContainer, customData);
}
}
restoreTextComponents(item);
updateItemDataComponents(connection, item, false);
return item;
}
protected void saveListTag(CompoundTag tag, ListTag<?> original, String name) {
@@ -291,6 +246,20 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
}
}
protected void saveKeyData(final StructuredDataKey<Key> key, final StructuredDataContainer data, final CompoundTag backupTag) {
final Key value = data.get(key);
if (value != null) {
backupTag.putString(key.identifier(), value.original());
}
}
protected void restoreKeyData(final StructuredDataKey<Key> key, final StructuredDataContainer data, final CompoundTag backupTag) {
final String value = backupTag.getString(key.identifier());
if (value != null) {
data.set(key, Key.of(value));
}
}
protected void saveIntData(final StructuredDataKey<Integer> key, final StructuredDataContainer data, final CompoundTag backupTag) {
final Integer variant = data.get(key);
if (variant != null) {
@@ -319,6 +288,25 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
}
}
protected void saveSoundEventHolder(final CompoundTag tag, final Holder<SoundEvent> holder) {
tag.put("sound_event", holderToTag(holder, this::saveSoundEvent));
}
protected void saveSoundEvent(final SoundEvent soundEvent, final CompoundTag tag) {
tag.putString("identifier", soundEvent.identifier());
if (soundEvent.fixedRange() != null) {
tag.putFloat("fixed_range", soundEvent.fixedRange());
}
}
protected Holder<SoundEvent> restoreSoundEventHolder(final CompoundTag tag) {
return restoreHolder(tag, "sound_event", soundEventTag -> {
final String identifier = soundEventTag.getString("identifier");
final FloatTag fixedRange = soundEventTag.getFloatTag("fixed_range");
return new SoundEvent(identifier, fixedRange != null ? fixedRange.asFloat() : null);
});
}
@Override
public String nbtTagName() {
return "VB|" + protocol.getClass().getSimpleName();
@@ -17,10 +17,12 @@
*/
package com.viaversion.viabackwards.protocol.template;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.Types1_21_5;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;
@@ -33,14 +35,10 @@ import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
// ChunkType1_21_5
// RecipeDisplayRewriter
// Types1_21_4
final class BlockItemPacketRewriter1_99 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_4, Protocol1_98To1_99> {
final class BlockItemPacketRewriter1_99 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_4, Protocol1_99To1_98> {
public BlockItemPacketRewriter1_99(final Protocol1_98To1_99 protocol) {
super(protocol, Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY);
/*super(protocol,
Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY, Types1_OLD.ITEM, Types1_OLD.ITEM_ARRAY,
Types1_21_4.ITEM_COST, Types1_21_4.OPTIONAL_ITEM_COST, Types1_OLD.ITEM_COST, Types1_OLD.OPTIONAL_ITEM_COST
);*/
public BlockItemPacketRewriter1_99(final Protocol1_99To1_98 protocol) {
super(protocol);
}
@Override
@@ -54,7 +52,7 @@ final class BlockItemPacketRewriter1_99 extends BackwardsStructuredItemRewriter<
blockRewriter.registerBlockEntityData(ClientboundPackets1_21_2.BLOCK_ENTITY_DATA);
// registerOpenScreen(ClientboundPackets1_21_2.OPEN_SCREEN);
protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerSetCursorItem(ClientboundPackets1_21_2.SET_CURSOR_ITEM);
registerSetPlayerInventory(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_2.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21_2.CONTAINER_SET_CONTENT);
@@ -63,11 +61,35 @@ final class BlockItemPacketRewriter1_99 extends BackwardsStructuredItemRewriter<
registerSetEquipment(ClientboundPackets1_21_2.SET_EQUIPMENT);
registerMerchantOffers1_20_5(ClientboundPackets1_21_2.MERCHANT_OFFERS);
registerContainerClick1_21_5(ServerboundPackets1_21_4.CONTAINER_CLICK);
registerSetCreativeModeSlot1_21_5(ServerboundPackets1_21_4.SET_CREATIVE_MODE_SLOT, Types1_21_5.LENGTH_PREFIXED_ITEM, Types1_21_5.LENGTH_PREFIXED_ITEM); // Change this
registerSetCreativeModeSlot1_21_5(ServerboundPackets1_21_4.SET_CREATIVE_MODE_SLOT);
final RecipeDisplayRewriter<ClientboundPacket1_21_2> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21_2.UPDATE_RECIPES);
recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21_2.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_2.PLACE_GHOST_RECIPE);
}
@Override
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {
super.handleItemDataComponentsToClient(connection, item, container);
// downgradeData(item, container); // static import VV method
}
@Override
protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {
super.handleItemDataComponentsToServer(connection, item, container);
// upgradeData(item, container); // static import VV method
}
@Override
protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {
super.restoreBackupData(item, container, customData);
// restore any data if needed here
}
@Override
protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {
super.backupInconvertibleData(connection, item, dataContainer, backupTag);
// back up any data if needed here, called before the method below
}
}
@@ -17,40 +17,42 @@
*/
package com.viaversion.viabackwards.protocol.template;
import com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_2;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
// Replace if needed
// Types1_OLD
// Types1_21_4
final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_98To1_99> {
// VersionedTypes
final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_99To1_98> {
public EntityPacketRewriter1_99(final Protocol1_98To1_99 protocol) {
super(protocol, Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType, Types1_21_4.ENTITY_DATA_TYPES.booleanType);
public EntityPacketRewriter1_99(final Protocol1_99To1_98 protocol) {
super(protocol, VersionedTypes.V1_21_4.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_4.entityDataTypes.booleanType);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, /*Types1_OLD.ENTITY_DATA_LIST, */Types1_21_4.ENTITY_DATA_LIST); // Specify old and new entity data list if changed
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
registerPlayerAbilities(ClientboundPackets1_21_2.PLAYER_ABILITIES);
registerGameEvent(ClientboundPackets1_21_2.GAME_EVENT);
registerLogin1_20_5(ClientboundPackets1_21_2.LOGIN);
registerRespawn1_20_5(ClientboundPackets1_21_2.RESPAWN);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol);
final RegistryDataRewriter registryDataRewriter = new BackwardsRegistryRewriter(protocol);
protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle);
}
@Override
protected void registerRewrites() {
final EntityDataTypes1_21_2 mappedEntityDataTypes = VersionedTypes.V1_21_4.entityDataTypes;
/*filter().handler((event, data) -> {
int id = data.dataType().typeId();
if (id >= ac) {
@@ -59,17 +61,17 @@ final class EntityPacketRewriter1_99 extends EntityRewriter<ClientboundPacket1_2
id--;
}
data.setDataType(Types1_21_4.ENTITY_DATA_TYPES.byId(id));
data.setDataType(mappedEntityDataTypess.byId(id));
});*/
registerEntityDataTypeHandler1_20_3(
Types1_21_4.ENTITY_DATA_TYPES.itemType,
Types1_21_4.ENTITY_DATA_TYPES.blockStateType,
Types1_21_4.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21_4.ENTITY_DATA_TYPES.particleType,
Types1_21_4.ENTITY_DATA_TYPES.particlesType,
Types1_21_4.ENTITY_DATA_TYPES.componentType,
Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType
mappedEntityDataTypes.itemType,
mappedEntityDataTypes.blockStateType,
mappedEntityDataTypes.optionalBlockStateType,
mappedEntityDataTypes.particleType,
mappedEntityDataTypes.particlesType,
mappedEntityDataTypes.componentType,
mappedEntityDataTypes.optionalComponentType
);
// Remove entity data of new entity type
@@ -25,8 +25,10 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.data.item.ItemHasherBase;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
@@ -37,6 +39,7 @@ import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacke
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.util.SerializerVersion;
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
@@ -48,19 +51,20 @@ import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
// ClientboundConfigurationPackets1_21
// ServerboundConfigurationPackets1_20_5
// EntityTypes1_21_4 (UNMAPPED type)
// Types1_21_4.PARTICLE
// VersionedTypes.V1_21_5
// SerializerVersion.V1_21_5
// 1.99, 1.98
final class Protocol1_98To1_99 extends BackwardsProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_4> {
final class Protocol1_99To1_98 extends BackwardsProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_4> {
// ViaBackwards uses its own mappings and also needs a translatablerewriter for translation mappings
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.99", "1.98", Protocol1_20_5To1_21.class);
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.99", "1.98", Protocol1_20_5To1_21.class); // Change the VV (!) protocol class
private final EntityPacketRewriter1_99 entityRewriter = new EntityPacketRewriter1_99(this);
private final BlockItemPacketRewriter1_99 itemRewriter = new BlockItemPacketRewriter1_99(this);
private final ParticleRewriter<ClientboundPacket1_21_2> particleRewriter = new ParticleRewriter<>(this, Types1_21_4.PARTICLE/*, Types1_OLD.PARTICLE*/);
private final ParticleRewriter<ClientboundPacket1_21_2> particleRewriter = new ParticleRewriter<>(this);
private final NBTComponentRewriter<ClientboundPacket1_21_2> translatableRewriter = new ComponentRewriter1_99(this);
private final TagRewriter<ClientboundPacket1_21_2> tagRewriter = new TagRewriter<>(this);
public Protocol1_98To1_99() {
public Protocol1_99To1_98() {
super(ClientboundPacket1_21_2.class, ClientboundPacket1_21_2.class, ServerboundPacket1_21_4.class, ServerboundPacket1_21_4.class);
}
@@ -100,8 +104,9 @@ final class Protocol1_98To1_99 extends BackwardsProtocol<ClientboundPacket1_21_2
}
@Override
public void init(final UserConnection user) {
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_4.PLAYER));
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_4.PLAYER));
addItemHasher(connection, new ItemHasherBase(this, connection, SerializerVersion.V1_21_5, SerializerVersion.V1_21_5));
}
@Override
@@ -134,6 +139,16 @@ final class Protocol1_98To1_99 extends BackwardsProtocol<ClientboundPacket1_21_2
return tagRewriter;
}
@Override
public VersionedTypesHolder types() {
return VersionedTypes.V1_21_5;
}
@Override
public VersionedTypesHolder mappedTypes() {
return VersionedTypes.V1_21_5;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_4> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
import java.util.List;
@@ -80,7 +79,7 @@ public class EntityPacketRewriter1_10 extends LegacyEntityRewriter<ClientboundPa
map(Types.SHORT); // 9 - Velocity X
map(Types.SHORT); // 10 - Velocity Y
map(Types.SHORT); // 11 - Velocity Z
map(Types1_9.ENTITY_DATA_LIST); // 12 - Entity data
map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data
// Track entity
handler(getTrackerHandler(Types.UNSIGNED_BYTE, 0));
@@ -93,7 +92,7 @@ public class EntityPacketRewriter1_10 extends LegacyEntityRewriter<ClientboundPa
return;
}
List<EntityData> entityDataList = wrapper.get(Types1_9.ENTITY_DATA_LIST, 0);
List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);
handleEntityData(wrapper.get(Types.VAR_INT, 0), entityDataList, wrapper.user());
EntityReplacement entityReplacement = entityDataForType(type);
@@ -122,14 +121,14 @@ public class EntityPacketRewriter1_10 extends LegacyEntityRewriter<ClientboundPa
map(Types.DOUBLE); // 4 - Z
map(Types.BYTE); // 5 - Yaw
map(Types.BYTE); // 6 - Pitch
map(Types1_9.ENTITY_DATA_LIST); // 7 - Entity data list
map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list
handler(getTrackerAndDataHandler(Types1_9.ENTITY_DATA_LIST, EntityTypes1_11.EntityType.PLAYER));
handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));
}
});
registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types1_9.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);
}
@Override
@@ -24,7 +24,6 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
public class EntityPacketRewriter1_11_1 extends LegacyEntityRewriter<ClientboundPackets1_9_3, Protocol1_11_1To1_11> {
@@ -72,13 +71,13 @@ public class EntityPacketRewriter1_11_1 extends LegacyEntityRewriter<Clientbound
map(Types.SHORT); // 9 - Velocity X
map(Types.SHORT); // 10 - Velocity Y
map(Types.SHORT); // 11 - Velocity Z
map(Types1_9.ENTITY_DATA_LIST); // 12 - Entity data
map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data
// Track entity
handler(getTrackerHandler());
// Rewrite entity type / data
handler(getMobSpawnRewriter1_11(Types1_9.ENTITY_DATA_LIST));
handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_9));
}
});
@@ -96,14 +95,14 @@ public class EntityPacketRewriter1_11_1 extends LegacyEntityRewriter<Clientbound
map(Types.DOUBLE); // 4 - Z
map(Types.BYTE); // 5 - Yaw
map(Types.BYTE); // 6 - Pitch
map(Types1_9.ENTITY_DATA_LIST); // 7 - Entity data list
map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list
handler(getTrackerAndDataHandler(Types1_9.ENTITY_DATA_LIST, EntityTypes1_11.EntityType.PLAYER));
handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));
}
});
registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types1_9.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);
}
@Override
@@ -30,7 +30,6 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
import java.util.List;
@@ -104,17 +103,17 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
map(Types.SHORT); // 9 - Velocity X
map(Types.SHORT); // 10 - Velocity Y
map(Types.SHORT); // 11 - Velocity Z
map(Types1_9.ENTITY_DATA_LIST); // 12 - Entity data
map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data
// Track entity
handler(getTrackerHandler(Types.UNSIGNED_BYTE, 0));
// Rewrite entity type / data
handler(getMobSpawnRewriter(Types1_9.ENTITY_DATA_LIST));
handler(getMobSpawnRewriter(Types.ENTITY_DATA_LIST1_9));
// Sub 1.11 clients will error if the list is empty
handler(wrapper -> {
List<EntityData> entityDataList = wrapper.get(Types1_9.ENTITY_DATA_LIST, 0);
List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);
if (entityDataList.isEmpty()) {
entityDataList.add(new EntityData(0, EntityDataTypes1_9.BYTE, (byte) 0));
}
@@ -136,12 +135,12 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
map(Types.DOUBLE); // 4 - Z
map(Types.BYTE); // 5 - Yaw
map(Types.BYTE); // 6 - Pitch
map(Types1_9.ENTITY_DATA_LIST); // 7 - Entity data list
map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list
handler(getTrackerAndDataHandler(Types1_9.ENTITY_DATA_LIST, EntityTypes1_11.EntityType.PLAYER));
handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));
handler(wrapper -> {
// Sub 1.11 clients will cry if the list is empty
List<EntityData> entityDataList = wrapper.get(Types1_9.ENTITY_DATA_LIST, 0);
List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);
if (entityDataList.isEmpty()) {
entityDataList.add(new EntityData(0, EntityDataTypes1_9.BYTE, (byte) 0));
}
@@ -150,7 +149,7 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
});
registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types1_9.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);
protocol.registerClientbound(ClientboundPackets1_9_3.ENTITY_EVENT, new PacketHandlers() {
@Override
@@ -31,8 +31,6 @@ import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_12;
import com.viaversion.viaversion.api.type.types.version.Types1_9;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
@@ -84,13 +82,13 @@ public class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPa
map(Types.SHORT); // 9 - Velocity X
map(Types.SHORT); // 10 - Velocity Y
map(Types.SHORT); // 11 - Velocity Z
map(Types1_12.ENTITY_DATA_LIST, Types1_9.ENTITY_DATA_LIST); // 12 - Entity data
map(Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9); // 12 - Entity data
// Track entity
handler(getTrackerHandler());
// Rewrite entity type / data
handler(getMobSpawnRewriter1_11(Types1_9.ENTITY_DATA_LIST));
handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_9));
}
});
@@ -106,9 +104,9 @@ public class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPa
map(Types.DOUBLE); // 4 - Z
map(Types.BYTE); // 5 - Yaw
map(Types.BYTE); // 6 - Pitch
map(Types1_12.ENTITY_DATA_LIST, Types1_9.ENTITY_DATA_LIST); // 7 - Entity data list
map(Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list
handler(getTrackerAndDataHandler(Types1_9.ENTITY_DATA_LIST, EntityTypes1_12.EntityType.PLAYER));
handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_12.EntityType.PLAYER));
}
});
@@ -142,7 +140,7 @@ public class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPa
registerRespawn(ClientboundPackets1_12.RESPAWN);
registerRemoveEntities(ClientboundPackets1_12.REMOVE_ENTITIES);
registerSetEntityData(ClientboundPackets1_12.SET_ENTITY_DATA, Types1_12.ENTITY_DATA_LIST, Types1_9.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_12.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9);
protocol.registerClientbound(ClientboundPackets1_12.UPDATE_ATTRIBUTES, new PacketHandlers() {
@Override
@@ -38,7 +38,6 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_12;
import com.viaversion.viaversion.api.type.types.version.Types1_13;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;
@@ -130,7 +129,7 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
map(Types.SHORT);
map(Types.SHORT);
map(Types.SHORT);
map(Types1_13.ENTITY_DATA_LIST, Types1_12.ENTITY_DATA_LIST);
map(Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);
handler(wrapper -> {
int type = wrapper.get(Types.VAR_INT, 1);
@@ -152,7 +151,7 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
});
// Rewrite entity type / ddata
handler(getMobSpawnRewriter1_11(Types1_12.ENTITY_DATA_LIST));
handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_12));
}
});
@@ -166,9 +165,9 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
map(Types.DOUBLE);
map(Types.BYTE);
map(Types.BYTE);
map(Types1_13.ENTITY_DATA_LIST, Types1_12.ENTITY_DATA_LIST);
map(Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);
handler(getTrackerAndDataHandler(Types1_12.ENTITY_DATA_LIST, EntityTypes1_13.EntityType.PLAYER));
handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_12, EntityTypes1_13.EntityType.PLAYER));
}
});
@@ -208,7 +207,7 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
});
registerRemoveEntities(ClientboundPackets1_13.REMOVE_ENTITIES);
registerSetEntityData(ClientboundPackets1_13.SET_ENTITY_DATA, Types1_13.ENTITY_DATA_LIST, Types1_12.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_13.SET_ENTITY_DATA, Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);
// Face Player (new packet)
protocol.registerClientbound(ClientboundPackets1_13.PLAYER_LOOK_AT, null, wrapper -> {
@@ -150,12 +150,7 @@ public class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2>
if (action == 0) { // Add
String name = packetWrapper.passthrough(Types.STRING);
storage.usernames().put(uuid, name);
int nProperties = packetWrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < nProperties; j++) {
packetWrapper.passthrough(Types.STRING);
packetWrapper.passthrough(Types.STRING);
packetWrapper.passthrough(Types.OPTIONAL_STRING);
}
packetWrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
packetWrapper.passthrough(Types.VAR_INT);
packetWrapper.passthrough(Types.VAR_INT);
packetWrapper.passthrough(Types.OPTIONAL_COMPONENT);
@@ -20,6 +20,7 @@ package com.viaversion.viabackwards.protocol.v1_14_1to1_14;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.protocol.v1_14_1to1_14.rewriter.EntityPacketRewriter1_14_1;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_15;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;
@@ -41,6 +42,7 @@ public class Protocol1_14_1To1_14 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_15.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
}
@Override
@@ -23,7 +23,6 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
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.version.Types1_14;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;
@@ -41,7 +40,8 @@ public class EntityPacketRewriter1_14_1 extends LegacyEntityRewriter<Clientbound
registerTracker(ClientboundPackets1_14.ADD_GLOBAL_ENTITY, EntityTypes1_14.LIGHTNING_BOLT);
registerTracker(ClientboundPackets1_14.ADD_PAINTING, EntityTypes1_14.PAINTING);
registerTracker(ClientboundPackets1_14.ADD_PLAYER, EntityTypes1_14.PLAYER);
registerTracker(ClientboundPackets1_14.LOGIN, EntityTypes1_14.PLAYER, Types.INT);
registerJoinGame(ClientboundPackets1_14.LOGIN, EntityTypes1_14.PLAYER);
registerRespawn(ClientboundPackets1_14.RESPAWN);
registerRemoveEntities(ClientboundPackets1_14.REMOVE_ENTITIES);
protocol.registerClientbound(ClientboundPackets1_14.ADD_ENTITY, new PacketHandlers() {
@@ -20,6 +20,7 @@ package com.viaversion.viabackwards.protocol.v1_14to1_13_2;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.data.BackwardsMappingData1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.BlockItemPacketRewriter1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.CommandRewriter1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.EntityPacketRewriter1_14;
@@ -34,16 +35,15 @@ import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.Protocol1_13_2To1_14;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_13, ServerboundPackets1_14, ServerboundPackets1_13> {
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.14", "1.13.2", Protocol1_13_2To1_14.class);
public static final BackwardsMappingData1_14 MAPPINGS = new BackwardsMappingData1_14();
private final EntityPacketRewriter1_14 entityRewriter = new EntityPacketRewriter1_14(this);
private final BlockItemPacketRewriter1_14 itemRewriter = new BlockItemPacketRewriter1_14(this);
private final ParticleRewriter<ClientboundPackets1_14> particleRewriter = new ParticleRewriter<>(this);
@@ -0,0 +1,42 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_14to1_13_2.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viaversion.protocols.v1_13_2to1_14.Protocol1_13_2To1_14;
public final class BackwardsMappingData1_14 extends BackwardsMappingData {
public BackwardsMappingData1_14() {
super("1.14", "1.13.2", Protocol1_13_2To1_14.class);
}
@Override
protected void loadExtras(final CompoundTag data) {
super.loadExtras(data);
if (ViaBackwards.getConfig().scaffoldingToWater()) {
for (int i = 11099; i <= 11130; i++) {
blockStateMappings.setNewId(i, 49);
}
}
}
}
@@ -106,6 +106,7 @@ public class EntityPacketRewriter1_15 extends EntityRewriter<ClientboundPackets1
map(Types.INT); // 0 - Entity ID
map(Types.UNSIGNED_BYTE); // 1 - Gamemode
map(Types.INT); // 2 - Dimension
handler(getDimensionHandler(1));
read(Types.LONG); // Seed
@@ -18,6 +18,10 @@
package com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter;
import com.google.common.collect.Sets;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;
@@ -29,10 +33,6 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_16;
import com.viaversion.viaversion.protocols.v1_15_2to1_16.data.DimensionRegistries1_16;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.TagUtil;
@@ -95,6 +95,10 @@ public class EntityPacketRewriter1_16_2 extends EntityRewriter<ClientboundPacket
wrapper.write(Types.STRING, getDimensionFromData(dimensionData));
});
map(Types.STRING); // Dimension
handler(wrapper -> {
final String world = wrapper.get(Types.STRING, 1);
trackWorld(wrapper.user(), world);
});
map(Types.LONG); // Seed
handler(wrapper -> {
int maxPlayers = wrapper.read(Types.VAR_INT);
@@ -109,7 +113,8 @@ public class EntityPacketRewriter1_16_2 extends EntityRewriter<ClientboundPacket
CompoundTag dimensionData = wrapper.read(Types.NAMED_COMPOUND_TAG);
wrapper.write(Types.STRING, getDimensionFromData(dimensionData));
tracker(wrapper.user()).clearEntities();
final String world = wrapper.passthrough(Types.STRING);
trackWorld(wrapper.user(), world);
});
}
@@ -230,12 +230,7 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
wrapper.passthrough(Types.UUID);
if (action == 0) { // Add
wrapper.passthrough(Types.STRING);
int properties = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Types.STRING);
wrapper.passthrough(Types.STRING);
wrapper.passthrough(Types.OPTIONAL_STRING);
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
wrapper.passthrough(Types.VAR_INT);
wrapper.passthrough(Types.VAR_INT);
// Display Name
@@ -131,14 +131,7 @@ public final class EntityPacketRewriter1_19_3 extends EntityRewriter<Clientbound
for (int i = 0; i < entries; i++) {
playerInfoPacket.write(Types.UUID, wrapper.read(Types.UUID));
playerInfoPacket.write(Types.STRING, wrapper.read(Types.STRING)); // Player Name
final int properties = wrapper.read(Types.VAR_INT);
playerInfoPacket.write(Types.VAR_INT, properties);
for (int j = 0; j < properties; j++) {
playerInfoPacket.write(Types.STRING, wrapper.read(Types.STRING)); // Name
playerInfoPacket.write(Types.STRING, wrapper.read(Types.STRING)); // Value
playerInfoPacket.write(Types.OPTIONAL_STRING, wrapper.read(Types.OPTIONAL_STRING)); // Signature
}
playerInfoPacket.write(Types.PROFILE_PROPERTY_ARRAY, wrapper.read(Types.PROFILE_PROPERTY_ARRAY));
// Now check for the other parts individually and add dummy values if not present
final ProfileKey profileKey;
@@ -20,6 +20,7 @@ package com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.api.entities.storage.EntityPositionHandler;
import com.viaversion.viabackwards.api.entities.storage.EntityReplacement;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
@@ -70,6 +71,13 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
final int entityId = wrapper.get(Types.VAR_INT, 0);
final int entityType = wrapper.get(Types.VAR_INT, 1);
if (!ViaBackwards.getConfig().mapDisplayEntities()) {
if (entityType == EntityTypes1_19_4.BLOCK_DISPLAY.getId() || entityType == EntityTypes1_19_4.ITEM_DISPLAY.getId() || entityType == EntityTypes1_19_4.TEXT_DISPLAY.getId()) {
wrapper.cancel();
return;
}
}
// First track (and remap) entity, then put storage for block display entity
getSpawnTrackerWithDataHandler1_19(EntityTypes1_19_4.FALLING_BLOCK).handle(wrapper);
if (entityType != EntityTypes1_19_4.BLOCK_DISPLAY.getId()) {
@@ -296,14 +296,7 @@ public final class Protocol1_19To1_18_2 extends BackwardsProtocol<ClientboundPac
public void register() {
map(Types.UUID); // UUID
map(Types.STRING); // Name
handler(wrapper -> {
final int properties = wrapper.read(Types.VAR_INT);
for (int i = 0; i < properties; i++) {
wrapper.read(Types.STRING); // Name
wrapper.read(Types.STRING); // Value
wrapper.read(Types.OPTIONAL_STRING); // Optional signature
}
});
read(Types.PROFILE_PROPERTY_ARRAY);
}
});
@@ -17,21 +17,19 @@
*/
package com.viaversion.viabackwards.protocol.v1_19to1_18_2.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.viaversion.protocols.v1_18_2to1_19.Protocol1_18_2To1_19;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BackwardsMappingData1_19 extends BackwardsMappingData {
private final boolean sculkShriekerToCryingObsidian = ViaBackwards.getConfig().sculkShriekerToCryingObsidian();
private final Int2ObjectMap<CompoundTag> defaultChatTypes = new Int2ObjectOpenHashMap<>();
public BackwardsMappingData1_19() {
@@ -40,15 +38,15 @@ public final class BackwardsMappingData1_19 extends BackwardsMappingData {
@Override
protected void loadExtras(final CompoundTag data) {
super.loadExtras(data);
if (sculkShriekerToCryingObsidian) {
blockMappings.setNewId(850, 750);
if (ViaBackwards.getConfig().sculkShriekerToCryingObsidian()) {
for (int i = 18900; i <= 18907; i++) {
blockStateMappings.setNewId(i, 16082);
}
itemMappings.setNewId(329, 1065);
}
super.loadExtras(data);
final ListTag<CompoundTag> chatTypes = BackwardsMappingDataLoader.INSTANCE.loadNBT("chat-types-1.19.1.nbt").getListTag("values", CompoundTag.class);
for (final CompoundTag chatType : chatTypes) {
final NumberTag idTag = chatType.getNumberTag("id");
@@ -56,14 +54,6 @@ public final class BackwardsMappingData1_19 extends BackwardsMappingData {
}
}
@Override
public int getNewItemId(final int id) {
if (sculkShriekerToCryingObsidian && id == 329) {
return 1065;
}
return super.getNewItemId(id);
}
public @Nullable CompoundTag chatType(final int id) {
return defaultChatTypes.get(id);
}
@@ -190,7 +190,7 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
}
}
if (!found) {
throw new IllegalStateException("Could not find dimension " + dimensionKey + " in dimension registry");
throw new IllegalArgumentException("Could not find dimension " + dimensionKey + " in dimension registry");
}
// Add biome category and track biomes
@@ -268,14 +268,7 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
wrapper.passthrough(Types.UUID); // UUID
if (action == 0) { // Add player
wrapper.passthrough(Types.STRING); // Player Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
wrapper.passthrough(Types.VAR_INT); // Gamemode
wrapper.passthrough(Types.VAR_INT); // Ping
wrapper.passthrough(Types.OPTIONAL_COMPONENT); // Display name
@@ -291,13 +291,7 @@ public final class Protocol1_20_3To1_20_2 extends BackwardsProtocol<ClientboundP
wrapper.passthrough(Types.UUID);
if (actions.get(0)) {
wrapper.passthrough(Types.STRING); // Player Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
}
if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {
wrapper.passthrough(Types.UUID); // Session UUID
@@ -17,20 +17,30 @@
*/
package com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;
import com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.ParticleMappings;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_20_3;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;
import com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPacket1_20_2;
import com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPackets1_20_2;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.StringUtil;
import java.util.logging.Level;
public final class BlockItemPacketRewriter1_20_3 extends BackwardsItemRewriter<ClientboundPacket1_20_3, ServerboundPacket1_20_2, Protocol1_20_3To1_20_2> {
@@ -45,8 +55,8 @@ public final class BlockItemPacketRewriter1_20_3 extends BackwardsItemRewriter<C
blockRewriter.registerBlockUpdate(ClientboundPackets1_20_3.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_20_3.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent(ClientboundPackets1_20_3.LEVEL_EVENT, 1010, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_20_3.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_20_3.BLOCK_ENTITY_DATA);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_20_3.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new, this::updateBlockEntity);
blockRewriter.registerBlockEntityData(ClientboundPackets1_20_3.BLOCK_ENTITY_DATA, this::updateBlockEntity);
registerCooldown(ClientboundPackets1_20_3.COOLDOWN);
registerSetContent1_17_1(ClientboundPackets1_20_3.CONTAINER_SET_CONTENT);
@@ -156,4 +166,25 @@ public final class BlockItemPacketRewriter1_20_3 extends BackwardsItemRewriter<C
wrapper.read(Types.OPTIONAL_FLOAT); // Sound range
});
}
private void updateBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {
final CompoundTag tag = blockEntity.tag();
if (tag == null) {
return;
}
final StringTag customName = tag.getStringTag("CustomName");
if (customName == null) {
return;
}
try {
final JsonElement updatedComponent = ComponentUtil.convertJson(customName.getValue(), SerializerVersion.V1_20_3, SerializerVersion.V1_19_4);
customName.setValue(updatedComponent.toString());
} catch (final Exception e) {
if (!Via.getConfig().isSuppressTextComponentConversionWarnings()) {
protocol.getLogger().log(Level.SEVERE, "Error during custom name conversion: " + StringUtil.forLogging(customName.getValue()), e);
}
}
}
}
@@ -40,6 +40,8 @@ import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
@@ -112,6 +114,7 @@ public final class Protocol1_20_5To1_20_3 extends BackwardsProtocol<ClientboundP
translatableRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_20_5.PLAYER_COMBAT_KILL);
translatableRewriter.registerComponentPacket(ClientboundPackets1_20_5.SYSTEM_CHAT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_20_5.DISGUISED_CHAT);
translatableRewriter.registerPlayerChat(ClientboundPackets1_20_5.PLAYER_CHAT, Types.VAR_INT);
translatableRewriter.registerPlayerInfoUpdate1_20_3(ClientboundPackets1_20_5.PLAYER_INFO_UPDATE);
translatableRewriter.registerPing();
@@ -164,14 +167,7 @@ public final class Protocol1_20_5To1_20_3 extends BackwardsProtocol<ClientboundP
registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {
wrapper.passthrough(Types.UUID); // UUID
wrapper.passthrough(Types.STRING); // Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < properties; i++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
wrapper.read(Types.BOOLEAN); // Strict error handling
});
@@ -267,6 +263,16 @@ public final class Protocol1_20_5To1_20_3 extends BackwardsProtocol<ClientboundP
return tagRewriter;
}
@Override
public VersionedTypesHolder types() {
return VersionedTypes.V1_20_5;
}
@Override
public VersionedTypesHolder mappedTypes() {
return new Types1_20_3();
}
@Override
protected PacketTypesProvider<ClientboundPacket1_20_5, ClientboundPacket1_20_3, ServerboundPacket1_20_5, ServerboundPacket1_20_3> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -0,0 +1,94 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_20_5to1_20_3;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.data.version.VersionedStructuredDataKeys;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.AbstractEntityDataTypes;
import com.viaversion.viaversion.api.minecraft.item.Item;
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.item.StructuredDataType;
import com.viaversion.viaversion.api.type.types.misc.ParticleType;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import java.util.List;
final class Types1_20_3 implements VersionedTypesHolder {
@Override
public Type<Item> item() {
return Types.ITEM1_20_2;
}
@Override
public Type<Item[]> itemArray() {
return Types.ITEM1_20_2_ARRAY;
}
@Override
public Type<Item> itemCost() {
return null;
}
@Override
public Type<Item> optionalItemCost() {
return null;
}
@Override
public Type<Item> lengthPrefixedItem() {
return null;
}
@Override
public StructuredDataType structuredData() {
return null;
}
@Override
public Type<StructuredData<?>[]> structuredDataArray() {
return null;
}
@Override
public VersionedStructuredDataKeys structuredDataKeys() {
return null;
}
@Override
public ParticleType particle() {
return com.viaversion.viaversion.api.type.types.version.Types1_20_3.PARTICLE;
}
@Override
public ArrayType<Particle> particles() {
return null;
}
@Override
public AbstractEntityDataTypes entityDataTypes() {
return com.viaversion.viaversion.api.type.types.version.Types1_20_3.ENTITY_DATA_TYPES;
}
@Override
public Type<List<EntityData>> entityDataList() {
return com.viaversion.viaversion.api.type.types.version.Types1_20_3.ENTITY_DATA_LIST;
}
}
@@ -27,6 +27,7 @@ import com.viaversion.viabackwards.api.rewriters.StructuredEnchantmentRewriter;
import com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.item.ItemHasher;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
@@ -39,7 +40,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.Fireworks;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_20_3;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPacket1_20_3;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPackets1_20_3;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;
@@ -59,7 +60,7 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
private final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this);
public BlockItemPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) {
super(protocol, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY, Types.ITEM1_20_2, Types.ITEM1_20_2_ARRAY);
super(protocol);
enchantmentRewriter.setRewriteIds(false); // Let VV handle it
}
@@ -106,7 +107,7 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
wrapper.passthrough(Types.INT); // Particle Count
// Move it to the beginning, move out arguments here
final Particle particle = wrapper.read(Types1_20_5.PARTICLE);
final Particle particle = wrapper.read(VersionedTypes.V1_20_5.particle());
protocol.getParticleRewriter().rewriteParticle(wrapper.user(), particle);
if (particle.id() == protocol.getMappingData().getParticleMappings().mappedId("entity_effect")) {
// Remove color argument
@@ -141,8 +142,8 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
wrapper.passthrough(Types.FLOAT); // Knockback Z
wrapper.passthrough(Types.VAR_INT); // Block interaction type
final Particle smallExplosionParticle = wrapper.passthroughAndMap(Types1_20_5.PARTICLE, Types1_20_3.PARTICLE);
final Particle largeExplosionParticle = wrapper.passthroughAndMap(Types1_20_5.PARTICLE, Types1_20_3.PARTICLE);
final Particle smallExplosionParticle = wrapper.passthroughAndMap(VersionedTypes.V1_20_5.particle(), Types1_20_3.PARTICLE);
final Particle largeExplosionParticle = wrapper.passthroughAndMap(VersionedTypes.V1_20_5.particle(), Types1_20_3.PARTICLE);
protocol.getParticleRewriter().rewriteParticle(wrapper.user(), smallExplosionParticle);
protocol.getParticleRewriter().rewriteParticle(wrapper.user(), largeExplosionParticle);
@@ -163,14 +164,14 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
wrapper.passthrough(Types.VAR_INT); // Container id
final int size = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < size; i++) {
final Item input = handleItemToClient(wrapper.user(), wrapper.read(Types1_20_5.ITEM_COST));
final Item input = handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.itemCost()));
cleanInput(input);
wrapper.write(Types.ITEM1_20_2, input);
final Item result = handleItemToClient(wrapper.user(), wrapper.read(Types1_20_5.ITEM));
final Item result = handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.item()));
wrapper.write(Types.ITEM1_20_2, result);
Item secondInput = wrapper.read(Types1_20_5.OPTIONAL_ITEM_COST);
Item secondInput = wrapper.read(VersionedTypes.V1_20_5.optionalItemCost());
if (secondInput != null) {
secondInput = handleItemToClient(wrapper.user(), secondInput);
cleanInput(secondInput);
@@ -360,8 +361,8 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
super.handleItemToClient(connection, item);
// Text components since we skip the usual rewrite method
updateComponent(connection, item, StructuredDataKey.ITEM_NAME, "item_name");
updateComponent(connection, item, StructuredDataKey.CUSTOM_NAME, "custom_name");
updateTextComponent(connection, item, StructuredDataKey.ITEM_NAME, "item_name");
updateTextComponent(connection, item, StructuredDataKey.CUSTOM_NAME, "custom_name");
final Tag[] lore = data.get(StructuredDataKey.LORE);
if (lore != null) {
for (final Tag tag : lore) {
@@ -391,9 +392,21 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
return oldItem;
}
// Items and data within components are handled in this protocol
@Override
protected void updateItemDataComponents(final UserConnection connection, final Item item, final boolean clientbound) {
// Items and data within components are handled in this protocol
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {
}
@Override
protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {
}
@Override
protected void handleRewritablesToClient(final UserConnection connection, final StructuredDataContainer container, @Nullable final ItemHasher itemHasher) {
}
@Override
protected void handleRewritablesToServer(final UserConnection connection, final StructuredDataContainer container) {
}
@Override
@@ -1,6 +1,6 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2024 ViaVersion and contributors
* 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
@@ -26,7 +26,7 @@ import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.SerializerVersion;
@@ -39,7 +39,7 @@ public final class ComponentRewriter1_20_5 extends JsonNBTComponentRewriter<Clie
public ComponentRewriter1_20_5(final BackwardsProtocol<ClientboundPacket1_20_5, ?, ?, ?> protocol) {
super(protocol, ReadType.NBT);
vvRewriter = new com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter.ComponentRewriter1_20_5<>(protocol, Types1_20_5.STRUCTURED_DATA);
vvRewriter = new com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter.ComponentRewriter1_20_5<>(protocol, VersionedTypes.V1_20_5.structuredData());
}
@Override
@@ -40,7 +40,7 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_20_3;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.data.entity.DimensionDataImpl;
import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.Protocol1_20_3To1_20_5;
@@ -67,7 +67,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_20_5.ADD_ENTITY, EntityTypes1_20_5.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_20_5.SET_ENTITY_DATA, Types1_20_5.ENTITY_DATA_LIST, Types1_20_3.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_20_5.SET_ENTITY_DATA, VersionedTypes.V1_20_5.entityDataList(), Types1_20_3.ENTITY_DATA_LIST);
registerRemoveEntities(ClientboundPackets1_20_5.REMOVE_ENTITIES);
protocol.registerClientbound(ClientboundPackets1_20_5.SET_EQUIPMENT, wrapper -> {
@@ -76,7 +76,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
byte slot;
do {
slot = wrapper.read(Types.BYTE);
final Item item = protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.read(Types1_20_5.ITEM));
final Item item = protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.item()));
final int rawSlot = slot & 0x7F;
if (rawSlot == 6) {
@@ -408,7 +408,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
protected void registerRewrites() {
filter().handler((event, data) -> {
final int typeId = data.dataType().typeId();
if (typeId == Types1_20_5.ENTITY_DATA_TYPES.particlesType.typeId()) {
if (typeId == VersionedTypes.V1_20_5.entityDataTypes.particlesType.typeId()) {
final Particle[] particles = data.value();
int color = 0;
for (final Particle particle : particles) {
@@ -419,19 +419,19 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
}
data.setTypeAndValue(Types1_20_3.ENTITY_DATA_TYPES.varIntType, removeAlpha(color));
return;
} else if (typeId == Types1_20_5.ENTITY_DATA_TYPES.armadilloState.typeId() || typeId == Types1_20_5.ENTITY_DATA_TYPES.wolfVariantType.typeId()) {
} else if (typeId == VersionedTypes.V1_20_5.entityDataTypes.armadilloState.typeId() || typeId == VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType.typeId()) {
event.cancel();
return;
}
int id = typeId;
if (typeId >= Types1_20_5.ENTITY_DATA_TYPES.armadilloState.typeId()) {
if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.armadilloState.typeId()) {
id--;
}
if (typeId >= Types1_20_5.ENTITY_DATA_TYPES.wolfVariantType.typeId()) {
if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType.typeId()) {
id--;
}
if (typeId >= Types1_20_5.ENTITY_DATA_TYPES.particlesType.typeId()) {
if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.particlesType.typeId()) {
id--;
}
data.setDataType(Types1_20_3.ENTITY_DATA_TYPES.byId(id));
@@ -31,11 +31,14 @@ import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage;
import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.RecipeStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
@@ -95,6 +98,7 @@ public final class Protocol1_21_2To1_21 extends BackwardsProtocol<ClientboundPac
translatableRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_2.PLAYER_COMBAT_KILL);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_2.SYSTEM_CHAT);
translatableRewriter.registerDisguisedChat(ClientboundPackets1_21_2.DISGUISED_CHAT);
translatableRewriter.registerPlayerChat(ClientboundPackets1_21_2.PLAYER_CHAT, ChatType.TYPE);
translatableRewriter.registerPing();
registerServerbound(ServerboundPackets1_20_5.CLIENT_INFORMATION, this::clientInformation);
@@ -110,14 +114,7 @@ public final class Protocol1_21_2To1_21 extends BackwardsProtocol<ClientboundPac
registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {
wrapper.passthrough(Types.UUID); // UUID
wrapper.passthrough(Types.STRING); // Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < properties; i++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
wrapper.write(Types.BOOLEAN, true); // Strict error handling. Always enabled for newer clients, so mimic that behavior
});
@@ -194,6 +191,16 @@ public final class Protocol1_21_2To1_21 extends BackwardsProtocol<ClientboundPac
return tagRewriter;
}
@Override
public VersionedTypesHolder types() {
return VersionedTypes.V1_21_2;
}
@Override
public VersionedTypesHolder mappedTypes() {
return VersionedTypes.V1_21;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21, ServerboundPacket1_21_2, ServerboundPacket1_20_5> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -19,7 +19,6 @@ package com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter;
import com.viaversion.nbt.tag.ByteTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.FloatTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.Tag;
@@ -38,6 +37,8 @@ import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2;
import com.viaversion.viaversion.api.minecraft.item.data.DeathProtection;
import com.viaversion.viaversion.api.minecraft.item.data.Enchantable;
import com.viaversion.viaversion.api.minecraft.item.data.Enchantments;
import com.viaversion.viaversion.api.minecraft.item.data.Equippable;
import com.viaversion.viaversion.api.minecraft.item.data.FoodProperties1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.Instrument1_21_2;
@@ -47,8 +48,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.UseCooldown;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
@@ -66,10 +66,7 @@ import static com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.BlockIt
public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_20_5, Protocol1_21_2To1_21> {
public BlockItemPacketRewriter1_21_2(final Protocol1_21_2To1_21 protocol) {
super(protocol,
Types1_21_2.ITEM, Types1_21_2.ITEM_ARRAY, Types1_21.ITEM, Types1_21.ITEM_ARRAY,
Types1_21_2.ITEM_COST, Types1_21_2.OPTIONAL_ITEM_COST, Types1_21.ITEM_COST, Types1_21.OPTIONAL_ITEM_COST
);
super(protocol);
}
@Override
@@ -214,11 +211,11 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
wrapper.write(Types.VAR_INT, 0); // Block interaction type
final Particle explosionParticle = wrapper.read(Types1_21.PARTICLE);
final Particle explosionParticle = wrapper.read(VersionedTypes.V1_21.particle());
protocol.getParticleRewriter().rewriteParticle(wrapper.user(), explosionParticle);
// As small and large explosion particle
wrapper.write(Types1_21_2.PARTICLE, explosionParticle);
wrapper.write(Types1_21_2.PARTICLE, explosionParticle);
wrapper.write(VersionedTypes.V1_21_2.particle(), explosionParticle);
wrapper.write(VersionedTypes.V1_21_2.particle(), explosionParticle);
new SoundRewriter<>(protocol).soundHolderHandler().handle(wrapper);
});
@@ -320,6 +317,21 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
updateItemData(item);
restoreInconvertibleData(item);
// Enchantments with level 0 are not supported anymore, if an older client for some reason sends it (for example
// using stored items in the hotbar lists), we need to remove it.
final Enchantments enchantments = data.get(StructuredDataKey.ENCHANTMENTS1_20_5);
if (enchantments != null) {
// The enchantments might be used to create a glint, set glint override to true if the enchantments are empty after filtering
final boolean removed = enchantments.enchantments().values().removeIf(level -> level == 0);
if (removed && enchantments.size() == 0) {
data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);
}
}
final Enchantments storedEnchantments = data.get(StructuredDataKey.STORED_ENCHANTMENTS1_20_5);
if (storedEnchantments != null) {
storedEnchantments.enchantments().values().removeIf(level -> level == 0);
}
return item;
}
@@ -340,9 +352,9 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
backupTag.put("repairable", holderSetToTag(repairable));
}
final Integer enchantable = data.get(StructuredDataKey.ENCHANTABLE);
final Enchantable enchantable = data.get(StructuredDataKey.ENCHANTABLE);
if (enchantable != null) {
backupTag.putInt("enchantable", enchantable);
backupTag.putInt("enchantable", enchantable.value());
}
final UseCooldown useCooldown = data.get(StructuredDataKey.USE_COOLDOWN);
@@ -355,9 +367,9 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
backupTag.put("use_cooldown", tag);
}
final String itemModel = data.get(StructuredDataKey.ITEM_MODEL);
final Key itemModel = data.get(StructuredDataKey.ITEM_MODEL);
if (itemModel != null) {
backupTag.putString("item_model", itemModel);
backupTag.putString("item_model", itemModel.original());
}
final Equippable equippable = data.get(StructuredDataKey.EQUIPPABLE1_21_2);
@@ -365,7 +377,7 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
final CompoundTag tag = new CompoundTag();
tag.putInt("equipment_slot", equippable.equipmentSlot());
convertSoundEventHolder(tag, equippable.soundEvent());
saveSoundEventHolder(tag, equippable.soundEvent());
final String model = equippable.model();
if (model != null) {
tag.putString("model", model);
@@ -389,9 +401,9 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
backupTag.putBoolean("glider", true);
}
final String tooltipStyle = data.get(StructuredDataKey.TOOLTIP_STYLE);
final Key tooltipStyle = data.get(StructuredDataKey.TOOLTIP_STYLE);
if (tooltipStyle != null) {
backupTag.putString("tooltip_style", tooltipStyle);
backupTag.putString("tooltip_style", tooltipStyle.original());
}
final DeathProtection deathProtection = data.get(StructuredDataKey.DEATH_PROTECTION);
@@ -437,7 +449,7 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
} else if (effect.type() == Types.SOUND_EVENT && effect.value() instanceof Holder sound) {
tag.putString("type", "play_sound");
convertSoundEventHolder(tag, sound);
saveSoundEventHolder(tag, sound);
}
}
@@ -454,15 +466,6 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
}
}
private void convertSoundEventHolder(final CompoundTag tag, final Holder<SoundEvent> holder) {
tag.put("sound_event", holderToTag(holder, (event, soundEventTag) -> {
soundEventTag.putString("identifier", event.identifier());
if (event.fixedRange() != null) {
soundEventTag.putFloat("fixed_range", event.fixedRange());
}
}));
}
private Consumable1_21_2.ConsumeEffect<?> convertConsumableEffect(final CompoundTag tag) {
final int id = tag.getInt("id");
final String type = tag.getString("type");
@@ -486,7 +489,7 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
final float probability = tag.getFloat("probability");
return new Consumable1_21_2.ConsumeEffect<>(id, Types.FLOAT, probability);
} else if ("play_sound".equals(type)) {
final Holder<SoundEvent> sound = convertSoundEventHolder(tag);
final Holder<SoundEvent> sound = restoreSoundEventHolder(tag);
return new Consumable1_21_2.ConsumeEffect<>(id, Types.SOUND_EVENT, sound);
}
return null;
@@ -502,14 +505,6 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
return new PotionEffectData(amplifier, duration, ambient, showParticles, showIcon, hiddenEffect != null ? convertPotionEffectData(hiddenEffect) : null);
}
private Holder<SoundEvent> convertSoundEventHolder(final CompoundTag tag) {
return restoreHolder(tag, "sound_event", soundEventTag -> {
final String identifier = soundEventTag.getString("identifier");
final FloatTag fixedRange = soundEventTag.getFloatTag("fixed_range");
return new SoundEvent(identifier, fixedRange != null ? fixedRange.asFloat() : null);
});
}
private void restoreInconvertibleData(final Item item) {
final StructuredDataContainer data = item.dataContainer();
final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);
@@ -532,7 +527,7 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
final IntTag enchantable = backupTag.getIntTag("enchantable");
if (enchantable != null) {
data.set(StructuredDataKey.ENCHANTABLE, enchantable.asInt());
data.set(StructuredDataKey.ENCHANTABLE, new Enchantable(enchantable.asInt()));
}
final CompoundTag useCooldown = backupTag.getCompoundTag("use_cooldown");
@@ -544,13 +539,13 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
final String itemModel = backupTag.getString("item_model");
if (itemModel != null) {
data.set(StructuredDataKey.ITEM_MODEL, itemModel);
data.set(StructuredDataKey.ITEM_MODEL, Key.of(itemModel));
}
final CompoundTag equippable = backupTag.getCompoundTag("equippable");
if (equippable != null) {
final int equipmentSlot = equippable.getInt("equipment_slot");
final Holder<SoundEvent> soundEvent = convertSoundEventHolder(equippable);
final Holder<SoundEvent> soundEvent = restoreSoundEventHolder(equippable);
final String model = equippable.getString("model");
final String cameraOverlay = equippable.getString("camera_overlay");
final HolderSet allowedEntities = equippable.contains("allowed_entities") ? restoreHolderSet(equippable, "allowed_entities") : null;
@@ -567,7 +562,7 @@ public final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItem
final String tooltipStyle = backupTag.getString("tooltip_style");
if (tooltipStyle != null) {
data.set(StructuredDataKey.TOOLTIP_STYLE, tooltipStyle);
data.set(StructuredDataKey.TOOLTIP_STYLE, Key.of(tooltipStyle));
}
final ListTag<CompoundTag> deathProtection = backupTag.getListTag("death_protection", CompoundTag.class);
@@ -36,8 +36,7 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
@@ -66,12 +65,12 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
private boolean warned = ViaBackwards.getConfig().suppressEmulationWarnings();
public EntityPacketRewriter1_21_2(final Protocol1_21_2To1_21 protocol) {
super(protocol, Types1_21.ENTITY_DATA_TYPES.optionalComponentType, Types1_21.ENTITY_DATA_TYPES.booleanType);
super(protocol, VersionedTypes.V1_21.entityDataTypes.optionalComponentType, VersionedTypes.V1_21.entityDataTypes.booleanType);
}
@Override
public void registerPackets() {
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, Types1_21_2.ENTITY_DATA_LIST, Types1_21.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
protocol.registerClientbound(ClientboundPackets1_21_2.ADD_ENTITY, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
@@ -94,11 +93,11 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
// Add boat type to entity data
final List<EntityData> data = new ArrayList<>();
final int boatType = type.isOrHasParent(EntityTypes1_21_2.ABSTRACT_CHEST_BOAT) ? chestBoatTypeFromEntityType(type) : boatTypeFromEntityType(type);
data.add(new EntityData(11, Types1_21.ENTITY_DATA_TYPES.varIntType, boatType));
data.add(new EntityData(11, VersionedTypes.V1_21.entityDataTypes.varIntType, boatType));
final PacketWrapper entityDataPacket = wrapper.create(ClientboundPackets1_21.SET_ENTITY_DATA);
entityDataPacket.write(Types.VAR_INT, entityId);
entityDataPacket.write(Types1_21.ENTITY_DATA_LIST, data);
entityDataPacket.write(VersionedTypes.V1_21.entityDataList, data);
entityDataPacket.send(Protocol1_21_2To1_21.class);
}
});
@@ -317,14 +316,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
// Player input no longer sets the sneaking state on the server
// Send the change separately if needed (= when in a vehicle and player commands aren't sent by the old client)
final PlayerStorage sneakingStorage = wrapper.user().get(PlayerStorage.class);
if (sneakingStorage.setSneaking(sneaking)) {
final PacketWrapper playerCommandPacket = wrapper.create(ServerboundPackets1_21_2.PLAYER_COMMAND);
playerCommandPacket.write(Types.VAR_INT, tracker(wrapper.user()).clientEntityId());
playerCommandPacket.write(Types.VAR_INT, sneaking ? 0 : 1); // Start/stop sneaking
playerCommandPacket.write(Types.VAR_INT, 0); // Data
playerCommandPacket.sendToServer(Protocol1_21_2To1_21.class);
}
sendSneakingPlayerCommand(wrapper, sneaking);
});
protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS, wrapper -> {
@@ -385,13 +377,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
wrapper.passthrough(Types.UUID);
if (actions.get(0)) {
wrapper.passthrough(Types.STRING); // Player Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
}
if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {
wrapper.passthrough(Types.UUID); // Session UUID
@@ -423,6 +409,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
final ClientVehicleStorage storage = wrapper.user().get(ClientVehicleStorage.class);
if (storage != null && vehicleId == storage.vehicleId()) {
wrapper.user().remove(ClientVehicleStorage.class);
sendSneakingPlayerCommand(wrapper, false);
}
final int clientEntityId = tracker(wrapper.user()).clientEntityId();
@@ -443,6 +430,7 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
for (final int entityId : entityIds) {
if (entityId == vehicleStorage.vehicleId()) {
wrapper.user().remove(ClientVehicleStorage.class);
sendSneakingPlayerCommand(wrapper, false);
break;
}
}
@@ -544,8 +532,8 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
explosionPacket.write(Types.FLOAT, (float) movementY);
explosionPacket.write(Types.FLOAT, (float) movementZ);
explosionPacket.write(Types.VAR_INT, 0); // Block interaction
explosionPacket.write(Types1_21.PARTICLE, new Particle(0)); // Small explosion
explosionPacket.write(Types1_21.PARTICLE, new Particle(0)); // Large explosion
explosionPacket.write(VersionedTypes.V1_21.particle(), new Particle(0)); // Small explosion
explosionPacket.write(VersionedTypes.V1_21.particle(), new Particle(0)); // Large explosion
explosionPacket.write(Types.SOUND_EVENT, Holder.of(new SoundEvent("", null))); // Explosion sound
explosionPacket.send(Protocol1_21_2To1_21.class);
@@ -568,6 +556,17 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
}
}
private void sendSneakingPlayerCommand(final PacketWrapper wrapper, final boolean sneaking) {
final PlayerStorage sneakingStorage = wrapper.user().get(PlayerStorage.class);
if (sneakingStorage.setSneaking(sneaking)) {
final PacketWrapper playerCommandPacket = wrapper.create(ServerboundPackets1_21_2.PLAYER_COMMAND);
playerCommandPacket.write(Types.VAR_INT, tracker(wrapper.user()).clientEntityId());
playerCommandPacket.write(Types.VAR_INT, sneaking ? 0 : 1); // Start/stop sneaking
playerCommandPacket.write(Types.VAR_INT, 0); // Data
playerCommandPacket.sendToServer(Protocol1_21_2To1_21.class);
}
}
private int boatTypeFromEntityType(final EntityType type) {
if (type == EntityTypes1_21_2.OAK_BOAT) {
return 0;
@@ -623,15 +622,15 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter<Clientbound
@Override
protected void registerRewrites() {
filter().mapDataType(Types1_21.ENTITY_DATA_TYPES::byId);
filter().mapDataType(VersionedTypes.V1_21.entityDataTypes::byId);
registerEntityDataTypeHandler1_20_3(
Types1_21.ENTITY_DATA_TYPES.itemType,
Types1_21.ENTITY_DATA_TYPES.blockStateType,
Types1_21.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21.ENTITY_DATA_TYPES.particleType,
Types1_21.ENTITY_DATA_TYPES.particlesType,
Types1_21.ENTITY_DATA_TYPES.componentType,
Types1_21.ENTITY_DATA_TYPES.optionalComponentType
VersionedTypes.V1_21.entityDataTypes.itemType,
VersionedTypes.V1_21.entityDataTypes.blockStateType,
VersionedTypes.V1_21.entityDataTypes.optionalBlockStateType,
VersionedTypes.V1_21.entityDataTypes.particleType,
VersionedTypes.V1_21.entityDataTypes.particlesType,
VersionedTypes.V1_21.entityDataTypes.componentType,
VersionedTypes.V1_21.entityDataTypes.optionalComponentType
);
registerBlockStateHandler(EntityTypes1_21_2.ABSTRACT_MINECART, 11);
@@ -21,15 +21,13 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
public final class ParticleRewriter1_21_2 extends ParticleRewriter<ClientboundPacket1_21_2> {
public ParticleRewriter1_21_2(final Protocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {
super(protocol, Types1_21_2.PARTICLE, Types1_21.PARTICLE);
super(protocol);
}
@Override
@@ -26,7 +26,7 @@ import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
import java.util.ArrayList;
import java.util.Comparator;
@@ -77,11 +77,11 @@ public final class RecipeStorage implements StorableObject {
for (int i = 0; i < ingredient.length; i++) {
copy[i] = ingredient[i].copy();
}
wrapper.write(Types1_21_2.ITEM_ARRAY, copy);
wrapper.write(VersionedTypes.V1_21_2.itemArray(), copy);
}
void writeResult(final PacketWrapper wrapper, final Item result) {
wrapper.write(Types1_21_2.ITEM, result.copy());
wrapper.write(VersionedTypes.V1_21_2.item(), result.copy());
}
void writeCategory(final PacketWrapper wrapper) {
@@ -286,7 +286,7 @@ public final class RecipeStorage implements StorableObject {
yield new Item[]{new StructuredItem(rewriteItemId(id), 1)};
}
case 3 -> {
final Item item = wrapper.read(Types1_21_2.ITEM);
final Item item = wrapper.read(VersionedTypes.V1_21_2.item());
protocol.getItemRewriter().handleItemToClient(wrapper.user(), item);
if (item.isEmpty()) {
protocol.getLogger().warning("Empty item in recipe");
@@ -29,11 +29,12 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
@@ -91,6 +92,7 @@ public final class Protocol1_21_4To1_21_2 extends BackwardsProtocol<ClientboundP
translatableRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_2.PLAYER_COMBAT_KILL);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_2.SYSTEM_CHAT);
translatableRewriter.registerDisguisedChat(ClientboundPackets1_21_2.DISGUISED_CHAT);
translatableRewriter.registerPlayerChat(ClientboundPackets1_21_2.PLAYER_CHAT, ChatType.TYPE);
translatableRewriter.registerPing();
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21_2.EXPLODE);
@@ -105,7 +107,7 @@ public final class Protocol1_21_4To1_21_2 extends BackwardsProtocol<ClientboundP
wrapper.passthrough(Types.FLOAT); // Offset Z
wrapper.passthrough(Types.FLOAT); // Particle Data
wrapper.passthrough(Types.INT); // Particle Count
final Particle particle = wrapper.passthroughAndMap(Types1_21_4.PARTICLE, Types1_21_2.PARTICLE);
final Particle particle = wrapper.passthroughAndMap(VersionedTypes.V1_21_4.particle(), VersionedTypes.V1_21_2.particle());
particleRewriter.rewriteParticle(wrapper.user(), particle);
});
@@ -130,13 +132,7 @@ public final class Protocol1_21_4To1_21_2 extends BackwardsProtocol<ClientboundP
wrapper.passthrough(Types.UUID);
if (actions.get(0)) {
wrapper.passthrough(Types.STRING); // Player Name
final int properties = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < properties; j++) {
wrapper.passthrough(Types.STRING); // Name
wrapper.passthrough(Types.STRING); // Value
wrapper.passthrough(Types.OPTIONAL_STRING); // Signature
}
wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);
}
if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {
wrapper.passthrough(Types.UUID); // Session UUID
@@ -208,6 +204,16 @@ public final class Protocol1_21_4To1_21_2 extends BackwardsProtocol<ClientboundP
return tagRewriter;
}
@Override
public VersionedTypesHolder types() {
return VersionedTypes.V1_21_4;
}
@Override
public VersionedTypesHolder mappedTypes() {
return VersionedTypes.V1_21_2;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_2> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -32,8 +32,6 @@ import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;
@@ -47,10 +45,7 @@ import static com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.Block
public final class BlockItemPacketRewriter1_21_4 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_2, Protocol1_21_4To1_21_2> {
public BlockItemPacketRewriter1_21_4(final Protocol1_21_4To1_21_2 protocol) {
super(protocol,
Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY, Types1_21_2.ITEM, Types1_21_2.ITEM_ARRAY,
Types1_21_4.ITEM_COST, Types1_21_4.OPTIONAL_ITEM_COST, Types1_21_2.ITEM_COST, Types1_21_2.OPTIONAL_ITEM_COST
);
super(protocol);
}
@Override
@@ -27,8 +27,7 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
@@ -40,13 +39,13 @@ import com.viaversion.viaversion.util.Key;
public final class EntityPacketRewriter1_21_4 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_21_4To1_21_2> {
public EntityPacketRewriter1_21_4(final Protocol1_21_4To1_21_2 protocol) {
super(protocol, Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType, Types1_21_4.ENTITY_DATA_TYPES.booleanType);
super(protocol, VersionedTypes.V1_21_4.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_4.entityDataTypes.booleanType);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21_2.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA, Types1_21_4.ENTITY_DATA_LIST, Types1_21_2.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_21_2.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_2.REMOVE_ENTITIES);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol) {
@@ -147,15 +146,15 @@ public final class EntityPacketRewriter1_21_4 extends EntityRewriter<Clientbound
@Override
protected void registerRewrites() {
filter().mapDataType(Types1_21_2.ENTITY_DATA_TYPES::byId);
filter().mapDataType(VersionedTypes.V1_21_2.entityDataTypes::byId);
registerEntityDataTypeHandler1_20_3(
Types1_21_2.ENTITY_DATA_TYPES.itemType,
Types1_21_2.ENTITY_DATA_TYPES.blockStateType,
Types1_21_2.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21_2.ENTITY_DATA_TYPES.particleType,
Types1_21_2.ENTITY_DATA_TYPES.particlesType,
Types1_21_2.ENTITY_DATA_TYPES.componentType,
Types1_21_2.ENTITY_DATA_TYPES.optionalComponentType
VersionedTypes.V1_21_2.entityDataTypes.itemType,
VersionedTypes.V1_21_2.entityDataTypes.blockStateType,
VersionedTypes.V1_21_2.entityDataTypes.optionalBlockStateType,
VersionedTypes.V1_21_2.entityDataTypes.particleType,
VersionedTypes.V1_21_2.entityDataTypes.particlesType,
VersionedTypes.V1_21_2.entityDataTypes.componentType,
VersionedTypes.V1_21_2.entityDataTypes.optionalComponentType
);
registerBlockStateHandler(EntityTypes1_21_4.ABSTRACT_MINECART, 11);
@@ -168,7 +167,7 @@ public final class EntityPacketRewriter1_21_4 extends EntityRewriter<Clientbound
case 2 -> "large";
default -> "medium";
};
data.setTypeAndValue(Types1_21_4.ENTITY_DATA_TYPES.stringType, type);
data.setTypeAndValue(VersionedTypes.V1_21_4.entityDataTypes.stringType, type);
});
}
@@ -20,15 +20,13 @@ package com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.types.version.Types1_21_2;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
public final class ParticleRewriter1_21_4 extends ParticleRewriter<ClientboundPacket1_21_2> {
public ParticleRewriter1_21_4(final Protocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {
super(protocol, Types1_21_4.PARTICLE, Types1_21_2.PARTICLE);
super(protocol);
}
@Override
@@ -23,16 +23,21 @@ import com.viaversion.viabackwards.api.rewriters.SoundRewriter;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.BlockItemPacketRewriter1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.ComponentRewriter1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.EntityPacketRewriter1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HashedItemConverterStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_2;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;
import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.Types1_21_5;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
@@ -58,7 +63,7 @@ public final class Protocol1_21_5To1_21_4 extends BackwardsProtocol<ClientboundP
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.21.5", "1.21.4", Protocol1_21_4To1_21_5.class);
private final EntityPacketRewriter1_21_5 entityRewriter = new EntityPacketRewriter1_21_5(this);
private final BlockItemPacketRewriter1_21_5 itemRewriter = new BlockItemPacketRewriter1_21_5(this);
private final ParticleRewriter<ClientboundPacket1_21_5> particleRewriter = new ParticleRewriter<>(this, Types1_21_5.PARTICLE, Types1_21_4.PARTICLE) {
private final ParticleRewriter<ClientboundPacket1_21_5> particleRewriter = new ParticleRewriter<>(this) {
@Override
public void rewriteParticle(final UserConnection connection, final Particle particle) {
if (particle.id() == MAPPINGS.getParticleMappings().id("tinted_leaves")) {
@@ -180,6 +185,7 @@ public final class Protocol1_21_5To1_21_4 extends BackwardsProtocol<ClientboundP
@Override
public void init(final UserConnection user) {
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_4.PLAYER));
user.put(new HashedItemConverterStorage(this));
}
@Override
@@ -212,6 +218,16 @@ public final class Protocol1_21_5To1_21_4 extends BackwardsProtocol<ClientboundP
return tagRewriter;
}
@Override
public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> types() {
return VersionedTypes.V1_21_5;
}
@Override
public Types1_20_5<StructuredDataKeys1_21_2, EntityDataTypes1_21_2> mappedTypes() {
return VersionedTypes.V1_21_4;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_5, ClientboundPacket1_21_2, ServerboundPacket1_21_5, ServerboundPacket1_21_4> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -17,10 +17,6 @@
*/
package com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;
import static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.downgradeItemData;
import static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.updateItemData;
import static com.viaversion.viaversion.util.MathUtil.ceilLog2;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.FloatTag;
import com.viaversion.nbt.tag.IntArrayTag;
@@ -30,6 +26,7 @@ import com.viaversion.nbt.tag.LongArrayTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HashedItemConverterStorage;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HorseDataStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.Mappings;
@@ -48,7 +45,6 @@ import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.item.HashedStructuredItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimMaterial;
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimPattern;
@@ -59,6 +55,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.Equippable;
import com.viaversion.viaversion.api.minecraft.item.data.ProvidesTrimMaterial;
import com.viaversion.viaversion.api.minecraft.item.data.ToolProperties;
import com.viaversion.viaversion.api.minecraft.item.data.TooltipDisplay;
import com.viaversion.viaversion.api.minecraft.item.data.TropicalFishPattern;
import com.viaversion.viaversion.api.minecraft.item.data.Weapon;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
@@ -68,8 +65,8 @@ import com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_19_4;
import com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_21_5;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.Types1_21_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.data.item.ItemHasherBase;
import com.viaversion.viaversion.libs.fastutil.ints.IntLinkedOpenHashSet;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;
import com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;
@@ -86,17 +83,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.downgradeItemData;
import static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.updateItemData;
import static com.viaversion.viaversion.util.MathUtil.ceilLog2;
public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_5, ServerboundPacket1_21_4, Protocol1_21_5To1_21_4> {
private static final int SIGN_BOCK_ENTITY_ID = 7;
private static final int HANGING_SIGN_BOCK_ENTITY_ID = 8;
private static final int SADDLE_EQUIPMENT_SLOT = 7;
private static final byte SADDLED_FLAG = 4;
static final byte SADDLED_FLAG = 4;
public BlockItemPacketRewriter1_21_5(final Protocol1_21_5To1_21_4 protocol) {
super(protocol,
Types1_21_5.ITEM, Types1_21_5.ITEM_ARRAY, Types1_21_4.ITEM, Types1_21_4.ITEM_ARRAY,
Types1_21_5.ITEM_COST, Types1_21_5.OPTIONAL_ITEM_COST, Types1_21_4.ITEM_COST, Types1_21_4.OPTIONAL_ITEM_COST
);
super(protocol);
}
@Override
@@ -157,7 +155,7 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
wrapper.passthrough(Types.SHORT); // Slot
final Item item = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));
wrapper.write(Types1_21_5.LENGTH_PREFIXED_ITEM, item);
wrapper.write(VersionedTypes.V1_21_5.lengthPrefixedItem(), item);
});
protocol.registerServerbound(ServerboundPackets1_21_4.CONTAINER_CLICK, wrapper -> {
@@ -167,16 +165,17 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
wrapper.passthrough(Types.BYTE); // Button
wrapper.passthrough(Types.VAR_INT); // Mode
// We have no way of actually reconstructing the items here
// Try our best to get a hashed item out of it - will be wrong for some data component types that don't have their conversion implemented
final HashedItemConverterStorage hashedItemConverter = wrapper.user().get(HashedItemConverterStorage.class);
final int affectedItems = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);
for (int i = 0; i < affectedItems; i++) {
wrapper.passthrough(Types.SHORT); // Slot
final Item item = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));
wrapper.write(Types.HASHED_ITEM, new HashedStructuredItem(item.identifier(), item.amount()));
wrapper.write(Types.HASHED_ITEM, ItemHasherBase.toHashedItem(hashedItemConverter.hasher(), item));
}
final Item carriedItem = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));
wrapper.write(Types.HASHED_ITEM, new HashedStructuredItem(carriedItem.identifier(), carriedItem.amount()));
wrapper.write(Types.HASHED_ITEM, ItemHasherBase.toHashedItem(hashedItemConverter.hasher(), carriedItem));
});
protocol.registerClientbound(ClientboundPackets1_21_5.SET_EQUIPMENT, wrapper -> {
@@ -188,7 +187,8 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
final int equipmentSlot = value & 0x7F;
if (equipmentSlot == SADDLE_EQUIPMENT_SLOT) {
if (trackedEntity != null && trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.ABSTRACT_HORSE)) {
sendSaddledEntityData(wrapper.user(), trackedEntity, entityId);
final Item item = wrapper.read(itemType());
sendSaddledEntityData(wrapper.user(), trackedEntity, entityId, item.identifier() == 800);
}
wrapper.cancel();
return;
@@ -269,22 +269,30 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
wrapper.write(Types.STRING, namespace + ":textures/" + path + ".png");
}
private void sendSaddledEntityData(final UserConnection connection, final TrackedEntity trackedEntity, final int entityId) {
private void sendSaddledEntityData(final UserConnection connection, final TrackedEntity trackedEntity, final int entityId, final boolean saddled) {
byte data = 0;
if (trackedEntity.hasData()) {
final HorseDataStorage horseDataStorage = trackedEntity.data().get(HorseDataStorage.class);
if (horseDataStorage != null) {
if (horseDataStorage.saddled() == saddled) {
return;
}
data = horseDataStorage.data();
}
}
trackedEntity.data().put(new HorseDataStorage(data, true));
data = (byte) (data | SADDLED_FLAG);
trackedEntity.data().put(new HorseDataStorage(data, saddled));
if (saddled) {
data = (byte) (data | SADDLED_FLAG);
}
final PacketWrapper entityDataPacket = PacketWrapper.create(ClientboundPackets1_21_2.SET_ENTITY_DATA, connection);
final List<EntityData> entityDataList = new ArrayList<>();
entityDataList.add(new EntityData(17, Types1_21_4.ENTITY_DATA_TYPES.byteType, data));
entityDataList.add(new EntityData(17, VersionedTypes.V1_21_4.entityDataTypes.byteType, data));
entityDataPacket.write(Types.VAR_INT, entityId);
entityDataPacket.write(Types1_21_4.ENTITY_DATA_LIST, entityDataList);
entityDataPacket.write(VersionedTypes.V1_21_4.entityDataList, entityDataList);
entityDataPacket.send(Protocol1_21_5To1_21_4.class);
}
@@ -323,12 +331,8 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
}
@Override
public Item handleItemToClient(final UserConnection connection, final Item item) {
super.handleItemToClient(connection, item);
final StructuredDataContainer dataContainer = item.dataContainer();
final CompoundTag backupTag = new CompoundTag();
protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {
super.backupInconvertibleData(connection, item, dataContainer, backupTag);
final ToolProperties toolProperties = dataContainer.get(StructuredDataKey.TOOL1_21_5);
if (toolProperties != null && toolProperties.canDestroyBlocksInCreative()) {
backupTag.putBoolean("tool", true);
@@ -390,10 +394,10 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
itemDamageTag.putString("bypassed_by", blocksAttacks.bypassedByTag());
}
if (blocksAttacks.blockSound() != null) {
blocksAttackTag.put("block_sound", holderToTag(blocksAttacks.blockSound(), this::soundToTag));
blocksAttackTag.put("block_sound", holderToTag(blocksAttacks.blockSound(), this::saveSoundEvent));
}
if (blocksAttacks.disableSound() != null) {
blocksAttackTag.put("disable_sound", holderToTag(blocksAttacks.disableSound(), this::soundToTag));
blocksAttackTag.put("disable_sound", holderToTag(blocksAttacks.disableSound(), this::saveSoundEvent));
}
}
@@ -402,13 +406,17 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
backupTag.put("hidden_components", new IntArrayTag(tooltipDisplay.hiddenComponents().toIntArray()));
}
saveStringData(StructuredDataKey.PROVIDES_BANNER_PATTERNS, dataContainer, backupTag);
final TropicalFishPattern tropicalFishPattern = dataContainer.get(StructuredDataKey.TROPICAL_FISH_PATTERN);
if (tropicalFishPattern != null) {
backupTag.putInt("tropical_fish_pattern", tropicalFishPattern.packedId());
}
saveKeyData(StructuredDataKey.PROVIDES_BANNER_PATTERNS, dataContainer, backupTag);
saveFloatData(StructuredDataKey.POTION_DURATION_SCALE, dataContainer, backupTag);
saveIntData(StructuredDataKey.VILLAGER_VARIANT, dataContainer, backupTag);
saveIntData(StructuredDataKey.FOX_VARIANT, dataContainer, backupTag);
saveIntData(StructuredDataKey.SALMON_SIZE, dataContainer, backupTag);
saveIntData(StructuredDataKey.PARROT_VARIANT, dataContainer, backupTag);
saveIntData(StructuredDataKey.TROPICAL_FISH_PATTERN, dataContainer, backupTag);
saveIntData(StructuredDataKey.TROPICAL_FISH_BASE_COLOR, dataContainer, backupTag);
saveIntData(StructuredDataKey.TROPICAL_FISH_PATTERN_COLOR, dataContainer, backupTag);
saveIntData(StructuredDataKey.MOOSHROOM_VARIANT, dataContainer, backupTag);
@@ -447,34 +455,25 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
}
});
saveHolderData(StructuredDataKey.BREAK_SOUND, dataContainer, backupTag, this::soundToTag);
if (!backupTag.isEmpty()) {
saveTag(createCustomTag(item), backupTag, "backup");
}
downgradeItemData(item);
return item;
}
private void soundToTag(final SoundEvent soundEvent, final CompoundTag tag) {
tag.putString("identifier", soundEvent.identifier());
if (soundEvent.fixedRange() != null) {
tag.putFloat("fixed_range", soundEvent.fixedRange());
}
saveHolderData(StructuredDataKey.BREAK_SOUND, dataContainer, backupTag, this::saveSoundEvent);
}
@Override
public Item handleItemToServer(final UserConnection connection, final Item item) {
super.handleItemToServer(connection, item);
restoreData(item.dataContainer());
updateItemData(item);
return item;
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer) {
super.handleItemDataComponentsToClient(connection, item, dataContainer);
downgradeItemData(item);
}
private void restoreData(final StructuredDataContainer data) {
final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);
if (customData == null || !(customData.remove(nbtTagName("backup")) instanceof final CompoundTag backupTag)) {
@Override
protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {
super.handleItemDataComponentsToServer(connection, item, container);
updateItemData(item);
}
@Override
protected void restoreBackupData(final Item item, final StructuredDataContainer data, final CompoundTag customData) {
super.restoreBackupData(item, data, customData);
if (!(customData.remove(nbtTagName("backup")) instanceof final CompoundTag backupTag)) {
return;
}
@@ -544,13 +543,17 @@ public final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItem
}
}
restoreStringData(StructuredDataKey.PROVIDES_BANNER_PATTERNS, data, backupTag);
final IntTag tropicalFishPattern = backupTag.getIntTag("tropical_fish_pattern");
if (tropicalFishPattern != null) {
data.set(StructuredDataKey.TROPICAL_FISH_PATTERN, new TropicalFishPattern(tropicalFishPattern.asInt()));
}
restoreKeyData(StructuredDataKey.PROVIDES_BANNER_PATTERNS, data, backupTag);
restoreFloatData(StructuredDataKey.POTION_DURATION_SCALE, data, backupTag);
restoreIntData(StructuredDataKey.VILLAGER_VARIANT, data, backupTag);
restoreIntData(StructuredDataKey.FOX_VARIANT, data, backupTag);
restoreIntData(StructuredDataKey.SALMON_SIZE, data, backupTag);
restoreIntData(StructuredDataKey.PARROT_VARIANT, data, backupTag);
restoreIntData(StructuredDataKey.TROPICAL_FISH_PATTERN, data, backupTag);
restoreIntData(StructuredDataKey.TROPICAL_FISH_BASE_COLOR, data, backupTag);
restoreIntData(StructuredDataKey.TROPICAL_FISH_PATTERN_COLOR, data, backupTag);
restoreIntData(StructuredDataKey.MOOSHROOM_VARIANT, data, backupTag);
@@ -32,6 +32,7 @@ import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.TagUtil;
import java.util.HashSet;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.viaversion.viaversion.util.TagUtil.getNamespacedCompoundTag;
import static com.viaversion.viaversion.util.TagUtil.getNamespacedCompoundTagList;
@@ -80,7 +81,14 @@ public final class ComponentRewriter1_21_5 extends NBTComponentRewriter<Clientbo
switch (action) {
case "open_url" -> clickEventTag.put("value", clickEventTag.getStringTag("url"));
case "change_page" -> clickEventTag.putString("value", Integer.toString(clickEventTag.getInt("page")));
case "run_command", "suggest_command" -> clickEventTag.put("value", clickEventTag.getStringTag("command"));
case "run_command" -> {
final StringTag command = clickEventTag.getStringTag("command");
if (command != null && !command.getValue().startsWith("/")) {
command.setValue("/" + command.getValue());
}
clickEventTag.put("value", command);
}
case "suggest_command" -> clickEventTag.put("value", clickEventTag.getStringTag("command"));
}
}
@@ -102,22 +110,19 @@ public final class ComponentRewriter1_21_5 extends NBTComponentRewriter<Clientbo
final CompoundTag componentsTag = hoverEventTag.getCompoundTag("components");
handleShowItem(connection, contents, componentsTag);
if (componentsTag != null) {
hoverEventTag.remove("components");
contents.put("components", componentsTag);
}
}
@Override
protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, @Nullable final CompoundTag componentsTag) {
super.handleShowItem(connection, itemTag, componentsTag);
if (componentsTag == null) {
return;
}
hoverEventTag.remove("components");
contents.put("components", componentsTag);
final CompoundTag useRemainder = TagUtil.getNamespacedCompoundTag(componentsTag, "use_remainder");
if (useRemainder != null) {
handleShowItem(connection, useRemainder);
}
handleContainerContents(connection, componentsTag);
handleItemArrayContents(connection, componentsTag, "bundle_contents");
handleItemArrayContents(connection, componentsTag, "charged_projectiles");
handleWrittenBookContents(connection, componentsTag);
insertUglyJson(componentsTag, connection);
updateDataComponents(componentsTag);
@@ -234,8 +239,9 @@ public final class ComponentRewriter1_21_5 extends NBTComponentRewriter<Clientbo
contents.put("name", nameTag);
}
if (hoverEventTag.remove("id") instanceof StringTag typeTag) {
contents.put("type", typeTag);
if (hoverEventTag.remove("id") instanceof StringTag idTag) {
idTag.setValue(protocol.getEntityRewriter().mappedEntityIdentifier(idTag.getValue()));
contents.put("type", idTag);
}
final Tag uuidTag = hoverEventTag.remove("uuid");
@@ -22,6 +22,7 @@ import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HashedItemConverterStorage;
import com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HorseDataStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
@@ -31,16 +32,19 @@ import com.viaversion.viaversion.api.minecraft.WolfVariant;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_2;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_21_4;
import com.viaversion.viaversion.api.type.types.version.Types1_21_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
import com.viaversion.viaversion.util.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -50,12 +54,12 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
"chicken_variant", "test_environment", "test_instance", "wolf_sound_variant");
public EntityPacketRewriter1_21_5(final Protocol1_21_5To1_21_4 protocol) {
super(protocol, Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType, Types1_21_4.ENTITY_DATA_TYPES.booleanType);
super(protocol, protocol.mappedTypes().entityDataTypes().optionalComponentType, protocol.mappedTypes().entityDataTypes().booleanType);
}
@Override
public void registerPackets() {
registerSetEntityData(ClientboundPackets1_21_5.SET_ENTITY_DATA, Types1_21_5.ENTITY_DATA_LIST, Types1_21_4.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_21_5.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_5.REMOVE_ENTITIES);
protocol.appendClientbound(ClientboundPackets1_21_5.ADD_ENTITY, wrapper -> {
@@ -115,6 +119,12 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
updateTrim(entries, trimPatternRegistry ? "template_item" : "ingredient");
return super.handle(connection, key, entries);
}
if (key.equals("enchantment")) {
updateEnchantment(entries);
return super.handle(connection, key, entries);
}
if (!key.equals("wolf_variant")) {
return super.handle(connection, key, entries);
}
@@ -153,6 +163,14 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
}
final RegistryEntry[] entries = wrapper.read(Types.REGISTRY_ENTRY_ARRAY);
if (registryKey.equals("enchantment")) {
final List<String> identifiers = new ArrayList<>(entries.length);
for (final RegistryEntry entry : entries) {
identifiers.add(entry.key());
}
wrapper.user().get(HashedItemConverterStorage.class).setEnchantments(identifiers);
}
wrapper.write(Types.REGISTRY_ENTRY_ARRAY, registryDataRewriter.handle(wrapper.user(), registryKey, entries));
});
@@ -197,6 +215,20 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
});
}
private void updateEnchantment(final RegistryEntry[] entries) {
for (final RegistryEntry entry : entries) {
if (entry.tag() == null) {
continue;
}
final CompoundTag enchantment = (CompoundTag) entry.tag();
final ListTag<StringTag> slots = enchantment.getListTag("slots", StringTag.class);
if (slots != null) {
slots.getValue().removeIf(tag -> tag.getValue().equals("saddle")); // Remove saddle slot
}
}
}
private String visibility(final int id) {
return switch (id) {
case 0 -> "always";
@@ -219,69 +251,73 @@ public final class EntityPacketRewriter1_21_5 extends EntityRewriter<Clientbound
@Override
protected void registerRewrites() {
final EntityDataTypes1_21_5 entityDataTypes = VersionedTypes.V1_21_5.entityDataTypes;
final EntityDataTypes1_21_2 mappedEntityDataTypes = VersionedTypes.V1_21_4.entityDataTypes;
filter().handler((event, data) -> {
final int id = data.dataType().typeId();
if (id == Types1_21_5.ENTITY_DATA_TYPES.wolfVariantType.typeId()) {
if (id == entityDataTypes.wolfVariantType.typeId()) {
final int type = data.value();
final Holder<WolfVariant> variant = Holder.of(type);
data.setTypeAndValue(Types1_21_4.ENTITY_DATA_TYPES.wolfVariantType, variant);
data.setTypeAndValue(mappedEntityDataTypes.wolfVariantType, variant);
return;
}
int mappedId = id;
if (id == Types1_21_5.ENTITY_DATA_TYPES.cowVariantType.typeId()
|| id == Types1_21_5.ENTITY_DATA_TYPES.pigVariantType.typeId()
|| id == Types1_21_5.ENTITY_DATA_TYPES.chickenVariantType.typeId()
|| id == Types1_21_5.ENTITY_DATA_TYPES.wolfSoundVariantType.typeId()) {
if (id == entityDataTypes.cowVariantType.typeId()
|| id == entityDataTypes.pigVariantType.typeId()
|| id == entityDataTypes.chickenVariantType.typeId()
|| id == entityDataTypes.wolfSoundVariantType.typeId()) {
event.cancel();
return;
} else if (id > Types1_21_5.ENTITY_DATA_TYPES.chickenVariantType.typeId()) {
} else if (id > entityDataTypes.chickenVariantType.typeId()) {
mappedId -= 4;
} else if (id > Types1_21_5.ENTITY_DATA_TYPES.pigVariantType.typeId()) {
} else if (id > entityDataTypes.pigVariantType.typeId()) {
mappedId -= 3;
} else if (id > Types1_21_5.ENTITY_DATA_TYPES.wolfSoundVariantType.typeId()) {
} else if (id > entityDataTypes.wolfSoundVariantType.typeId()) {
mappedId -= 2;
} else if (id > Types1_21_5.ENTITY_DATA_TYPES.cowVariantType.typeId()) {
} else if (id > entityDataTypes.cowVariantType.typeId()) {
mappedId -= 1;
}
data.setDataType(Types1_21_4.ENTITY_DATA_TYPES.byId(mappedId));
data.setDataType(mappedEntityDataTypes.byId(mappedId));
});
registerEntityDataTypeHandler1_20_3(
Types1_21_4.ENTITY_DATA_TYPES.itemType,
Types1_21_4.ENTITY_DATA_TYPES.blockStateType,
Types1_21_4.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_21_4.ENTITY_DATA_TYPES.particleType,
Types1_21_4.ENTITY_DATA_TYPES.particlesType,
Types1_21_4.ENTITY_DATA_TYPES.componentType,
Types1_21_4.ENTITY_DATA_TYPES.optionalComponentType
mappedEntityDataTypes.itemType,
mappedEntityDataTypes.blockStateType,
mappedEntityDataTypes.optionalBlockStateType,
mappedEntityDataTypes.particleType,
mappedEntityDataTypes.particlesType,
mappedEntityDataTypes.componentType,
mappedEntityDataTypes.optionalComponentType
);
filter().type(EntityTypes1_21_5.ABSTRACT_MINECART).addIndex(13); // Custom display
filter().type(EntityTypes1_21_5.ABSTRACT_MINECART).index(11).handler((event, data) -> {
final int state = (int) data.getValue();
final int mappedBlockState = protocol.getMappingData().getNewBlockStateId(state);
data.setTypeAndValue(Types1_21_5.ENTITY_DATA_TYPES.varIntType, mappedBlockState);
event.createExtraData(new EntityData(13, Types1_21_4.ENTITY_DATA_TYPES.booleanType, true));
data.setTypeAndValue(entityDataTypes.varIntType, mappedBlockState);
event.createExtraData(new EntityData(13, mappedEntityDataTypes.booleanType, true));
});
filter().type(EntityTypes1_21_5.MOOSHROOM).index(17).handler(((event, data) -> {
final int typeId = data.value();
final String typeName = typeId == 0 ? "red" : "brown";
data.setTypeAndValue(Types1_21_5.ENTITY_DATA_TYPES.stringType, typeName);
data.setTypeAndValue(entityDataTypes.stringType, typeName);
}));
filter().type(EntityTypes1_21_5.ABSTRACT_HORSE).index(17).handler((event, data) -> {
// Store data for later
// Store data and set saddled flag if needed
final TrackedEntity entity = event.trackedEntity();
if (entity.hasData()) {
final HorseDataStorage horseDataStorage = entity.data().get(HorseDataStorage.class);
if (horseDataStorage != null && horseDataStorage.saddled()) {
entity.data().put(new HorseDataStorage(data.value(), true));
return;
}
final byte horseData = data.value();
boolean saddled = false;
final HorseDataStorage horseDataStorage;
if (entity.hasData() && (horseDataStorage = entity.data().get(HorseDataStorage.class)) != null && horseDataStorage.saddled()) {
saddled = true;
data.setValue((byte) (horseData | BlockItemPacketRewriter1_21_5.SADDLED_FLAG));
}
entity.data().put(new HorseDataStorage(data.value(), false));
entity.data().put(new HorseDataStorage(horseData, saddled));
});
filter().type(EntityTypes1_21_5.CHICKEN).cancel(17); // Chicken variant
@@ -0,0 +1,51 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_5to1_21_4.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.codec.CodecContext;
import com.viaversion.viaversion.api.minecraft.codec.CodecContext.RegistryAccess;
import com.viaversion.viaversion.api.minecraft.codec.hash.Hasher;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.codec.CodecRegistryContext;
import com.viaversion.viaversion.codec.hash.HashFunction;
import com.viaversion.viaversion.codec.hash.HashOps;
import com.viaversion.viaversion.util.SerializerVersion;
import java.util.ArrayList;
import java.util.List;
public class HashedItemConverterStorage implements StorableObject {
private final List<String> enchantments = new ArrayList<>();
private final Hasher hasher;
public HashedItemConverterStorage(final Protocol<?, ?, ?, ?> protocol) {
final RegistryAccess registryAccess = RegistryAccess.of(this.enchantments, protocol.getMappingData());
final CodecContext context = new CodecRegistryContext(protocol, SerializerVersion.V1_21_5, SerializerVersion.V1_21_5, registryAccess, false);
this.hasher = new HashOps(context, HashFunction.CRC32C);
}
public void setEnchantments(final List<String> enchantments) {
this.enchantments.clear();
this.enchantments.addAll(enchantments);
}
public Hasher hasher() {
return hasher; // reusable
}
}
@@ -0,0 +1,357 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.rewriters.SoundRewriter;
import com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.DialogViewProvider;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.BlockItemPacketRewriter1_21_6;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.ComponentRewriter1_21_6;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.EntityPacketRewriter1_21_6;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ServerLinks;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.data.item.ItemHasherBase;
import com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPacket1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.Protocol1_21_5To1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundConfigurationPackets1_21_6;
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.rewriter.AttributeRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.SerializerVersion;
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
public final class Protocol1_21_6To1_21_5 extends BackwardsProtocol<ClientboundPacket1_21_6, ClientboundPacket1_21_5, ServerboundPacket1_21_6, ServerboundPacket1_21_5> {
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.21.6", "1.21.5", Protocol1_21_5To1_21_6.class);
private final EntityPacketRewriter1_21_6 entityRewriter = new EntityPacketRewriter1_21_6(this);
private final BlockItemPacketRewriter1_21_6 itemRewriter = new BlockItemPacketRewriter1_21_6(this);
private final ParticleRewriter<ClientboundPacket1_21_6> particleRewriter = new ParticleRewriter<>(this);
private final NBTComponentRewriter<ClientboundPacket1_21_6> translatableRewriter = new ComponentRewriter1_21_6(this);
private final TagRewriter<ClientboundPacket1_21_6> tagRewriter = new TagRewriter<>(this);
public Protocol1_21_6To1_21_5() {
super(ClientboundPacket1_21_6.class, ClientboundPacket1_21_5.class, ServerboundPacket1_21_6.class, ServerboundPacket1_21_5.class);
}
@Override
protected void registerPackets() {
super.registerPackets();
registerClientbound(ClientboundPackets1_21_6.UPDATE_TAGS, this::updateTags);
registerClientbound(ClientboundConfigurationPackets1_21_6.UPDATE_TAGS, this::updateTags);
final SoundRewriter<ClientboundPacket1_21_6> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_6.SOUND);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_6.SOUND_ENTITY);
soundRewriter.registerStopSound(ClientboundPackets1_21_6.STOP_SOUND);
new StatisticsRewriter<>(this).register(ClientboundPackets1_21_6.AWARD_STATS);
new AttributeRewriter<>(this).register1_21(ClientboundPackets1_21_6.UPDATE_ATTRIBUTES);
new CommandRewriter1_19_4<>(this) {
@Override
public void handleArgument(final PacketWrapper wrapper, final String argumentType) {
if (argumentType.equals("minecraft:hex_color") || argumentType.equals("minecraft:dialog")) {
wrapper.write(Types.VAR_INT, 0); // Word
} else {
super.handleArgument(wrapper, argumentType);
}
}
}.registerDeclareCommands1_19(ClientboundPackets1_21_6.COMMANDS);
translatableRewriter.registerOpenScreen1_14(ClientboundPackets1_21_6.OPEN_SCREEN);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_ACTION_BAR_TEXT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_TITLE_TEXT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_SUBTITLE_TEXT);
translatableRewriter.registerBossEvent(ClientboundPackets1_21_6.BOSS_EVENT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.DISCONNECT);
translatableRewriter.registerTabList(ClientboundPackets1_21_6.TAB_LIST);
translatableRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_6.PLAYER_COMBAT_KILL);
translatableRewriter.registerPlayerInfoUpdate1_21_4(ClientboundPackets1_21_6.PLAYER_INFO_UPDATE);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SYSTEM_CHAT);
translatableRewriter.registerDisguisedChat(ClientboundPackets1_21_6.DISGUISED_CHAT);
translatableRewriter.registerPlayerChat1_21_5(ClientboundPackets1_21_6.PLAYER_CHAT);
translatableRewriter.registerPing();
particleRewriter.registerLevelParticles1_21_4(ClientboundPackets1_21_6.LEVEL_PARTICLES);
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21_6.EXPLODE);
registerClientbound(ClientboundPackets1_21_6.CHANGE_DIFFICULTY, wrapper -> {
final int difficulty = wrapper.read(Types.VAR_INT);
wrapper.write(Types.UNSIGNED_BYTE, (short) difficulty);
});
registerServerbound(ServerboundPackets1_21_5.CHANGE_DIFFICULTY, wrapper -> {
final short difficulty = wrapper.read(Types.UNSIGNED_BYTE);
wrapper.write(Types.VAR_INT, (int) difficulty);
});
// Are you sure you want to see this? This is your last chance to turn back.
registerClientbound(ClientboundPackets1_21_6.SHOW_DIALOG, null, wrapper -> {
wrapper.cancel();
final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);
final ServerLinks serverLinks = wrapper.user().get(ServerLinks.class);
final int id = wrapper.read(Types.VAR_INT) - 1;
CompoundTag tag;
if (id == -1) {
tag = (CompoundTag) wrapper.read(Types.TAG);
} else {
tag = registryAndTags.fromRegistry(id);
}
final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);
provider.openDialog(wrapper.user(), new Dialog(registryAndTags, serverLinks, tag));
});
registerClientbound(ClientboundConfigurationPackets1_21_6.SHOW_DIALOG, null, wrapper -> {
wrapper.cancel();
final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);
final ServerLinks serverLinks = wrapper.user().get(ServerLinks.class);
final CompoundTag tag = (CompoundTag) wrapper.read(Types.TAG);
final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);
provider.openDialog(wrapper.user(), new Dialog(registryAndTags, serverLinks, tag));
});
registerClientbound(ClientboundPackets1_21_6.CLEAR_DIALOG, null, this::clearDialog);
registerClientbound(ClientboundConfigurationPackets1_21_6.CLEAR_DIALOG, null, this::clearDialog);
registerClientbound(ClientboundPackets1_21_6.SERVER_LINKS, this::storeServerLinks);
registerClientbound(ClientboundConfigurationPackets1_21_6.SERVER_LINKS, this::storeServerLinks);
registerServerbound(ServerboundPackets1_21_5.CHAT_COMMAND, wrapper -> {
final String command = wrapper.passthrough(Types.STRING);
final ClickEvents clickEvents = wrapper.user().get(ClickEvents.class);
if (clickEvents.handleChatCommand(wrapper.user(), command)) {
wrapper.cancel();
}
});
// The ones below are specific to the chest dialog view provider
registerServerbound(ServerboundPackets1_21_5.CONTAINER_CLOSE, wrapper -> {
final ChestDialogStorage storage = wrapper.user().get(ChestDialogStorage.class);
if (storage == null) {
return;
}
final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);
if (storage.phase() == ChestDialogStorage.Phase.ANVIL_VIEW) {
wrapper.cancel();
provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);
return;
}
if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {
wrapper.cancel();
if (storage.closeButtonEnabled()) {
provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);
} else {
provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.WAITING_FOR_RESPONSE);
}
return;
}
final boolean allowClosing = storage.allowClosing();
if (!allowClosing) {
wrapper.cancel();
if (storage.dialog().canCloseWithEscape()) {
provider.clickButton(wrapper.user(), Dialog.AfterAction.CLOSE, storage.dialog().actionButton());
} else {
provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);
}
}
storage.setAllowClosing(false);
});
registerServerbound(ServerboundPackets1_21_5.RENAME_ITEM, wrapper -> {
final ChestDialogStorage storage = wrapper.user().get(ChestDialogStorage.class);
if (storage == null || storage.phase() != ChestDialogStorage.Phase.ANVIL_VIEW) {
return;
}
wrapper.cancel();
final String name = wrapper.read(Types.STRING);
final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);
provider.updateAnvilText(wrapper.user(), name);
});
appendServerbound(ServerboundPackets1_21_5.CONTAINER_CLICK, wrapper -> {
final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);
if (provider == null) {
return;
}
final int containerId = wrapper.get(Types.VAR_INT, 0);
final int slot = wrapper.get(Types.SHORT, 0);
final byte button = wrapper.get(Types.BYTE, 0);
final int mode = wrapper.get(Types.VAR_INT, 2);
if (provider.clickDialog(wrapper.user(), containerId, slot, button, mode)) {
wrapper.cancel();
}
});
cancelClientbound(ClientboundPackets1_21_6.TRACKED_WAYPOINT);
}
private void clearDialog(final PacketWrapper wrapper) {
wrapper.cancel();
final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);
provider.closeDialog(wrapper.user());
}
private void updateTags(final PacketWrapper wrapper) {
tagRewriter.handleGeneric(wrapper);
wrapper.resetReader();
final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);
final int length = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < length; i++) {
final String registryKey = wrapper.read(Types.STRING);
final boolean dialog = "dialog".equals(Key.stripMinecraftNamespace(registryKey));
if (dialog) {
final int tagsSize = wrapper.read(Types.VAR_INT);
for (int j = 0; j < tagsSize; j++) {
final String key = wrapper.read(Types.STRING);
final int[] ids = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);
registryAndTags.storeTags(key, ids);
}
} else {
wrapper.write(Types.STRING, registryKey); // Write back
final int tagsSize = wrapper.passthrough(Types.VAR_INT);
for (int j = 0; j < tagsSize; j++) {
wrapper.passthrough(Types.STRING);
wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);
}
}
}
if (registryAndTags.tagsSent()) {
wrapper.set(Types.VAR_INT, 0, length - 1); // Dialog tags have been read, remove from size
}
}
private void storeServerLinks(final PacketWrapper wrapper) {
final ServerLinks serverLinks = new ServerLinks();
final int length = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < length; i++) {
if (wrapper.passthrough(Types.BOOLEAN)) {
final int id = wrapper.passthrough(Types.VAR_INT);
final String url = wrapper.passthrough(Types.STRING);
serverLinks.storeLink(id, url);
} else {
final Tag tag = wrapper.passthrough(Types.COMPOUND_TAG);
final String url = wrapper.passthrough(Types.STRING);
serverLinks.storeLink(tag, url);
}
}
wrapper.user().put(serverLinks);
}
@Override
public void init(final UserConnection user) {
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_6.PLAYER));
addItemHasher(user, new ItemHasherBase(this, user, SerializerVersion.V1_21_5, SerializerVersion.V1_21_5));
user.put(new RegistryAndTags());
user.put(new ClickEvents());
}
@Override
public void register(final ViaProviders providers) {
providers.register(DialogViewProvider.class, new ChestDialogViewProvider(this));
}
@Override
public BackwardsMappingData getMappingData() {
return MAPPINGS;
}
@Override
public EntityPacketRewriter1_21_6 getEntityRewriter() {
return entityRewriter;
}
@Override
public BlockItemPacketRewriter1_21_6 getItemRewriter() {
return itemRewriter;
}
@Override
public ParticleRewriter<ClientboundPacket1_21_6> getParticleRewriter() {
return particleRewriter;
}
@Override
public NBTComponentRewriter<ClientboundPacket1_21_6> getComponentRewriter() {
return translatableRewriter;
}
@Override
public TagRewriter<ClientboundPacket1_21_6> getTagRewriter() {
return tagRewriter;
}
@Override
public VersionedTypesHolder types() {
return VersionedTypes.V1_21_6;
}
@Override
public VersionedTypesHolder mappedTypes() {
return VersionedTypes.V1_21_5;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_6, ClientboundPacket1_21_5, ServerboundPacket1_21_6, ServerboundPacket1_21_5> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),
packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_5.class, ClientboundConfigurationPackets1_21.class),
packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class),
packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_5.class, ServerboundConfigurationPackets1_20_5.class)
);
}
}
@@ -0,0 +1,171 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.Input;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;
import com.viaversion.viaversion.util.Key;
import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public class Button implements Widget {
public static final Button DEFAULT = defaulted();
private static final String[] CLICK_EVENTS = {
"open_url",
"run_command",
"suggest_command",
"show_dialog",
"change_page",
"copy_to_clipboard",
"custom"
};
private final Dialog dialog;
private final Tag label;
private final int width;
private final @Nullable Tag tooltip;
private @Nullable CompoundTag clickEvent;
private @Nullable Template template;
private @Nullable String id;
private @Nullable CompoundTag additions;
public Button(final Dialog dialog, final CompoundTag tag) {
this.dialog = dialog;
this.label = tag.get("label");
this.tooltip = tag.get("tooltip");
final int width = tag.getInt("width", 150);
if (width < 1 || width > 1024) {
throw new IllegalArgumentException("Width must be between 1 and 1024, got: " + width);
}
this.width = width;
final CompoundTag actionTag = tag.getCompoundTag("action");
if (actionTag == null) {
return;
}
final String type = actionTag.getString("type");
if (type == null) {
throw new IllegalArgumentException("Action type is missing in tag: " + tag);
}
for (final String event : CLICK_EVENTS) {
if (Key.stripMinecraftNamespace(type).equals(event)) {
clickEvent = actionTag;
clickEvent.put("action", clickEvent.remove("type"));
return;
}
}
if (Key.stripMinecraftNamespace(type).equals("dynamic/run_command")) {
template = Template.fromString(actionTag.getString("template"));
} else if (Key.stripMinecraftNamespace(type).equals("dynamic/custom")) {
id = actionTag.getString("id");
additions = actionTag.getCompoundTag("additions");
}
}
private static Button defaulted() {
final CompoundTag tag = new CompoundTag();
final CompoundTag label = new CompoundTag();
tag.put("label", label);
label.putString("translate", "gui.ok");
return new Button(null, tag);
}
public static Button openUrl(final Tag label, final String url) {
final CompoundTag tag = new CompoundTag();
final CompoundTag actionTag = new CompoundTag();
final CompoundTag openUrlTag = new CompoundTag();
tag.put("label", label);
tag.put("action", actionTag);
actionTag.putString("type", "open_url");
actionTag.put("action", openUrlTag);
openUrlTag.putString("url", url);
return new Button(null, tag);
}
/**
* Creates a click event for this button based on the provided inputs.
*
* @return a CompoundTag representing the click event, which can be used in a button action.
*/
public @Nullable CompoundTag clickEvent() {
if (clickEvent != null) {
return clickEvent;
}
if (dialog == null) {
return null;
}
final Input[] inputs = dialog.widgets()
.stream()
.filter(widget -> widget instanceof Input)
.toArray(Input[]::new);
if (template != null) {
final Map<String, String> substitutions = new HashMap<>();
for (final Input input : inputs) {
substitutions.put(input.key(), input.asCommandSubstitution());
}
final CompoundTag tag = new CompoundTag();
tag.putString("action", "run_command");
tag.putString("command", template.instantiate(substitutions));
return tag;
}
if (id != null) {
final CompoundTag additions = this.additions != null ? this.additions.copy() : new CompoundTag();
for (final Input input : inputs) {
additions.put(input.key(), input.asTag());
}
final CompoundTag tag = new CompoundTag();
tag.putString("action", "custom");
tag.putString("id", id);
tag.put("payload", additions);
return tag;
}
return null;
}
public Tag label() {
return label;
}
public @Nullable Tag tooltip() {
return tooltip;
}
public int width() {
return width;
}
}
@@ -0,0 +1,253 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data;
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.viabackwards.protocol.v1_21_6to1_21_5.data.input.BooleanInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.NumberRangeInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.SingleOptionInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.ItemWidget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.TextWidget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ServerLinks;
import com.viaversion.viaversion.util.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* The dialog structure. See the used subclasses for further details on the
* specific structure. Note that the data hold here is directly from the server
* and needs to be remapped manually when used. See {@link ChestDialogViewProvider} for an example
*/
public final class Dialog implements Widget {
private final List<Widget> widgets = new ArrayList<>();
private final Tag title;
private final @Nullable Tag externalTitle;
private final boolean canCloseWithEscape;
private final AfterAction afterAction;
private @Nullable Button actionButton;
private @Nullable Button yesButton;
private @Nullable Button noButton;
private int columns;
private int buttonWidth;
public Dialog(final RegistryAndTags registryAndTags, final ServerLinks serverLinks, final CompoundTag tag) {
final String type = tag.getString("type");
if (type == null) {
throw new IllegalArgumentException("Dialog type is missing in tag: " + tag);
}
this.title = tag.get("title");
this.externalTitle = tag.get("external_title");
this.canCloseWithEscape = tag.getBoolean("can_close_with_escape", true);
this.afterAction = AfterAction.valueOf(tag.getString("after_action", "close").toUpperCase(Locale.ROOT));
ListTag<CompoundTag> bodyListTag = tag.getListTag("body", CompoundTag.class);
if (bodyListTag == null) {
CompoundTag bodyTag = tag.getCompoundTag("body");
if (bodyTag != null) {
bodyListTag = new ListTag<>(CompoundTag.class);
bodyListTag.add(bodyTag);
}
}
if (bodyListTag != null) {
for (final CompoundTag bodyTag : bodyListTag) {
fillBodyWidget(bodyTag);
}
}
final ListTag<CompoundTag> inputListTag = tag.getListTag("inputs", CompoundTag.class);
if (inputListTag != null) {
for (final CompoundTag inputTag : inputListTag) {
fillInputWidget(inputTag);
}
}
switch (Key.stripMinecraftNamespace(type)) {
case "notice" -> fillNoticeDialog(tag);
case "server_links" -> fillServerLinksDialog(serverLinks, tag);
case "dialog_list" -> fillDialogList(registryAndTags, serverLinks, tag);
case "multi_action" -> fillMultiActionDialog(tag);
case "confirmation" -> fillConfirmationDialog(tag);
default -> throw new IllegalArgumentException("Unknown dialog type: " + type + " in tag: " + tag);
}
}
private void fillBodyWidget(final CompoundTag tag) {
final String type = tag.getString("type");
if (type == null) {
throw new IllegalArgumentException("Dialog type is missing in tag: " + tag);
}
if (Key.stripMinecraftNamespace(type).equals("plain_message")) {
widgets.add(new TextWidget(tag));
} else if (Key.stripMinecraftNamespace(type).equals("item")) {
widgets.add(new ItemWidget(tag));
} else {
throw new IllegalArgumentException("Unknown dialog body type: " + type + " in tag: " + tag);
}
}
private void fillInputWidget(final CompoundTag tag) {
final String type = tag.getString("type");
if (type == null) {
throw new IllegalArgumentException("Dialog type is missing in tag: " + tag);
}
widgets.add(switch (Key.stripMinecraftNamespace(type)) {
case "boolean" -> new BooleanInput(tag);
case "number_range" -> new NumberRangeInput(tag);
case "single_option" -> new SingleOptionInput(tag);
case "text" -> new TextInput(tag);
default -> throw new IllegalArgumentException("Unknown dialog input type: " + type + " in tag: " + tag);
});
}
private void fillNoticeDialog(final CompoundTag tag) {
final CompoundTag actionTag = tag.getCompoundTag("action");
actionButton = actionTag == null ? Button.DEFAULT : actionButton;
}
private void fillServerLinksDialog(final ServerLinks serverLinks, final CompoundTag tag) {
fillDialogBase(tag);
buttonWidth = tag.getInt("button_width", 150);
if (serverLinks == null) {
return;
}
for (final Map.Entry<Tag, String> entry : serverLinks.links().entrySet()) {
final Tag label = entry.getKey();
final String url = entry.getValue();
widgets.add(Button.openUrl(label, url));
}
}
private void fillDialogList(final RegistryAndTags registryAndTags, final ServerLinks serverLinks, final CompoundTag tag) {
// Hold them as either a list of inlined, a singleton inlined, a registry entry or a tag.
ListTag<CompoundTag> dialogsTag = tag.getListTag("dialogs", CompoundTag.class);
if (dialogsTag == null) {
CompoundTag dialogTag = tag.getCompoundTag("dialogs");
if (dialogTag != null) {
dialogsTag = new ListTag<>(CompoundTag.class);
dialogsTag.add(dialogTag);
}
StringTag registryDialogTag = tag.getStringTag("dialogs");
if (registryDialogTag != null) {
dialogsTag = new ListTag<>(CompoundTag.class);
final String key = registryDialogTag.getValue();
if (key.startsWith("#")) {
for (final CompoundTag entry : registryAndTags.fromRegistryKey(key.substring(1))) {
dialogsTag.add(entry);
}
} else {
dialogsTag.add(registryAndTags.fromRegistry(key));
}
}
}
widgets.addAll(dialogsTag.stream().map(dialog -> new Dialog(registryAndTags, serverLinks, dialog)).toList());
fillDialogBase(tag);
buttonWidth = tag.getInt("button_width", 150);
}
private void fillDialogBase(final CompoundTag tag) {
final CompoundTag exitActionTag = tag.getCompoundTag("exit_action");
actionButton = exitActionTag == null ? null : new Button(this, exitActionTag);
final int columns = tag.getInt("columns", 2);
if (columns < 1) {
throw new IllegalArgumentException("Columns must be non-negative, got: " + columns);
}
this.columns = columns;
}
private void fillMultiActionDialog(final CompoundTag tag) {
final ListTag<CompoundTag> actionsTag = tag.getListTag("actions", CompoundTag.class);
if (actionsTag == null || actionsTag.isEmpty()) {
throw new IllegalArgumentException("Actions must not be empty in tag: " + tag);
}
widgets.addAll(actionsTag.stream().map(actionTag -> new Button(this, actionTag)).toList());
fillDialogBase(tag);
}
private void fillConfirmationDialog(final CompoundTag tag) {
yesButton = new Button(this, tag.getCompoundTag("yes"));
noButton = new Button(this, tag.getCompoundTag("no"));
}
public Tag title() {
return title;
}
public @Nullable Tag externalTitle() {
return externalTitle;
}
public boolean canCloseWithEscape() {
return canCloseWithEscape;
}
public AfterAction afterAction() {
return afterAction;
}
public enum AfterAction {
CLOSE,
NONE,
WAIT_FOR_RESPONSE
}
public @Nullable Button actionButton() {
return actionButton;
}
public @Nullable Button yesButton() {
return yesButton;
}
public @Nullable Button noButton() {
return noButton;
}
public int columns() {
return columns;
}
public int buttonWidth() {
return buttonWidth;
}
public List<Widget> widgets() {
return widgets;
}
}
@@ -0,0 +1,97 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import java.util.List;
import java.util.Map;
public record Template(List<String> segments, List<String> variables) {
private static final int MAX_LENGTH = 2000000;
public static Template fromString(final String string) {
final Builder<String> segmentsBuilder = ImmutableList.builder();
final Builder<String> variablesBuilder = ImmutableList.builder();
int currentIndex = 0;
int dollarIndex = string.indexOf('$');
while (dollarIndex != -1) {
if (dollarIndex != string.length() - 1 && string.charAt(dollarIndex + 1) == '(') {
segmentsBuilder.add(string.substring(currentIndex, dollarIndex));
final int closingParenIndex = string.indexOf(41, dollarIndex + 1);
if (closingParenIndex == -1) {
throw new IllegalArgumentException("Unterminated macro variable");
}
final String variableName = string.substring(dollarIndex + 2, closingParenIndex);
if (!isValidVariableName(variableName)) {
throw new IllegalArgumentException("Invalid macro variable name '" + variableName + "'");
}
variablesBuilder.add(variableName);
currentIndex = closingParenIndex + 1;
dollarIndex = string.indexOf(36, currentIndex);
} else {
dollarIndex = string.indexOf(36, dollarIndex + 1);
}
}
if (currentIndex == 0) {
throw new IllegalArgumentException("No variables in macro");
}
if (currentIndex != string.length()) {
segmentsBuilder.add(string.substring(currentIndex));
}
return new Template(segmentsBuilder.build(), variablesBuilder.build());
}
private static boolean isValidVariableName(final String string) {
for (int i = 0; i < string.length(); i++) {
final char c = string.charAt(i);
if (!Character.isLetterOrDigit(c) && c != '_') {
return false;
}
}
return true;
}
public String instantiate(final Map<String, String> map) {
return substitute(variables().stream().map(string -> map.getOrDefault(string, "")).toList());
}
private String substitute(final List<String> list) {
final StringBuilder out = new StringBuilder();
for (int i = 0; i < this.variables.size(); i++) {
out.append(this.segments.get(i)).append(list.get(i));
if (out.length() > MAX_LENGTH) {
throw new IllegalArgumentException("Output too long (> " + MAX_LENGTH + ")");
}
}
if (this.segments.size() > this.variables.size()) {
out.append(this.segments.get(this.segments.size() - 1));
if (out.length() > MAX_LENGTH) {
throw new IllegalArgumentException("Output too long (> " + MAX_LENGTH + ")");
}
}
return out.toString();
}
}
@@ -0,0 +1,82 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.input;
import com.viaversion.nbt.tag.ByteTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
public final class BooleanInput implements Input {
private final String key;
private final Tag label;
private final boolean initial;
private final String onTrue;
private final String onFalse;
private boolean value;
public BooleanInput(final CompoundTag tag) {
this.key = tag.getString("key");
this.label = tag.get("label");
this.initial = tag.getBoolean("initial", false);
this.onTrue = tag.getString("on_true", "true");
this.onFalse = tag.getString("on_false", "false");
this.value = initial;
}
@Override
public String key() {
return key;
}
@Override
public String asCommandSubstitution() {
return value ? onTrue : onFalse;
}
@Override
public Tag asTag() {
return new ByteTag(value);
}
public Tag label() {
return label;
}
public boolean initial() {
return initial;
}
public String onTrue() {
return onTrue;
}
public String onFalse() {
return onFalse;
}
public boolean value() {
return value;
}
public void setValue(final boolean value) {
this.value = value;
}
}
@@ -0,0 +1,31 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.input;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;
public interface Input extends Widget {
String key();
String asCommandSubstitution();
Tag asTag();
}
@@ -0,0 +1,114 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.input;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.FloatTag;
import com.viaversion.nbt.tag.Tag;
import static com.viaversion.viabackwards.utils.ChatUtil.text;
import static com.viaversion.viabackwards.utils.ChatUtil.translate;
public final class NumberRangeInput implements Input {
private final String key;
private final Tag label;
private final String labelFormat;
private final float start;
private final float end;
private final Float initial;
private final Float step;
private float value;
public NumberRangeInput(final CompoundTag tag) {
this.key = tag.getString("key");
this.label = tag.get("label");
this.labelFormat = tag.getString("label_format", "options.generic_value");
this.start = tag.getFloat("start");
this.end = tag.getFloat("end");
this.initial = tag.getFloat("initial", (this.start + this.end) / 2F);
final FloatTag stepTag = tag.getFloatTag("step");
if (stepTag != null && stepTag.asFloat() < 0F) {
throw new IllegalArgumentException("Step must be non-negative, got: " + stepTag.asFloat());
}
this.step = stepTag == null ? -1F : stepTag.asFloat();
this.value = initial;
}
@Override
public String key() {
return key;
}
@Override
public String asCommandSubstitution() {
return valueAsString();
}
@Override
public Tag asTag() {
return new FloatTag(value);
}
public Tag label() {
return label;
}
public String labelFormat() {
return labelFormat;
}
public float start() {
return start;
}
public float end() {
return end;
}
public float initial() {
return initial;
}
public float step() {
return step;
}
public float value() {
return value;
}
public String valueAsString() {
final int asInt = (int) value;
return asInt == value ? Integer.toString(asInt) : Float.toString(value);
}
public void setValue(final float value) {
this.value = value;
}
public void setClampedValue(final float value) {
this.value = (value < start) ? start : Math.min(value, end);
}
public Tag displayName() {
return translate(labelFormat, label, text(valueAsString()));
}
}
@@ -0,0 +1,126 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.input;
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 java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class SingleOptionInput implements Input {
private final String key;
private final Entry[] options;
private final @Nullable Tag label;
private int value;
public SingleOptionInput(final CompoundTag tag) {
final ListTag<CompoundTag> options = tag.getListTag("options", CompoundTag.class);
if (options == null || options.isEmpty()) {
throw new IllegalArgumentException("Options must not be empty in tag: " + tag);
}
this.key = tag.getString("key");
this.options = options.stream().map(Entry::new).toArray(Entry[]::new);
this.label = tag.getBoolean("label_visible", true) ? tag.get("label") : null;
for (int i = 0; i < this.options.length; i++) {
if (this.options[i].initial()) {
if (this.value != 0) {
throw new IllegalArgumentException("Multiple initial options found in tag: " + tag);
}
this.value = i;
}
}
}
@Override
public String key() {
return key;
}
@Override
public String asCommandSubstitution() {
return this.options[value].id;
}
@Override
public Tag asTag() {
return new StringTag(asCommandSubstitution());
}
public Entry[] options() {
return options;
}
public @Nullable Tag label() {
return label;
}
public int value() {
return value;
}
public void setValue(final int value) {
if (value < 0 || value >= options.length) {
throw new IllegalArgumentException("Value must be between 0 and " + (options.length - 1));
}
this.value = value;
}
public void setClampedValue(final int value) {
if (value < 0) {
this.value = 0;
} else if (value >= options.length) {
this.value = 0;
} else {
this.value = value;
}
}
public static class Entry {
private final String id;
private final Tag display;
private final boolean initial;
public Entry(final CompoundTag tag) {
id = tag.getString("id");
display = tag.get("display");
initial = tag.getBoolean("initial", false);
}
public String id() {
return id;
}
public Tag display() {
return display;
}
public boolean initial() {
return initial;
}
public Tag computeDisplay() {
return Objects.requireNonNullElseGet(display, () -> new StringTag(id));
}
}
}
@@ -0,0 +1,121 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.input;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class TextInput implements Input {
private final String key;
private final @Nullable Tag label;
private final String initial;
private final int maxLength;
private final @Nullable MultilineOptions[] options;
private String value;
public TextInput(final CompoundTag tag) {
this.key = tag.getString("key");
this.label = tag.getBoolean("label_visible", true) ? tag.get("label") : null;
this.initial = tag.getString("initial", "");
this.maxLength = tag.getInt("max_length", 32);
if (this.maxLength < 1) {
throw new IllegalArgumentException("Max length must be at least 1, got: " + this.maxLength);
}
final ListTag<CompoundTag> multilineList = tag.getListTag("multiline", CompoundTag.class);
if (multilineList != null && !multilineList.isEmpty()) {
this.options = multilineList.stream().map(MultilineOptions::new).toArray(MultilineOptions[]::new);
} else {
this.options = null;
}
this.value = initial;
}
@Override
public String key() {
return key;
}
@Override
public String asCommandSubstitution() {
return value;
}
@Override
public Tag asTag() {
return new StringTag(value);
}
public @Nullable Tag label() {
return label;
}
public String initial() {
return initial;
}
public int maxLength() {
return maxLength;
}
public @Nullable MultilineOptions[] options() {
return options;
}
public String value() {
return value;
}
public void setValue(final String value) {
if (value.length() > maxLength) {
throw new IllegalArgumentException("Value exceeds max length of " + maxLength + ": " + value);
}
this.value = value;
}
public void setClampedValue(final String value) {
if (value.length() > maxLength) {
this.value = value.substring(0, maxLength);
} else {
this.value = value;
}
}
public static class MultilineOptions {
private final @Nullable Integer maxLines;
public MultilineOptions(final CompoundTag tag) {
final IntTag maxLinesTag = tag.getIntTag("max_lines");
if (maxLinesTag != null && maxLinesTag.asInt() < 1) {
throw new IllegalArgumentException("Max lines must be at least 1, got: " + maxLinesTag);
}
this.maxLines = maxLinesTag != null ? maxLinesTag.asInt() : null;
}
public @Nullable Integer maxLines() {
return maxLines;
}
}
}
@@ -0,0 +1,69 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.widget;
import com.viaversion.nbt.tag.CompoundTag;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class ItemWidget implements Widget {
private final CompoundTag item;
private final @Nullable TextWidget description;
private final boolean showTooltip;
private final int width;
private final int height;
public ItemWidget(final CompoundTag tag) {
this.item = tag.getCompoundTag("item");
if (this.item == null) {
throw new IllegalArgumentException("Item tag is missing in ItemWidget tag: " + tag);
}
this.description = tag.contains("description") ? new TextWidget(tag.getCompoundTag("description")) : null;
this.showTooltip = tag.getBoolean("show_tooltip", true);
final int width = tag.getInt("width", 16);
final int height = tag.getInt("height", 16);
if (width < 1 || width > 256) {
throw new IllegalArgumentException("Width must be between 1 and 256, got: " + width);
}
if (height < 1 || height > 256) {
throw new IllegalArgumentException("Height must be between 1 and 256, got: " + height);
}
this.width = width;
this.height = height;
}
public CompoundTag item() {
return item;
}
public @Nullable TextWidget description() {
return description;
}
public boolean showTooltip() {
return showTooltip;
}
public int width() {
return width;
}
public int height() {
return height;
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.widget;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
public final class TextWidget implements Widget {
private final Tag label;
private final int width;
public TextWidget(final CompoundTag tag) {
this.label = tag.get("contents");
this.width = tag.getInt("width", 200);
}
public Tag label() {
return label;
}
public int width() {
return width;
}
}
@@ -0,0 +1,21 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.data.widget;
public interface Widget {
}
@@ -0,0 +1,596 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.provider;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Button;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.BooleanInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.NumberRangeInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.SingleOptionInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.ItemWidget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.TextWidget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;
import com.viaversion.viabackwards.utils.ChatUtil;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.StructuredItem;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.MathUtil;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.viaversion.viabackwards.utils.ChatUtil.fixStyle;
import static com.viaversion.viabackwards.utils.ChatUtil.text;
import static com.viaversion.viabackwards.utils.ChatUtil.translate;
/**
* Default dialog view emulator. The layout has been stretched into a lot sub-functions for plugins to override.
*/
public class ChestDialogViewProvider implements DialogViewProvider {
private static final int INVENTORY_SIZE = 27;
private static final int INVENTORY_LAST_ROW = INVENTORY_SIZE - 9;
private final Protocol1_21_6To1_21_5 protocol;
public ChestDialogViewProvider(final Protocol1_21_6To1_21_5 protocol) {
this.protocol = protocol;
}
@Override
public void openDialog(final UserConnection connection, final Dialog dialog) {
final State state = connection.getProtocolInfo().getClientState();
if (state == State.CONFIGURATION) {
// TODO Implement by ending and re-starting the configuration phase
return;
}
// Batch text widgets following one another into a single MultiTextWidget to be display properly.
final List<Tag> texts = new ArrayList<>();
for (final Widget widget : new ArrayList<>(dialog.widgets())) {
if (widget instanceof final TextWidget textWidget) {
texts.add(textWidget.label());
dialog.widgets().remove(textWidget);
} else if (!texts.isEmpty()) {
dialog.widgets().add(dialog.widgets().indexOf(widget), new MultiTextWidget(texts.toArray(Tag[]::new)));
texts.clear();
}
}
final ChestDialogStorage previousStorage = connection.get(ChestDialogStorage.class);
final ChestDialogStorage storage = new ChestDialogStorage(this, dialog);
if (previousStorage != null) {
storage.setPreviousDialog(previousStorage.dialog());
}
connection.put(storage);
openChestView(connection, storage, ChestDialogStorage.Phase.DIALOG_VIEW);
}
public void openChestView(final UserConnection connection, final ChestDialogStorage storage, ChestDialogStorage.Phase phase) {
storage.setPhase(connection, phase);
final PacketWrapper openScreen = PacketWrapper.create(ClientboundPackets1_21_5.OPEN_SCREEN, connection);
openScreen.write(Types.VAR_INT, storage.containerId());
openScreen.write(Types.VAR_INT, 2); // Container type id
openScreen.write(Types.TAG, storage.dialog().title());
openScreen.send(Protocol1_21_6To1_21_5.class);
updateDialog(connection, storage.dialog());
}
@Override
public void closeDialog(final UserConnection connection) {
final State state = connection.getProtocolInfo().getClientState();
if (state == State.CONFIGURATION) {
// TODO Implement by ending and re-starting the configuration phase
return;
}
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
final PacketWrapper containerClose = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_CLOSE, connection);
containerClose.write(Types.VAR_INT, storage.containerId());
containerClose.send(Protocol1_21_6To1_21_5.class);
if (storage.previousDialog() != null) {
openDialog(connection, storage.previousDialog());
} else {
connection.remove(ChestDialogStorage.class);
}
}
public boolean clickDialog(final UserConnection connection, final int container, int slot, final byte mouse, final int mode) {
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
if (storage == null || storage.containerId() != container) {
return false;
}
if (mode != 0 || slot < 0 || slot >= INVENTORY_SIZE) {
updateDialog(connection, storage.dialog()); // Resync inventory view
return true;
}
if (storage.phase() == ChestDialogStorage.Phase.ANVIL_VIEW) {
openChestView(connection, storage, ChestDialogStorage.Phase.DIALOG_VIEW);
return true;
}
if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {
if (slot == storage.actionIndex() && storage.closeButtonEnabled()) {
closeDialog(connection);
} else {
updateDialog(connection, storage.dialog()); // Resync inventory view
}
return true;
}
// Page navigation
if (slot == INVENTORY_SIZE - 1) {
final int pages = MathUtil.ceil(storage.items().length / (float) INVENTORY_LAST_ROW);
if (mouse == 0) {
storage.page++;
} else if (mouse == 1) {
storage.page--;
}
storage.page = MathUtil.clamp(storage.page, 0, pages - 1);
}
slot += storage.page * INVENTORY_LAST_ROW;
// Input widgets
final List<Widget> widgets = storage.dialog().widgets();
if (slot < widgets.size()) {
final Widget widget = widgets.get(slot);
if (widget instanceof final BooleanInput booleanInput) {
clickBooleanInput(booleanInput);
} else if (widget instanceof final NumberRangeInput numberRangeInput) {
clickNumberRangeInput(numberRangeInput, mouse);
} else if (widget instanceof final TextInput textInput) {
clickTextInput(connection, textInput);
} else if (widget instanceof final SingleOptionInput singleOptionInput) {
clickSingleOptionInput(singleOptionInput);
} else if (widget instanceof final Button button) {
clickButton(connection, storage.dialog().afterAction(), button);
} else if (widget instanceof final Dialog dialog) {
clickDialogButton(connection, dialog);
}
}
// And some special cases.
if (slot == storage.confirmationYesIndex()) {
final Button yesButton = storage.dialog().yesButton();
if (yesButton != null) {
clickButton(connection, storage.dialog().afterAction(), yesButton);
}
}
if (slot == storage.confirmationNoIndex()) {
final Button noButton = storage.dialog().noButton();
if (noButton != null) {
clickButton(connection, storage.dialog().afterAction(), noButton);
}
}
if (slot == storage.actionIndex()) {
final Button actionButton = storage.dialog().actionButton();
if (actionButton != null) {
clickButton(connection, storage.dialog().afterAction(), actionButton);
}
}
// Resync inventory view if the actions above didn't close the dialog.
if (connection.has(ChestDialogStorage.class) && storage.phase() == ChestDialogStorage.Phase.DIALOG_VIEW) {
updateDialog(connection, storage.dialog());
}
return true;
}
public void updateDialog(final UserConnection connection, final Dialog dialog) {
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
final PacketWrapper containerSetContent = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_SET_CONTENT, connection);
containerSetContent.write(Types.VAR_INT, storage.containerId());
containerSetContent.write(Types.VAR_INT, 0); // Revision
containerSetContent.write(VersionedTypes.V1_21_5.itemArray, getItems(connection, storage, dialog));
containerSetContent.write(VersionedTypes.V1_21_5.item, StructuredItem.empty());
containerSetContent.send(Protocol1_21_6To1_21_5.class);
}
protected Item createPageNavigationItem() {
return createItem(
"minecraft:arrow",
text("§9§lPage navigation"),
"§9Left click: §6Go to next page",
"§9Right click: §6Go to previous page"
);
}
protected Item createActionButtonItem(final UserConnection connection, final Button button) {
final Tag label = handleTag(connection, button.label());
if (button.tooltip() == null) {
return createItem("minecraft:oak_button", label);
} else {
return createItem("minecraft:oak_button", label, handleTag(connection, button.tooltip()));
}
}
protected Item createCloseButtonItem(final Tag label) {
return createItem("minecraft:oak_button", label);
}
protected Item getItemWidget(final UserConnection connection, final ItemWidget itemWidget) {
final String identifier = itemWidget.item().getString("id");
final int count = itemWidget.item().getInt("count", 1);
final Tag label = text(Key.stripMinecraftNamespace(identifier));
final Item item = createItem(identifier, label);
item.setAmount(count);
if (itemWidget.description() != null) {
item.dataContainer().set(StructuredDataKey.LORE, new Tag[]{
handleTag(connection, fixStyle(itemWidget.description().label()))
});
}
if (!itemWidget.showTooltip()) {
item.dataContainer().set(StructuredDataKey.HIDE_TOOLTIP);
}
// If we were to parse item components from NBT for chat items they would be parsed here and stored into the data container.
// In VV, chat items are rewritten manually at the time being and therefore no conversion code exists.
return item;
}
protected Item getMultiTextWidget(final UserConnection connection, final MultiTextWidget multiTextWidget) {
final Tag name = handleTag(connection, multiTextWidget.labels()[0]);
final int length = multiTextWidget.labels().length;
if (length == 1) {
return createItem("minecraft:paper", name);
}
final Tag[] lore = new Tag[length - 1];
for (int i = 1; i < length; i++) {
lore[i - 1] = handleTag(connection, fixStyle(multiTextWidget.labels()[i]));
}
return createItem("minecraft:paper", name, lore);
}
protected Item getBooleanInput(final UserConnection connection, final BooleanInput booleanInput) {
final String item = booleanInput.value() ? "minecraft:lime_dye" : "minecraft:gray_dye";
final Tag[] label = ChatUtil.split(booleanInput.label(), "\n");
// The only one that supports newlines in the label
if (label.length == 1) {
return createItem(
item,
handleTag(connection, booleanInput.label()),
text("§9Left click: §6Toggle value")
);
} else {
final Tag[] lore = new Tag[label.length];
for (int i = 1; i < label.length; i++) {
lore[i - 1] = handleTag(connection, fixStyle(label[i]));
}
lore[lore.length - 1] = text("§9Left click: §6Toggle value");
return createItem(
item,
handleTag(connection, label[0]),
lore
);
}
}
protected void clickBooleanInput(final BooleanInput booleanInput) {
booleanInput.setValue(!booleanInput.value());
}
protected Item getNumberRangeInput(final UserConnection connection, final NumberRangeInput numberRangeInput) {
final Tag label = handleTag(connection, numberRangeInput.displayName());
return createItem(
"minecraft:clock",
label,
"§9Left click: §6Increase value by " + numberRangeInput.step(),
"§9Right click: §6Decrease value by " + numberRangeInput.step(),
"§7(Value between §a" + numberRangeInput.start() + " §7and §a" + numberRangeInput.end() + "§7)"
);
}
protected void clickNumberRangeInput(final NumberRangeInput numberRangeInput, final int mouse) {
float value = numberRangeInput.value();
if (mouse == 0) { // Left click
value += numberRangeInput.step();
} else if (mouse == 1) { // Right click
value -= numberRangeInput.step();
}
numberRangeInput.setClampedValue(value);
}
protected Item getTextInput(final UserConnection connection, final TextInput textInput) {
final Tag currentValue = text("§7Current value: §a" + textInput.value());
if (textInput.label() == null) {
return createItem("minecraft:writable_book", currentValue);
} else {
final Tag label = handleTag(connection, textInput.label());
return createItem("minecraft:writable_book", label, currentValue, text("§9Left click: §6Edit text"));
}
}
protected void clickTextInput(final UserConnection connection, final TextInput textInput) {
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
openAnvilView(connection, storage, text("§7Edit text"), textInput.value(), textInput);
}
protected Item getSingleOptionInput(final UserConnection connection, final SingleOptionInput singleOptionInput) {
final Tag displayName = singleOptionInput.options()[singleOptionInput.value()].computeDisplay();
final Tag label;
if (singleOptionInput.label() != null) {
label = translate("options.generic_value", singleOptionInput.label(), displayName);
} else {
label = displayName;
}
return createItem(
"minecraft:bookshelf",
handleTag(connection, label),
"§9Left click: §6Go to next option",
"§9Right click: §6Go to previous option"
);
}
protected void clickSingleOptionInput(final SingleOptionInput singleOptionInput) {
singleOptionInput.setClampedValue(singleOptionInput.value() + 1);
}
protected Item getButton(final UserConnection connection, final Button button) {
return createItem("minecraft:oak_button", handleTag(connection, button.label()));
}
public void clickButton(final UserConnection connection, final Dialog.AfterAction afterAction, @Nullable final Button button) {
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
switch (afterAction) {
case CLOSE -> closeDialog(connection);
case WAIT_FOR_RESPONSE -> storage.setPhase(null, ChestDialogStorage.Phase.WAITING_FOR_RESPONSE);
}
if (button == null || button.clickEvent() == null) {
return;
}
final CompoundTag clickEvent = button.clickEvent();
final String action = Key.stripMinecraftNamespace(clickEvent.getString("action"));
switch (action) {
case "open_url" -> {
// We can't open a URL for the client, so roughly emulate by opening an Anvil containing the URL.
final String url = clickEvent.getString("url");
openAnvilView(connection, storage, text("Open URL"), url, null);
}
case "run_command" -> {
// The vanilla client validates for signed argument types and has more requirements for this click event,
// but we can't do this here and therefore just always send the packet assuming this is correct...
final PacketWrapper chatCommand = PacketWrapper.create(ServerboundPackets1_21_6.CHAT_COMMAND, connection);
String command = clickEvent.getString("command");
if (command.startsWith("/")) {
command = command.substring(1);
}
chatCommand.write(Types.STRING, command);
chatCommand.sendToServer(Protocol1_21_6To1_21_5.class);
}
case "copy_to_clipboard" -> {
// Same as above, we can't access the clipboard
final String value = clickEvent.getString("value");
openAnvilView(connection, storage, text("Copy to clipboard"), value, null);
}
}
ClickEvents.handleClickEvent(connection, clickEvent); // Handle show_dialog and custom
}
protected Item createTextInputItem(final String value) {
return createItem("minecraft:paper", text(value), "§9Left click/close: §6Set text");
}
protected Item createTextCopyItem(final String value) {
return createItem("minecraft:paper", text(value), "§9Left click: §6Close");
}
private void openAnvilView(
final UserConnection connection,
final ChestDialogStorage storage,
final Tag title,
final String value,
final TextInput textInput
) {
storage.setPhase(connection, ChestDialogStorage.Phase.ANVIL_VIEW);
final PacketWrapper openScreen = PacketWrapper.create(ClientboundPackets1_21_5.OPEN_SCREEN, connection);
openScreen.write(Types.VAR_INT, storage.containerId());
openScreen.write(Types.VAR_INT, 8); // Container type id
openScreen.write(Types.TAG, title);
openScreen.send(Protocol1_21_6To1_21_5.class);
final Item[] items = new Item[1];
items[0] = textInput != null ? createTextInputItem(value) : createTextCopyItem(value);
storage.setCurrentTextInput(textInput);
final PacketWrapper containerSetContent = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_SET_CONTENT, connection);
containerSetContent.write(Types.VAR_INT, storage.containerId());
containerSetContent.write(Types.VAR_INT, 0); // Revision
containerSetContent.write(VersionedTypes.V1_21_5.itemArray, items);
containerSetContent.write(VersionedTypes.V1_21_5.item, StructuredItem.empty());
containerSetContent.send(Protocol1_21_6To1_21_5.class);
}
public void updateAnvilText(final UserConnection connection, final String value) {
if (value.isEmpty()) {
return;
}
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
if (storage.currentTextInput() != null) {
storage.currentTextInput().setClampedValue(value);
}
}
protected Item getDialog(final UserConnection connection, final Dialog dialog) {
final Tag title = dialog.externalTitle() != null ? dialog.externalTitle() : dialog.title();
return createItem("minecraft:command_block", handleTag(connection, title));
}
protected void clickDialogButton(final UserConnection connection, final Dialog dialog) {
closeDialog(connection);
openDialog(connection, dialog);
}
protected Item getItem(final UserConnection connection, final Widget widget) {
if (widget instanceof final ItemWidget itemWidget) {
return getItemWidget(connection, itemWidget);
} else if (widget instanceof final MultiTextWidget multiTextWidget) {
return getMultiTextWidget(connection, multiTextWidget);
} else if (widget instanceof final BooleanInput booleanInput) {
return getBooleanInput(connection, booleanInput);
} else if (widget instanceof final NumberRangeInput numberRangeInput) {
return getNumberRangeInput(connection, numberRangeInput);
} else if (widget instanceof TextInput textInput) {
return getTextInput(connection, textInput);
} else if (widget instanceof SingleOptionInput singleOptionInput) {
return getSingleOptionInput(connection, singleOptionInput);
} else if (widget instanceof Button button) {
return getButton(connection, button);
} else if (widget instanceof Dialog dialog) {
return getDialog(connection, dialog);
}
throw new IllegalArgumentException("Unknown widget type: " + widget.getClass().getName());
}
protected Item[] getItems(final UserConnection connection, final ChestDialogStorage storage, final Dialog dialog) {
final Item[] items = StructuredItem.emptyArray(INVENTORY_SIZE);
int confirmationYesIndex = -1;
int confirmationNoIndex = -1;
int actionIndex = -1;
if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {
actionIndex = 13;
items[actionIndex] = createCloseButtonItem(storage.closeButtonLabel());
storage.setItems(items, confirmationYesIndex, confirmationNoIndex, actionIndex);
return items;
}
final List<Widget> widgets = dialog.widgets();
if (widgets.size() > INVENTORY_LAST_ROW) {
final int begin = storage.page * INVENTORY_LAST_ROW;
final int end = Math.min((storage.page + 1) * INVENTORY_LAST_ROW, widgets.size());
for (int i = 0; i < end - begin; i++) {
items[i] = getItem(connection, widgets.get(begin + i));
}
items[INVENTORY_SIZE - 1] = createPageNavigationItem();
} else {
for (int i = 0; i < widgets.size(); i++) {
items[i] = getItem(connection, widgets.get(i));
}
}
// And some special cases.
if (dialog.yesButton() != null && dialog.noButton() != null) {
confirmationYesIndex = widgets.isEmpty() ? 11 : INVENTORY_SIZE - 7;
confirmationNoIndex = widgets.isEmpty() ? 15 : INVENTORY_SIZE - 3;
items[confirmationYesIndex] = createActionButtonItem(connection, dialog.yesButton());
items[confirmationNoIndex] = createActionButtonItem(connection, dialog.noButton());
}
if (dialog.actionButton() != null) {
actionIndex = widgets.isEmpty() ? 13 : INVENTORY_SIZE - 9;
items[actionIndex] = createActionButtonItem(connection, dialog.actionButton());
}
storage.setItems(items, confirmationYesIndex, confirmationNoIndex, actionIndex);
return items;
}
/**
* Handles the 1.21.6 tag inside the Dialog structure and rewrites it to the 1.21.5 format. This is necessary
* for replacing translations used by Dialogs which only exist in 1.21.6+. Call this function for every text component
* you display from the server.
*
* @param connection the user connection
* @param tag the text component as a tag, or null if no tag is present
* @return the rewritten tag, or null if the input tag was null
*/
private @Nullable Tag handleTag(final UserConnection connection, final @Nullable Tag tag) {
if (tag == null) {
return null;
}
protocol.getComponentRewriter().processTag(connection, tag);
return tag;
}
/**
* This doesn't actually exist in Minecraft but is created if multiple {@link TextWidget} follow one another in order
* to improve readability in chest inventories.
*/
public record MultiTextWidget(Tag[] labels) implements Widget {
}
// -------------------------------------------------------------------------------------
protected Item createItem(final String identifier, final Tag name) {
return createItem(identifier, name, new String[0]);
}
protected Item createItem(final String identifier, final Tag name, final Tag... description) {
final int id = protocol.getMappingData().getFullItemMappings().mappedId(identifier);
final StructuredDataContainer data = new StructuredDataContainer();
data.setIdLookup(protocol, true);
data.set(StructuredDataKey.ITEM_NAME, name);
if (description != null) {
data.set(StructuredDataKey.LORE, description);
}
return new StructuredItem(id, 1, data);
}
protected Item createItem(final String identifier, final Tag name, final String... description) {
final int id = protocol.getMappingData().getFullItemMappings().mappedId(identifier);
final StructuredDataContainer data = new StructuredDataContainer();
data.setIdLookup(protocol, true);
data.set(StructuredDataKey.ITEM_NAME, name);
if (description.length > 0) {
final List<Tag> lore = new ArrayList<>();
for (final String s : description) {
lore.add(text(s));
}
data.set(StructuredDataKey.LORE, lore.toArray(new Tag[0]));
}
return new StructuredItem(id, 1, data);
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.provider;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.Provider;
/**
* Interface for providing dialog view functionality to this protocol. Requires a storage
* to save per-user data while being able to have open and close logic static.
* <p>
* See {@link Dialog} for the structure of a dialog.
* <p>
* See {@link ChestDialogViewProvider} for the protocol level emulation using a chest.
*/
public interface DialogViewProvider extends Provider {
void openDialog(final UserConnection connection, final Dialog dialog);
void closeDialog(final UserConnection connection);
}
@@ -0,0 +1,152 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21;
import com.viaversion.viaversion.api.minecraft.item.data.Equippable;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPacket1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
import static com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.BlockItemPacketRewriter1_21_6.downgradeItemData;
import static com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.BlockItemPacketRewriter1_21_6.upgradeItemData;
public final class BlockItemPacketRewriter1_21_6 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_6, ServerboundPacket1_21_5, Protocol1_21_6To1_21_5> {
public BlockItemPacketRewriter1_21_6(final Protocol1_21_6To1_21_5 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = BlockRewriter.for1_20_2(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_6.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_6.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_6.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21_6.LEVEL_EVENT, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21_6.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_21_5::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_21_6.BLOCK_ENTITY_DATA);
protocol.registerClientbound(ClientboundPackets1_21_6.SET_CURSOR_ITEM, this::passthroughClientboundItem);
registerSetPlayerInventory(ClientboundPackets1_21_6.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_6.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21_6.CONTAINER_SET_CONTENT);
registerSetSlot1_21_2(ClientboundPackets1_21_6.CONTAINER_SET_SLOT);
registerAdvancements1_20_3(ClientboundPackets1_21_6.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_21_6.SET_EQUIPMENT);
registerMerchantOffers1_20_5(ClientboundPackets1_21_6.MERCHANT_OFFERS);
registerContainerClick1_21_5(ServerboundPackets1_21_5.CONTAINER_CLICK);
registerSetCreativeModeSlot1_21_5(ServerboundPackets1_21_5.SET_CREATIVE_MODE_SLOT);
final RecipeDisplayRewriter<ClientboundPacket1_21_6> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21_6.UPDATE_RECIPES);
recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21_6.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_6.PLACE_GHOST_RECIPE);
}
@Override
protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {
downgradeItemData(item);
super.handleItemDataComponentsToClient(connection, item, container);
}
@Override
protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {
upgradeItemData(item);
super.handleItemDataComponentsToServer(connection, item, container);
}
@Override
protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {
super.backupInconvertibleData(connection, item, dataContainer, backupTag);
final AttributeModifiers1_21 attributeModifiers = dataContainer.get(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6);
if (attributeModifiers != null) {
final ListTag<CompoundTag> modifiersBackup = new ListTag<>(CompoundTag.class);
boolean needsBackup = false;
for (final AttributeModifiers1_21.AttributeModifier modifier : attributeModifiers.modifiers()) {
if (modifier.display().id() != 0) {
needsBackup = true;
}
final CompoundTag modifierBackup = new CompoundTag();
modifiersBackup.add(modifierBackup);
modifierBackup.putInt("id", modifier.display().id());
if (modifier.display() instanceof AttributeModifiers1_21.OverrideText overrideText) {
modifierBackup.put("text", overrideText.component());
}
}
if (needsBackup) {
backupTag.put("attribute_modifiers_displays", modifiersBackup);
}
}
final Equippable equippable = dataContainer.get(StructuredDataKey.EQUIPPABLE1_21_6);
if (equippable != null && equippable.canBeSheared()) {
final CompoundTag equippableTag = new CompoundTag();
equippableTag.putBoolean("can_be_sheared", true);
saveSoundEventHolder(equippableTag, equippable.shearingSound());
backupTag.put("equippable", equippableTag);
}
}
@Override
protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {
super.restoreBackupData(item, container, customData);
if (!(customData.remove(nbtTagName("backup")) instanceof final CompoundTag backupTag)) {
return;
}
final ListTag<CompoundTag> attributeModifiersDisplays = backupTag.getListTag("attribute_modifiers_displays", CompoundTag.class);
if (attributeModifiersDisplays != null) {
container.replace(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_5, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6, modifiers -> {
final AttributeModifiers1_21.AttributeModifier[] updatedModifiers = new AttributeModifiers1_21.AttributeModifier[modifiers.modifiers().length];
for (int i = 0; i < modifiers.modifiers().length; i++) {
final CompoundTag modifierBackup = attributeModifiersDisplays.get(i);
final int id = modifierBackup.getInt("id");
final AttributeModifiers1_21.Display display = id == 2 ? new AttributeModifiers1_21.OverrideText(modifierBackup.get("text")) : new AttributeModifiers1_21.Display(id);
final AttributeModifiers1_21.AttributeModifier modifier = modifiers.modifiers()[i];
updatedModifiers[i] = new AttributeModifiers1_21.AttributeModifier(modifier.attribute(), modifier.modifier(), modifier.slotType(), display);
}
return new AttributeModifiers1_21(updatedModifiers);
});
}
final CompoundTag equippableTag = backupTag.getCompoundTag("equippable");
if (equippableTag != null) {
container.replace(StructuredDataKey.EQUIPPABLE1_21_5, StructuredDataKey.EQUIPPABLE1_21_6, equippable -> new Equippable(
equippable.equipmentSlot(), equippable.soundEvent(), equippable.model(), equippable.cameraOverlay(), equippable.allowedEntities(),
equippable.dispensable(), equippable.swappable(), equippable.damageOnHurt(), equippable.equipOnInteract(),
equippableTag.getBoolean("can_be_sheared"),
restoreSoundEventHolder(equippableTag)
));
}
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
public final class ComponentRewriter1_21_6 extends NBTComponentRewriter<ClientboundPacket1_21_6> {
public ComponentRewriter1_21_6(final BackwardsProtocol<ClientboundPacket1_21_6, ?, ?, ?> protocol) {
super(protocol);
}
@Override
protected void processCompoundTag(final UserConnection connection, final CompoundTag tag) {
super.processCompoundTag(connection, tag);
final CompoundTag clickEvent = tag.getCompoundTag("click_event");
if (clickEvent == null) {
return;
}
final String action = clickEvent.getString("action");
final ChestDialogStorage chestDialogStorage = connection.get(ChestDialogStorage.class);
if (chestDialogStorage != null) {
// Short way out - remove when in dialog, buttons are handled separately
if ("show_dialog".equals(action) || "custom".equals(action)) {
tag.remove("click_event");
return;
}
}
// Make them run a command generated by us executing the actual logic
if ("show_dialog".equals(action)) {
final ClickEvents clickEvents = connection.get(ClickEvents.class);
final String command = clickEvents.storeClickEvent(clickEvent.copy());
clickEvent.putString("action", "run_command");
clickEvent.putString("command", command);
clickEvent.remove("dialog");
} else if ("custom".equals(action)) {
final ClickEvents clickEvents = connection.get(ClickEvents.class);
final String command = clickEvents.storeClickEvent(clickEvent.copy());
clickEvent.putString("action", "run_command");
clickEvent.putString("command", command);
clickEvent.remove("id");
clickEvent.remove("payload");
}
}
}
@@ -0,0 +1,125 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.KeyMappings;
public final class EntityPacketRewriter1_21_6 extends EntityRewriter<ClientboundPacket1_21_6, Protocol1_21_6To1_21_5> {
public EntityPacketRewriter1_21_6(final Protocol1_21_6To1_21_5 protocol) {
super(protocol, VersionedTypes.V1_21_6.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_6.entityDataTypes.booleanType);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21_6.ADD_ENTITY, EntityTypes1_21_6.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_6.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_6.REMOVE_ENTITIES);
registerPlayerAbilities(ClientboundPackets1_21_6.PLAYER_ABILITIES);
registerGameEvent(ClientboundPackets1_21_6.GAME_EVENT);
registerLogin1_20_5(ClientboundPackets1_21_6.LOGIN);
registerRespawn1_20_5(ClientboundPackets1_21_6.RESPAWN);
final RegistryDataRewriter registryDataRewriter = new BackwardsRegistryRewriter(protocol) {
@Override
public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {
if (Key.stripMinecraftNamespace(key).equals("dialog")) {
final String[] keys = new String[entries.length];
for (int i = 0; i < entries.length; i++) {
keys[i] = Key.stripMinecraftNamespace(entries[i].key());
}
final Object2ObjectMap<String, CompoundTag> dialogs = new Object2ObjectArrayMap<>();
for (final RegistryEntry entry : entries) {
if (entry.tag() instanceof final CompoundTag tag) {
dialogs.put(Key.stripMinecraftNamespace(entry.key()), tag);
}
}
final RegistryAndTags registryAndTags = connection.get(RegistryAndTags.class);
registryAndTags.storeRegistry(new KeyMappings(keys), dialogs);
}
return super.handle(connection, key, entries);
}
};
registryDataRewriter.remove("dialog"); // Tracked and now removed
protocol.registerClientbound(ClientboundConfigurationPackets1_21_6.REGISTRY_DATA, registryDataRewriter::handle);
protocol.registerServerbound(ServerboundPackets1_21_5.PLAYER_COMMAND, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Entity ID
// press_shift_key and release_shift_key gone. The server uses (the already sent) player input instead
final int action = wrapper.read(Types.VAR_INT);
if (action < 2) {
wrapper.cancel();
}
wrapper.write(Types.VAR_INT, action - 2);
});
}
@Override
protected void registerRewrites() {
final EntityDataTypes1_21_5 entityDataTypes = VersionedTypes.V1_21_5.entityDataTypes;
filter().mapDataType(entityDataTypes::byId);
registerEntityDataTypeHandler1_20_3(
entityDataTypes.itemType,
entityDataTypes.blockStateType,
entityDataTypes.optionalBlockStateType,
entityDataTypes.particleType,
entityDataTypes.particlesType,
entityDataTypes.componentType,
entityDataTypes.optionalComponentType
);
filter().type(EntityTypes1_21_6.HANGING_ENTITY).removeIndex(8); // Direction
filter().type(EntityTypes1_21_6.HAPPY_GHAST).cancel(17); // Leash holder
filter().type(EntityTypes1_21_6.HAPPY_GHAST).cancel(18); // Stays still
}
@Override
public void onMappingDataLoaded() {
mapTypes();
mapEntityTypeWithData(EntityTypes1_21_6.HAPPY_GHAST, EntityTypes1_21_6.GHAST).tagName();
}
@Override
public EntityType typeFromId(final int type) {
return EntityTypes1_21_6.getTypeFromId(type);
}
}
@@ -0,0 +1,193 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.storage;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;
import java.util.concurrent.atomic.AtomicInteger;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.viaversion.viabackwards.utils.ChatUtil.text;
import static com.viaversion.viabackwards.utils.ChatUtil.translate;
/**
* Per-user storage for {@link ChestDialogViewProvider}
*/
public final class ChestDialogStorage implements StorableObject {
private static final byte MIN_FAKE_ID = Byte.MAX_VALUE / 2 + 1;
private static final byte MAX_FAKE_ID = Byte.MAX_VALUE - 1;
private static final AtomicInteger FAKE_ID_COUNTER = new AtomicInteger(MIN_FAKE_ID);
private static final Tag[] RESPONSE_BUTTON_LABELS = new Tag[] {
text(""),
translate("gui.waitingForResponse.button.inactive", 4),
translate("gui.waitingForResponse.button.inactive", 3),
translate("gui.waitingForResponse.button.inactive", 2),
translate("gui.waitingForResponse.button.inactive", 1),
translate("gui.back")
};
private final ChestDialogViewProvider provider;
private final Dialog dialog;
private int containerId;
private Dialog previousDialog;
public int page;
private Item[] items;
private int confirmationYesIndex = -1;
private int confirmationNoIndex = -1;
private int actionIndex = -1;
private @Nullable Phase phase;
private int ticksWaitingForResponse = 0;
private boolean closeButtonEnabled;
private Tag closeButtonLabel;
private TextInput currentTextInput;
private boolean allowClosing;
public ChestDialogStorage(final ChestDialogViewProvider provider, final Dialog dialog) {
this.provider = provider;
this.dialog = dialog;
}
public void tick(final UserConnection connection) {
if (phase != Phase.WAITING_FOR_RESPONSE) {
return;
}
final int index = ticksWaitingForResponse++ / 20;
if (index > RESPONSE_BUTTON_LABELS.length - 1) {
return;
}
closeButtonEnabled = index >= 1;
closeButtonLabel = RESPONSE_BUTTON_LABELS[index];
provider.updateDialog(connection, dialog);
}
public Dialog dialog() {
return dialog;
}
public @Nullable Dialog previousDialog() {
return previousDialog;
}
public void setPreviousDialog(final Dialog previousDialog) {
this.previousDialog = previousDialog;
}
public int containerId() {
return containerId;
}
public Item[] items() {
return items;
}
public void setItems(final Item[] items, final int confirmationYesIndex, final int confirmationNoIndex, final int actionIndex) {
this.items = items;
this.confirmationYesIndex = confirmationYesIndex;
this.confirmationNoIndex = confirmationNoIndex;
this.actionIndex = actionIndex;
}
public int confirmationYesIndex() {
return confirmationYesIndex;
}
public int confirmationNoIndex() {
return confirmationNoIndex;
}
public int actionIndex() {
return actionIndex;
}
public @Nullable Phase phase() {
return phase;
}
public boolean closeButtonEnabled() {
return closeButtonEnabled;
}
public Tag closeButtonLabel() {
return closeButtonLabel;
}
public TextInput currentTextInput() {
return currentTextInput;
}
public boolean allowClosing() {
return allowClosing;
}
public void setPhase(final UserConnection connection, final @Nullable Phase phase) {
if (phase == Phase.DIALOG_VIEW || phase == Phase.ANVIL_VIEW) {
if (this.phase != null) {
final PacketWrapper containerClose = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_CLOSE, connection);
containerClose.write(Types.VAR_INT, containerId);
containerClose.send(Protocol1_21_6To1_21_5.class);
}
currentTextInput = null;
final int id = FAKE_ID_COUNTER.getAndIncrement();
if (id > MAX_FAKE_ID) {
FAKE_ID_COUNTER.set(MIN_FAKE_ID);
containerId = MIN_FAKE_ID;
} else {
containerId = (byte) id;
}
}
this.phase = phase;
}
public void setCurrentTextInput(final TextInput currentTextInput) {
this.currentTextInput = currentTextInput;
}
public void setAllowClosing(final boolean allowClosing) {
this.allowClosing = allowClosing;
}
public enum Phase {
DIALOG_VIEW,
ANVIL_VIEW,
WAITING_FOR_RESPONSE
}
}
@@ -0,0 +1,87 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.storage;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.DialogViewProvider;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;
import com.viaversion.viaversion.util.Key;
import java.util.UUID;
public final class ClickEvents implements StorableObject {
private final Object2ObjectMap<String, CompoundTag> clickEvents = new Object2ObjectArrayMap<>();
public String storeClickEvent(final CompoundTag clickEvent) {
final String id = "vv_" + UUID.randomUUID();
if (clickEvents.containsKey(id)) {
return storeClickEvent(clickEvent); // Ensure unique ID
}
clickEvents.put(id, clickEvent);
return id;
}
public boolean handleChatCommand(final UserConnection connection, final String command) {
final CompoundTag clickEvent = clickEvents.get(command);
if (clickEvent != null) {
handleClickEvent(connection, clickEvent);
return true;
} else {
return false;
}
}
public static void handleClickEvent(final UserConnection connection, final CompoundTag clickEvent) {
final String action = Key.stripMinecraftNamespace(clickEvent.getString("action"));
if ("show_dialog".equals(action)) {
final RegistryAndTags registryAndTags = connection.get(RegistryAndTags.class);
final ServerLinks serverLinks = connection.get(ServerLinks.class);
CompoundTag dialogTag = clickEvent.getCompoundTag("dialog");
if (dialogTag == null) {
final StringTag dialogReferenceTag = clickEvent.getStringTag("dialog");
if (dialogReferenceTag != null) { // No tags here
dialogTag = registryAndTags.fromRegistry(Key.stripMinecraftNamespace(dialogReferenceTag.getValue()));
}
}
final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);
provider.openDialog(connection, new Dialog(registryAndTags, serverLinks, dialogTag));
} else if ("custom".equals(action)) {
final String id = clickEvent.getString("id");
final CompoundTag payload = clickEvent.getCompoundTag("payload");
final PacketWrapper customClickAction = PacketWrapper.create(ServerboundPackets1_21_6.CUSTOM_CLICK_ACTION, connection);
customClickAction.write(Types.STRING, id);
customClickAction.write(Types.CUSTOM_CLICK_ACTION_TAG, payload);
customClickAction.sendToServer(Protocol1_21_6To1_21_5.class);
}
}
}
@@ -0,0 +1,74 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.storage;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.KeyMappings;
public final class RegistryAndTags implements StorableObject {
private KeyMappings dialogMappings;
private Object2ObjectMap<String, CompoundTag> dialogs;
private Object2ObjectMap<String, int[]> dialogTags;
public void storeRegistry(final KeyMappings dialogMappings, final Object2ObjectMap<String, CompoundTag> dialogs) {
this.dialogMappings = dialogMappings;
this.dialogs = dialogs;
}
public CompoundTag fromRegistry(final int id) {
return dialogs.get(dialogMappings.idToKey(id));
}
public CompoundTag fromRegistry(final String key) {
return dialogs.get(Key.stripMinecraftNamespace(key));
}
public boolean tagsSent() {
return dialogTags != null && !dialogTags.isEmpty();
}
public void storeTags(final String key, final int[] ids) {
if (dialogTags == null) {
dialogTags = new Object2ObjectArrayMap<>();
}
dialogTags.put(Key.stripMinecraftNamespace(key), ids);
}
public int[] fromKey(final String key) {
return dialogTags.get(Key.stripMinecraftNamespace(key));
}
public CompoundTag[] fromRegistryKey(final String key) {
final int[] ids = fromKey(key);
if (ids == null) {
return null;
}
final CompoundTag[] tags = new CompoundTag[ids.length];
for (int i = 0; i < ids.length; i++) {
tags[i] = fromRegistry(ids[i]);
}
return tags;
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.storage;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import static com.viaversion.viabackwards.utils.ChatUtil.translate;
public final class ServerLinks implements StorableObject {
private static final CompoundTag REPORT_BUG = translate("known_server_link.report_bug");
private static final CompoundTag COMMUNITY_GUIDELINES = translate("known_server_link.community_guidelines");
private static final CompoundTag SUPPORT = translate("known_server_link.support");
private static final CompoundTag STATUS = translate("known_server_link.status");
private static final CompoundTag FEEDBACK = translate("known_server_link.feedback");
private static final CompoundTag COMMUNITY = translate("known_server_link.community");
private static final CompoundTag WEBSITE = translate("known_server_link.website");
private static final CompoundTag FORUMS = translate("known_server_link.forums");
private static final CompoundTag NEWS = translate("known_server_link.news");
private static final CompoundTag ANNOUNCEMENTS = translate("known_server_link.announcements");
private final Object2ObjectMap<Tag, String> links = new Object2ObjectArrayMap<>();
public void storeLink(final Tag tag, final String uri) {
links.put(tag, uri);
}
public void storeLink(final int id, final String uri) {
switch (id) {
case 1 -> storeLink(COMMUNITY_GUIDELINES, uri);
case 2 -> storeLink(SUPPORT, uri);
case 3 -> storeLink(STATUS, uri);
case 4 -> storeLink(FEEDBACK, uri);
case 5 -> storeLink(COMMUNITY, uri);
case 6 -> storeLink(WEBSITE, uri);
case 7 -> storeLink(FORUMS, uri);
case 8 -> storeLink(NEWS, uri);
case 9 -> storeLink(ANNOUNCEMENTS, uri);
default -> storeLink(REPORT_BUG, uri);
}
}
public Object2ObjectMap<Tag, String> links() {
return links;
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_6to1_21_5.task;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;
import com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.protocol.ProtocolRunnable;
public final class ChestDialogViewTask extends ProtocolRunnable {
public ChestDialogViewTask() {
super(Protocol1_21_6To1_21_5.class);
}
@Override
public void run(final UserConnection connection) {
final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);
if (storage != null) {
storage.tick(connection);
}
}
}
@@ -0,0 +1,150 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_7to1_21_6;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.rewriters.SoundRewriter;
import com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;
import com.viaversion.viabackwards.api.rewriters.text.TranslatableRewriter;
import com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter.BlockItemPacketRewriter1_21_7;
import com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter.EntityPacketRewriter1_21_7;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.data.item.ItemHasherBase;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundConfigurationPackets1_21_6;
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_6to1_21_7.Protocol1_21_6To1_21_7;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.StatisticsRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.util.SerializerVersion;
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
public final class Protocol1_21_7To1_21_6 extends BackwardsProtocol<ClientboundPacket1_21_6, ClientboundPacket1_21_6, ServerboundPacket1_21_6, ServerboundPacket1_21_6> {
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.21.7", "1.21.6", Protocol1_21_6To1_21_7.class);
private final EntityPacketRewriter1_21_7 entityRewriter = new EntityPacketRewriter1_21_7(this);
private final BlockItemPacketRewriter1_21_7 itemRewriter = new BlockItemPacketRewriter1_21_7(this);
private final ParticleRewriter<ClientboundPacket1_21_6> particleRewriter = new ParticleRewriter<>(this);
private final NBTComponentRewriter<ClientboundPacket1_21_6> translatableRewriter = new NBTComponentRewriter<>(this);
private final TagRewriter<ClientboundPacket1_21_6> tagRewriter = new TagRewriter<>(this);
public Protocol1_21_7To1_21_6() {
super(ClientboundPacket1_21_6.class, ClientboundPacket1_21_6.class, ServerboundPacket1_21_6.class, ServerboundPacket1_21_6.class);
}
@Override
protected void registerPackets() {
super.registerPackets();
tagRewriter.registerGeneric(ClientboundPackets1_21_6.UPDATE_TAGS);
tagRewriter.registerGeneric(ClientboundConfigurationPackets1_21_6.UPDATE_TAGS);
final SoundRewriter<ClientboundPacket1_21_6> soundRewriter = new SoundRewriter<>(this);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_6.SOUND);
soundRewriter.registerSound1_19_3(ClientboundPackets1_21_6.SOUND_ENTITY);
soundRewriter.registerStopSound(ClientboundPackets1_21_6.STOP_SOUND);
new StatisticsRewriter<>(this).register(ClientboundPackets1_21_6.AWARD_STATS);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_ACTION_BAR_TEXT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_TITLE_TEXT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SET_SUBTITLE_TEXT);
translatableRewriter.registerBossEvent(ClientboundPackets1_21_6.BOSS_EVENT);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.DISCONNECT);
translatableRewriter.registerTabList(ClientboundPackets1_21_6.TAB_LIST);
translatableRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_21_6.PLAYER_COMBAT_KILL);
translatableRewriter.registerPlayerInfoUpdate1_21_4(ClientboundPackets1_21_6.PLAYER_INFO_UPDATE);
translatableRewriter.registerComponentPacket(ClientboundPackets1_21_6.SYSTEM_CHAT);
translatableRewriter.registerDisguisedChat(ClientboundPackets1_21_6.DISGUISED_CHAT);
translatableRewriter.registerPlayerChat1_21_5(ClientboundPackets1_21_6.PLAYER_CHAT);
translatableRewriter.registerPing();
particleRewriter.registerLevelParticles1_21_4(ClientboundPackets1_21_6.LEVEL_PARTICLES);
particleRewriter.registerExplode1_21_2(ClientboundPackets1_21_6.EXPLODE);
}
@Override
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_6.PLAYER));
addItemHasher(connection, new ItemHasherBase(this, connection, SerializerVersion.V1_21_6, SerializerVersion.V1_21_6));
}
@Override
public BackwardsMappingData getMappingData() {
return MAPPINGS;
}
@Override
public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> types() {
return VersionedTypes.V1_21_6;
}
@Override
public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> mappedTypes() {
return VersionedTypes.V1_21_6;
}
@Override
public TranslatableRewriter getComponentRewriter() {
return translatableRewriter;
}
@Override
public EntityPacketRewriter1_21_7 getEntityRewriter() {
return entityRewriter;
}
@Override
public BlockItemPacketRewriter1_21_7 getItemRewriter() {
return itemRewriter;
}
@Override
public ParticleRewriter<ClientboundPacket1_21_6> getParticleRewriter() {
return particleRewriter;
}
@Override
public TagRewriter<ClientboundPacket1_21_6> getTagRewriter() {
return tagRewriter;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21_6, ClientboundPacket1_21_6, ServerboundPacket1_21_6, ServerboundPacket1_21_6> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),
packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),
packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class),
packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class)
);
}
}
@@ -0,0 +1,63 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_7to1_21_6.rewriter;
import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;
import com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
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.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
public final class BlockItemPacketRewriter1_21_7 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_6, ServerboundPacket1_21_6, Protocol1_21_7To1_21_6> {
public BlockItemPacketRewriter1_21_7(final Protocol1_21_7To1_21_6 protocol) {
super(protocol);
}
@Override
public void registerPackets() {
final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = BlockRewriter.for1_20_2(protocol);
blockRewriter.registerBlockEvent(ClientboundPackets1_21_6.BLOCK_EVENT);
blockRewriter.registerBlockUpdate(ClientboundPackets1_21_6.BLOCK_UPDATE);
blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_6.SECTION_BLOCKS_UPDATE);
blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21_6.LEVEL_EVENT, 2001);
blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21_6.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_21_5::new);
blockRewriter.registerBlockEntityData(ClientboundPackets1_21_6.BLOCK_ENTITY_DATA);
registerSetCursorItem(ClientboundPackets1_21_6.SET_CURSOR_ITEM);
registerSetPlayerInventory(ClientboundPackets1_21_6.SET_PLAYER_INVENTORY);
registerCooldown1_21_2(ClientboundPackets1_21_6.COOLDOWN);
registerSetContent1_21_2(ClientboundPackets1_21_6.CONTAINER_SET_CONTENT);
registerSetSlot1_21_2(ClientboundPackets1_21_6.CONTAINER_SET_SLOT);
registerAdvancements1_20_3(ClientboundPackets1_21_6.UPDATE_ADVANCEMENTS);
registerSetEquipment(ClientboundPackets1_21_6.SET_EQUIPMENT);
registerMerchantOffers1_20_5(ClientboundPackets1_21_6.MERCHANT_OFFERS);
registerContainerClick1_21_5(ServerboundPackets1_21_6.CONTAINER_CLICK);
registerSetCreativeModeSlot1_21_5(ServerboundPackets1_21_6.SET_CREATIVE_MODE_SLOT);
final RecipeDisplayRewriter<ClientboundPacket1_21_6> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(protocol);
recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21_6.UPDATE_RECIPES);
recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21_6.RECIPE_BOOK_ADD);
recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21_6.PLACE_GHOST_RECIPE);
}
}
@@ -0,0 +1,71 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* 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.viabackwards.protocol.v1_21_7to1_21_6.rewriter;
import com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
public final class EntityPacketRewriter1_21_7 extends EntityRewriter<ClientboundPacket1_21_6, Protocol1_21_7To1_21_6> {
public EntityPacketRewriter1_21_7(final Protocol1_21_7To1_21_6 protocol) {
super(protocol, VersionedTypes.V1_21_6.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_6.entityDataTypes.booleanType);
}
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21_6.ADD_ENTITY, EntityTypes1_21_4.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21_6.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21_6.REMOVE_ENTITIES);
registerPlayerAbilities(ClientboundPackets1_21_6.PLAYER_ABILITIES);
registerGameEvent(ClientboundPackets1_21_6.GAME_EVENT);
registerLogin1_20_5(ClientboundPackets1_21_6.LOGIN);
registerRespawn1_20_5(ClientboundPackets1_21_6.RESPAWN);
final RegistryDataRewriter registryDataRewriter = new BackwardsRegistryRewriter(protocol);
protocol.registerClientbound(ClientboundConfigurationPackets1_21_6.REGISTRY_DATA, registryDataRewriter::handle);
}
@Override
protected void registerRewrites() {
final EntityDataTypes1_21_5 mappedEntityDataTypes = VersionedTypes.V1_21_6.entityDataTypes;
registerEntityDataTypeHandler1_20_3(
mappedEntityDataTypes.itemType,
mappedEntityDataTypes.blockStateType,
mappedEntityDataTypes.optionalBlockStateType,
mappedEntityDataTypes.particleType,
mappedEntityDataTypes.particlesType,
mappedEntityDataTypes.componentType,
mappedEntityDataTypes.optionalComponentType
);
}
@Override
public EntityType typeFromId(final int type) {
return EntityTypes1_21_6.getTypeFromId(type);
}
}
@@ -29,13 +29,16 @@ import com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.OpenScreenStor
import com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.PlayerRotationStorage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_20_5;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.ChatType;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundConfigurationPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;
@@ -59,7 +62,7 @@ public final class Protocol1_21To1_20_5 extends BackwardsProtocol<ClientboundPac
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.21", "1.20.5", Protocol1_20_5To1_21.class);
private final EntityPacketRewriter1_21 entityRewriter = new EntityPacketRewriter1_21(this);
private final BlockItemPacketRewriter1_21 itemRewriter = new BlockItemPacketRewriter1_21(this);
private final ParticleRewriter<ClientboundPacket1_21> particleRewriter = new ParticleRewriter<>(this, Types1_21.PARTICLE, Types1_20_5.PARTICLE);
private final ParticleRewriter<ClientboundPacket1_21> particleRewriter = new ParticleRewriter<>(this);
private final JsonNBTComponentRewriter<ClientboundPacket1_21> translatableRewriter = new ComponentRewriter1_21(this);
private final TagRewriter<ClientboundPacket1_21> tagRewriter = new TagRewriter<>(this);
@@ -141,10 +144,12 @@ public final class Protocol1_21To1_20_5 extends BackwardsProtocol<ClientboundPac
if (chatType.isDirect()) {
// Oh well
wrapper.write(Types.VAR_INT, 0);
return;
} else {
wrapper.write(Types.VAR_INT, chatType.id());
}
wrapper.write(Types.VAR_INT, chatType.id());
translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Name
translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_TAG)); // Target Name
});
registerClientbound(ClientboundPackets1_21.UPDATE_ATTRIBUTES, wrapper -> {
@@ -228,6 +233,16 @@ public final class Protocol1_21To1_20_5 extends BackwardsProtocol<ClientboundPac
return tagRewriter;
}
@Override
public Types1_21 types() {
return VersionedTypes.V1_21;
}
@Override
public Types1_20_5<StructuredDataKeys1_20_5, EntityDataTypes1_20_5> mappedTypes() {
return VersionedTypes.V1_20_5;
}
@Override
protected PacketTypesProvider<ClientboundPacket1_21, ClientboundPacket1_20_5, ServerboundPacket1_20_5, ServerboundPacket1_20_5> createPacketTypesProvider() {
return new SimplePacketTypesProvider<>(
@@ -39,8 +39,6 @@ import com.viaversion.viaversion.api.minecraft.item.data.Enchantments;
import com.viaversion.viaversion.api.minecraft.item.data.JukeboxPlayable;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;
import com.viaversion.viaversion.libs.mcstructs.text.TextFormatting;
import com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent;
@@ -65,10 +63,7 @@ public final class BlockItemPacketRewriter1_21 extends BackwardsStructuredItemRe
private final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this);
public BlockItemPacketRewriter1_21(final Protocol1_21To1_20_5 protocol) {
super(protocol,
Types1_21.ITEM, Types1_21.ITEM_ARRAY, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY,
Types1_21.ITEM_COST, Types1_21.OPTIONAL_ITEM_COST, Types1_20_5.ITEM_COST, Types1_20_5.OPTIONAL_ITEM_COST
);
super(protocol);
}
@Override
@@ -180,8 +175,10 @@ public final class BlockItemPacketRewriter1_21 extends BackwardsStructuredItemRe
final var component = SerializerVersion.V1_20_5.toComponent(description);
component.getStyle().setItalic(false);
component.getStyle().setFormatting(TextFormatting.GRAY);
component.getSiblings().add(new StringComponent(" "));
component.getSiblings().add(new TranslationComponent(EnchantmentRewriter.ENCHANTMENT_LEVEL_TRANSLATION.formatted(level)));
if (level != 1 || storage.enchantmentMaxLevel(id) != 1) {
component.getSiblings().add(new StringComponent(" "));
component.getSiblings().add(new TranslationComponent(EnchantmentRewriter.ENCHANTMENT_LEVEL_TRANSLATION.formatted(level)));
}
return SerializerVersion.V1_20_5.toTag(component);
};
@@ -283,12 +280,7 @@ public final class BlockItemPacketRewriter1_21 extends BackwardsStructuredItemRe
if (jukeboxPlayable.song().hasHolder()) {
final Holder<JukeboxPlayable.JukeboxSong> songHolder = jukeboxPlayable.song().holder();
tag.put("song", holderToTag(songHolder, (song, songTag) -> {
songTag.put("sound_event", holderToTag(song.soundEvent(), (soundEvent, soundEventTag) -> {
soundEventTag.putString("identifier", soundEvent.identifier());
if (soundEvent.fixedRange() != null) {
soundEventTag.putFloat("fixed_range", soundEvent.fixedRange());
}
}));
saveSoundEventHolder(songTag, song.soundEvent());
songTag.put("description", song.description());
songTag.putFloat("length_in_seconds", song.lengthInSeconds());
songTag.putInt("comparator_output", song.comparatorOutput());
@@ -314,11 +306,7 @@ public final class BlockItemPacketRewriter1_21 extends BackwardsStructuredItemRe
song = EitherHolder.of(songIdentifier);
} else {
song = EitherHolder.of(restoreHolder(tag, "song", songTag -> {
final Holder<SoundEvent> soundEvent = restoreHolder(songTag, "sound_event", soundTag -> {
final String identifier = soundTag.getString("identifier");
final Float fixedRange = soundTag.contains("fixed_range") ? soundTag.getFloat("fixed_range") : null;
return new SoundEvent(identifier, fixedRange);
});
final Holder<SoundEvent> soundEvent = restoreSoundEventHolder(songTag);
final Tag description = songTag.get("description");
final float lengthInSeconds = songTag.getFloat("length_in_seconds");
final int comparatorOutput = songTag.getInt("comparator_output");
@@ -30,11 +30,13 @@ import com.viaversion.viaversion.api.minecraft.WolfVariant;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_20_5;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
import com.viaversion.viaversion.api.type.types.version.Types1_21;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.data.Paintings1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;
@@ -51,7 +53,7 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
private final Map<String, PaintingData> oldPaintings = new HashMap<>();
public EntityPacketRewriter1_21(final Protocol1_21To1_20_5 protocol) {
super(protocol, Types1_20_5.ENTITY_DATA_TYPES.optionalComponentType, Types1_20_5.ENTITY_DATA_TYPES.booleanType);
super(protocol, protocol.mappedTypes().entityDataTypes().optionalComponentType, protocol.mappedTypes().entityDataTypes().booleanType);
for (int i = 0; i < Paintings1_20_5.PAINTINGS.length; i++) {
final PaintingVariant painting = Paintings1_20_5.PAINTINGS[i];
@@ -62,7 +64,7 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
@Override
public void registerPackets() {
registerTrackerWithData1_19(ClientboundPackets1_21.ADD_ENTITY, EntityTypes1_20_5.FALLING_BLOCK);
registerSetEntityData(ClientboundPackets1_21.SET_ENTITY_DATA, Types1_21.ENTITY_DATA_LIST, Types1_20_5.ENTITY_DATA_LIST);
registerSetEntityData(ClientboundPackets1_21.SET_ENTITY_DATA);
registerRemoveEntities(ClientboundPackets1_21.REMOVE_ENTITIES);
final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol);
@@ -83,13 +85,15 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
storage.setPaintings(new KeyMappings(keys), paintingMappingsForEntries(entries));
} else if (enchantment) {
final Tag[] descriptions = new Tag[entries.length];
final int[] maxLevels = new int[entries.length];
for (int i = 0; i < entries.length; i++) {
final RegistryEntry entry = entries[i];
if (entry.tag() != null) {
descriptions[i] = ((CompoundTag) entry.tag()).get("description");
if (entry.tag() instanceof final CompoundTag tag) {
descriptions[i] = tag.get("description");
maxLevels[i] = tag.getInt("max_level");
}
}
storage.setEnchantments(new KeyMappings(keys), descriptions);
storage.setEnchantments(new KeyMappings(keys), descriptions, maxLevels);
} else {
final int[] jukeboxSongMappings = new int[keys.length];
for (int i = 0; i < keys.length; i++) {
@@ -176,36 +180,37 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
@Override
protected void registerRewrites() {
final EntityDataTypes1_20_5 mappedEntityDataTypes = VersionedTypes.V1_20_5.entityDataTypes;
filter().handler((event, data) -> {
final EntityDataType type = data.dataType();
if (type == Types1_21.ENTITY_DATA_TYPES.wolfVariantType) {
if (type == VersionedTypes.V1_21.entityDataTypes.wolfVariantType) {
final Holder<WolfVariant> variant = data.value();
if (variant.hasId()) {
data.setTypeAndValue(Types1_20_5.ENTITY_DATA_TYPES.wolfVariantType, variant.id());
data.setTypeAndValue(mappedEntityDataTypes.wolfVariantType, variant.id());
} else {
event.cancel();
}
} else if (type == Types1_21.ENTITY_DATA_TYPES.paintingVariantType) {
} else if (type == VersionedTypes.V1_21.entityDataTypes.paintingVariantType) {
final Holder<PaintingVariant> variant = data.value();
if (variant.hasId()) {
final EnchantmentsPaintingsStorage storage = event.user().get(EnchantmentsPaintingsStorage.class);
final int mappedId = storage.mappedPainting(variant.id());
data.setTypeAndValue(Types1_20_5.ENTITY_DATA_TYPES.paintingVariantType, mappedId);
data.setTypeAndValue(mappedEntityDataTypes.paintingVariantType, mappedId);
} else {
event.cancel();
}
} else {
data.setDataType(Types1_20_5.ENTITY_DATA_TYPES.byId(type.typeId()));
data.setDataType(mappedEntityDataTypes.byId(type.typeId()));
}
});
registerEntityDataTypeHandler1_20_3(
Types1_20_5.ENTITY_DATA_TYPES.itemType,
Types1_20_5.ENTITY_DATA_TYPES.blockStateType,
Types1_20_5.ENTITY_DATA_TYPES.optionalBlockStateType,
Types1_20_5.ENTITY_DATA_TYPES.particleType,
Types1_20_5.ENTITY_DATA_TYPES.particlesType,
Types1_20_5.ENTITY_DATA_TYPES.componentType,
Types1_20_5.ENTITY_DATA_TYPES.optionalComponentType
mappedEntityDataTypes.itemType,
mappedEntityDataTypes.blockStateType,
mappedEntityDataTypes.optionalBlockStateType,
mappedEntityDataTypes.particleType,
mappedEntityDataTypes.particlesType,
mappedEntityDataTypes.componentType,
mappedEntityDataTypes.optionalComponentType
);
registerBlockStateHandler(EntityTypes1_20_5.ABSTRACT_MINECART, 11);
}
@@ -27,15 +27,17 @@ public final class EnchantmentsPaintingsStorage implements StorableObject {
private KeyMappings paintings;
private int[] paintingMappings;
private Tag[] enchantmentDescriptions;
private int[] enchantmentMaxLevels;
private int[] jubeboxSongsToItems;
public KeyMappings enchantments() {
return enchantments;
}
public void setEnchantments(final KeyMappings enchantment, final Tag[] enchantmentDescriptions) {
public void setEnchantments(final KeyMappings enchantment, final Tag[] enchantmentDescriptions, final int[] enchantmentMaxLevels) {
this.enchantments = enchantment;
this.enchantmentDescriptions = enchantmentDescriptions;
this.enchantmentMaxLevels = enchantmentMaxLevels;
}
public KeyMappings paintings() {
@@ -67,4 +69,8 @@ public final class EnchantmentsPaintingsStorage implements StorableObject {
public @Nullable Tag enchantmentDescription(final int id) {
return id > 0 && id < enchantmentDescriptions.length ? enchantmentDescriptions[id] : null;
}
public int enchantmentMaxLevel(final int id) {
return id > 0 && id < enchantmentMaxLevels.length ? enchantmentMaxLevels[id] : -1;
}
}
@@ -17,13 +17,18 @@
*/
package com.viaversion.viabackwards.utils;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.libs.mcstructs.text.Style;
import com.viaversion.viaversion.libs.mcstructs.text.TextComponent;
import com.viaversion.viaversion.libs.mcstructs.text.TextFormatting;
import com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;
import com.viaversion.viaversion.libs.mcstructs.text.stringformat.StringFormat;
import com.viaversion.viaversion.libs.mcstructs.text.stringformat.handling.ColorHandling;
import com.viaversion.viaversion.libs.mcstructs.text.stringformat.handling.DeserializerUnknownHandling;
import com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;
import com.viaversion.viaversion.util.SerializerVersion;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@@ -34,6 +39,49 @@ public final class ChatUtil {
private static final Pattern UNUSED_COLOR_PATTERN = Pattern.compile("(?>(?>§[0-fk-or])*(§r|\\Z))|(?>(?>§[0-f])*(§[0-f]))");
private static final Pattern UNUSED_COLOR_PATTERN_PREFIX = Pattern.compile("(?>(?>§[0-fk-or])*(§r))|(?>(?>§[0-f])*(§[0-f]))");
// 1.21.6 methods mainly for dialog screens
public static Tag translate(final String key, final Tag... arguments) {
// Same as below but converting "our" text components to MCStructs components
final TextComponent component = new TranslationComponent(key, Arrays.stream(arguments).map(SerializerVersion.V1_21_6::toComponent).toArray());
return SerializerVersion.V1_21_6.toTag(component);
}
public static Tag translate(final String key, final Object... arguments) {
final TextComponent component = new TranslationComponent(key, arguments);
return SerializerVersion.V1_21_6.toTag(component);
}
public static Tag[] split(final Tag tag, final String delimiter) {
final TextComponent component = SerializerVersion.V1_21_6.toComponent(tag);
return Arrays.stream(TextUtils.split(component, delimiter, false)).map(SerializerVersion.V1_21_6::toTag).toArray(Tag[]::new);
}
public static Tag fixStyle(final Tag tag) {
final TextComponent component = SerializerVersion.V1_21_6.toComponent(tag);
if (component.getStyle().getColor() == null) {
component.getStyle().setFormatting(TextFormatting.WHITE);
}
component.getStyle().setItalic(false);
return SerializerVersion.V1_21_6.toTag(component);
}
public static CompoundTag translate(final String key) {
final CompoundTag tag = new CompoundTag();
tag.putString("translate", key);
return tag;
}
public static CompoundTag text(final String text) {
final CompoundTag tag = new CompoundTag();
tag.putString("text", text);
tag.putString("color", "white");
tag.putBoolean("italic", false);
return tag;
}
// Sub 1.12 methods
public static String removeUnusedColor(String legacy, char defaultColor) {
return removeUnusedColor(legacy, defaultColor, false);
}
@@ -28,11 +28,19 @@ bedrock-at-y-0: false
# If disabled, the client will see them as end portal frames.
sculk-shriekers-to-crying-obsidian: true
#
# Shows scaffolding as water for 1.13.2 clients on 1.14+ servers. This fixes collision issues.
# If disabled, the client will see them as hay blocks.
scaffolding-to-water: false
#
# Maps the darkness effect to blindness for 1.18.2 clients on 1.19+ servers.
map-darkness-effect: true
#
# If enabled, 1.21.3 clients will receive the first float of 1.21.4+ custom model data as int. Disable if you handle this change yourself.
map-custom-model-data: true
#
# If enabled, 1.19.3 clients will receive display entities as armor stands with custom entity data on 1.19.4+ servers. Note that
# this does not support all features display entities offer.
map-display-entities: true
#
# Suppresses warnings of missing emulations for certain features that are not supported (e.g. world height in 1.17+).
suppress-emulation-warnings: false
@@ -1,4 +1,226 @@
{
"1.21.7": {
"item.minecraft.music_disc_lava_chicken.desc": "Hyper Potions - Lava Chicken",
"jukebox_song.minecraft.lava_chicken": "Hyper Potions - Lava Chicken",
"painting.minecraft.dennis.title": "Dennis"
},
"1.21.6": {
"advancements.adventure.heart_transplanter.description": "Place a Creaking Heart with the correct alignment between two Pale Oak Log blocks",
"advancements.adventure.heart_transplanter.title": "Heart Transplanter",
"advancements.husbandry.place_dried_ghast_in_water.description": "Place a Dried Ghast block into water",
"advancements.husbandry.place_dried_ghast_in_water.title": "Stay Hydrated!",
"argument.hexcolor.invalid": "Invalid hex color code '%s'",
"argument.resource_or_id.no_such_element": "Can't find element '%s' in registry '%s'",
"argument.waypoint.invalid": "Selected entity is not a waypoint",
"attribute.name.camera_distance": "Camera Distance",
"attribute.name.waypoint_receive_range": "Waypoint Receive Range",
"attribute.name.waypoint_transmit_range": "Waypoint Transmit Range",
"block.minecraft.dried_ghast": "Dried Ghast",
"book.edit.title": "Book Edit Screen",
"book.sign.title": "Book Sign Screen",
"book.sign.titlebox": "Title",
"book.view.title": "Book View Screen",
"commands.datapack.create.already_exists": "Pack with name '%s' already exists",
"commands.datapack.create.invalid_full_name": "Invalid new pack name '%s'",
"commands.datapack.create.invalid_name": "Invalid characters in new pack name '%s'",
"commands.datapack.create.io_failure": "Can't create pack with name '%s', check logs",
"commands.datapack.create.metadata_encode_failure": "Failed to encode metadata for pack with name '%s': %s",
"commands.datapack.create.success": "Created new empty pack with name '%s'",
"commands.dialog.clear.multiple": "Cleared dialog for %s players",
"commands.dialog.clear.single": "Cleared dialog for %s",
"commands.dialog.show.multiple": "Displayed dialog to %s players",
"commands.dialog.show.single": "Displayed dialog to %s",
"commands.version.build_time": "build_time = %s",
"commands.version.data": "data = %s",
"commands.version.header": "Server version info:",
"commands.version.id": "id = %s",
"commands.version.name": "name = %s",
"commands.version.pack.data": "pack_data = %s",
"commands.version.pack.resource": "pack_resource = %s",
"commands.version.protocol": "protocol = %s (%s)",
"commands.version.series": "series = %s",
"commands.version.stable.no": "stable = no",
"commands.version.stable.yes": "stable = yes",
"commands.waypoint.list.empty": "No waypoints in %s",
"commands.waypoint.list.success": "%s waypoint(s) in %s: %s",
"commands.waypoint.modify.color": "Waypoint color is now %s",
"commands.waypoint.modify.color.reset": "Reset waypoint color",
"commands.waypoint.modify.style": "Waypoint style changed",
"dataPack.locator_bar.description": "Show the direction of other players in multiplayer",
"dataPack.locator_bar.name": "Locator Bar",
"debug.version.header": "Client version info:",
"debug.version.help": "F3 + V = Client version info",
"entity.minecraft.happy_ghast": "Happy Ghast",
"gamerule.locatorBar": "Enable player Locator Bar",
"gamerule.locatorBar.description": "When enabled, a bar is shown on the screen to indicate the direction of players.",
"gui.waitingForResponse.button.inactive": "Back (%ss)",
"gui.waitingForResponse.title": "Waiting for Server",
"item.minecraft.black_harness": "Black Harness",
"item.minecraft.blue_harness": "Blue Harness",
"item.minecraft.brown_harness": "Brown Harness",
"item.minecraft.cyan_harness": "Cyan Harness",
"item.minecraft.gray_harness": "Gray Harness",
"item.minecraft.green_harness": "Green Harness",
"item.minecraft.happy_ghast_spawn_egg": "Happy Ghast Spawn Egg",
"item.minecraft.harness": "Harness",
"item.minecraft.light_blue_harness": "Light Blue Harness",
"item.minecraft.light_gray_harness": "Light Gray Harness",
"item.minecraft.lime_harness": "Lime Harness",
"item.minecraft.magenta_harness": "Magenta Harness",
"item.minecraft.music_disc_tears.desc": "Amos Roddy - Tears",
"item.minecraft.orange_harness": "Orange Harness",
"item.minecraft.pink_harness": "Pink Harness",
"item.minecraft.purple_harness": "Purple Harness",
"item.minecraft.red_harness": "Red Harness",
"item.minecraft.white_harness": "White Harness",
"item.minecraft.yellow_harness": "Yellow Harness",
"jukebox_song.minecraft.tears": "Amos Roddy - Tears",
"key.quickActions": "Quick Actions",
"mco.configure.world.buttons.region_preference": "Select Region...",
"mco.configure.world.close.question.title": "Need to make changes without disruption?",
"mco.configure.world.loading": "Loading Realm",
"mco.configure.world.region_preference": "Region Preference",
"mco.configure.world.region_preference.title": "Region Preference Selection",
"mco.configure.world.settings.expired": "You cannot edit settings of an expired Realm",
"mco.connect.region": "Server region: %s",
"mco.errorMessage.realmsService.configurationError": "An unexpected error occurred while editing world options",
"mco.play.button.realm.closed": "Realm is closed",
"menu.custom_options": "Custom Options...",
"menu.custom_options.title": "Custom Options",
"menu.custom_options.tooltip": "Note: Custom options are provided by third-party servers and/or content.\nHandle with care!",
"menu.custom_screen_info.button_narration": "This is a custom screen. Learn more.",
"menu.custom_screen_info.contents": "The contents of this screen are controlled by third-party servers and maps that are not owned, operated, or supervised by Mojang Studios or Microsoft.\n\nHandle with care! Always be careful when following links and never give away your personal information, including login details.\n\nIf this screen prevents you from playing, you can also disconnect from the current server by using the button below.",
"menu.custom_screen_info.disconnect": "Custom screen rejected",
"menu.custom_screen_info.title": "Note about custom screens",
"menu.custom_screen_info.tooltip": "This is a custom screen. Click here to learn more.",
"menu.quick_actions": "Quick Actions...",
"menu.quick_actions.title": "Quick Actions",
"multiplayer.confirm_command.parse_errors": "You are trying to execute an unrecognized or invalid command.\nAre you sure?\nCommand: %s",
"multiplayer.confirm_command.permissions_required": "You are trying to execute a command that requires elevated permissions.\nThis might negatively affect your game.\nAre you sure?\nCommand: %s",
"multiplayer.confirm_command.title": "Confirm Command Execution",
"music.game.a_familiar_room": "Aaron Cherof - A Familiar Room",
"music.game.an_ordinary_day": "Kumi Tanioka - An Ordinary Day",
"music.game.ancestry": "Lena Raine - Ancestry",
"music.game.below_and_above": "Amos Roddy - Below and Above",
"music.game.broken_clocks": "Amos Roddy - Broken Clocks",
"music.game.bromeliad": "Aaron Cherof - Bromeliad",
"music.game.clark": "C418 - Clark",
"music.game.comforting_memories": "Kumi Tanioka - Comforting Memories",
"music.game.creative.aria_math": "C418 - Aria Math",
"music.game.creative.biome_fest": "C418 - Biome Fest",
"music.game.creative.blind_spots": "C418 - Blind Spots",
"music.game.creative.dreiton": "C418 - Dreiton",
"music.game.creative.haunt_muskie": "C418 - Haunt Muskie",
"music.game.creative.taswell": "C418 - Taswell",
"music.game.crescent_dunes": "Aaron Cherof - Crescent Dunes",
"music.game.danny": "C418 - Danny",
"music.game.deeper": "Lena Raine - Deeper",
"music.game.dry_hands": "C418 - Dry Hands",
"music.game.echo_in_the_wind": "Aaron Cherof - Echo in the Wind",
"music.game.eld_unknown": "Lena Raine - Eld Unknown",
"music.game.end.alpha": "C418 - Alpha",
"music.game.end.boss": "C418 - Boss",
"music.game.end.the_end": "C418 - The End",
"music.game.endless": "Lena Raine - Endless",
"music.game.featherfall": "Aaron Cherof - Featherfall",
"music.game.fireflies": "Amos Roddy - Fireflies",
"music.game.floating_dream": "Kumi Tanioka - Floating Dream",
"music.game.haggstrom": "C418 - Haggstrom",
"music.game.infinite_amethyst": "Lena Raine - Infinite Amethyst",
"music.game.key": "C418 - Key",
"music.game.komorebi": "Kumi Tanioka - komorebi",
"music.game.left_to_bloom": "Lena Raine - Left to Bloom",
"music.game.lilypad": "Amos Roddy - Lilypad",
"music.game.living_mice": "C418 - Living Mice",
"music.game.mice_on_venus": "C418 - Mice on Venus",
"music.game.minecraft": "C418 - Minecraft",
"music.game.nether.ballad_of_the_cats": "C418 - Ballad of the Cats",
"music.game.nether.concrete_halls": "C418 - Concrete Halls",
"music.game.nether.crimson_forest.chrysopoeia": "Lena Raine - Chrysopoeia",
"music.game.nether.dead_voxel": "C418 - Dead Voxel",
"music.game.nether.nether_wastes.rubedo": "Lena Raine - Rubedo",
"music.game.nether.soulsand_valley.so_below": "Lena Raine - So Below",
"music.game.nether.warmth": "C418 - Warmth",
"music.game.one_more_day": "Lena Raine - One More Day",
"music.game.os_piano": "Amos Roddy - O's Piano",
"music.game.oxygene": "C418 - Oxygène",
"music.game.pokopoko": "Kumi Tanioka - pokopoko",
"music.game.puzzlebox": "Aaron Cherof - Puzzlebox",
"music.game.stand_tall": "Lena Raine - Stand Tall",
"music.game.subwoofer_lullaby": "C418 - Subwoofer Lullaby",
"music.game.swamp.aerie": "Lena Raine - Aerie",
"music.game.swamp.firebugs": "Lena Raine - Firebugs",
"music.game.swamp.labyrinthine": "Lena Raine - Labyrinthine",
"music.game.sweden": "C418 - Sweden",
"music.game.watcher": "Aaron Cherof - Watcher",
"music.game.water.axolotl": "C418 - Axolotl",
"music.game.water.dragon_fish": "C418 - Dragon Fish",
"music.game.water.shuniji": "C418 - Shuniji",
"music.game.wending": "Lena Raine - Wending",
"music.game.wet_hands": "C418 - Wet Hands",
"music.game.yakusoku": "Kumi Tanioka - yakusoku",
"music.menu.beginning_2": "C418 - Beginning 2",
"music.menu.floating_trees": "C418 - Floating Trees",
"music.menu.moog_city_2": "C418 - Moog City 2",
"music.menu.mutation": "C418 - Mutation",
"narration.item": "Item: %s",
"options.music_frequency": "Music Frequency",
"options.music_frequency.constant": "Constant",
"options.music_frequency.frequent": "Frequent",
"options.music_frequency.tooltip": "Changes how frequently music plays while in a game world.",
"options.renderCloudsDistance": "Cloud Distance",
"options.showNowPlayingToast": "Show Music Toast",
"options.showNowPlayingToast.tooltip": "Displays a toast whenever a song starts playing. The same toast is constantly displayed in the in-game pause menu while a song is playing.",
"realms.configuration.region_preference.automatic_owner": "Automatic (Realm owner ping)",
"realms.configuration.region_preference.automatic_player": "Automatic (first to join session)",
"realms.configuration.region.australia_east": "New South Wales, Australia",
"realms.configuration.region.australia_southeast": "Victoria, Australia",
"realms.configuration.region.brazil_south": "Brazil",
"realms.configuration.region.central_india": "India",
"realms.configuration.region.central_us": "Iowa, USA",
"realms.configuration.region.east_asia": "Hong Kong",
"realms.configuration.region.east_us": "Virginia, USA",
"realms.configuration.region.east_us_2": "North Carolina, USA",
"realms.configuration.region.france_central": "France",
"realms.configuration.region.japan_east": "Eastern Japan",
"realms.configuration.region.japan_west": "Western Japan",
"realms.configuration.region.korea_central": "South Korea",
"realms.configuration.region.north_central_us": "Illinois, USA",
"realms.configuration.region.north_europe": "Ireland",
"realms.configuration.region.south_central_us": "Texas, USA",
"realms.configuration.region.southeast_asia": "Singapore",
"realms.configuration.region.sweden_central": "Sweden",
"realms.configuration.region.uae_north": "United Arab Emirates (UAE)",
"realms.configuration.region.uk_south": "Southern England",
"realms.configuration.region.west_central_us": "Utah, USA",
"realms.configuration.region.west_europe": "Netherlands",
"realms.configuration.region.west_us": "California, USA",
"realms.configuration.region.west_us_2": "Washington, USA",
"soundCategory.ui": "UI",
"stat.minecraft.happy_ghast_one_cm": "Distance by Happy Ghast",
"subtitles.block.dried_ghast.ambient": "Sounds of dryness",
"subtitles.block.dried_ghast.ambient_water": "Dried Ghast rehydrates",
"subtitles.block.dried_ghast.place_in_water": "Dried Ghast soaks",
"subtitles.block.dried_ghast.transition": "Dried Ghast feels better",
"subtitles.entity.ghastling.ambient": "Ghastling coos",
"subtitles.entity.ghastling.death": "Ghastling dies",
"subtitles.entity.ghastling.hurt": "Ghastling hurts",
"subtitles.entity.ghastling.spawn": "Ghastling appears",
"subtitles.entity.happy_ghast.ambient": "Happy Ghast croons",
"subtitles.entity.happy_ghast.death": "Happy Ghast dies",
"subtitles.entity.happy_ghast.equip": "Harness equips",
"subtitles.entity.happy_ghast.harness_goggles_down": "Happy Ghast is ready",
"subtitles.entity.happy_ghast.harness_goggles_up": "Happy Ghast stops",
"subtitles.entity.happy_ghast.hurt": "Happy Ghast hurts",
"subtitles.entity.happy_ghast.unequip": "Harness unequips",
"subtitles.item.horse_armor.unequip": "Horse Armor snips away",
"subtitles.item.lead.break": "Lead snaps",
"subtitles.item.lead.tied": "Lead tied",
"subtitles.item.lead.untied": "Lead untied",
"subtitles.item.llama_carpet.unequip": "Carpet snips away",
"subtitles.item.saddle.unequip": "Saddle snips away",
"subtitles.item.shears.snip": "Shears snip"
},
"1.21.5": {
"argument.nbt.expected.compound": "Expected compound tag",
"argument.resource_selector.not_found": "No matches for selector '%s' of type '%s'",
+3 -3
View File
@@ -1,8 +1,8 @@
projectVersion=5.3.2
projectVersion=5.4.1
# Smile emoji
mcVersions=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
mcVersionRange=1.10-1.21.5
mcVersions=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
mcVersionRange=1.10-1.21.6
velocityVersion=3.4
org.gradle.configureondemand=true
+2 -2
View File
@@ -3,7 +3,7 @@ metadata.format.version = "1.1"
[versions]
# ViaVersion
viaver = "5.3.2-SNAPSHOT"
viaver = "5.4.1-mc1.21.7-rc2-SNAPSHOT"
# Common provided
netty = "4.0.20.Final"
@@ -16,7 +16,7 @@ checkerQual = "3.49.1"
paper = "1.16.5-R0.1-SNAPSHOT"
velocity = "3.1.1"
fabricLoader = "0.11.6"
viaProxy = "3.4.1-SNAPSHOT"
viaProxy = "3.4.3-SNAPSHOT"
[libraries]
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+2 -2
View File
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -2
View File
@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+2 -2
View File
@@ -1,7 +1,5 @@
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "viabackwards-parent"
dependencyResolutionManagement {
repositories {
maven("https://repo.viaversion.com")
@@ -19,6 +17,8 @@ pluginManagement {
}
}
rootProject.name = "viabackwards-parent"
includeBuild("build-logic")
setupViaSubproject("common")