Compare commits

...
125 Commits
Author SHA1 Message Date
Nassim Jahnke fdfbee3099 Remove cushion display name 2026-10-29 11:31:43 +01:00
Nassim Jahnke 4e021f23ad 5.12.2 Release 2026-10-27 16:24:13 +01:00
Nassim Jahnke 9bf8a6b447 Remove map decorations from hover events in 26.3->26.2 2026-10-27 16:24:02 +01:00
olcxja 0452ac26f3 Fix unknown block states in registry data for pre-26.3 clients (#1372) 2026-10-27 16:03:46 +01:00
Nassim Jahnke 4938de8a33 Add more vanilla limits/sanity checks 2026-10-27 15:36:38 +01:00
Nassim Jahnke c3bdf7230c Fix project id 2026-10-21 13:35:56 +02:00
Nassim Jahnke 104d68c29f Update deps, move from minotaur to mod-publish-plugin
Minotaur is barely maintained, and mod-publish-plugin lets us remove the annoying version handling
2026-10-21 13:29:09 +02:00
Nassim Jahnke c7702cc5a3 Update VV usage 2026-10-18 22:13:36 +02:00
Florian Reuth 90fb84e012 Fix items atlas text components in 1.21.11->1.21.9 being invalid (#1364) 2026-10-18 22:05:44 +02:00
Florian Reuth 2aec24449e Add missing creative item backup code to 1.21.2->1.21 item handling (#1358) 2026-10-12 18:58:31 +02:00
Nassim Jahnke cbafc0564e Replace block state providers with dummy entries
They're never shown or used by the client.

Closes #1357
2026-10-11 20:36:51 +02:00
Florian Reuth e7b588b965 Handle long arrays inside lists in 1.12->1.11.1 (#1355) 2026-10-11 20:15:24 +02:00
Florian Reuth d85de3461a Fix spawner handling registering in 1.11->1.10 (#1354) 2026-10-11 20:14:31 +02:00
Florian Reuth 924179d2af Always rewrite enchantments in 1.19->1.18.2 (#1356) 2026-10-11 20:10:20 +02:00
mfishma 465dc5bbef Fix 26.3 map decorations not getting restored (#1350) 2026-10-11 09:27:38 +02:00
Rocco 71038072ed Save original int map ID if conversion is lossy in 1.13->1.12 (#1341) 2026-10-11 09:26:05 +02:00
mfishma 6cdf663490 Map poplar leaves backwards to varied leaves in 26.3->26.2 2026-10-09 16:44:14 +02:00
Nassim Jahnke 0ba32cf4a5 Handle experimental minecart velocity in 1.21.2->1.21
Fixes #1333
2026-10-09 15:46:04 +02:00
Nassim Jahnke 5b1c323274 Bump version to 5.12.2-SNAPSHOT 2026-10-09 15:35:31 +02:00
Nassim Jahnke 1d954e23ab Separate Fabric platform on modrinth
... or it'll fail when specifying versions below 1.14
2026-10-08 16:05:02 +02:00
Nassim Jahnke cece459a36 5.12.1 Release 2026-10-08 15:42:33 +02:00
TomoyaandNassim Jahnke d413b4af3a Fix some enchantment term rewriting in 26.3->26.2 (#1346)
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2026-10-08 15:13:55 +02:00
Florian Reuth ec63e9f808 Back up non-default canDestroyBlocksInCreative in 1.21.5->1.21.4 (#1345) 2026-10-01 18:37:24 +02:00
Florian Reuth 5a84625078 Backup wolf collar in 1.21.5->1.21.4 (#1344) 2026-10-01 18:36:35 +02:00
Florian Reuth a36dfbc799 Fix conditions swap in kinetic weapon restore in 1.21.11->1.21.9 (#1343) 2026-10-01 14:56:13 +02:00
Florian Reuth db6fc3d60d Fix jukebox_playable restore in 1.21->1.20.5 (#1342) 2026-10-01 14:54:58 +02:00
Nassim Jahnke 9a5b384e55 Remove no longer needed head yaw packet for mannequins 2026-09-29 18:41:36 +02:00
Florian Reuth f801af36f8 Update VV api usage (#1340) 2026-09-29 18:41:17 +02:00
Rocco 883ca8dc01 Send player head yaw on spawn in 1.20.2->1.20 (#1337) 2026-09-29 18:27:35 +02:00
Florian Reuth 41fbca556e Handle tag displays without tag key in 26.3->26.2 (#1336) 2026-09-29 18:24:11 +02:00
Rocco 32e81d9887 Fix 1.12.2 red=0 dust particle color (#1323) 2026-09-29 18:19:52 +02:00
terbin 906b3afa0b Add statistic mappings for 1.17->1.16.2 (#1339) 2026-09-29 14:46:47 +02:00
Rocco ddc776c284 Change resin trim material to gold for 1.21.2 clients (#1330) 2026-09-28 19:54:49 +02:00
Florian Reuth c73db36674 Update VV api usage (#1335) 2026-09-28 19:35:18 +02:00
Happy-FZM 7bb4a5d69c Move Dyeable to correct version 2026-09-25 15:01:13 +02:00
Rocco 4bb1b681e6 Add potion color mappings for new 1.19.4 colors (#1329) 2026-09-22 21:58:28 +02:00
Nassim Jahnke 53c43db7ad Add missing tags across protocols 2026-09-20 13:08:36 +02:00
Florian Reuth 3f9073ee68 Always show end credits in 1.21->1.20.5 (#1324) 2026-09-20 13:00:13 +02:00
Nassim Jahnke 283e40d32d Map cushion to carpet falling block
Closes #1322
2026-09-19 17:27:04 +02:00
Rocco 53105e644d Update armor from containers in 1.21.2->1.21 (#1321) 2026-09-19 11:41:11 +02:00
Rocco cd4470c7df Calculate painting offsets in 1.19->1.18.2 (#1313) 2026-09-19 11:39:27 +02:00
Nassim Jahnke 64e049fa0f Bump version to 4.12.1-SNAPSHOT 2026-09-19 11:12:16 +02:00
Nassim Jahnke 321f708674 5.12.0 Release 2026-09-18 16:45:21 +02:00
Nassim Jahnke de7fa4e6a3 Remove brewing stand total fuel and total brewing time 2026-09-16 11:51:03 +02:00
Florian Reuth a3e47e2d66 Backup/Restore new map decoration types
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-14 22:22:21 +02:00
Nassim Jahnke f638e8488d Update mappings 2026-09-14 17:08:41 +02:00
Nassim Jahnke 91f5a62de0 Update map decoration type ids 2026-09-14 17:06:48 +02:00
Florian Reuth 8e09ed3aea Move client_tick_end emulation to movement packets during movement
Prevents sending of multiple position packets per client tick which 26.3+ no longer allows.

Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 18:01:16 +02:00
Florian Reuth 6c97db3867 Handle block_state_property registry changes
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 15:38:40 +02:00
Florian Reuth c0305804a4 Replace mask type in player_chat
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 14:42:35 +02:00
Florian Reuth 6518739c21 Update VV usage
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 14:03:32 +02:00
Florian Reuth 5436cc0062 Handle new type in player_action
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 13:37:32 +02:00
Florian Reuth 138b2013d6 Backup inconvertible item data for creative clients
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 12:56:23 +02:00
Nassim Jahnke 59eb5ccbc8 Fix sign update 2026-09-12 12:39:23 +02:00
Nassim Jahnke 446d878671 Add translation mappings 2026-09-12 12:35:41 +02:00
Nassim Jahnke a0fa6a5e04 Remove remaining new data in hover events 2026-09-12 12:33:45 +02:00
Florian Reuth 00cbc4d149 Update template protocol
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 12:02:00 +02:00
Florian Reuth 336327a25c Remove worldgen/block_state_provider registry
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 11:59:16 +02:00
Florian Reuth da0c9eebde Handle decorated_pot block entity data
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-12 00:22:43 +02:00
Florian Reuth 36ca84041d Fix UPDATE_ADVANCEMENTS position calculation
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-11 23:14:24 +02:00
Florian Reuth 631142f3da Handle EXPLODE play_sound boolean
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-11 22:25:01 +02:00
Florian Reuth ca18e1e713 Update mapped string argument type names
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-11 21:57:03 +02:00
Nassim Jahnke 14958dbdd8 Fix swing animation handling 2026-09-10 16:32:21 +02:00
Nassim Jahnke 7ee2e15325 26.3-pre-3 2026-09-10 16:32:21 +02:00
Nassim Jahnke fb90e750d5 Handle new particle randomization 2026-09-10 16:32:21 +02:00
Nassim Jahnke 5533e61c32 26.3-pre2 2026-09-10 16:32:21 +02:00
Nassim Jahnke 9105fc4d79 26.3-pre-1 2026-09-10 16:32:21 +02:00
Nassim Jahnke 86be811cfc Finish 26.3-snapshot-10 2026-09-10 16:32:21 +02:00
Nassim Jahnke 7025027115 WIP 26.3-snapshot-10 2026-09-10 16:32:21 +02:00
Nassim Jahnke 6466affb8b 26.3-snapshot-9 2026-09-10 16:32:20 +02:00
Nassim Jahnke a0ab595d9b 26.3-snapshot-7 2026-09-10 16:32:20 +02:00
Nassim Jahnke 4b7b039621 26.3-snapshot-5 2026-09-10 16:32:20 +02:00
Nassim Jahnke 9a0c78b25f 26.3-snapshot-4 2026-09-10 16:32:20 +02:00
Nassim Jahnke d9c4d1c151 Try to handle multiple steps better 2026-09-10 16:32:20 +02:00
Nassim Jahnke f919050171 26.3-snapshot-3 2026-09-10 16:32:20 +02:00
Nassim Jahnke 3c3040f22d 26.3-snapshot-2 2026-09-10 16:32:20 +02:00
Nassim Jahnke d63f1d1fad 26.3-snapshot-1 (minus item mappings) 2026-09-10 16:32:20 +02:00
icicle1133 9265832edf Null-check entity_properties predicate in 26.2->26.1 (#1317) 2026-09-07 17:51:06 +02:00
Nassim Jahnke 5615d0591f Remove 1.14->1.13 cat name 2026-09-04 21:04:57 +02:00
Rocco e7d8703f86 Rewrite cat to ocelot variants in 1.14->1.13.2 (#1311) 2026-09-04 21:04:01 +02:00
Creeam 441c7f831f Rename block tags 26.1 to 1.21.11 for client prediction (#1309) 2026-08-29 13:30:28 +02:00
fedpol1 87fb4c7e25 Handle environment attributes in timelines on the 26.1->1.21.11 step (#1307) 2026-08-28 16:48:19 +02:00
Nassim Jahnke 1aa00f4d1b [ci skip] Build script updates 2026-08-23 15:15:34 +02:00
Nassim Jahnke 4ad05a4470 Update VV usage 2026-08-05 17:47:24 +02:00
Nassim Jahnke 8fb466022a Fix held block rewriting in 1.13->1.12
Fixes #1303
2026-07-29 10:12:17 +02:00
Nassim Jahnke 799d8945a2 Remove warning when trying to open stonecutter 2026-07-29 09:53:12 +02:00
Nassim Jahnke 4bc4a4c942 Flip respawn_anchor_works default
Fixes #1302
2026-07-23 21:19:04 +02:00
Term4 483c0ec0d6 Don't send legacy clients their own death event (#1296) 2026-07-18 19:33:37 +02:00
amy ef665d2f95 Prevent Mannequin names from having formatting in 1.12.2 (#1298) 2026-07-18 10:00:48 +02:00
Nassim Jahnke 8fc519b781 Clean up build scripts 2026-07-16 10:38:37 +02:00
Nassim Jahnke fba3a6986a Bump version to 5.11.1-SNAPSHOT 2026-07-16 10:37:53 +02:00
Nassim Jahnke c1620746bc 5.11.0 Release 2026-07-12 18:05:35 +02:00
Nassim Jahnke 59860ebf01 Fix manual blockstates-1.13 loading 2026-07-12 18:04:40 +02:00
Nassim Jahnke a2eecded20 Update to mapping format v2 2026-07-12 10:56:35 +02:00
Nassim Jahnke d63f7e1575 Remove 1.21.11 enchantment effects
Fixes #1281
2026-07-09 20:58:17 +02:00
Nassim Jahnke f04e115b95 Bump version to 5.11.0-SNAPSHOT 2026-07-09 15:19:15 +02:00
Nassim Jahnke 54897e7e5f Update VV usage 2026-07-09 15:14:06 +02:00
Nassim Jahnke 0e53ac9693 Split multi-line text displays into multiple entities
Fixes #767
2026-07-09 12:35:27 +02:00
Nassim Jahnke d975df4d27 Fix creative mode clear action in 1.21.2->1.21
Fixes #940
2026-07-08 19:25:51 +02:00
Nassim Jahnke 206a5b4171 Pass new item and entity names as translatable
This removes the itemnames and entitynames sections from mapping data and instead allows for easier changes via resource packs.

For 1.20.5+ clients, entities use the `vb.entity.<type>` translate key; items use `vg.item.<type>`. The old-style string (e.g. "26.2 Sulfur" is added into the `fallback` field).

For clients below 1.20.5, the old style string is put directly into the `translate` field.
2026-07-08 18:35:39 +02:00
Nassim Jahnke 9e8a958de2 Add item names as translate components instead
Fixes #659
2026-07-08 16:21:13 +02:00
Nassim Jahnke 4e0618b2e5 Remove new environment attributes from registry data
Fixes #1292
2026-07-08 16:05:38 +02:00
Nassim Jahnke d6a3338731 Always map to unpowered_comparator in 1.13->1.12
The powered_comparator block goes unused, the state is instead in the powered property
Fixes #1222
Properly fixes #524
2026-07-08 15:09:18 +02:00
mfishmaandNassim Jahnke df630e8828 Scale new baby mobs down on older clients (#1268)
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2026-07-07 10:46:56 +02:00
Nassim Jahnke e1802a4ebc Fix sulfur caves legacy mapping 2026-07-06 12:04:55 +02:00
Florian Reuth 0bd189cb45 Update damage event entity id types in 1.19.4->.3 (#1291) 2026-07-05 20:55:44 +02:00
Nassim Jahnke 8d067dce31 Optimize block checking in chunk packets 2026-07-05 12:00:15 +02:00
Nassim Jahnke a5e622e670 Skip heavy 1.20.5-1.21.4 serverbound item handling on 1.21.5+ servers
This instead forces (only) hashed item data through
2026-07-03 19:57:23 +02:00
Nassim Jahnke 6f7a43d3a5 Move off 1.13 piston handler data loading; don't fully parse and re-serialize mapped item names 2026-07-03 13:01:30 +02:00
Nassim Jahnke f0ffd17df3 Fix code of conduct written tag type 2026-07-02 13:00:46 +02:00
booky 921f04c88c Send empty block entity for beds in 26.2->26.1 (#1286) 2026-07-01 21:53:58 +02:00
Nassim Jahnke bea4db86f1 Update VV usage 2026-06-20 18:06:39 +02:00
Florian Reuth 7709085bb6 Remove new sulfur cube archetype registry in 26.2->26.1 (#1280) 2026-06-20 09:43:40 +02:00
Nassim Jahnke b5db485ecc 5.10.0 Release 2026-06-19 19:18:19 +02:00
Nassim Jahnke d219cd75be Map sulfur cube to slime 2026-06-19 19:18:19 +02:00
Nassim Jahnke 72ccb3bd85 Update mappings 2026-06-19 19:18:19 +02:00
Nassim Jahnke bdd786e473 Update deps 2026-06-19 19:18:19 +02:00
Florian Reuth 97f1419156 Update BackwardsRegistryRewriter for 1.21.11+ registry structure 2026-06-19 19:18:19 +02:00
Nassim Jahnke aaf080d065 Update mappings 2026-06-19 19:18:19 +02:00
Nassim Jahnke b30a2e6733 26.2-pre-1 2026-06-19 19:18:18 +02:00
Nassim Jahnke 58c7f4debc 26.2-snapshot-7 2026-06-19 19:18:18 +02:00
Nassim Jahnke 67c08b86b6 26.2-snapshot-5 2026-06-19 19:18:18 +02:00
Nassim Jahnke 9a855c3278 26.2-snapshot-4 2026-06-19 19:18:18 +02:00
Nassim Jahnke 570b3b7afd 26.2-snapshot-3 2026-06-19 19:18:18 +02:00
Nassim Jahnke 5834077e93 26.2-snapshot-1 2026-06-19 19:18:17 +02:00
196 changed files with 5462 additions and 1139 deletions

No files matched your search

+3
View File
@@ -1,4 +1,5 @@
version: 2
# Disabled for the time being, being done manually instead
updates:
- package-ecosystem: "github-actions"
cooldown:
@@ -6,9 +7,11 @@ updates:
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
- package-ecosystem: "gradle"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
+3 -3
View File
@@ -11,16 +11,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
uses: gradle/actions/setup-gradle@v6.4.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@v6.1.0
with:
distribution: 'temurin'
java-version: 17
+4 -4
View File
@@ -16,16 +16,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
uses: gradle/actions/setup-gradle@v6.4.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@v6.1.0
with:
distribution: 'temurin'
java-version: 17
@@ -40,5 +40,5 @@ jobs:
- name: Publish to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew modrinth --stacktrace
run: ./gradlew publishMods --stacktrace
continue-on-error: true
+2
View File
@@ -34,4 +34,6 @@ bin/
# Misc
run/
.claude/
!.claude/CLAUDE.md
AGENTS.override.md
+1 -2
View File
@@ -7,8 +7,7 @@ repositories {
}
dependencies {
// version must be manually kept in sync with the one in root project settings.gradle.kts
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.4.1")
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.6.1")
// A nice no-conflict comment for patching in downgrading
}
@@ -51,15 +51,6 @@ abstract class GitCommand : ValueSource<String, GitCommand.GitCommandParameters>
}
fun Project.parseMinecraftSnapshotVersion(version: String): String? {
val separatorIndex = version.indexOf('-')
val lastSeparatorIndex = version.lastIndexOf('-')
if (separatorIndex == -1 || separatorIndex == lastSeparatorIndex) {
return null
}
return version.substring(separatorIndex + 1, lastSeparatorIndex)
}
fun JavaPluginExtension.javaTarget(version: Int) {
toolchain.languageVersion.set(JavaLanguageVersion.of(version))
}
Whitespace-only changes.
+2 -1
View File
@@ -1,6 +1,7 @@
plugins {
base
id("vb.build-logic")
id("vb.base-conventions") apply false
id("vb.shadow-conventions") apply false
}
allprojects {
+9 -6
View File
@@ -27,7 +27,8 @@ java {
// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy
// For further instructions see the ViaProxy repository README
val prepareViaProxyFiles by tasks.registering(Copy::class) {
val prepareViaProxyFiles = tasks.register<Copy>("prepareViaProxyFiles") {
description = "Prepares the ViaProxy run dir for the runViaProxy task"
dependsOn(project.tasks.shadowJar)
from(project.tasks.shadowJar.map { it.archiveFile.get().asFile })
@@ -37,27 +38,29 @@ val prepareViaProxyFiles by tasks.registering(Copy::class) {
rename { "${projectName}.jar" }
}
val cleanupViaProxyFiles by tasks.registering(Delete::class) {
val cleanupViaProxyFiles = tasks.register<Delete>("cleanupViaProxyFiles") {
description = "Deletes ViaProxy logs and the compiled project jar from its jars dir"
delete(
layout.projectDirectory.file("run/jars/${project.name}.jar"),
layout.projectDirectory.dir("run/logs")
)
}
val viaProxyConfiguration: Configuration by configurations.creating {
val viaProxyConfiguration: Configuration = configurations.create("viaProxyConfiguration") {
dependencies.add(rootProject.libs.viaProxy.get().copy().setTransitive(false))
}
tasks.register<JavaExec>("runViaProxy") {
description = "Runs ViaProxy locally"
dependsOn(prepareViaProxyFiles)
finalizedBy(cleanupViaProxyFiles)
mainClass.set("net.raphimc.viaproxy.ViaProxy")
classpath = viaProxyConfiguration
workingDir = layout.projectDirectory.dir("run").asFile
jvmArgs = listOf("-DskipUpdateCheck")
jvmArgs = listOf("-DskipUpdateCheck", "-Dviaproxy.gui.autoStart")
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
if (System.getProperty("viaproxy.disableExtraPlatforms") != null) {
jvmArgs("-Dviaproxy.enableViaBedrock=false", "-Dviaproxy.enableViaLegacy=false", "-Dviaproxy.enableViaAprilFools=false")
}
}
@@ -69,6 +69,8 @@ import com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;
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.protocol.v26_1to1_21_11.Protocol26_1To1_21_11;
import com.viaversion.viabackwards.protocol.v26_2to26_1.Protocol26_2To26_1;
import com.viaversion.viabackwards.protocol.v26_3to26_2.Protocol26_3To26_2;
import com.viaversion.viabackwards.utils.VersionInfo;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
@@ -80,7 +82,7 @@ import java.util.logging.Logger;
public interface ViaBackwardsPlatform {
String MINIMUM_VV_VERSION = "5.9.2";
String MINIMUM_VV_VERSION = "5.12.2";
default void init(final File configFile) {
init(new ViaBackwardsConfig(configFile, getLogger()));
@@ -167,6 +169,8 @@ public interface ViaBackwardsPlatform {
protocolManager.registerProtocol(new Protocol1_21_11To1_21_9(), ProtocolVersion.v1_21_9, ProtocolVersion.v1_21_11);
protocolManager.registerProtocol(new Protocol26_1To1_21_11(), ProtocolVersion.v1_21_11, ProtocolVersion.v26_1);
protocolManager.registerProtocol(new Protocol26_2To26_1(), ProtocolVersion.v26_1, ProtocolVersion.v26_2);
protocolManager.registerProtocol(new Protocol26_3To26_2(), ProtocolVersion.v26_2, ProtocolVersion.v26_3);
}
default void enable() {
@@ -34,7 +34,7 @@ import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.HashMap;
import com.viaversion.viaversion.util.Key;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -44,7 +44,6 @@ public class BackwardsMappingData extends MappingDataBase {
private final Class<? extends Protocol<?, ?, ?, ?>> vvProtocolClass;
protected Int2ObjectMap<MappedItem> backwardsItemMappings;
private Map<String, String> entityNames;
private Int2ObjectMap<String> enchantmentNames;
public BackwardsMappingData(final String unmappedVersion, final String mappedVersion) {
@@ -59,42 +58,23 @@ public class BackwardsMappingData extends MappingDataBase {
@Override
protected void loadExtras(final CompoundTag data) {
final CompoundTag itemNames = data.getCompoundTag("itemnames");
if (itemNames != null) {
final CompoundTag itemCustomModelDatas = data.getCompoundTag("custom_model_data");
if (itemCustomModelDatas != null) {
Preconditions.checkNotNull(itemMappings);
backwardsItemMappings = new Int2ObjectOpenHashMap<>(itemNames.size());
final CompoundTag extraItemData = data.getCompoundTag("itemdata");
for (final Map.Entry<String, Tag> entry : itemNames.entrySet()) {
final StringTag name = (StringTag) entry.getValue();
final int id = Integer.parseInt(entry.getKey());
Integer customModelData = null;
if (extraItemData != null && extraItemData.contains(entry.getKey())) {
final CompoundTag entryTag = extraItemData.getCompoundTag(entry.getKey());
final NumberTag customModelDataTag = entryTag.getNumberTag("custom_model_data");
customModelData = customModelDataTag != null ? customModelDataTag.asInt() : null;
}
backwardsItemMappings.put(id, new MappedItem(getNewItemId(id), name.getValue(), customModelData));
}
backwardsItemMappings = new Int2ObjectOpenHashMap<>(itemCustomModelDatas.size());
loadBackwardsItemMappings(itemCustomModelDatas);
}
this.entityNames = loadNameByStringMappings(data, "entitynames");
this.enchantmentNames = loadNameByIdMappings(data, "enchantmentnames");
}
private @Nullable Map<String, String> loadNameByStringMappings(final CompoundTag data, final String key) {
final CompoundTag nameMappings = data.getCompoundTag(key);
if (nameMappings == null) {
return null;
protected void loadBackwardsItemMappings(final CompoundTag itemCustomModelDatas) {
for (final Map.Entry<String, Tag> entry : itemCustomModelDatas.entrySet()) {
final int id = Integer.parseInt(entry.getKey());
final int customModelData = ((NumberTag) entry.getValue()).asInt();
final String identifier = getFullItemMappings().identifier(id);
final String fallbackName = unmappedVersion + " " + nameFromIdentifier(identifier);
backwardsItemMappings.put(id, new MappedItem(getNewItemId(id), identifier, fallbackName, customModelData));
}
final Map<String, String> map = new HashMap<>(nameMappings.size());
for (final Map.Entry<String, Tag> entry : nameMappings.entrySet()) {
final StringTag mappedTag = (StringTag) entry.getValue();
map.put(entry.getKey(), mappedTag.getValue());
}
return map;
}
private @Nullable Int2ObjectMap<String> loadNameByIdMappings(final CompoundTag data, final String key) {
@@ -161,14 +141,6 @@ public class BackwardsMappingData extends MappingDataBase {
return getFullSoundMappings().mappedIdentifier(id);
}
public @Nullable String mappedEntityName(final String entityName) {
if (entityNames == null) {
getLogger().log(Level.SEVERE, "No entity mappings found when requesting them for " + entityName, new RuntimeException());
return null;
}
return entityNames.get(entityName);
}
public @Nullable String mappedEnchantmentName(final int enchantmentId) {
if (enchantmentNames == null) {
ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, "No enchantment name mappings found when requesting " + enchantmentId, new RuntimeException());
@@ -194,4 +166,12 @@ public class BackwardsMappingData extends MappingDataBase {
protected @Nullable CompoundTag readMappingsFile(final String name) {
return BackwardsMappingDataLoader.INSTANCE.loadNBTFromDir(name);
}
public static String nameFromIdentifier(final String identifier) {
final StringBuilder result = new StringBuilder();
for (final String split : Key.stripMinecraftNamespace(identifier).split("_")) {
result.append(' ').append(Character.toUpperCase(split.charAt(0))).append(split.substring(1));
}
return result.substring(1);
}
}
@@ -17,26 +17,43 @@
*/
package com.viaversion.viabackwards.api.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.util.ComponentUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
import com.viaversion.viaversion.util.Key;
public class MappedItem {
private final int id;
private final String jsonName;
private final Tag tagName;
private final Integer customModelData;
private final int customModelData;
public MappedItem(final int id, final String name) {
this(id, name, null);
public MappedItem(final int id, String identifier, final String fallbackName, final int customModelData) {
this.id = id;
identifier = Key.stripMinecraftNamespace(identifier);
final String translateKey = "vb.item." + identifier;
this.jsonName = itemJsonName(fallbackName);
this.tagName = itemTagName(translateKey, fallbackName);
this.customModelData = customModelData;
}
public MappedItem(final int id, final String name, @Nullable final Integer customModelData) {
this.id = id;
this.jsonName = ComponentUtil.legacyToJsonString("§f" + name, true);
this.tagName = ComponentUtil.jsonStringToTag(jsonName);
this.customModelData = customModelData;
// Make explicitly non-italic and white to override default item formatting.
// Properly parsing is expensive...
private static String itemJsonName(final String name) {
return """
{"italic":false,"color":"white","text":%s}
""".formatted(new JsonPrimitive(name).toString());
}
private static Tag itemTagName(final String translateKey, final String fallbackName) {
// Proper translate with fallback field (valid for 1.19.4+ clients)
final CompoundTag tag = new CompoundTag();
tag.putString("color", "white");
tag.putString("translate", translateKey);
tag.putString("fallback", fallbackName);
tag.putBoolean("italic", false);
return tag;
}
public int id() {
@@ -48,10 +65,10 @@ public class MappedItem {
}
public Tag tagName() {
return tagName;
return tagName.copy();
}
public @Nullable Integer customModelData() {
public int customModelData() {
return customModelData;
}
}
@@ -0,0 +1,48 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.api.entities;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
/**
* Storage object used within {@link TrackedEntity} to track the scaling state of an entity.
* This ensures that scale updates are only sent when the calculated scale actually changes.
*/
public final class EntityScaleData implements StorableObject {
private boolean isBaby;
private float scale = 1.0f;
public boolean isBaby() {
return isBaby;
}
public void setBaby(boolean baby) {
this.isBaby = baby;
}
public float scale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
}
}
@@ -0,0 +1,102 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.api.entities;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.data.FullMappings;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;
/**
* A helper for injecting entity scaling on older clients.
* This helper listens for the baby entity data flag and sends an UPDATE_ATTRIBUTES packet
* to scale the entity, since older clients might not support the newer entity variants natively.
*/
public class EntityScaleHelper {
private final ClientboundPacketType updateAttributesPacket;
private final EntityRewriter<?, ?> rewriter;
/**
* Constructs a new entity scale helper.
*
* @param rewriter entity rewriter
* @param updateAttributesPacket UPDATE_ATTRIBUTES packet of the mapped protocol
* @param <CM> mapped clientbound packet type
*/
public <CM extends ClientboundPacketType, P extends Protocol<?, CM, ?, ?>> EntityScaleHelper(final EntityRewriter<?, P> rewriter, final CM updateAttributesPacket) {
this.rewriter = rewriter;
this.updateAttributesPacket = updateAttributesPacket;
}
/**
* Registers a scaling factor for a specific baby entity type.
*
* @param type entity type that requires scaling when it is a baby
* @param babyScaleFactor multiplier to apply to the minecraft:scale attribute when it is a baby
* @param babyIndex entity data index for the baby property in this specific protocol
*/
public void addBabyScale(final EntityType type, final float babyScaleFactor, final int babyIndex) {
rewriter.filter().type(type).index(babyIndex).handler(((event, data) -> trackAndSend(event, data, babyScaleFactor)));
}
/**
* Checks the entity data, tracks the scale state and sends an UPDATE_ATTRIBUTES packet if needed.
*/
public void trackAndSend(final EntityDataHandlerEvent event, final EntityData data, final float babyScaleFactor) {
if (event.trackedEntity() == null) {
return;
}
EntityScaleData scaleData = event.trackedEntity().get(EntityScaleData.class);
if (scaleData == null) {
scaleData = new EntityScaleData();
event.trackedEntity().put(scaleData);
}
final boolean isBaby = data.value();
if (scaleData.isBaby() == isBaby) {
return;
}
scaleData.setBaby(isBaby);
scaleData.setScale(isBaby ? babyScaleFactor : 1.0f);
final FullMappings attributeMappings = rewriter.protocol().getMappingData().getAttributeMappings();
int attributeId = attributeMappings.mappedId("scale");
if (attributeId == -1) {
attributeId = attributeMappings.mappedId("generic.scale");
Preconditions.checkArgument(attributeId != -1);
}
final PacketWrapper attributesPacket = PacketWrapper.create(updateAttributesPacket, event.user());
attributesPacket.write(Types.VAR_INT, event.entityId());
attributesPacket.write(Types.VAR_INT, 1); // 1 attribute
attributesPacket.write(Types.VAR_INT, attributeId);
attributesPacket.write(Types.DOUBLE, (double) scaleData.scale());
attributesPacket.write(Types.VAR_INT, 0); // 0 modifiers
attributesPacket.send(rewriter.protocol().getClass());
}
}
@@ -18,12 +18,10 @@
package com.viaversion.viabackwards.api.entities.storage;
import com.viaversion.viabackwards.api.rewriters.EntityRewriterBase;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.ProtocolLogger;
import java.util.function.Supplier;
public class EntityPositionHandler {
@@ -32,7 +30,6 @@ public class EntityPositionHandler {
private final EntityRewriterBase<?, ?> entityRewriter;
private final Class<? extends EntityPositionStorage> storageClass;
private final Supplier<? extends EntityPositionStorage> storageSupplier;
private boolean warnedForMissingEntity;
public EntityPositionHandler(EntityRewriterBase<?, ?> entityRewriter,
Class<? extends EntityPositionStorage> storageClass, Supplier<? extends EntityPositionStorage> storageSupplier) {
@@ -51,27 +48,18 @@ public class EntityPositionHandler {
}
public void cacheEntityPosition(PacketWrapper wrapper, int entityId, double x, double y, double z, boolean create, boolean relative) {
StoredEntityData storedEntity = entityRewriter.tracker(wrapper.user()).entityData(entityId);
if (storedEntity == null) {
if (Via.getManager().isDebug()) { // There is too many plugins violating this out there, and reading seems to be hard! :>
ProtocolLogger logger = entityRewriter.protocol().getLogger();
logger.warning("Stored entity with id " + entityId + " missing at position: " + x + " - " + y + " - " + z + " in " + storageClass.getSimpleName());
if (entityId == -1 && x == 0 && y == 0 && z == 0) {
logger.warning("DO NOT REPORT THIS TO VIA, THIS IS A PLUGIN ISSUE");
} else if (!warnedForMissingEntity) {
warnedForMissingEntity = true;
logger.warning("This is very likely caused by a plugin sending a teleport packet for an entity outside of the player's range.");
}
}
final TrackedEntity entity = entityRewriter.tracker(wrapper.user()).entity(entityId);
if (entity == null) {
// Bad plugins
return;
}
EntityPositionStorage positionStorage;
if (create) {
positionStorage = storageSupplier.get();
storedEntity.put(positionStorage);
entity.put(positionStorage);
} else {
positionStorage = storedEntity.get(storageClass);
positionStorage = entity.get(storageClass);
if (positionStorage == null) {
entityRewriter.protocol().getLogger().warning("Stored entity with id " + entityId + " missing " + storageClass.getSimpleName());
return;
@@ -86,9 +74,9 @@ public class EntityPositionHandler {
}
public EntityPositionStorage getStorage(UserConnection user, int entityId) {
StoredEntityData storedEntity = entityRewriter.tracker(user).entityData(entityId);
final TrackedEntity entity = entityRewriter.tracker(user).entity(entityId);
EntityPositionStorage entityStorage;
if (storedEntity == null || (entityStorage = storedEntity.get(EntityPositionStorage.class)) == null) {
if (entity == null || (entityStorage = entity.get(EntityPositionStorage.class)) == null) {
entityRewriter.protocol().getLogger().warning("Untracked entity with id " + entityId + " in " + storageClass.getSimpleName());
return null;
}
@@ -17,7 +17,9 @@
*/
package com.viaversion.viabackwards.api.entities.storage;
public abstract class EntityPositionStorage {
import com.viaversion.viaversion.api.connection.StorableObject;
public abstract class EntityPositionStorage implements StorableObject {
private double x;
private double y;
private double z;
@@ -18,8 +18,10 @@
package com.viaversion.viabackwards.api.entities.storage;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.util.ComponentUtil;
import java.util.Locale;
@@ -30,18 +32,22 @@ public class EntityReplacement {
private final int id;
private final int replacementId;
private final String key;
private final String translateKey;
private final String fallbackName;
private ComponentType componentType = ComponentType.NONE;
private EntityDataCreator defaultData;
public EntityReplacement(BackwardsProtocol<?, ?, ?, ?> protocol, EntityType type, int replacementId) {
this(protocol, type.name(), type.getId(), replacementId);
this(protocol, type.name().toLowerCase(Locale.ROOT), type.getId(), replacementId);
}
public EntityReplacement(BackwardsProtocol<?, ?, ?, ?> protocol, String key, int id, int replacementId) {
this.protocol = protocol;
this.id = id;
this.replacementId = replacementId;
this.key = key.toLowerCase(Locale.ROOT);
this.key = key;
this.translateKey = "vb.entity." + key;
this.fallbackName = BackwardsMappingData.nameFromIdentifier(this.key);
}
public EntityReplacement jsonName() {
@@ -73,24 +79,26 @@ public class EntityReplacement {
}
/**
* @return custom mobname, can be either a String or a JsonElement
* Returns the custom entity name. Can be either a {@link String}, {@link StringTag} or {@link com.viaversion.viaversion.libs.gson.JsonElement}.
*
* @return custom entity name
*/
public @Nullable Object entityName() {
if (componentType == ComponentType.NONE) {
return null;
}
final String name = protocol.getMappingData().mappedEntityName(key);
if (name == null) {
return null;
}
if (componentType == ComponentType.JSON) {
return ComponentUtil.legacyToJson(name);
return ComponentUtil.plainToJson(fallbackName); // Pass on as plain, with or without section symbols
} else if (componentType == ComponentType.TAG) {
return new StringTag(name);
final CompoundTag tag = new CompoundTag();
tag.putString("color", "white");
tag.putString("translate", translateKey);
tag.putString("fallback", fallbackName);
tag.putBoolean("italic", false);
return tag;
}
return name;
return fallbackName;
}
public int replacementId() {
@@ -0,0 +1,57 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.api.entities.storage;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
public class PlayerPosRotStorage extends PlayerPositionStorage {
private float yRot; // yaw
private float xRot; // pitch
public float yRot() {
return yRot;
}
public void setYRot(final float yRot) {
this.yRot = yRot;
}
public float xRot() {
return xRot;
}
public void setXRot(final float xRot) {
this.xRot = xRot;
}
public void setRotation(final float yRot, final float xRot) {
this.yRot = yRot;
this.xRot = xRot;
}
public void setRotFromPacket(final PacketWrapper wrapper) {
this.yRot = wrapper.passthrough(Types.FLOAT);
this.xRot = wrapper.passthrough(Types.FLOAT);
}
public void setPosRotFromPacket(final PacketWrapper wrapper) {
this.setPosFromPacket(wrapper);
this.setRotFromPacket(wrapper);
}
}
@@ -18,15 +18,14 @@
package com.viaversion.viabackwards.api.entities.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
public abstract class PlayerPositionStorage implements StorableObject {
public class PlayerPositionStorage implements StorableObject {
private double x;
private double y;
private double z;
protected PlayerPositionStorage() {
}
public double x() {
return x;
}
@@ -57,6 +56,12 @@ public abstract class PlayerPositionStorage implements StorableObject {
this.z = z;
}
public void setPosFromPacket(PacketWrapper wrapper) {
this.x = wrapper.passthrough(Types.DOUBLE);
this.y = wrapper.passthrough(Types.DOUBLE);
this.z = wrapper.passthrough(Types.DOUBLE);
}
public void addRelativePosition(double relX, double relY, double relZ) {
this.x += relX;
this.y += relY;
@@ -0,0 +1,81 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.api.rewriters;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
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.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
// 1.20.5-1.21.4
public class BackwardsFullStructuredItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends BackwardsProtocol<C, ?, ?, S>> extends BackwardsStructuredItemRewriter<C, S, T> {
public BackwardsFullStructuredItemRewriter(final T protocol) {
super(protocol);
}
@Override
public Item handleItemToServer(final UserConnection connection, final Item item) {
if (Item.isEmpty(item)) {
return item;
}
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
if (protocolInfo.serverProtocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_5)
&& !protocol.getEntityRewriter().tracker(connection).canInstaBuild()) {
// We don't actually need anything but the amount, id, and hashes.
// Just pass it on without deeper handling, only leaving custom data.
final CompoundTag customData = item.dataContainer().get(StructuredDataKey.CUSTOM_DATA);
if (customData == null) {
// Not valid
item.dataContainer().data().clear();
} else {
customData.keySet().removeIf(key -> !key.equals(ORIGINAL_HASHES_KEY));
item.dataContainer().data().keySet().removeIf(key -> key != StructuredDataKey.CUSTOM_DATA);
}
}
return super.handleItemToServer(connection, item);
}
@Override
protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {
// Keep original hashes intact until 1.21.5
final Tag hashes = customData.get(ORIGINAL_HASHES_KEY);
super.restoreBackupData(item, container, customData);
if (hashes != null) {
customData.put(ORIGINAL_HASHES_KEY, hashes);
// Add back
if (!container.has(StructuredDataKey.CUSTOM_DATA)) {
container.set(StructuredDataKey.CUSTOM_DATA, customData);
customData.putBoolean(MARKER_KEY, true);
}
}
}
}
@@ -24,6 +24,7 @@ import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.data.MappedItem;
import com.viaversion.viabackwards.item.DataItemWithExtras;
@@ -36,6 +37,7 @@ import com.viaversion.viaversion.libs.gson.JsonElement;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
// <1.20.5
public class BackwardsItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends BackwardsProtocol<C, ?, ?, S>> extends BackwardsItemRewriterBase<C, S, T> {
@@ -113,7 +115,7 @@ public class BackwardsItemRewriter<C extends ClientboundPacketType, S extends Se
item.setIdentifier(data.id());
// Add custom model data
if (data.customModelData() != null && !item.tag().contains("CustomModelData")) {
if (ViaBackwards.getConfig().passOriginalItemNameToResourcePacks() && !item.tag().contains("CustomModelData")) {
item.tag().putInt("CustomModelData", data.customModelData());
}
@@ -18,11 +18,15 @@
package com.viaversion.viabackwards.api.rewriters;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.FullMappings;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.TagUtil;
import java.util.Map;
public class BackwardsRegistryRewriter extends RegistryDataRewriter {
@@ -44,13 +48,19 @@ public class BackwardsRegistryRewriter extends RegistryDataRewriter {
final CompoundTag effects = biome.getCompoundTag("effects");
updateBiomeEffects(effects);
final CompoundTag attributes = biome.getCompoundTag("attributes");
if (attributes != null) {
updateAttributes(attributes);
}
}
}
return super.handle(connection, key, entries);
}
@Override
public void updateJukeboxSongs(final RegistryEntry[] entries) {
public void updateJukeboxSongs(final UserConnection connection, final RegistryEntry[] entries) {
super.updateJukeboxSongs(connection, entries);
for (final RegistryEntry entry : entries) {
if (entry.tag() == null) {
continue;
@@ -60,6 +70,28 @@ public class BackwardsRegistryRewriter extends RegistryDataRewriter {
}
}
@Override
protected boolean updateBlockState(final Tag blockStateTag) {
if (!(blockStateTag instanceof CompoundTag blockState) || blockState.getStringTag("Name") == null) {
return super.updateBlockState(blockStateTag);
}
// Up to 26.2, block states use "Name" and "Properties" instead of "id" and "properties"
final FullMappings blockMappings = protocol.getMappingData().getFullBlockMappings();
if (blockMappings == null) {
return false;
}
final int blockId = blockMappings.id(blockState.getString("Name"));
if (blockId == -1 || protocol.getMappingData().hasBlockChanged(blockId)) {
// Replace unknown or changed blocks with a dummy state, else the client will fail to load the registry
blockState.putString("Name", "minecraft:dirt");
blockState.remove("Properties");
return true;
}
return false;
}
private void updateBiomeEffects(final CompoundTag effects) {
updateSound(effects.getCompoundTag("mood_sound"), "sound");
updateSound(effects.getCompoundTag("additions_sound"), "sound");
@@ -67,6 +99,20 @@ public class BackwardsRegistryRewriter extends RegistryDataRewriter {
updateSound(effects, "ambient_sound");
}
private void updateAttributes(final CompoundTag attributes) {
final CompoundTag backgroundMusic = TagUtil.getNamespacedCompoundTag(attributes, "audio/background_music");
if (backgroundMusic != null) {
updateSound(backgroundMusic.getCompoundTag("default"), "sound");
}
final CompoundTag ambientSounds = TagUtil.getNamespacedCompoundTag(attributes, "audio/ambient_sounds");
if (ambientSounds != null) {
updateSound(ambientSounds, "loop");
updateSound(ambientSounds, "mood");
updateSound(ambientSounds, "additions");
}
}
private void updateSound(final CompoundTag tag, final String name) {
if (tag == null) {
return;
@@ -78,14 +124,16 @@ public class BackwardsRegistryRewriter extends RegistryDataRewriter {
}
final String mappedSound = protocol.getMappingData().getMappedNamedSound(sound);
if (mappedSound == null) {
return;
}
if (mappedSound.isEmpty()) {
if (mappedSound != null) {
tag.putString(name, mappedSound);
} else if (name.equals("sound_id")) {
// If it's already an inlined sound
tag.putString(name, "minecraft:intentionally_empty");
} else {
tag.putString(name, mappedSound);
// Write as custom sound, so the id doesn't actually need to exist later on
final CompoundTag soundEvent = new CompoundTag();
soundEvent.putString("sound_id", "minecraft:intentionally_empty");
tag.put(name, soundEvent);
}
}
}
@@ -49,6 +49,7 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;
// 1.21.5+
public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends BackwardsProtocol<C, ?, ?, S>> extends StructuredItemRewriter<C, S, T> {
@@ -76,11 +77,10 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
customTag.putInt(nbtTagName("id"), item.identifier()); // Save original id
// Add custom model data
final boolean addOriginalIdentifier = ViaBackwards.getConfig().passOriginalItemNameToResourcePacks();
if (mappedItem.customModelData() != null || addOriginalIdentifier) {
if (ViaBackwards.getConfig().passOriginalItemNameToResourcePacks()) {
if (connection.getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_4)) {
addCustomModelData(item, addOriginalIdentifier, mappedItem, customTag);
} else if (mappedItem.customModelData() != null && !dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) {
addCustomModelData(item, mappedItem, customTag);
} else if (!dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) {
dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, mappedItem.customModelData());
}
}
@@ -92,24 +92,22 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
}
}
private void addCustomModelData(final Item item, final boolean addOriginalIdentifier, final MappedItem mappedItem, final CompoundTag customTag) {
private void addCustomModelData(final Item item, final MappedItem mappedItem, final CompoundTag customTag) {
final StructuredDataContainer dataContainer = item.dataContainer();
CustomModelData1_21_4 customModelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4);
if (customModelData == null) {
final String[] strings = addOriginalIdentifier
? new String[]{protocol.getMappingData().getFullItemMappings().identifier(item.identifier())}
: new String[0];
// Add both the identifier string and custom model data
customModelData = new CustomModelData1_21_4(
mappedItem.customModelData() != null ? new float[]{mappedItem.customModelData().floatValue()} : new float[0],
new float[]{mappedItem.customModelData()},
new boolean[0],
strings,
new String[]{protocol.getMappingData().getFullItemMappings().identifier(item.identifier())},
EMPTY_INT_ARRAY
);
dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData);
// Add one global marker and one for this specific version, so it is removed at the correct protocol
customTag.putBoolean(GLOBAL_MODEL_DATA_MARKER, true);
customTag.putBoolean(nbtTagName("added_custom_model_data"), true);
} else if (addOriginalIdentifier && !customTag.contains(GLOBAL_MODEL_DATA_MARKER)) {
} else if (!customTag.contains(GLOBAL_MODEL_DATA_MARKER)) {
final String identifier = protocol.getMappingData().getFullItemMappings().identifier(item.identifier());
dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4(
customModelData.floats(), customModelData.booleans(), ArrayUtil.add(customModelData.strings(), identifier), customModelData.colors()
@@ -18,8 +18,7 @@
package com.viaversion.viabackwards.api.rewriters;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
@@ -37,111 +36,28 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
super(protocol, displayType, 2, displayVisibilityType, 3);
}
@Override
public void registerTrackerWithData(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Entity ID
wrapper.passthrough(Types.UUID); // Entity UUID
wrapper.passthrough(Types.VAR_INT); // Entity Type
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.BYTE); // Pitch
wrapper.passthrough(Types.BYTE); // Yaw
wrapper.passthrough(Types.INT); // Data
getSpawnTrackerWithDataHandler().handle(wrapper);
});
}
@Override
public void registerTrackerWithData1_19(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Entity id
wrapper.passthrough(Types.UUID); // Entity UUID
wrapper.passthrough(Types.VAR_INT); // Entity type
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.BYTE); // Pitch
wrapper.passthrough(Types.BYTE); // Yaw
wrapper.passthrough(Types.BYTE); // Head yaw
wrapper.passthrough(Types.VAR_INT); // Data
getSpawnTrackerWithDataHandler1_19().handle(wrapper);
});
}
public PacketHandler getSpawnTrackerWithDataHandler() {
return wrapper -> {
// Check against the UNMAPPED entity type
EntityType entityType = trackAndMapEntity(wrapper);
if (entityType == typeFromId("falling_block")) {
int entityId = wrapper.get(Types.VAR_INT, 0);
int typeId = wrapper.get(Types.VAR_INT, 1);
TrackedEntity entity = trackAndRewrite(wrapper, typeId, entityId);
if (entity.entityType() == fallingBlockType()) {
int blockState = wrapper.get(Types.INT, 0);
wrapper.set(Types.INT, 0, protocol.getMappingData().getNewBlockStateId(blockState));
}
};
}
public PacketHandler getSpawnTrackerWithDataHandler1_19() {
return wrapper -> {
if (protocol.getMappingData() == null) {
return;
}
public void trackSpawnWithData1_19(final PacketWrapper wrapper, final int data) {
if (protocol.getMappingData() == null) {
return;
}
// Check against the UNMAPPED entity type
EntityType entityType = trackAndMapEntity(wrapper);
if (entityType == typeFromId("falling_block")) {
int blockState = wrapper.get(Types.VAR_INT, 2);
wrapper.set(Types.VAR_INT, 2, protocol.getMappingData().getNewBlockStateId(blockState));
}
};
}
@Override
public void registerTracker(C packetType) {
protocol.registerClientbound(packetType, wrapper -> {
wrapper.passthrough(Types.VAR_INT); // Entity ID
wrapper.passthrough(Types.UUID); // Entity UUID
wrapper.passthrough(Types.VAR_INT); // Entity Type
trackAndMapEntity(wrapper);
});
}
/**
* Returns a handler to track the current world and uncache entity data on world changes.
*
* @return handler to track the current world
*/
public PacketHandler worldTrackerHandlerByKey() {
return wrapper -> {
EntityTracker tracker = tracker(wrapper.user());
String world = wrapper.get(Types.STRING, 1);
if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) {
tracker.clearEntities();
}
tracker.setCurrentWorld(world);
};
}
/**
* Sets the mapped entity id and returns the unmapped entity type.
*
* @param wrapper packet wrapper
* @return unmapped (!) entity type
*/
protected EntityType trackAndMapEntity(PacketWrapper wrapper) {
int entityId = wrapper.get(Types.VAR_INT, 0);
int typeId = wrapper.get(Types.VAR_INT, 1);
EntityType entityType = typeFromId(typeId);
if (entityType == null) {
return null;
TrackedEntity entity = trackAndRewrite(wrapper, typeId, entityId);
if (entity.entityType() == fallingBlockType()) {
wrapper.set(Types.VAR_INT, 2, protocol.getMappingData().getNewBlockStateId(data));
}
tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);
int mappedTypeId = newEntityId(entityType.getId());
if (typeId != mappedTypeId) {
wrapper.set(Types.VAR_INT, 1, mappedTypeId);
}
return entityType;
}
}
@@ -23,8 +23,6 @@ import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.entities.storage.EntityReplacement;
import com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.Particle;
@@ -39,7 +37,6 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -86,12 +83,12 @@ public abstract class EntityRewriterBase<C extends ClientboundPacketType, T exte
// Add it as new entity data
entityDataList.add(new EntityData(displayNameIndex, displayNameDataType, displayNameObject));
addDisplayVisibilityData(entityDataList);
} else if (displayName.getValue() == null || displayName.getValue().toString().isEmpty()) {
} else if (isEmptyDisplayName(displayName.getValue())) {
// Overwrite the existing null/empty display name
displayName.setValue(displayNameObject);
addDisplayVisibilityData(entityDataList);
}
} else if (displayName != null && (displayName.getValue() == null || displayName.getValue().toString().isEmpty())) {
} else if (displayName != null && (isEmptyDisplayName(displayName))) {
// Overwrite null/empty display name
displayName.setValue(displayNameObject);
addDisplayVisibilityData(entityDataList);
@@ -104,6 +101,17 @@ public abstract class EntityRewriterBase<C extends ClientboundPacketType, T exte
}
}
private boolean isEmptyDisplayName(@Nullable Object o) {
if (o == null) {
return true;
} else if (o instanceof String s) {
return s.isEmpty();
} else if (o instanceof JsonElement element) {
return element.isJsonNull() || (element.isJsonPrimitive() && element.getAsJsonPrimitive().getAsString().isEmpty());
}
return false;
}
private void addDisplayVisibilityData(List<EntityData> entityDataList) {
if (alwaysShowOriginalMobName()) {
removeData(displayVisibilityIndex, entityDataList);
@@ -140,10 +148,6 @@ public abstract class EntityRewriterBase<C extends ClientboundPacketType, T exte
return entityDataMappings.get(type.getId());
}
protected @Nullable StoredEntityData storedEntityData(EntityDataHandlerEvent event) {
return tracker(event.user()).entityData(event.entityId());
}
/**
* Maps an entity type to another with extra data.
* Note that both types should be of the same version.
@@ -227,6 +231,8 @@ public abstract class EntityRewriterBase<C extends ClientboundPacketType, T exte
});
}
// ------------------ Legacy methods
// ONLY TRACKS, DOESN'T REWRITE IDS
protected PacketHandler getTrackerHandler(Type<? extends Number> intType, int typeIndex) {
return wrapper -> {
@@ -242,23 +248,9 @@ public abstract class EntityRewriterBase<C extends ClientboundPacketType, T exte
return getTrackerHandler(Types.VAR_INT, 1);
}
protected PacketHandler getTrackerHandler(EntityType entityType) {
return wrapper -> tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);
}
protected PacketHandler getPlayerTrackerHandler() {
return wrapper -> {
final int entityId = wrapper.get(Types.INT, 0);
final EntityTracker tracker = tracker(wrapper.user());
tracker(wrapper.user()).setClientEntityId(entityId);
tracker.addEntity(entityId, tracker.playerType());
};
}
protected PacketHandler getDimensionHandler(int index) {
return wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(this.protocol.getClass());
ClientWorld clientWorld = wrapper.user().storables(this.protocol).clientWorld();
int dimensionId = wrapper.get(Types.INT, index);
if (clientWorld.setEnvironment(dimensionId)) {
tracker(wrapper.user()).clearEntities();
@@ -21,6 +21,7 @@ import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.api.entities.storage.EntityObjectData;
import com.viaversion.viabackwards.api.entities.storage.EntityReplacement;
import com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.ObjectType;
@@ -35,6 +36,7 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -50,7 +52,7 @@ public abstract class LegacyEntityRewriter<C extends ClientboundPacketType, T ex
}
protected EntityObjectData mapObjectType(ObjectType oldObjectType, ObjectType replacement, int data) {
EntityObjectData entData = new EntityObjectData(protocol, oldObjectType.getType().name(), oldObjectType.getId(), replacement.getId(), data);
EntityObjectData entData = new EntityObjectData(protocol, oldObjectType.getType().name().toLowerCase(Locale.ROOT), oldObjectType.getId(), replacement.getId(), data);
objectTypes.put(oldObjectType, entData);
return entData;
}
@@ -65,7 +67,7 @@ public abstract class LegacyEntityRewriter<C extends ClientboundPacketType, T ex
public void register() {
map(Types.INT);
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
if (clientWorld.setEnvironment(wrapper.get(Types.INT, 0))) {
tracker(wrapper.user()).clearEntities();
}
@@ -82,12 +84,13 @@ public abstract class LegacyEntityRewriter<C extends ClientboundPacketType, T ex
map(Types.UNSIGNED_BYTE); // 1 - Gamemode
map(Types.INT); // 2 - Dimension
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
clientWorld.setEnvironment(wrapper.get(Types.INT, 1));
final int entityId = wrapper.get(Types.INT, 0);
tracker(wrapper.user()).addEntity(entityId, playerType);
tracker(wrapper.user()).setClientEntityId(entityId);
final EntityTracker tracker = tracker(wrapper.user());
tracker.addEntity(entityId, playerType);
tracker.setClientEntityId(entityId);
});
}
});
@@ -33,6 +33,7 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.ObjectIterator;
import com.viaversion.viaversion.rewriter.IdRewriteFunction;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Limit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -195,7 +196,12 @@ public class StructuredEnchantmentRewriter {
}
final Enchantments enchantments = new Enchantments(tag.remove(itemRewriter.nbtTagName("show_" + key.identifier())) != null);
int i = 0;
for (final CompoundTag enchantment : enchantmentsTag) {
if (i++ > 255) {
break;
}
enchantments.add(enchantment.getInt("id"), enchantment.getInt("lvl"));
}
data.set(key, enchantments);
@@ -22,22 +22,19 @@ import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;
import com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;
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.chunk.ChunkType26_1;
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_21_11to26_1.Protocol1_21_11To26_1;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;
import com.viaversion.viaversion.protocols.v26_2to26_3.rewriter.RecipeDisplayRewriter26_3;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
@@ -66,7 +63,7 @@ final class Protocol99_1To98_1 extends BackwardsProtocol<ClientboundPacket26_1,
private final TagRewriter<ClientboundPacket26_1> tagRewriter = new TagRewriter<>(this);
private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);
private final BlockRewriter<ClientboundPacket26_1> blockRewriter = new BlockRewriter1_21_5<>(this, ChunkType26_1::new);
private final RecipeDisplayRewriter<ClientboundPacket26_1> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);
private final RecipeDisplayRewriter<ClientboundPacket26_1> recipeRewriter = new RecipeDisplayRewriter26_3<>(this);
public Protocol99_1To98_1() {
super(ClientboundPacket26_1.class, ClientboundPacket26_1.class, ServerboundPacket1_21_9.class, ServerboundPacket1_21_9.class);
@@ -75,14 +72,23 @@ final class Protocol99_1To98_1 extends BackwardsProtocol<ClientboundPacket26_1,
@Override
protected void registerPackets() {
super.registerPackets();
// Remove newly synchronized registries to prevent errors on older clients
//registryDataRewriter.remove("worldgen/block_state_provider");
}
@Override
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_11.PLAYER));
connection.addItemHasher(this.getClass(), new ItemHasherBase(this, connection));
addEntityTracker(connection);
addItemHasher(connection);
}
// If data beyond the tracker and hasher is required
//@Override
//public ProtocolStorablesBase createStorables() {
// return new ProtocolStorables99_1();
//}
@Override
public BackwardsMappingData getMappingData() {
return MAPPINGS;
@@ -98,8 +98,8 @@ public class Protocol1_10To1_9_3 extends BackwardsProtocol<ClientboundPackets1_9
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_10.EntityType.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_10.EntityType.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
}
@Override
@@ -50,7 +50,7 @@ public class BlockItemPacketRewriter1_10 extends LegacyBlockItemRewriter<Clientb
registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);
protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_10To1_9_3.class);
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment());
Chunk chunk = wrapper.passthrough(type);
@@ -39,8 +39,8 @@ public class Protocol1_11_1To1_11 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
}
@Override
@@ -56,8 +56,8 @@ public class Protocol1_11To1_10 extends BackwardsProtocol<ClientboundPackets1_9_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
if (!user.has(WindowTracker.class)) {
user.put(new WindowTracker());
@@ -22,27 +22,39 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;
public class SplashPotionMappings1_10 {
private static final Int2IntMap DATA = new Int2IntOpenHashMap(14, 0.99F);
private static final Int2IntMap DATA = new Int2IntOpenHashMap(26, 0.99F);
static {
DATA.defaultReturnValue(-1);
DATA.put(2039713, 5); // night vision
DATA.put(8356754, 7); // invisibility
DATA.put(2293580, 9); // jump boost
DATA.put(14981690, 12); // fire resistance
DATA.put(8171462, 14); // swiftness
DATA.put(5926017, 17); // slowness
DATA.put(3035801, 19); // water breathing
DATA.put(16262179, 21); // instant health
DATA.put(4393481, 23); // instant damage
DATA.put(5149489, 25); // poison
DATA.put(13458603, 28); // regeneration
DATA.put(9643043, 31); // strength
DATA.put(4738376, 34); // weakness
DATA.put(3381504, 36); // luck
DATA.put(0x1F1FA1, 5); // night vision
DATA.put(0xC2FF66, 5); // night vision 1.19.4
DATA.put(0x7F8392, 7); // invisibility
DATA.put(0xF6F6F6, 7); // invisibility 1.19.4
DATA.put(0x22FF4C, 9); // jump boost
DATA.put(0xFDFF84, 9); // jump boost 1.19.4
DATA.put(0xE49A3A, 12); // fire resistance
DATA.put(0xFF9900, 12); // fire resistance 1.19.4
DATA.put(0x7CAFC6, 14); // swiftness
DATA.put(0x33EBFF, 14); // swiftness 1.19.4
DATA.put(0x5A6C81, 17); // slowness
DATA.put(0x8BAFE0, 17); // slowness 1.19.4
DATA.put(0x2E5299, 19); // water breathing
DATA.put(0x98DAC0, 19); // water breathing 1.19.4
DATA.put(0xF82423, 21); // instant health
DATA.put(0x430A09, 23); // instant damage
DATA.put(0xA9656A, 23); // instant damage 1.19.4
DATA.put(0x4E9331, 25); // poison
DATA.put(0x87A363, 25); // poison 1.19.4
DATA.put(0xCD5CAB, 28); // regeneration
DATA.put(0x932423, 31); // strength
DATA.put(0xFFC700, 31); // strength 1.19.4
DATA.put(0x484D48, 34); // weakness
DATA.put(0x339900, 36); // luck
DATA.put(0x59C106, 36); // luck 1.19.4
}
public static int getOldData(int data) {
return DATA.get(data);
// newer servers send as ARGB
return DATA.get(data & 0xFFFFFF);
}
}
@@ -28,7 +28,7 @@ import com.viaversion.viabackwards.protocol.v1_11to1_10.storage.ChestedHorseStor
import com.viaversion.viabackwards.protocol.v1_11to1_10.storage.WindowTracker;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;
@@ -40,7 +40,6 @@ import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;
import com.viaversion.viaversion.protocols.v1_10to1_11.data.EntityMappings1_11;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;
import com.viaversion.viaversion.util.IdAndData;
import java.util.Arrays;
import java.util.Optional;
@@ -147,7 +146,7 @@ public class BlockItemPacketRewriter1_11 extends LegacyBlockItemRewriter<Clientb
registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);
protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_11To1_10.class);
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); // Use the 1.10 Chunk type since nothing changed.
Chunk chunk = wrapper.passthrough(type);
@@ -252,7 +251,7 @@ public class BlockItemPacketRewriter1_11 extends LegacyBlockItemRewriter<Clientb
@Override
protected void registerRewrites() {
// Handle spawner block entity (map to itself with custom handler)
MappedLegacyBlockItem data = itemReplacements.computeIfAbsent(IdAndData.toRawData(52), s -> new MappedLegacyBlockItem(52));
MappedLegacyBlockItem data = blockReplacements.computeIfAbsent(compress(52, -1), s -> new MappedLegacyBlockItem(52, (short) -1, null, MappedLegacyBlockItem.Type.BLOCK));
data.setBlockEntityHandler((b, tag) -> EntityMappings1_11.toClientSpawner(tag, true));
enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName());
@@ -295,9 +294,9 @@ public class BlockItemPacketRewriter1_11 extends LegacyBlockItemRewriter<Clientb
private boolean isLlama(UserConnection user) {
WindowTracker tracker = user.get(WindowTracker.class);
if (tracker.getInventory() != null && tracker.getInventory().equals("EntityHorse")) {
EntityTracker entTracker = user.getEntityTracker(Protocol1_11To1_10.class);
StoredEntityData entityData = entTracker.entityData(tracker.getEntityId());
return entityData != null && entityData.type().is(EntityTypes1_11.EntityType.LLAMA);
EntityTracker entTracker = user.getEntityTracker(protocol);
TrackedEntity entityData = entTracker.entity(tracker.getEntityId());
return entityData != null && entityData.entityType().is(EntityTypes1_11.EntityType.LLAMA);
}
return false;
}
@@ -305,8 +304,8 @@ public class BlockItemPacketRewriter1_11 extends LegacyBlockItemRewriter<Clientb
private Optional<ChestedHorseStorage> getChestedHorse(UserConnection user) {
WindowTracker tracker = user.get(WindowTracker.class);
if (tracker.getInventory() != null && tracker.getInventory().equals("EntityHorse")) {
EntityTracker entTracker = user.getEntityTracker(Protocol1_11To1_10.class);
StoredEntityData entityData = entTracker.entityData(tracker.getEntityId());
EntityTracker entTracker = user.getEntityTracker(protocol);
TrackedEntity entityData = entTracker.entity(tracker.getEntityId());
if (entityData != null)
return Optional.of(entityData.get(ChestedHorseStorage.class));
}
@@ -23,7 +23,6 @@ import com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;
import com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;
import com.viaversion.viabackwards.protocol.v1_11to1_10.data.SplashPotionMappings1_10;
import com.viaversion.viabackwards.protocol.v1_11to1_10.storage.ChestedHorseStorage;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -259,9 +258,9 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
// Handle horse flags
filter().type(EntityTypes1_11.EntityType.ABSTRACT_HORSE).index(13).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
byte b = (byte) data.getValue();
if (entityData.has(ChestedHorseStorage.class) && entityData.get(ChestedHorseStorage.class).isChested()) {
final ChestedHorseStorage chestedHorseStorage = event.trackedEntity().get(ChestedHorseStorage.class);
if (chestedHorseStorage != null && chestedHorseStorage.isChested()) {
b |= 0x08; // Chested
data.setValue(b);
}
@@ -269,9 +268,8 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
// Create chested horse storage
filter().type(EntityTypes1_11.EntityType.CHESTED_HORSE).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
if (!entityData.has(ChestedHorseStorage.class)) {
entityData.put(new ChestedHorseStorage());
if (!event.trackedEntity().has(ChestedHorseStorage.class)) {
event.trackedEntity().put(new ChestedHorseStorage());
}
});
@@ -280,8 +278,7 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
// Handle chested horse
filter().type(EntityTypes1_11.EntityType.CHESTED_HORSE).index(15).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
ChestedHorseStorage storage = entityData.get(ChestedHorseStorage.class);
ChestedHorseStorage storage = event.trackedEntity().get(ChestedHorseStorage.class);
boolean b = (boolean) data.getValue();
storage.setChested(b);
event.cancel();
@@ -289,8 +286,7 @@ public class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPa
// Get rid of Liama entity data
filter().type(EntityTypes1_11.EntityType.LLAMA).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
ChestedHorseStorage storage = entityData.get(ChestedHorseStorage.class);
ChestedHorseStorage storage = event.trackedEntity().get(ChestedHorseStorage.class);
int index = event.index();
// Store them for later (:
@@ -18,7 +18,9 @@
package com.viaversion.viabackwards.protocol.v1_11to1_10.storage;
public class ChestedHorseStorage {
import com.viaversion.viaversion.api.connection.StorableObject;
public final class ChestedHorseStorage implements StorableObject {
private boolean chested;
private int liamaStrength;
private int liamaCarpetColor = -1;
@@ -71,8 +71,8 @@ public class Protocol1_12To1_11_1 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_12.EntityType.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_12.EntityType.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
user.put(new ShoulderTracker(user));
}
@@ -18,8 +18,10 @@
package com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter;
import com.google.common.primitives.Ints;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntArrayTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.LongArrayTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
@@ -131,7 +133,7 @@ public class BlockItemPacketRewriter1_12 extends LegacyBlockItemRewriter<Clientb
registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);
protocol.registerClientbound(ClientboundPackets1_12.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_12To1_11_1.class);
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); // Use the 1.9.4 Chunk type since nothing changed.
Chunk chunk = wrapper.passthrough(type);
@@ -230,6 +232,30 @@ public class BlockItemPacketRewriter1_12 extends LegacyBlockItemRewriter<Clientb
backupTag.put(entry.getKey(), fromLongArrayTag(tag));
iterator.remove();
hasLongArrayTag = true;
} else if (entry.getValue() instanceof ListTag<?> tag) {
if (tag.getElementType() == LongArrayTag.class) {
// Lists can't hold mixed tag types, so the whole list has to be removed
ListTag<IntArrayTag> intArrays = new ListTag<>(IntArrayTag.class);
for (Tag longArrayTag : tag) {
intArrays.add(fromLongArrayTag((LongArrayTag) longArrayTag));
}
backupTag.put(entry.getKey(), intArrays);
iterator.remove();
hasLongArrayTag = true;
} else if (tag.getElementType() == CompoundTag.class) {
// Backed up by list index
CompoundTag listBackupTag = new CompoundTag();
for (int i = 0; i < tag.size(); i++) {
CompoundTag nestedBackupTag = new CompoundTag();
if (handleNbtToClient((CompoundTag) tag.get(i), nestedBackupTag)) {
listBackupTag.put(Integer.toString(i), nestedBackupTag);
}
}
if (!listBackupTag.isEmpty()) {
backupTag.put(entry.getKey(), listBackupTag);
hasLongArrayTag = true;
}
}
}
}
return hasLongArrayTag;
@@ -252,11 +278,33 @@ public class BlockItemPacketRewriter1_12 extends LegacyBlockItemRewriter<Clientb
private void handleNbtToServer(CompoundTag compoundTag, CompoundTag backupTag) {
// Restore the removed long array tags
for (Map.Entry<String, Tag> entry : backupTag) {
if (entry.getValue() instanceof CompoundTag) {
CompoundTag nestedTag = compoundTag.getCompoundTag(entry.getKey());
handleNbtToServer(nestedTag, (CompoundTag) entry.getValue());
} else {
compoundTag.put(entry.getKey(), fromIntArrayTag((IntArrayTag) entry.getValue()));
if (entry.getValue() instanceof CompoundTag nestedBackupTag) {
Tag tag = compoundTag.get(entry.getKey());
if (tag instanceof CompoundTag nestedTag) {
handleNbtToServer(nestedTag, nestedBackupTag);
} else if (tag instanceof ListTag<?> listTag) {
handleListToServer(listTag, nestedBackupTag);
}
} else if (entry.getValue() instanceof IntArrayTag intArrayTag) {
compoundTag.put(entry.getKey(), fromIntArrayTag(intArrayTag));
} else if (entry.getValue() instanceof ListTag<?> intArrays) {
ListTag<LongArrayTag> longArrays = new ListTag<>(LongArrayTag.class);
for (Tag intArrayTag : intArrays) {
if (intArrayTag instanceof IntArrayTag) {
longArrays.add(fromIntArrayTag((IntArrayTag) intArrayTag));
}
}
compoundTag.put(entry.getKey(), longArrays);
}
}
}
private void handleListToServer(ListTag<?> listTag, CompoundTag backupTag) {
for (Map.Entry<String, Tag> entry : backupTag) {
Integer index = Ints.tryParse(entry.getKey());
if (index != null && index >= 0 && index < listTag.size()
&& listTag.get(index) instanceof CompoundTag nestedTag && entry.getValue() instanceof CompoundTag nestedBackupTag) {
handleNbtToServer(nestedTag, nestedBackupTag);
}
}
}
@@ -275,7 +323,7 @@ public class BlockItemPacketRewriter1_12 extends LegacyBlockItemRewriter<Clientb
private LongArrayTag fromIntArrayTag(IntArrayTag tag) {
long[] longArray = new long[tag.length() / 2];
int[] intArray = tag.getValue();
for (int i = 0, j = 0; i < intArray.length; i += 2, j++) {
for (int i = 0, j = 0; j < longArray.length; i += 2, j++) {
longArray[j] = (long) intArray[i] << 32 | ((long) intArray[i + 1] & 0xFFFFFFFFL);
}
return new LongArrayTag(longArray);
@@ -23,7 +23,6 @@ import com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;
import com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;
import com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage.ParrotStorage;
import com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage.ShoulderTracker;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_12;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -118,7 +117,7 @@ public class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPa
map(Types.INT); // 2 - Dimension
handler(getDimensionHandler(1));
handler(getPlayerTrackerHandler());
handler(playerTrackerHandler());
handler(wrapper -> {
ShoulderTracker tracker = wrapper.user().get(ShoulderTracker.class);
@@ -208,16 +207,14 @@ public class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPa
// Create Parrot storage
filter().type(EntityTypes1_12.EntityType.PARROT).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
if (!entityData.has(ParrotStorage.class)) {
entityData.put(new ParrotStorage());
if (!event.trackedEntity().has(ParrotStorage.class)) {
event.trackedEntity().put(new ParrotStorage());
}
});
// Parrot remove animal entity data
filter().type(EntityTypes1_12.EntityType.PARROT).cancel(12); // Is baby
filter().type(EntityTypes1_12.EntityType.PARROT).index(13).handler((event, data) -> {
StoredEntityData entityData = storedEntityData(event);
ParrotStorage storage = entityData.get(ParrotStorage.class);
ParrotStorage storage = event.trackedEntity().get(ParrotStorage.class);
boolean isSitting = (((byte) data.getValue()) & 0x01) == 0x01;
boolean isTamed = (((byte) data.getValue()) & 0x04) == 0x04;
@@ -18,7 +18,9 @@
package com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage;
public class ParrotStorage {
import com.viaversion.viaversion.api.connection.StorableObject;
public final class ParrotStorage implements StorableObject {
private boolean tamed = true;
private boolean sitting = true;
@@ -159,8 +159,8 @@ public class Protocol1_13_1To1_13 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(getClass(), new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));
user.addClientWorld(getClass(), new ClientWorld());
user.storables(this).setEntityTracker(new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
}
@Override
@@ -34,7 +34,7 @@ public class WorldPacketRewriter1_13_1 {
BlockRewriter<ClientboundPackets1_13> blockRewriter = BlockRewriter.legacy(protocol);
protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13_1To1_13.class);
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
Chunk chunk = wrapper.passthrough(ChunkType1_13.forEnvironment(clientWorld.getEnvironment()));
blockRewriter.handleChunk(chunk);
@@ -28,10 +28,8 @@ import com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.BlockItemPack
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.EntityPacketRewriter1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.PlayerPacketRewriter1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.SoundPacketRewriter1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.PlayerPositionStorage1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.TabCompleteStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.ProtocolStorables1_13;
import com.viaversion.viabackwards.utils.BackwardsProtocolLogger;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
@@ -86,7 +84,7 @@ public class Protocol1_13To1_12_2 extends BackwardsProtocol<ClientboundPackets1_
super.registerPackets();
PaintingNames1_13.init();
Via.getManager().getProviders().register(BackwardsBlockEntityProvider.class, new BackwardsBlockEntityProvider());
Via.getManager().getProviders().register(BackwardsBlockEntityProvider.class, new BackwardsBlockEntityProvider(this));
translatableRewriter.registerLoginDisconnect();
translatableRewriter.registerBossEvent(ClientboundPackets1_13.BOSS_EVENT);
@@ -113,17 +111,18 @@ public class Protocol1_13To1_12_2 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));
final ProtocolStorables1_13 storables = user.storables(this);
storables.setClientWorld(new ClientWorld());
user.put(new BackwardsBlockStorage());
user.put(new TabCompleteStorage());
if (ViaBackwards.getConfig().isFix1_13FacePlayer() && !user.has(PlayerPositionStorage1_13.class)) {
user.put(new PlayerPositionStorage1_13());
if (ViaBackwards.getConfig().isFix1_13FacePlayer()) {
storables.setPlayerPositionStorage(new PlayerPositionStorage1_13());
}
}
user.put(new NoteBlockStorage());
@Override
public ProtocolStorables1_13 createStorables() {
return new ProtocolStorables1_13();
}
@Override
@@ -18,63 +18,15 @@
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;
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_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData;
import java.util.Map;
import java.util.StringJoiner;
public class PistonHandler implements BackwardsBlockEntityProvider.BackwardsBlockEntityHandler {
private final Object2IntMap<String> pistonIds = new Object2IntOpenHashMap<>();
public PistonHandler() {
pistonIds.defaultReturnValue(-1);
if (Via.getConfig().isServersideBlockConnections()) {
Map<String, Integer> keyToId = ConnectionData.getKeyToId();
for (Map.Entry<String, Integer> entry : keyToId.entrySet()) {
if (!entry.getKey().contains("piston")) {
continue;
}
addEntries(entry.getKey(), entry.getValue());
}
} else {
ListTag<StringTag> blockStates = MappingDataLoader.INSTANCE.loadNBT("blockstates-1.13.nbt").getListTag("blockstates", StringTag.class);
for (int id = 0; id < blockStates.size(); id++) {
StringTag state = blockStates.get(id);
String key = state.getValue();
if (!key.contains("piston")) {
continue;
}
addEntries(key, id);
}
}
}
// There doesn't seem to be a nicer way around it :(
private void addEntries(String data, int id) {
id = Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(id);
pistonIds.put(data, id);
String substring = data.substring(10);
if (!substring.startsWith("piston") && !substring.startsWith("sticky_piston")) return;
// Swap properties and add them to the map
String[] split = data.substring(0, data.length() - 1).split("\\[");
String[] properties = split[1].split(",");
data = split[0] + "[" + properties[1] + "," + properties[0] + "]";
pistonIds.put(data, id);
}
@Override
public CompoundTag transform(int blockId, CompoundTag tag) {
CompoundTag blockState = tag.getCompoundTag("blockState");
@@ -83,7 +35,7 @@ public class PistonHandler implements BackwardsBlockEntityProvider.BackwardsBloc
String dataFromTag = getDataFromTag(blockState);
if (dataFromTag == null) return tag;
int id = pistonIds.getInt(dataFromTag);
int id = Protocol1_13To1_12_2.MAPPINGS.getPistonIds().getInt(dataFromTag);
if (id == -1) {
//TODO see why this could be null and if this is bad
return tag;
@@ -0,0 +1,140 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation);either version 3 of the License);or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not);see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
// The mildly inconvenient alternative to the very inconvenient task of adding full 1.12 identifiers
public final class BackwardsItemMappings {
private static final Int2ObjectMap<String> MODEL_DATA_TO_NAME = new Int2ObjectOpenHashMap<>();
static {
add("acacia_button", 245);
add("birch_button", 243);
add("spruce_button", 242);
add("jungle_button", 244);
add("dark_oak_button", 246);
add("acacia_trapdoor", 191);
add("birch_trapdoor", 189);
add("spruce_trapdoor", 188);
add("jungle_trapdoor", 190);
add("dark_oak_trapdoor", 192);
add("acacia_pressure_plate", 164);
add("birch_pressure_plate", 162);
add("spruce_pressure_plate", 161);
add("jungle_pressure_plate", 163);
add("dark_oak_pressure_plate", 165);
add("acacia_boat", 762);
add("birch_boat", 760);
add("spruce_boat", 759);
add("jungle_boat", 761);
add("dark_oak_boat", 763);
add("blue_ice", 453);
add("pufferfish_bucket", 547);
add("salmon_bucket", 548);
add("cod_bucket", 549);
add("tropical_fish_bucket", 550);
add("heart_of_the_sea", 784);
add("nautilus_shell", 783);
add("phantom_membrane", 782);
add("turtle_helmet", 465);
add("turtle_egg", 427);
add("scute", 466);
add("trident", 781);
add("sea_pickle", 80);
add("seagrass", 79);
add("conduit", 454);
add("kelp", 554);
add("dried_kelp", 611);
add("dried_kelp_block", 555);
add("stripped_oak_log", 38);
add("stripped_acacia_log", 42);
add("stripped_birch_log", 40);
add("stripped_spruce_log", 39);
add("stripped_jungle_log", 41);
add("stripped_dark_oak_log", 43);
add("stripped_oak_wood", 44);
add("stripped_acacia_wood", 48);
add("stripped_birch_wood", 46);
add("stripped_spruce_wood", 45);
add("stripped_jungle_wood", 47);
add("stripped_dark_oak_wood", 49);
add("oak_wood", 50);
add("spruce_wood", 51);
add("birch_wood", 52);
add("jungle_wood", 53);
add("acacia_wood", 54);
add("dark_oak_wood", 55);
add("prismarine_slab", 128);
add("prismarine_brick_slab", 129);
add("dark_prismarine_slab", 130);
add("prismarine_stairs", 346);
add("prismarine_brick_stairs", 347);
add("dark_prismarine_stairs", 348);
add("drowned_spawn_egg", 643);
add("phantom_spawn_egg", 658);
add("dolphin_spawn_egg", 641);
add("turtle_spawn_egg", 674);
add("cod_spawn_egg", 638);
add("salmon_spawn_egg", 663);
add("pufferfish_spawn_egg", 661);
add("tropical_fish_spawn_egg", 673);
add("tube_coral", 438);
add("brain_coral", 439);
add("bubble_coral", 440);
add("fire_coral", 441);
add("horn_coral", 442);
add("tube_coral_fan", 443);
add("brain_coral_fan", 444);
add("bubble_coral_fan", 445);
add("fire_coral_fan", 446);
add("horn_coral_fan", 447);
add("dead_tube_coral_fan", 448);
add("dead_brain_coral_fan", 449);
add("dead_bubble_coral_fan", 450);
add("dead_fire_coral_fan", 451);
add("dead_horn_coral_fan", 452);
add("dead_tube_coral_block", 428);
add("dead_brain_coral_block", 429);
add("dead_bubble_coral_block", 430);
add("dead_fire_coral_block", 431);
add("dead_horn_coral_block", 432);
add("tube_coral_block", 433);
add("brain_coral_block", 434);
add("bubble_coral_block", 435);
add("fire_coral_block", 436);
add("horn_coral_block", 437);
add("smooth_quartz", 131);
add("smooth_red_sandstone", 132);
add("smooth_sandstone", 133);
add("smooth_stone", 134);
add("pumpkin", 181);
add("mushroom_stem", 205);
add("debug_stick", 768);
}
public static String identifier(final int id) {
return MODEL_DATA_TO_NAME.get(id);
}
private static void add(final String key, final int id) {
MODEL_DATA_TO_NAME.put(id, key);
}
}
@@ -19,10 +19,18 @@
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viabackwards.api.data.MappedItem;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.data.BlockStates1_13;
import com.viaversion.viaversion.protocols.v1_12_2to1_13.data.StatisticMappings1_13;
import java.util.HashMap;
import java.util.Map;
@@ -30,6 +38,7 @@ import java.util.Map;
public class BackwardsMappingData1_13 extends BackwardsMappingData {
private final Int2ObjectMap<String> statisticMappings = new Int2ObjectOpenHashMap<>();
private final Map<String, String> translateMappings = new HashMap<>();
private final Object2IntMap<String> pistonIds = new Object2IntOpenHashMap<>();
public BackwardsMappingData1_13() {
super("1.13", "1.12", Protocol1_12_2To1_13.class);
@@ -45,21 +54,61 @@ public class BackwardsMappingData1_13 extends BackwardsMappingData {
for (Map.Entry<String, String> entry : Protocol1_12_2To1_13.MAPPINGS.getTranslateMapping().entrySet()) {
translateMappings.put(entry.getValue(), entry.getKey());
}
pistonIds.defaultReturnValue(-1);
Object2IntMap<String> keyToId = ConnectionData.getKeyToId();
if (!keyToId.isEmpty()) {
for (final Object2IntMap.Entry<String> entry : keyToId.object2IntEntrySet()) {
if (!entry.getKey().contains("piston")) {
continue;
}
addPistonEntries(entry.getKey(), entry.getIntValue());
}
} else { // not loaded by VV
CompoundTag blockStatesData = MappingDataLoader.INSTANCE.loadNBT("blockstates-1.13.nbt");
BlockStates1_13.forEach(blockStatesData, (key, id) -> {
if (key.contains("piston")) {
addPistonEntries(key, id);
}
});
}
}
@Override
protected void loadBackwardsItemMappings(final CompoundTag itemCustomModelDatas) {
for (final Map.Entry<String, Tag> entry : itemCustomModelDatas.entrySet()) {
final int id = Integer.parseInt(entry.getKey());
final int customModelData = ((NumberTag) entry.getValue()).asInt();
final String identifier = BackwardsItemMappings.identifier(id);
final String fallbackName = unmappedVersion + " " + nameFromIdentifier(identifier);
backwardsItemMappings.put(id, new MappedItem(getNewItemId(id), identifier, fallbackName, customModelData));
}
}
private void addPistonEntries(String data, int id) {
id = getNewBlockStateId(id);
pistonIds.put(data, id);
String substring = data.substring(10);
if (!substring.startsWith("piston") && !substring.startsWith("sticky_piston")) return;
// Swap properties and add them to the map
String[] split = data.substring(0, data.length() - 1).split("\\[");
String[] properties = split[1].split(",");
data = split[0] + "[" + properties[1] + "," + properties[0] + "]";
pistonIds.put(data, id);
}
@Override
public int getNewBlockStateId(int id) {
// Comparator funkyness: https://github.com/ViaVersion/ViaBackwards/issues/524
// Always map to unpowered_comparator; powered_comparator is unused in 1.12 https://github.com/ViaVersion/ViaBackwards/issues/1222
if (id >= 5635 && id <= 5650) {
if (id < 5639) {
id += 4;
} else if (id < 5643) {
id -= 4;
} else if (id < 5647) {
id += 4;
} else {
id -= 4;
int mappedId = super.getNewBlockStateId(id);
if (mappedId >= 2400) {
mappedId -= 16;
}
return mappedId;
}
int mappedId = super.getNewBlockStateId(id);
@@ -85,4 +134,8 @@ public class BackwardsMappingData1_13 extends BackwardsMappingData {
public Map<String, String> getTranslateMappings() {
return translateMappings;
}
public Object2IntMap<String> getPistonIds() {
return pistonIds;
}
}
@@ -57,6 +57,6 @@ public class PaintingNames1_13 {
}
public static String getStringId(int id) {
return PAINTINGS.getOrDefault(id, "kebab");
return PAINTINGS.getOrDefault(id, "Kebab");
}
}
@@ -76,6 +76,11 @@ public class ParticleIdMappings1_12_2 {
float b = wrapper.read(Types.FLOAT);
float scale = wrapper.read(Types.FLOAT);
if (r == 0.0) {
// In 1.12.2 and below, a zero x offset becomes r = 1.0
r = Float.MIN_NORMAL;
}
wrapper.set(Types.FLOAT, 3, r); // 5 - Offset X index=3
wrapper.set(Types.FLOAT, 4, g); // 6 - Offset Y index=4
wrapper.set(Types.FLOAT, 5, b); // 7 - Offset Z index=5
@@ -20,6 +20,7 @@ package com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.BannerHandler;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.BedHandler;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.FlowerPotHandler;
@@ -27,6 +28,7 @@ import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.SkullHandler;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.SpawnerHandler;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.ProtocolStorables1_13;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.platform.providers.Provider;
@@ -36,8 +38,10 @@ import java.util.Map;
public class BackwardsBlockEntityProvider implements Provider {
private final Map<String, BackwardsBlockEntityProvider.BackwardsBlockEntityHandler> handlers = new HashMap<>();
private final Protocol1_13To1_12_2 protocol;
public BackwardsBlockEntityProvider() {
public BackwardsBlockEntityProvider(final Protocol1_13To1_12_2 protocol) {
this.protocol = protocol;
handlers.put("flower_pot", new FlowerPotHandler()); // TODO requires special treatment, manually send
handlers.put("bed", new BedHandler());
handlers.put("banner", new BannerHandler());
@@ -75,7 +79,7 @@ public class BackwardsBlockEntityProvider implements Provider {
return tag;
}
BackwardsBlockStorage storage = user.get(BackwardsBlockStorage.class);
BackwardsBlockStorage storage = user.<ProtocolStorables1_13>storables(protocol).backwardsBlockStorage();
Integer blockId = storage.get(position);
if (blockId == null) {
return tag;
@@ -34,6 +34,7 @@ import com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.ProtocolStorables1_13;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
@@ -140,7 +141,8 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
blockId = blockId - 483 + 219;
if (blockId == 25) { // Note block
final NoteBlockStorage noteBlockStorage = wrapper.user().get(NoteBlockStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
final NoteBlockStorage noteBlockStorage = storables.noteBlockStorage();
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
final Pair<Integer, Integer> update = noteBlockStorage.getNoteBlockUpdate(position);
@@ -185,7 +187,8 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
int chunkMinZ = wrapper.passthrough(Types.INT) << 4;
int chunkMaxX = chunkMinX + 15;
int chunkMaxZ = chunkMinZ + 15;
BackwardsBlockStorage blockStorage = wrapper.user().get(BackwardsBlockStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
BackwardsBlockStorage blockStorage = storables.backwardsBlockStorage();
blockStorage.getBlocks().entrySet().removeIf(entry -> {
BlockPosition position = entry.getKey();
return position.x() >= chunkMinX && position.z() >= chunkMinZ
@@ -202,14 +205,15 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
handler(wrapper -> {
int blockState = wrapper.read(Types.VAR_INT);
BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
// Note block special treatment
if (blockState >= 249 && blockState <= 748) { // Note block states id range
wrapper.user().get(NoteBlockStorage.class).storeNoteBlockUpdate(position, blockState);
storables.noteBlockStorage().storeNoteBlockUpdate(position, blockState);
}
// Store blocks
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
BackwardsBlockStorage storage = storables.backwardsBlockStorage();
storage.checkAndStore(position, blockState);
wrapper.write(Types.VAR_INT, protocol.getMappingData().getNewBlockStateId(blockState));
@@ -228,7 +232,8 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
map(Types.INT); // 1 - Chunk Z
map(Types.BLOCK_CHANGE_ARRAY);
handler(wrapper -> {
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
BackwardsBlockStorage storage = storables.backwardsBlockStorage();
for (BlockChangeRecord record : wrapper.get(Types.BLOCK_CHANGE_ARRAY, 0)) {
int chunkX = wrapper.get(Types.INT, 0);
@@ -253,7 +258,8 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
});
protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_12_2.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
ClientWorld clientWorld = storables.clientWorld();
ChunkType1_9_3 type_old = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment());
ChunkType1_13 type = ChunkType1_13.forEnvironment(clientWorld.getEnvironment());
@@ -261,7 +267,7 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
// Handle Block Entities before block rewrite
BackwardsBlockEntityProvider provider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);
BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);
BackwardsBlockStorage storage = storables.backwardsBlockStorage();
for (CompoundTag tag : chunk.getBlockEntities()) {
StringTag idTag = tag.getStringTag("id");
if (idTag == null) continue;
@@ -291,6 +297,8 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
}
// Rewrite new blocks to old blocks
int baseX = chunk.getX() << 4;
int baseZ = chunk.getZ() << 4;
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) {
@@ -298,29 +306,21 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
}
DataPalette palette = section.palette(PaletteType.BLOCKS);
int baseY = i << 4;
// Flower pots require a special treatment, they are no longer block entities :(
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = palette.idAt(x, y, z);
palette.forEachMatchingCoordinate(FlowerPotHandler::isFlowah, idx -> {
int block = palette.idAt(idx);
BlockPosition pos = new BlockPosition(
ChunkSection.xFromIndex(idx) + baseX,
(short) (ChunkSection.yFromIndex(idx) + baseY),
ChunkSection.zFromIndex(idx) + baseZ
);
storage.checkAndStore(pos, block);
// Check if the block is a flower
if (FlowerPotHandler.isFlowah(block)) {
BlockPosition pos = new BlockPosition(
(x + (chunk.getX() << 4)),
(short) (y + (i << 4)),
(z + (chunk.getZ() << 4))
);
// Store block
storage.checkAndStore(pos, block);
CompoundTag nbt = provider.transform(wrapper.user(), pos, "minecraft:flower_pot");
chunk.getBlockEntities().add(nbt);
}
}
}
}
CompoundTag nbt = provider.transform(wrapper.user(), pos, "minecraft:flower_pot");
chunk.getBlockEntities().add(nbt);
});
for (int j = 0; j < palette.size(); j++) {
int mappedBlockStateId = protocol.getMappingData().getNewBlockStateId(palette.idByIndex(j));
@@ -463,8 +463,12 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
if (item.identifier() == 358) { // map
Tag mapTag = tag.remove("map");
if (!gotRawIdFromTag && mapTag instanceof NumberTag) {
item.setData(((NumberTag) mapTag).asShort());
if (!gotRawIdFromTag && mapTag instanceof NumberTag numberTag) {
item.setData((short) (numberTag.asInt() & 0x7FFF));
// Keep original ID if conversion is lossy
if (numberTag.asInt() > Short.MAX_VALUE) {
tag.put("map", numberTag);
}
}
}
@@ -667,7 +671,9 @@ public class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<Clientbou
}
if (item.identifier() == 358) { // map
if (tag == null) item.setTag(tag = new CompoundTag());
tag.putInt("map", item.data());
if (!tag.contains("map")) {
tag.putInt("map", item.data());
}
}
// NBT Changes
@@ -24,9 +24,8 @@ import com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.EntityIdMappings1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.PaintingNames1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.ParticleIdMappings1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.PlayerPositionStorage1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.ProtocolStorables1_13;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
@@ -64,7 +63,8 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
handler(wrapper -> {
if (!ViaBackwards.getConfig().isFix1_13FacePlayer()) return;
PlayerPositionStorage1_13 playerStorage = wrapper.user().get(PlayerPositionStorage1_13.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
PlayerPositionStorage1_13 playerStorage = storables.playerPositionStorage();
byte bitField = wrapper.get(Types.BYTE, 0);
playerStorage.setX(toSet(bitField, 0, playerStorage.x(), wrapper.get(Types.DOUBLE, 0)));
playerStorage.setY(toSet(bitField, 1, playerStorage.y(), wrapper.get(Types.DOUBLE, 1)));
@@ -177,8 +177,10 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
map(Types.VAR_INT);
map(Types.UUID);
handler(getTrackerHandler(EntityTypes1_13.EntityType.PAINTING));
handler(wrapper -> {
int entityId = wrapper.get(Types.VAR_INT, 0);
tracker(wrapper.user()).addEntity(entityId, EntityTypes1_13.EntityType.PAINTING);
int motive = wrapper.read(Types.VAR_INT);
String title = PaintingNames1_13.getStringId(motive);
wrapper.write(Types.STRING, title);
@@ -194,13 +196,14 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
map(Types.INT); // 0 - Dimension ID
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_12_2.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
ClientWorld clientWorld = storables.clientWorld();
int dimensionId = wrapper.get(Types.INT, 0);
if (clientWorld.setEnvironment(dimensionId)) {
tracker(wrapper.user()).clearEntities();
wrapper.user().get(BackwardsBlockStorage.class).clear();
wrapper.user().get(NoteBlockStorage.class).clear();
storables.backwardsBlockStorage().clear();
storables.noteBlockStorage().clear();
}
});
}
@@ -221,7 +224,8 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
final double y = wrapper.read(Types.DOUBLE);
final double z = wrapper.read(Types.DOUBLE);
PlayerPositionStorage1_13 positionStorage = wrapper.user().get(PlayerPositionStorage1_13.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
PlayerPositionStorage1_13 positionStorage = storables.playerPositionStorage();
// Send teleport packet to client
PacketWrapper positionAndLook = wrapper.create(ClientboundPackets1_12_1.PLAYER_POSITION);
@@ -243,10 +247,9 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
if (!ViaBackwards.getConfig().isFix1_13FacePlayer()) {
return;
}
final double x = wrapper.passthrough(Types.DOUBLE);
final double y = wrapper.passthrough(Types.DOUBLE);
final double z = wrapper.passthrough(Types.DOUBLE);
wrapper.user().get(PlayerPositionStorage1_13.class).setPosition(x, y, z);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
storables.playerPositionStorage().setPosFromPacket(wrapper);
};
protocol.registerServerbound(ServerboundPackets1_12_1.MOVE_PLAYER_POS, movementRemapper); // Player Position
protocol.registerServerbound(ServerboundPackets1_12_1.MOVE_PLAYER_POS_ROT, movementRemapper); // Player Position And Look (serverbound)
@@ -352,6 +355,14 @@ public class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPa
event.cancel();
});
filter().dataType(EntityDataTypes1_12.OPTIONAL_BLOCK_STATE).handler(((event, data) -> {
final int blockState = data.value();
if (blockState != 0) {
final int combined = Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(blockState);
data.setValue(((combined >> 4) & 0xFFF) | ((combined & 0xF) << 12));
}
}));
}
@Override
@@ -21,6 +21,7 @@ import com.google.common.base.Joiner;
import com.viaversion.viabackwards.ViaBackwards;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.ParticleIdMappings1_12_2;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.ProtocolStorables1_13;
import com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.TabCompleteStorage;
import com.viaversion.viabackwards.utils.ChatUtil;
import com.viaversion.viaversion.api.Via;
@@ -142,7 +143,8 @@ public class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2>
@Override
public void register() {
handler(packetWrapper -> {
TabCompleteStorage storage = packetWrapper.user().get(TabCompleteStorage.class);
ProtocolStorables1_13 storables = packetWrapper.user().storables(protocol);
TabCompleteStorage storage = storables.tabCompleteStorage();
int action = packetWrapper.passthrough(Types.VAR_INT);
int nPlayers = packetWrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < nPlayers; i++) {
@@ -236,7 +238,8 @@ public class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2>
protocol.registerClientbound(ClientboundPackets1_13.COMMANDS, null, wrapper -> {
wrapper.cancel();
TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
TabCompleteStorage storage = storables.tabCompleteStorage();
if (!storage.commands().isEmpty()) {
storage.commands().clear();
@@ -274,7 +277,8 @@ public class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2>
});
protocol.registerClientbound(ClientboundPackets1_13.COMMAND_SUGGESTIONS, wrapper -> {
TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
TabCompleteStorage storage = storables.tabCompleteStorage();
if (storage.lastRequest() == null) {
wrapper.cancel();
return;
@@ -299,7 +303,8 @@ public class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2>
});
protocol.registerServerbound(ServerboundPackets1_12_1.COMMAND_SUGGESTION, wrapper -> {
TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);
ProtocolStorables1_13 storables = wrapper.user().storables(protocol);
TabCompleteStorage storage = storables.tabCompleteStorage();
List<String> suggestions = new ArrayList<>();
String command = wrapper.read(Types.STRING);
@@ -18,7 +18,6 @@
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;
import com.viaversion.viaversion.libs.fastutil.ints.IntSet;
@@ -26,7 +25,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BackwardsBlockStorage implements StorableObject {
public class BackwardsBlockStorage {
// This BlockStorage is very exclusive (;
private static final IntSet WHITELIST = new IntOpenHashSet(779);
@@ -17,13 +17,12 @@
*/
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.util.Pair;
public class NoteBlockStorage implements StorableObject {
public class NoteBlockStorage {
private static final int MAX_NOTE_ID = 24;
@@ -0,0 +1,49 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class ProtocolStorables1_13 extends ProtocolStorablesBase {
private final BackwardsBlockStorage backwardsBlockStorage = new BackwardsBlockStorage();
private final TabCompleteStorage tabCompleteStorage = new TabCompleteStorage();
private final NoteBlockStorage noteBlockStorage = new NoteBlockStorage();
private PlayerPositionStorage1_13 playerPositionStorage;
public BackwardsBlockStorage backwardsBlockStorage() {
return backwardsBlockStorage;
}
public TabCompleteStorage tabCompleteStorage() {
return tabCompleteStorage;
}
public NoteBlockStorage noteBlockStorage() {
return noteBlockStorage;
}
public @Nullable PlayerPositionStorage1_13 playerPositionStorage() {
return playerPositionStorage;
}
public void setPlayerPositionStorage(final PlayerPositionStorage1_13 playerPositionStorage) {
this.playerPositionStorage = playerPositionStorage;
}
}
@@ -17,14 +17,13 @@
*/
package com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class TabCompleteStorage implements StorableObject {
public class TabCompleteStorage {
private final Map<UUID, String> usernames = new HashMap<>();
private final Set<String> commands = new HashSet<>();
private int lastId;
@@ -41,8 +41,8 @@ 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());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_15.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
}
@Override
@@ -27,7 +27,7 @@ import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.EntityPacketR
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.PlayerPacketRewriter1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.SoundPacketRewriter1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ProtocolStorables1_14;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.RegistryType;
@@ -121,7 +121,8 @@ public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_
}
//TODO Soft memory leak: Don't store light if chunk is already loaded
wrapper.user().get(ChunkLightStorage.class).setStoredLight(skyLight, blockLight, x, z);
ProtocolStorables1_14 storables = wrapper.user().storables(this);
storables.chunkLightStorage().setStoredLight(skyLight, blockLight, x, z);
wrapper.cancel();
});
}
@@ -132,14 +133,14 @@ public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_14.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_14.PLAYER));
final ProtocolStorables1_14 storables = user.storables(this);
storables.setClientWorld(new ClientWorld());
}
if (!user.has(ChunkLightStorage.class)) {
user.put(new ChunkLightStorage());
}
user.put(new DifficultyStorage());
@Override
public ProtocolStorables1_14 createStorables() {
return new ProtocolStorables1_14();
}
@Override
@@ -27,6 +27,7 @@ import com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;
import com.viaversion.viabackwards.item.DataItemWithExtras;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ProtocolStorables1_14;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
@@ -36,6 +37,7 @@ import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLightImpl;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;
@@ -129,7 +131,7 @@ public class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<Clientbou
}
if (stringType == null) {
protocol.getLogger().warning("Can't open inventory for player! Type: " + type);
// TODO stonecutter, if reasonably possible
wrapper.cancel();
return;
}
@@ -220,7 +222,7 @@ public class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<Clientbou
handler(wrapper -> {
int entityId = wrapper.get(Types.VAR_INT, 0);
EntityType entityType = wrapper.user().getEntityTracker(Protocol1_14To1_13_2.class).entityType(entityId);
EntityType entityType = wrapper.user().getEntityTracker(protocol).entityType(entityId);
if (entityType == null) return;
if (entityType.isOrHasParent(EntityTypes1_14.ABSTRACT_HORSE)) {
@@ -346,11 +348,12 @@ public class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<Clientbou
});
protocol.registerClientbound(ClientboundPackets1_14.LEVEL_CHUNK, wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_14To1_13_2.class);
ProtocolStorables1_14 storables = wrapper.user().storables(protocol);
ClientWorld clientWorld = storables.clientWorld();
Chunk chunk = wrapper.read(ChunkType1_14.TYPE);
wrapper.write(ChunkType1_13.forEnvironment(clientWorld.getEnvironment()), chunk);
ChunkLightStorage.ChunkLight chunkLight = wrapper.user().get(ChunkLightStorage.class).getStoredLight(chunk.getX(), chunk.getZ());
ChunkLightStorage.ChunkLight chunkLight = storables.chunkLightStorage().getStoredLight(chunk.getX(), chunk.getZ());
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
@@ -373,16 +376,11 @@ public class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<Clientbou
DataPalette palette = section.palette(PaletteType.BLOCKS);
if (Via.getConfig().isNonFullBlockLightFix() && section.getNonAirBlocksCount() != 0 && sectionLight.hasBlockLight()) {
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
int id = palette.idAt(x, y, z);
if (Protocol1_13_2To1_14.MAPPINGS.getNonFullBlocks().contains(id)) {
sectionLight.getBlockLightNibbleArray().set(x, y, z, 0);
}
}
}
}
NibbleArray blockLight = sectionLight.getBlockLightNibbleArray();
palette.forEachMatchingCoordinate(
id -> Protocol1_13_2To1_14.MAPPINGS.getNonFullBlocks().contains(id),
idx -> blockLight.set(ChunkSection.xFromIndex(idx), ChunkSection.yFromIndex(idx), ChunkSection.zFromIndex(idx), 0)
);
}
for (int j = 0; j < palette.size(); j++) {
@@ -395,7 +393,8 @@ public class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<Clientbou
protocol.registerClientbound(ClientboundPackets1_14.FORGET_LEVEL_CHUNK, wrapper -> {
int x = wrapper.passthrough(Types.INT);
int z = wrapper.passthrough(Types.INT);
wrapper.user().get(ChunkLightStorage.class).unloadChunk(x, z);
ProtocolStorables1_14 storables = wrapper.user().storables(protocol);
storables.chunkLightStorage().unloadChunk(x, z);
});
protocol.replaceClientbound(ClientboundPackets1_14.LEVEL_EVENT, new PacketHandlers() {
@@ -21,11 +21,10 @@ import com.viaversion.viabackwards.api.entities.storage.EntityPositionHandler;
import com.viaversion.viabackwards.api.entities.storage.EntityReplacement;
import com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.EntityPositionStorage1_14;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ProtocolStorables1_14;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.Particle;
@@ -64,6 +63,12 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
if (status != 3) return;
EntityTracker tracker = tracker(wrapper.user());
if (entityId == tracker.clientEntityId()) {
// Clients up to 1.13.2 run the local death handling on their own death event, clearing
// their own inventory; the death screen still shows from the health update
wrapper.cancel();
return;
}
EntityType entityType = tracker.entityType(entityId);
if (entityType != EntityTypes1_14.PLAYER) return;
@@ -308,9 +313,10 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
map(Types.INT); // 2 - Dimension
handler(getDimensionHandler(1));
handler(getPlayerTrackerHandler());
handler(playerTrackerHandler());
handler(wrapper -> {
short difficulty = wrapper.user().get(DifficultyStorage.class).getDifficulty();
ProtocolStorables1_14 storables = wrapper.user().storables(protocol);
short difficulty = storables.difficultyStorage().getDifficulty();
wrapper.write(Types.UNSIGNED_BYTE, difficulty);
wrapper.passthrough(Types.UNSIGNED_BYTE); // Max Players
@@ -319,7 +325,7 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
final int entityId = wrapper.get(Types.INT, 0);
final StoredEntityData storedEntity = tracker(wrapper.user()).entityData(entityId);
final TrackedEntity storedEntity = tracker(wrapper.user()).entity(entityId);
storedEntity.put(new EntityPositionStorage1_14());
});
}
@@ -331,17 +337,18 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
map(Types.INT); // 0 - Dimension ID
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_14To1_13_2.class);
ProtocolStorables1_14 storables = wrapper.user().storables(protocol);
ClientWorld clientWorld = storables.clientWorld();
int dimensionId = wrapper.get(Types.INT, 0);
if (clientWorld.setEnvironment(dimensionId)) {
EntityTracker tracker = tracker(wrapper.user());
tracker.clearEntities();
wrapper.user().get(ChunkLightStorage.class).clear();
tracker.entityData(tracker.clientEntityId()).put(new EntityPositionStorage1_14());
storables.chunkLightStorage().clear();
tracker.entity(tracker.clientEntityId()).put(new EntityPositionStorage1_14());
}
short difficulty = wrapper.user().get(DifficultyStorage.class).getDifficulty();
short difficulty = storables.difficultyStorage().getDifficulty();
wrapper.write(Types.UNSIGNED_BYTE, difficulty);
});
}
@@ -359,7 +366,8 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
private void trackAndCacheEntityPosition(PacketWrapper wrapper, EntityType type) {
// Tracks the entity + cache the position for the entity
tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), type);
final int entityId = wrapper.get(Types.VAR_INT, 0);
tracker(wrapper.user()).addEntity(entityId, type);
if (type == EntityTypes1_14.PAINTING) {
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
@@ -476,9 +484,21 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
filter().type(EntityTypes1_14.CAT).handler((event, data) -> {
if (event.index() == 15) {
data.setValue(1);
} else if (event.index() == 13) {
data.setValue((byte) ((byte) data.getValue() & 0x4));
Integer catVariant = data.value();
int ocelotVariant = catVariant != null ? switch (catVariant) {
// new ocelot mappings
case 0, 1, 2, 3 -> catVariant;
// Map to tuxedo
case 10 -> 1; // all_black
// Map to red
case 5, 6 -> 2; // calico, persian
// Map to siamese
case 4, 7, 8, 9 -> 3; // british_shorthair, ragdoll, white, jellie
default -> 0; // Untamed
} : 0;
data.setValue(ocelotVariant);
}
});
@@ -519,7 +539,7 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
@Override
public void onMappingDataLoaded() {
super.onMappingDataLoaded();
mapEntityTypeWithData(EntityTypes1_14.CAT, EntityTypes1_14.OCELOT).jsonName();
mapEntityTypeWithData(EntityTypes1_14.CAT, EntityTypes1_14.OCELOT);
mapEntityTypeWithData(EntityTypes1_14.TRADER_LLAMA, EntityTypes1_14.LLAMA).jsonName();
mapEntityTypeWithData(EntityTypes1_14.FOX, EntityTypes1_14.WOLF).jsonName();
mapEntityTypeWithData(EntityTypes1_14.PANDA, EntityTypes1_14.POLAR_BEAR).jsonName();
@@ -18,7 +18,7 @@
package com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ProtocolStorables1_14;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
@@ -41,7 +41,8 @@ public class PlayerPacketRewriter1_14 extends RewriterBase<Protocol1_14To1_13_2>
read(Types.BOOLEAN); // Locked
handler(wrapper -> {
byte difficulty = wrapper.get(Types.UNSIGNED_BYTE, 0).byteValue();
wrapper.user().get(DifficultyStorage.class).setDifficulty(difficulty);
ProtocolStorables1_14 storables = wrapper.user().storables(protocol);
storables.difficultyStorage().setDifficulty(difficulty);
});
}
});
@@ -19,7 +19,7 @@ package com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;
import com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.EntityPositionStorage1_14;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Types;
@@ -45,7 +45,7 @@ public class SoundPacketRewriter1_14 extends RewriterBase<Protocol1_14To1_13_2>
int category = wrapper.read(Types.VAR_INT);
int entityId = wrapper.read(Types.VAR_INT);
StoredEntityData storedEntity = wrapper.user().getEntityTracker(protocol.getClass()).entityData(entityId);
TrackedEntity storedEntity = wrapper.user().getEntityTracker(protocol).entity(entityId);
EntityPositionStorage1_14 entityStorage;
if (storedEntity == null || (entityStorage = storedEntity.get(EntityPositionStorage1_14.class)) == null) {
protocol.getLogger().warning("Untracked entity with id " + entityId);
@@ -17,12 +17,11 @@
*/
package com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.libs.fastutil.longs.Long2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.Arrays;
public class ChunkLightStorage implements StorableObject {
public class ChunkLightStorage {
public static final byte[] FULL_LIGHT = new byte[2048];
public static final byte[] EMPTY_LIGHT = new byte[2048];
@@ -17,9 +17,7 @@
*/
package com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class DifficultyStorage implements StorableObject {
public class DifficultyStorage {
private byte difficulty;
public byte getDifficulty() {
@@ -0,0 +1,34 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_14 extends ProtocolStorablesBase {
private final ChunkLightStorage chunkLightStorage = new ChunkLightStorage();
private final DifficultyStorage difficultyStorage = new DifficultyStorage();
public ChunkLightStorage chunkLightStorage() {
return chunkLightStorage;
}
public DifficultyStorage difficultyStorage() {
return difficultyStorage;
}
}
@@ -88,8 +88,8 @@ public class Protocol1_15To1_14_4 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(getClass(), new EntityTrackerBase(user, EntityTypes1_15.PLAYER));
user.addClientWorld(getClass(), new ClientWorld());
user.storables(this).setEntityTracker(new EntityTrackerBase(user, EntityTypes1_15.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
user.put(new ImmediateRespawnStorage());
}
@@ -113,7 +113,7 @@ public class EntityPacketRewriter1_15 extends EntityRewriter<ClientboundPackets1
map(Types.VAR_INT); // 5 - View Distance
map(Types.BOOLEAN); // 6 - Reduce Debug Info
handler(getPlayerTrackerHandler());
handler(playerTrackerHandler());
handler(wrapper -> {
boolean immediateRespawn = !wrapper.read(Types.BOOLEAN); // Inverted
@@ -136,9 +136,13 @@ public class EntityPacketRewriter1_15 extends EntityRewriter<ClientboundPackets1
map(Types.DOUBLE); // 4 - Z
map(Types.BYTE); // 5 - Yaw
map(Types.BYTE); // 6 - Pitch
handler(wrapper -> wrapper.write(Types1_14.ENTITY_DATA_LIST, new ArrayList<>())); // Entity data is no longer sent in 1.15, so we have to send an empty one
handler(wrapper -> {
final int entityId = wrapper.get(Types.VAR_INT, 0);
tracker(wrapper.user()).addEntity(entityId, EntityTypes1_15.PLAYER);
handler(getTrackerHandler(EntityTypes1_15.PLAYER));
// Entity data is no longer sent in 1.15, so we have to send an empty one
wrapper.write(Types1_14.ENTITY_DATA_LIST, new ArrayList<>());
});
}
});
@@ -24,6 +24,8 @@ import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.BlockItemPa
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.CommandRewriter1_16_2;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.EntityPacketRewriter1_16_2;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.BiomeStorage;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.ProtocolStorables1_16_2;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import com.viaversion.viabackwards.utils.BackwardsProtocolLogger;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.RegistryType;
@@ -109,8 +111,15 @@ public class Protocol1_16_2To1_16_1 extends BackwardsProtocol<ClientboundPackets
@Override
public void init(UserConnection user) {
user.put(new BiomeStorage());
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_16_2.PLAYER));
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_16_2.PLAYER));
final ProtocolStorables1_16_2 storables = user.storables(this);
// BiomeStorage is also used in 1.16->1.15.2
user.put(storables.biomeStorage());
}
@Override
public ProtocolStorablesBase createStorables() {
return new ProtocolStorables1_16_2();
}
@Override
@@ -26,6 +26,7 @@ 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;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.BiomeStorage;
import com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.ProtocolStorables1_16_2;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16_2;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -73,7 +74,8 @@ public class EntityPacketRewriter1_16_2 extends EntityRewriter<ClientboundPacket
if (wrapper.user().getProtocolInfo().protocolVersion().olderThanOrEqualTo(ProtocolVersion.v1_15_2)) {
// Store biomes for <1.16 client handling
ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, "worldgen/biome");
BiomeStorage biomeStorage = wrapper.user().get(BiomeStorage.class);
ProtocolStorables1_16_2 storables = wrapper.user().storables(protocol);
BiomeStorage biomeStorage = storables.biomeStorage();
biomeStorage.clear();
for (CompoundTag biome : biomes) {
StringTag name = biome.getStringTag("name");
@@ -102,7 +104,7 @@ public class EntityPacketRewriter1_16_2 extends EntityRewriter<ClientboundPacket
wrapper.write(Types.UNSIGNED_BYTE, (short) Math.min(maxPlayers, 255));
});
// ...
handler(getPlayerTrackerHandler());
handler(playerTrackerHandler());
}
});
@@ -0,0 +1,29 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_16_2 extends ProtocolStorablesBase {
private final BiomeStorage biomeStorage = new BiomeStorage();
public BiomeStorage biomeStorage() {
return biomeStorage;
}
}
@@ -19,9 +19,11 @@ package com.viaversion.viabackwards.protocol.v1_16_4to1_16_3;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage.PlayerHandStorage;
import com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage.ProtocolStorables1_16_4;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;
import com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;
@@ -43,7 +45,8 @@ public class Protocol1_16_4To1_16_3 extends BackwardsProtocol<ClientboundPackets
if (slot == 1) {
wrapper.write(Types.VAR_INT, 40); // offhand
} else {
wrapper.write(Types.VAR_INT, wrapper.user().get(PlayerHandStorage.class).getCurrentHand());
ProtocolStorables1_16_4 storables = wrapper.user().storables(Protocol1_16_4To1_16_3.this);
wrapper.write(Types.VAR_INT, storables.playerHandStorage().getCurrentHand());
}
});
}
@@ -51,12 +54,17 @@ public class Protocol1_16_4To1_16_3 extends BackwardsProtocol<ClientboundPackets
registerServerbound(ServerboundPackets1_16_2.SET_CARRIED_ITEM, wrapper -> {
short slot = wrapper.passthrough(Types.SHORT);
wrapper.user().get(PlayerHandStorage.class).setCurrentHand(slot);
ProtocolStorables1_16_4 storables = wrapper.user().storables(this);
storables.playerHandStorage().setCurrentHand(slot);
});
}
@Override
public void init(UserConnection user) {
user.put(new PlayerHandStorage());
}
@Override
public ProtocolStorablesBase createStorables() {
return new ProtocolStorables1_16_4();
}
}
@@ -17,9 +17,7 @@
*/
package com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class PlayerHandStorage implements StorableObject {
public class PlayerHandStorage {
private int currentHand;
@@ -0,0 +1,29 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_16_4 extends ProtocolStorablesBase {
private final PlayerHandStorage playerHandStorage = new PlayerHandStorage();
public PlayerHandStorage playerHandStorage() {
return playerHandStorage;
}
}
@@ -23,9 +23,7 @@ import com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.BlockItemPack
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.CommandRewriter1_16;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.EntityPacketRewriter1_16;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.TranslatableRewriter1_16;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerAttributesStorage;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerSneakStorage;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WorldNameTracker;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.ProtocolStorables1_16;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.RegistryType;
@@ -117,9 +115,11 @@ public class Protocol1_16To1_15_2 extends BackwardsProtocol<ClientboundPackets1_
wrapper.passthrough(Types.VAR_INT); // player id
int action = wrapper.passthrough(Types.VAR_INT);
if (action == 0) {
wrapper.user().get(PlayerSneakStorage.class).setSneaking(true);
ProtocolStorables1_16 storables = wrapper.user().storables(this);
storables.playerSneakStorage().setSneaking(true);
} else if (action == 1) {
wrapper.user().get(PlayerSneakStorage.class).setSneaking(false);
ProtocolStorables1_16 storables = wrapper.user().storables(this);
storables.playerSneakStorage().setSneaking(false);
}
});
@@ -138,7 +138,8 @@ public class Protocol1_16To1_15_2 extends BackwardsProtocol<ClientboundPackets1_
}
// New boolean: Whether the client is sneaking
wrapper.write(Types.BOOLEAN, wrapper.user().get(PlayerSneakStorage.class).isSneaking());
ProtocolStorables1_16 storables = wrapper.user().storables(this);
wrapper.write(Types.BOOLEAN, storables.playerSneakStorage().isSneaking());
});
registerServerbound(ServerboundPackets1_14.PLAYER_ABILITIES, wrapper -> {
@@ -155,12 +156,13 @@ public class Protocol1_16To1_15_2 extends BackwardsProtocol<ClientboundPackets1_
@Override
public void init(UserConnection user) {
user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_16.PLAYER));
user.addClientWorld(this.getClass(), new ClientWorld());
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_16.PLAYER));
user.storables(this).setClientWorld(new ClientWorld());
}
user.put(new PlayerSneakStorage());
user.put(new WorldNameTracker());
user.put(new PlayerAttributesStorage());
@Override
public ProtocolStorables1_16 createStorables() {
return new ProtocolStorables1_16();
}
@Override
@@ -20,10 +20,10 @@ package com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerAttributesStorage;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.ProtocolStorables1_16;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WolfDataMaskStorage;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WorldNameTracker;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.Particle;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
@@ -100,8 +100,9 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
public void register() {
map(dimensionTransformer); // Dimension Type
handler(wrapper -> {
ProtocolStorables1_16 storables = wrapper.user().storables(protocol);
// Grab the tracker for world names
WorldNameTracker worldNameTracker = wrapper.user().get(WorldNameTracker.class);
WorldNameTracker worldNameTracker = storables.worldNameTracker();
String nextWorldName = wrapper.read(Types.STRING); // World Name
wrapper.passthrough(Types.LONG); // Seed
@@ -109,7 +110,7 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
wrapper.read(Types.BYTE); // Previous gamemode
// Grab client world
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_16To1_15_2.class);
ClientWorld clientWorld = storables.clientWorld();
int dimension = wrapper.get(Types.INT, 0);
// Send a dummy respawn with a different dimension if the world name was different and the same dimension was used
@@ -135,7 +136,7 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
wrapper.set(Types.STRING, 0, "flat");
}
final PlayerAttributesStorage attributes = wrapper.user().get(PlayerAttributesStorage.class);
final PlayerAttributesStorage attributes = storables.playerAttributesStorage();
final boolean keepPlayerAttributes = wrapper.read(Types.BOOLEAN);
if (keepPlayerAttributes) {
// Ensure packet order
@@ -162,13 +163,13 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
read(Types.NAMED_COMPOUND_TAG); // whatever this is
map(dimensionTransformer); // Dimension Type
handler(wrapper -> {
WorldNameTracker worldNameTracker = wrapper.user().get(WorldNameTracker.class);
worldNameTracker.setWorldName(wrapper.read(Types.STRING)); // Save the world name
ProtocolStorables1_16 storables = wrapper.user().storables(protocol);
storables.worldNameTracker().setWorldName(wrapper.read(Types.STRING)); // Save the world name
});
map(Types.LONG); // Seed
map(Types.UNSIGNED_BYTE); // Max players
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_16To1_15_2.class);
ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
clientWorld.setEnvironment(wrapper.get(Types.INT, 1));
wrapper.write(Types.STRING, "default"); // Level type
@@ -193,7 +194,8 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
registerSetEntityData(ClientboundPackets1_16.SET_ENTITY_DATA, Types1_16.ENTITY_DATA_LIST, Types1_14.ENTITY_DATA_LIST);
protocol.registerClientbound(ClientboundPackets1_16.UPDATE_ATTRIBUTES, wrapper -> {
final PlayerAttributesStorage attributes = wrapper.user().get(PlayerAttributesStorage.class);
ProtocolStorables1_16 storables = wrapper.user().storables(protocol);
final PlayerAttributesStorage attributes = storables.playerAttributesStorage();
final int entityId = wrapper.passthrough(Types.VAR_INT);
@@ -290,18 +292,14 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
filter().type(EntityTypes1_16.WOLF).index(16).handler((event, data) -> {
byte mask = data.value();
StoredEntityData entityData = tracker(event.user()).entityData(event.entityId());
entityData.put(new WolfDataMaskStorage(mask));
event.trackedEntity().put(new WolfDataMaskStorage(mask));
});
filter().type(EntityTypes1_16.WOLF).index(20).handler((event, data) -> {
StoredEntityData entityData = tracker(event.user()).entityDataIfPresent(event.entityId());
byte previousMask = 0;
if (entityData != null) {
WolfDataMaskStorage wolfData = entityData.get(WolfDataMaskStorage.class);
if (wolfData != null) {
previousMask = wolfData.tameableMask();
}
WolfDataMaskStorage wolfData = event.trackedEntity().get(WolfDataMaskStorage.class);
if (wolfData != null) {
previousMask = wolfData.tameableMask();
}
int angerTime = data.value();
@@ -18,7 +18,6 @@
package com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;
import com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;
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;
@@ -27,7 +26,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public final class PlayerAttributesStorage implements StorableObject {
public final class PlayerAttributesStorage {
private final Map<String, Attribute> attributes = new HashMap<>();
@@ -17,9 +17,7 @@
*/
package com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class PlayerSneakStorage implements StorableObject {
public class PlayerSneakStorage {
private boolean sneaking;
public boolean isSneaking() {
@@ -0,0 +1,39 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_16 extends ProtocolStorablesBase {
private final PlayerSneakStorage playerSneakStorage = new PlayerSneakStorage();
private final WorldNameTracker worldNameTracker = new WorldNameTracker();
private final PlayerAttributesStorage playerAttributesStorage = new PlayerAttributesStorage();
public PlayerSneakStorage playerSneakStorage() {
return playerSneakStorage;
}
public WorldNameTracker worldNameTracker() {
return worldNameTracker;
}
public PlayerAttributesStorage playerAttributesStorage() {
return playerAttributesStorage;
}
}
@@ -17,7 +17,9 @@
*/
package com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;
public final class WolfDataMaskStorage {
import com.viaversion.viaversion.api.connection.StorableObject;
public final class WolfDataMaskStorage implements StorableObject {
private byte tameableMask;
@@ -17,9 +17,7 @@
*/
package com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class WorldNameTracker implements StorableObject {
public class WorldNameTracker {
private String worldName;
public String getWorldName() {
@@ -21,7 +21,7 @@ import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.InventoryStateIds;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.ProtocolStorables1_17_1;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
@@ -56,17 +56,17 @@ public final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPac
registerClientbound(ClientboundPackets1_17_1.CONTAINER_CLOSE, wrapper -> {
short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE);
wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);
wrapper.user().<ProtocolStorables1_17_1>storables(this).inventoryStateIds().removeStateId(containerId);
});
registerClientbound(ClientboundPackets1_17_1.CONTAINER_SET_SLOT, wrapper -> {
byte containerId = wrapper.passthrough(Types.BYTE);
int stateId = wrapper.read(Types.VAR_INT);
wrapper.user().get(InventoryStateIds.class).setStateId(containerId, stateId);
wrapper.user().<ProtocolStorables1_17_1>storables(this).inventoryStateIds().setStateId(containerId, stateId);
});
registerClientbound(ClientboundPackets1_17_1.CONTAINER_SET_CONTENT, wrapper -> {
short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE);
int stateId = wrapper.read(Types.VAR_INT);
wrapper.user().get(InventoryStateIds.class).setStateId(containerId, stateId);
wrapper.user().<ProtocolStorables1_17_1>storables(this).inventoryStateIds().setStateId(containerId, stateId);
// Length is encoded as a var int in 1.17.1
wrapper.write(Types.ITEM1_13_2_SHORT_ARRAY, wrapper.read(Types.ITEM1_13_2_ARRAY));
@@ -74,7 +74,7 @@ public final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPac
// Carried item - forward as CONTAINER_SET_SLOT for <1.17 clients
Item carried = wrapper.read(Types.ITEM1_13_2);
PlayerLastCursorItem lastCursorItem = wrapper.user().get(PlayerLastCursorItem.class);
PlayerLastCursorItem lastCursorItem = wrapper.user().<ProtocolStorables1_17_1>storables(this).playerLastCursorItem();
if (lastCursorItem != null) {
// For click drag ghost item fix -- since the state ID is always wrong,
// the server always resends the entire window contents after a drag action,
@@ -97,11 +97,11 @@ public final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPac
registerServerbound(ServerboundPackets1_17.CONTAINER_CLOSE, wrapper -> {
byte containerId = wrapper.passthrough(Types.BYTE);
wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);
wrapper.user().<ProtocolStorables1_17_1>storables(this).inventoryStateIds().removeStateId(containerId);
});
registerServerbound(ServerboundPackets1_17.CONTAINER_CLICK, wrapper -> {
byte containerId = wrapper.passthrough(Types.BYTE);
int stateId = wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);
int stateId = wrapper.user().<ProtocolStorables1_17_1>storables(this).inventoryStateIds().removeStateId(containerId);
wrapper.write(Types.VAR_INT, stateId == Integer.MAX_VALUE ? 0 : stateId);
});
@@ -158,7 +158,7 @@ public final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPac
}
@Override
public void init(UserConnection connection) {
connection.put(new InventoryStateIds());
public ProtocolStorables1_17_1 createStorables() {
return new ProtocolStorables1_17_1();
}
}
@@ -0,0 +1,35 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_17_1 extends ProtocolStorablesBase {
private final InventoryStateIds inventoryStateIds = new InventoryStateIds();
private final PlayerLastCursorItem playerLastCursorItem = new PlayerLastCursorItem();
public InventoryStateIds inventoryStateIds() {
return inventoryStateIds;
}
public PlayerLastCursorItem playerLastCursorItem() {
return playerLastCursorItem;
}
}
@@ -24,6 +24,8 @@ import com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter.BlockItemPacketRewriter1_17;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter.EntityPacketRewriter1_17;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.ProtocolStorables1_17;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.api.minecraft.TagData;
@@ -205,7 +207,11 @@ public final class Protocol1_17To1_16_4 extends BackwardsProtocol<ClientboundPac
@Override
public void init(UserConnection user) {
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_17.PLAYER));
user.put(new PlayerLastCursorItem());
}
@Override
public ProtocolStorablesBase createStorables() {
return new ProtocolStorables1_17();
}
@Override
@@ -26,8 +26,12 @@ import com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;
import com.viaversion.viabackwards.api.rewriters.MapColorRewriter;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.data.MapColorMappings1_16_4;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.Protocol1_17_1To1_17;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.InventoryStateIds;
import com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.ProtocolStorables1_17_1;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;
import com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.ProtocolStorables1_17;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
@@ -99,13 +103,15 @@ public final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<Cli
// that we can't easily predict. Force a state ID mismatch so the server
// sends a full inventory resync instead of individual slot corrections.
if (mode != 0) {
InventoryStateIds stateIds = wrapper.user().get(InventoryStateIds.class);
ProtocolStorables1_17_1 storables1_17_1 = wrapper.user().storables(Via.getManager().getProtocolManager().getProtocol(Protocol1_17_1To1_17.class));
InventoryStateIds stateIds = storables1_17_1.inventoryStateIds();
if (stateIds != null) {
stateIds.setStateId(wrapper.get(Types.BYTE, 0), -1);
}
}
PlayerLastCursorItem state = wrapper.user().get(PlayerLastCursorItem.class);
ProtocolStorables1_17 storables = wrapper.user().storables(protocol);
PlayerLastCursorItem state = storables.playerLastCursorItem();
if (mode == 0 && button == 0 && clicked != null) {
// Left click PICKUP
// Carried item will (usually) be the entire clicked stack
@@ -154,7 +160,8 @@ public final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<Cli
// carried item, and the server will helpfully send this packet allowing us
// to update the internal state. This is necessary for fixing multiple sequential
// click drag actions without intermittent pickup actions.
wrapper.user().get(PlayerLastCursorItem.class).setLastCursorItem(carried);
ProtocolStorables1_17 storables = wrapper.user().storables(protocol);
storables.playerLastCursorItem().setLastCursorItem(carried);
}
wrapper.write(Types.ITEM1_13_2, handleItemToClient(wrapper.user(), carried));
@@ -228,7 +235,7 @@ public final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<Cli
map(Types.VAR_INT); // Z
map(Types.BOOLEAN); // Trust edges
handler(wrapper -> {
EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_17To1_16_4.class);
EntityTracker tracker = wrapper.user().getEntityTracker(protocol);
int startFromSection = Math.max(0, -(tracker.currentMinY() >> 4));
long[] skyLightMask = wrapper.read(Types.LONG_ARRAY_PRIMITIVE);
@@ -344,7 +351,7 @@ public final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<Cli
});
protocol.registerClientbound(ClientboundPackets1_17.LEVEL_CHUNK, wrapper -> {
EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_17To1_16_4.class);
EntityTracker tracker = wrapper.user().getEntityTracker(protocol);
int currentWorldSectionHeight = tracker.currentWorldSectionHeight();
Chunk chunk = wrapper.read(new ChunkType1_17(currentWorldSectionHeight));
@@ -101,7 +101,7 @@ public final class EntityPacketRewriter1_17 extends EntityRewriter<ClientboundPa
wrapper.set(Types.BYTE, 1, (byte) 0);
}
});
handler(getPlayerTrackerHandler());
handler(playerTrackerHandler());
handler(worldDataTrackerHandler(1));
handler(wrapper -> {
CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);
@@ -17,10 +17,9 @@
*/
package com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.item.Item;
public class PlayerLastCursorItem implements StorableObject {
public class PlayerLastCursorItem {
private Item lastCursorItem;
public Item getLastCursorItem() {
@@ -0,0 +1,29 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage;
import com.viaversion.viaversion.connection.ProtocolStorablesBase;
public final class ProtocolStorables1_17 extends ProtocolStorablesBase {
private final PlayerLastCursorItem playerLastCursorItem = new PlayerLastCursorItem();
public PlayerLastCursorItem playerLastCursorItem() {
return playerLastCursorItem;
}
}
@@ -54,6 +54,7 @@ import com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPacke
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
import com.viaversion.viaversion.util.CipherUtil;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Limit;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.util.TagUtil;
import java.security.SignatureException;
@@ -103,8 +104,9 @@ public final class Protocol1_19_1To1_19 extends BackwardsProtocol<ClientboundPac
// Replace with 1.19 chat types
// Ensures that the client has a chat type for system message, with and without overlay
registry.put("minecraft:chat_type", Protocol1_18_2To1_19.MAPPINGS.chatRegistry());
entityRewriter.trackWorld(wrapper.user(), wrapper.get(Types.STRING, 1));
});
handler(entityRewriter.worldTrackerHandlerByKey());
}
});
@@ -229,7 +231,7 @@ public final class Protocol1_19_1To1_19 extends BackwardsProtocol<ClientboundPac
final SignableCommandArgumentsProvider argumentsProvider = Via.getManager().getProviders().get(SignableCommandArgumentsProvider.class);
if (chatSession != null && argumentsProvider != null) {
final int signatures = wrapper.read(Types.VAR_INT);
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
for (int i = 0; i < signatures; i++) {
wrapper.read(Types.STRING); // Argument name
wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature
@@ -41,7 +41,9 @@ import com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.BitSetType;
import com.viaversion.viaversion.api.type.types.ByteArrayType;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;
@@ -61,6 +63,7 @@ import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
import com.viaversion.viaversion.util.CipherUtil;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Limit;
import com.viaversion.viaversion.util.Pair;
import java.security.SignatureException;
import java.util.BitSet;
@@ -70,6 +73,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundPackets1_19_3, ClientboundPackets1_19_1, ServerboundPackets1_19_3, ServerboundPackets1_19_1> {
public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData("1.19.3", "1.19", Protocol1_19_1To1_19_3.class);
private static final Type<PlayerMessageSignature[]> LAST_SEEN_MESSAGES_TYPE = new ArrayType<>(Types.PLAYER_MESSAGE_SIGNATURE, 5);
private final EntityPacketRewriter1_19_3 entityRewriter = new EntityPacketRewriter1_19_3(this);
private final BlockItemPacketRewriter1_19_3 itemRewriter = new BlockItemPacketRewriter1_19_3(this);
private final ParticleRewriter<ClientboundPackets1_19_3> particleRewriter = new ParticleRewriter<>(this);
@@ -207,7 +211,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
map(Types.LONG); // Salt
read(Types.BYTE_ARRAY_PRIMITIVE); // Signature
read(Types.BOOLEAN); // Signed preview
read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages
read(LAST_SEEN_MESSAGES_TYPE); // Last seen messages
read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message
handler(wrapper -> {
final ChatSession1_19_3 chatSession = wrapper.user().get(ChatSession1_19_3.class);
@@ -250,7 +254,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
final long timestamp = wrapper.get(Types.LONG, 0);
final long salt = wrapper.get(Types.LONG, 1);
final int signatures = wrapper.read(Types.VAR_INT);
final int signatures = Limit.max(wrapper.read(Types.VAR_INT), 8);
for (int i = 0; i < signatures; i++) {
wrapper.read(Types.STRING); // Name
wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature
@@ -282,7 +286,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
wrapper.write(Types.VAR_INT, offset);
wrapper.write(new BitSetType(20), acknowledged);
});
read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages
read(LAST_SEEN_MESSAGES_TYPE); // Last seen messages
read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message
}
});
@@ -21,6 +21,7 @@ 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_19_4to1_19_3.rewriter.BlockItemPacketRewriter1_19_4;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter.ComponentRewriter1_19_4;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter.EntityPacketRewriter1_19_4;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.EntityTracker1_19_4;
import com.viaversion.viaversion.api.connection.UserConnection;
@@ -37,8 +38,6 @@ import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.CommandRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public final class Protocol1_19_4To1_19_3 extends BackwardsProtocol<ClientboundPackets1_19_4, ClientboundPackets1_19_3, ServerboundPackets1_19_4, ServerboundPackets1_19_3> {
@@ -47,7 +46,7 @@ public final class Protocol1_19_4To1_19_3 extends BackwardsProtocol<ClientboundP
private final EntityPacketRewriter1_19_4 entityRewriter = new EntityPacketRewriter1_19_4(this);
private final BlockItemPacketRewriter1_19_4 itemRewriter = new BlockItemPacketRewriter1_19_4(this);
private final ParticleRewriter<ClientboundPackets1_19_4> particleRewriter = new ParticleRewriter<>(this);
private final JsonNBTComponentRewriter<ClientboundPackets1_19_4> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);
private final JsonNBTComponentRewriter<ClientboundPackets1_19_4> translatableRewriter = new ComponentRewriter1_19_4(this);
private final TagRewriter<ClientboundPackets1_19_4> tagRewriter = new TagRewriter<>(this);
private final BlockRewriter<ClientboundPackets1_19_4> blockRewriter = BlockRewriter.for1_18(this, ChunkType1_18::new);
@@ -84,7 +83,7 @@ public final class Protocol1_19_4To1_19_3 extends BackwardsProtocol<ClientboundP
wrapper.write(Types.OPTIONAL_COMPONENT, element);
final byte[] iconBytes = wrapper.read(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE);
final String iconBase64 = iconBytes != null ? "data:image/png;base64," + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8) : null;
final String iconBase64 = iconBytes != null ? "data:image/png;base64," + Base64.getEncoder().encodeToString(iconBytes) : null;
wrapper.write(Types.OPTIONAL_STRING, iconBase64);
});
@@ -0,0 +1,42 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter;
import com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;
public final class ComponentRewriter1_19_4 extends JsonNBTComponentRewriter<ClientboundPackets1_19_4> {
public ComponentRewriter1_19_4(final Protocol1_19_4To1_19_3 protocol) {
super(protocol, ReadType.JSON);
}
@Override
protected void handleTranslate(final JsonObject root, final String translate) {
super.handleTranslate(root, translate);
if ((translate.startsWith("vb.item.") || translate.startsWith("vb.entity.")) && root.has("fallback")) {
// Add fallback as actual translate value
final JsonElement fallback = root.remove("fallback");
root.add("translate", fallback);
}
}
}
@@ -27,7 +27,8 @@ import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.EntityTracker1_19_4;
import com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.LinkedEntityStorage;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;
@@ -39,13 +40,24 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_19_3;
import com.viaversion.viaversion.api.type.types.version.Types1_19_4;
import com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;
import com.viaversion.viaversion.libs.fastutil.ints.IntList;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
import com.viaversion.viaversion.libs.mcstructs.text.TextComponent;
import com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;
import com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;
import com.viaversion.viaversion.util.SerializerVersion;
import com.viaversion.viaversion.util.TagUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class EntityPacketRewriter1_19_4 extends EntityRewriter<ClientboundPackets1_19_4, Protocol1_19_4To1_19_3> {
private static final double TEXT_DISPLAY_Y_OFFSET = -0.25; // Move emulated armor stands down to match text display height offsets
private static final double TEXT_DISPLAY_LINE_HEIGHT = 0.25;
public EntityPacketRewriter1_19_4(final Protocol1_19_4To1_19_3 protocol) {
super(protocol, Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType, Types1_19_3.ENTITY_DATA_TYPES.booleanType);
@@ -81,27 +93,43 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
final double y = wrapper.get(Types.DOUBLE, 1);
if (entityType == EntityTypes1_19_4.TEXT_DISPLAY.getId()) {
wrapper.set(Types.DOUBLE, 1, y + TEXT_DISPLAY_Y_OFFSET);
wrapper.set(Types.DOUBLE, 1, y - TEXT_DISPLAY_LINE_HEIGHT);
}
// First track (and remap) entity, then put storage for block display entity
getSpawnTrackerWithDataHandler1_19().handle(wrapper);
if (entityType != EntityTypes1_19_4.BLOCK_DISPLAY.getId()) {
return;
// First track (and remap) the entity, then store its position. Positions of all entities are
// needed to anchor and move the emulated text display lines when riding other entities
// Check against the UNMAPPED entity type
final TrackedEntity entity = trackAndRewrite(wrapper, entityType, entityId);
if (entity.entityType() == EntityTypes1_19_4.FALLING_BLOCK) {
final int blockState = wrapper.get(Types.VAR_INT, 2);
wrapper.set(Types.VAR_INT, 2, protocol.getMappingData().getNewBlockStateId(blockState));
}
final StoredEntityData data = tracker(wrapper.user()).entityData(entityId);
if (data != null) {
final LinkedEntityStorage storage = new LinkedEntityStorage();
final double x = wrapper.get(Types.DOUBLE, 0);
final double z = wrapper.get(Types.DOUBLE, 2);
storage.setPosition(x, y, z);
data.put(storage);
}
final LinkedEntityStorage storage = new LinkedEntityStorage();
final double x = wrapper.get(Types.DOUBLE, 0);
final double z = wrapper.get(Types.DOUBLE, 2);
storage.setPosition(x, y, z);
entity.put(storage);
});
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.ADD_PLAYER, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
wrapper.passthrough(Types.UUID);
final double x = wrapper.passthrough(Types.DOUBLE);
final double y = wrapper.passthrough(Types.DOUBLE);
final double z = wrapper.passthrough(Types.DOUBLE);
// Also track players and their positions as possible vehicles of text displays
// (excluding the client player)
final EntityTracker1_19_4 tracker = tracker(wrapper.user());
final TrackedEntity entity = tracker.addEntity(entityId, EntityTypes1_19_4.PLAYER);
final LinkedEntityStorage storage = new LinkedEntityStorage();
storage.setPosition(x, y, z);
entity.put(storage);
});
protocol.registerClientbound(ClientboundPackets1_19_4.LOGIN, new PacketHandlers() {
@Override
public void register() {
@@ -151,8 +179,8 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
public void register() {
map(Types.VAR_INT, Types.INT); // Entity id
read(Types.VAR_INT); // Damage type
read(Types.VAR_INT); // Cause entity
read(Types.VAR_INT); // Direct cause entity
read(Types.OPTIONAL_VAR_INT); // Cause entity
read(Types.OPTIONAL_VAR_INT); // Direct cause entity
handler(wrapper -> {
// Source position
if (wrapper.read(Types.BOOLEAN)) {
@@ -193,8 +221,9 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
wrapper.write(Types.VAR_INT, duration == -1 ? 999999 : duration);
});
// Track the position of block display entities to later spawn the linked entities, we will put them
// as passengers but the spawn position needs to be in the players view distance
// Track the position of entities to later spawn and move the linked entities.
// Block display passengers move with their vehicle, but the text display line entities have to be
// moved manually - both when the display itself moves and when a vehicle moves
protocol.registerClientbound(ClientboundPackets1_19_4.TELEPORT_ENTITY, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
@@ -203,31 +232,147 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
final double z = wrapper.passthrough(Types.DOUBLE);
final EntityTracker1_19_4 tracker = tracker(wrapper.user());
if (tracker.entityType(entityId) == EntityTypes1_19_4.TEXT_DISPLAY) {
wrapper.set(Types.DOUBLE, 1, y + TEXT_DISPLAY_Y_OFFSET);
final TrackedEntity entity = tracker.entity(entityId);
if (entity == null) {
return;
}
final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);
if (storage != null) {
storage.setPosition(x, y, z);
final boolean textDisplay = entity.entityType() == EntityTypes1_19_4.TEXT_DISPLAY;
if (textDisplay) {
// Move emulated armor stands down to match text display height offsets
wrapper.set(Types.DOUBLE, 1, y - TEXT_DISPLAY_LINE_HEIGHT);
}
final LinkedEntityStorage storage = entity.get(LinkedEntityStorage.class);
if (storage == null) {
return;
}
final double deltaX = x - storage.x();
final double deltaY = y - storage.y();
final double deltaZ = z - storage.z();
storage.setPosition(x, y, z);
if (textDisplay) {
teleportTextDisplayLines(wrapper.user(), storage);
}
final int[] passengers = storage.passengers();
if (passengers != null) {
// Move emulated text display passengers along, keeping their offset to the vehicle
for (final int passenger : passengers) {
final LinkedEntityStorage passengerStorage = riddenTextDisplayStorage(tracker, passenger, entityId);
if (passengerStorage != null) {
passengerStorage.addRelativePosition(deltaX, deltaY, deltaZ);
sendTeleport(wrapper.user(), passenger, passengerStorage.x(), passengerStorage.y() - TEXT_DISPLAY_LINE_HEIGHT, passengerStorage.z());
teleportTextDisplayLines(wrapper.user(), passengerStorage);
}
}
}
});
final PacketHandler entityPositionHandler = wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
final double x = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
final double y = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
final double z = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
final EntityTracker1_19_4 tracker = tracker(wrapper.user());
final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);
if (storage != null) {
storage.addRelativePosition(x, y, z);
final TrackedEntity entity = tracker.entity(entityId);
if (entity == null) {
return;
}
final LinkedEntityStorage storage = entity.get(LinkedEntityStorage.class);
if (storage == null) {
return;
}
final short compressedX = wrapper.passthrough(Types.SHORT);
final short compressedY = wrapper.passthrough(Types.SHORT);
final short compressedZ = wrapper.passthrough(Types.SHORT);
final double deltaX = compressedX / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
final double deltaY = compressedY / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
final double deltaZ = compressedZ / EntityPositionHandler.RELATIVE_MOVE_FACTOR;
storage.addRelativePosition(deltaX, deltaY, deltaZ);
if (entity.entityType() == EntityTypes1_19_4.TEXT_DISPLAY) {
moveTextDisplayLines(wrapper.user(), storage, compressedX, compressedY, compressedZ);
}
// Move passengers along with vehicle
final int[] passengers = storage.passengers();
if (passengers != null) {
for (final int passenger : passengers) {
final LinkedEntityStorage passengerStorage = riddenTextDisplayStorage(tracker, passenger, entityId);
if (passengerStorage == null) {
continue;
}
passengerStorage.addRelativePosition(deltaX, deltaY, deltaZ);
sendRelativeMove(wrapper.user(), passenger, compressedX, compressedY, compressedZ);
moveTextDisplayLines(wrapper.user(), passengerStorage, compressedX, compressedY, compressedZ);
}
}
};
protocol.registerClientbound(ClientboundPackets1_19_4.MOVE_ENTITY_POS, entityPositionHandler);
protocol.registerClientbound(ClientboundPackets1_19_4.MOVE_ENTITY_POS_ROT, entityPositionHandler);
protocol.registerClientbound(ClientboundPackets1_19_4.SET_PASSENGERS, wrapper -> {
final int vehicleId = wrapper.passthrough(Types.VAR_INT);
final int[] passengers = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);
final EntityTracker1_19_4 tracker = tracker(wrapper.user());
final LinkedEntityStorage vehicleStorage = tracker.linkedEntityStorage(vehicleId);
if (vehicleStorage == null) {
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, passengers);
return;
}
// Unlink all previous passengers. Those still riding are attached again below
if (vehicleStorage.passengers() != null) {
for (final int previousPassenger : vehicleStorage.passengers()) {
final LinkedEntityStorage passengerStorage = tracker.linkedEntityStorage(previousPassenger);
if (passengerStorage != null && passengerStorage.isVehicle(vehicleId)) {
passengerStorage.setVehicleId(null);
}
}
}
boolean hasTextDisplay = false;
final IntList filteredPassengers = new IntArrayList(passengers.length);
for (final int passenger : passengers) {
if (tracker.entityType(passenger) != EntityTypes1_19_4.TEXT_DISPLAY) {
filteredPassengers.add(passenger);
continue;
}
hasTextDisplay = true;
final LinkedEntityStorage passengerStorage = tracker.linkedEntityStorage(passenger);
if (passengerStorage == null) {
filteredPassengers.add(passenger);
continue;
}
passengerStorage.setVehicleId(vehicleId);
if (passengerStorage.entities() == null) {
// Single-line text display
filteredPassengers.add(passenger);
continue;
}
// Keep the position if it is already close to the vehicle, else we'd have to calculate entity heights for passenger offsets
final double distanceX = passengerStorage.x() - vehicleStorage.x();
final double distanceY = passengerStorage.y() - vehicleStorage.y();
final double distanceZ = passengerStorage.z() - vehicleStorage.z();
if ((distanceX * distanceX) + (distanceY * distanceY) + (distanceZ * distanceZ) > 4 * 4) {
passengerStorage.setPosition(vehicleStorage.x(), vehicleStorage.y(), vehicleStorage.z());
sendTeleport(wrapper.user(), passenger, passengerStorage.x(), passengerStorage.y() - TEXT_DISPLAY_LINE_HEIGHT, passengerStorage.z());
teleportTextDisplayLines(wrapper.user(), passengerStorage);
}
}
// Only needed to move text display passengers along, so skip storing other passenger lists
vehicleStorage.setPassengers(hasTextDisplay ? passengers : null);
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, filteredPassengers.size() == passengers.length ? passengers : filteredPassengers.toIntArray());
});
}
@Override
@@ -259,17 +404,71 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
event.setIndex(2);
data.setDataType(Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType);
event.createExtraData(new EntityData(3, Types1_19_3.ENTITY_DATA_TYPES.booleanType, true)); // Show custom name
}));
filter().type(EntityTypes1_19_4.BLOCK_DISPLAY).index(22).handler((event, data) -> {
final int value = data.value();
// The armor stand name is rendered as a single line an doesn't support new lines,
// so extra lines have to be moved to their own armor stands, stacked upwards.
final EntityTracker1_19_4 tracker = tracker(event.user());
tracker.clearLinkedEntities(event.entityId());
final JsonElement component = data.value();
if (!containsNewLine(component)) { // Avoid parsing the full component where possible
clearTextDisplayLines(event.user(), tracker, event.entityId());
return;
}
final TextComponent[] lines = TextUtils.split(SerializerVersion.V1_19_4.toComponent(component), "\n", false);
if (lines.length <= 1) {
clearTextDisplayLines(event.user(), tracker, event.entityId());
return;
}
data.setValue(SerializerVersion.V1_19_4.toJson(lines[lines.length - 1])); // Bottom line stays on the actual entity
final LinkedEntityStorage storage = tracker.linkedEntityStorage(event.entityId());
if (storage == null) {
return;
}
final int extraLines = lines.length - 1;
int[] entities = storage.entities();
if (entities == null || entities.length != extraLines) {
final boolean mountedOnClient = entities == null && storage.vehicleId() != null;
tracker.clearLinkedEntities(storage);
final LinkedEntityStorage vehicleStorage = mountedOnClient ? tracker.linkedEntityStorage(storage.vehicleId()) : null;
if (vehicleStorage != null) {
// The display position wasn't tracked while it was mounted client-side; anchor it to the vehicle
storage.setPosition(vehicleStorage.x(), vehicleStorage.y(), vehicleStorage.z());
}
entities = new int[extraLines];
for (int i = 0; i < extraLines; i++) {
final double y = storage.y() + (TEXT_DISPLAY_LINE_HEIGHT * i);
entities[i] = tracker.spawnEntity(EntityTypes1_19_3.ARMOR_STAND, storage.x(), y, storage.z(), 0);
}
storage.setEntities(entities);
if (vehicleStorage != null) {
// The now multi-line display was mounted client-side; dismount and position it manually from now on
sendVehiclePassengers(event.user(), tracker, storage.vehicleId());
sendTeleport(event.user(), event.entityId(), storage.x(), storage.y() - TEXT_DISPLAY_LINE_HEIGHT, storage.z());
}
}
// Send in inverse order
for (int i = 0; i < extraLines; i++) {
sendLineData(event.user(), entities[i], SerializerVersion.V1_19_4.toJson(lines[extraLines - 1 - i]));
}
}));
filter().type(EntityTypes1_19_4.BLOCK_DISPLAY).index(22).handler((event, data) -> {
final int value = data.value();
final EntityTracker1_19_4 tracker = tracker(event.user());
final LinkedEntityStorage storage = event.trackedEntity().get(LinkedEntityStorage.class);
if (storage == null) {
return;
}
tracker.clearLinkedEntities(storage);
final int linkedEntity = tracker.spawnEntity(EntityTypes1_19_3.FALLING_BLOCK, storage.x(), storage.y(), storage.z(), value);
storage.setEntities(linkedEntity);
@@ -305,6 +504,134 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
filter().type(EntityTypes1_19_4.ABSTRACT_HORSE).addIndex(18); // Owner UUID
}
private void clearTextDisplayLines(final UserConnection connection, final EntityTracker1_19_4 tracker, final int entityId) {
final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);
if (storage == null || storage.entities() == null) {
return;
}
tracker.clearLinkedEntities(storage);
sendVehiclePassengers(connection, tracker, storage.vehicleId()); // Mount the now single-line display again
}
private LinkedEntityStorage riddenTextDisplayStorage(final EntityTracker1_19_4 tracker, final int entityId, final int vehicleId) {
final TrackedEntity entity = tracker.entity(entityId);
if (entity == null || entity.entityType() != EntityTypes1_19_4.TEXT_DISPLAY) {
return null;
}
final LinkedEntityStorage storage = entity.get(LinkedEntityStorage.class);
return storage != null && storage.entities() != null && storage.isVehicle(vehicleId) ? storage : null;
}
private boolean isMultiLineTextDisplay(final EntityTracker1_19_4 tracker, final int entityId) {
if (tracker.entityType(entityId) != EntityTypes1_19_4.TEXT_DISPLAY) {
return false;
}
final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);
return storage != null && storage.entities() != null;
}
private int[] withoutMultiLineTextDisplays(final EntityTracker1_19_4 tracker, final int[] passengers) {
final IntList filtered = new IntArrayList(passengers.length);
for (final int passenger : passengers) {
if (!isMultiLineTextDisplay(tracker, passenger)) {
filtered.add(passenger);
}
}
return filtered.size() == passengers.length ? passengers : filtered.toIntArray();
}
private void sendVehiclePassengers(final UserConnection connection, final EntityTracker1_19_4 tracker, @Nullable final Integer vehicleId) {
if (vehicleId == null) {
return;
}
final LinkedEntityStorage vehicleStorage = tracker.linkedEntityStorage(vehicleId);
if (vehicleStorage == null || vehicleStorage.passengers() == null) {
return;
}
final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_19_3.SET_PASSENGERS, connection);
wrapper.write(Types.VAR_INT, vehicleId);
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, withoutMultiLineTextDisplays(tracker, vehicleStorage.passengers()));
wrapper.send(Protocol1_19_4To1_19_3.class);
}
private void teleportTextDisplayLines(final UserConnection connection, final LinkedEntityStorage storage) {
final int[] entities = storage.entities();
if (entities == null) {
return;
}
for (int i = 0; i < entities.length; i++) {
sendTeleport(connection, entities[i], storage.x(), storage.y() + (TEXT_DISPLAY_LINE_HEIGHT * i), storage.z());
}
}
private void moveTextDisplayLines(final UserConnection connection, final LinkedEntityStorage storage, final short deltaX, final short deltaY, final short deltaZ) {
final int[] entities = storage.entities();
if (entities == null) {
return;
}
for (final int entity : entities) {
sendRelativeMove(connection, entity, deltaX, deltaY, deltaZ);
}
}
private void sendTeleport(final UserConnection connection, final int entityId, final double x, final double y, final double z) {
final PacketWrapper teleport = PacketWrapper.create(ClientboundPackets1_19_3.TELEPORT_ENTITY, connection);
teleport.write(Types.VAR_INT, entityId);
teleport.write(Types.DOUBLE, x);
teleport.write(Types.DOUBLE, y);
teleport.write(Types.DOUBLE, z);
teleport.write(Types.BYTE, (byte) 0); // Yaw
teleport.write(Types.BYTE, (byte) 0); // Pitch
teleport.write(Types.BOOLEAN, false); // On ground
teleport.send(Protocol1_19_4To1_19_3.class);
}
private void sendRelativeMove(final UserConnection connection, final int entityId, final short deltaX, final short deltaY, final short deltaZ) {
final PacketWrapper move = PacketWrapper.create(ClientboundPackets1_19_3.MOVE_ENTITY_POS, connection);
move.write(Types.VAR_INT, entityId);
move.write(Types.SHORT, deltaX);
move.write(Types.SHORT, deltaY);
move.write(Types.SHORT, deltaZ);
move.write(Types.BOOLEAN, false); // On ground
move.send(Protocol1_19_4To1_19_3.class);
}
private static boolean containsNewLine(final JsonElement element) {
if (element.isJsonPrimitive()) {
final JsonPrimitive primitive = element.getAsJsonPrimitive();
return primitive.isString() && primitive.getAsString().indexOf('\n') != -1;
} else if (element.isJsonArray()) {
for (final JsonElement entry : element.getAsJsonArray()) {
if (containsNewLine(entry)) {
return true;
}
}
} else if (element.isJsonObject()) {
for (final Map.Entry<String, JsonElement> entry : element.getAsJsonObject().entrySet()) {
if (containsNewLine(entry.getValue())) {
return true;
}
}
}
return false;
}
private void sendLineData(final UserConnection connection, final int entityId, final JsonElement text) {
final List<EntityData> data = new ArrayList<>();
data.add(new EntityData(0, Types1_19_3.ENTITY_DATA_TYPES.byteType, (byte) 0x20)); // Invisible
data.add(new EntityData(2, Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType, text)); // Custom name
data.add(new EntityData(3, Types1_19_3.ENTITY_DATA_TYPES.booleanType, true)); // Show custom name
data.add(new EntityData(5, Types1_19_3.ENTITY_DATA_TYPES.booleanType, true)); // No gravity
data.add(new EntityData(15, Types1_19_3.ENTITY_DATA_TYPES.byteType, (byte) (0x01 | 0x10))); // Small marker
final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_19_3.SET_ENTITY_DATA, connection);
wrapper.write(Types.VAR_INT, entityId);
wrapper.write(Types1_19_3.ENTITY_DATA_LIST, data);
wrapper.send(Protocol1_19_4To1_19_3.class);
}
@Override
public void onMappingDataLoaded() {
super.onMappingDataLoaded();
@@ -30,6 +30,7 @@ import com.viaversion.viaversion.libs.fastutil.ints.IntSet;
import com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class EntityTracker1_19_4 extends EntityTrackerBase {
@@ -58,44 +59,35 @@ public final class EntityTracker1_19_4 extends EntityTrackerBase {
addEntity.write(Types.SHORT, (short) 0); // Velocity Z
addEntity.send(Protocol1_19_4To1_19_3.class);
generatedEntities.add(entityId);
return entityId;
}
@Override
public void clearEntities() {
for (final int id : entities.keySet()) {
clearLinkedEntities(id);
public @Nullable TrackedEntity removeEntity(final int id) {
final TrackedEntity entity = super.removeEntity(id);
if (entity != null) {
clearLinkedEntities(entity.get(LinkedEntityStorage.class));
}
super.clearEntities();
return entity;
}
@Override
public void removeEntity(final int id) {
clearLinkedEntities(id);
super.removeEntity(id);
}
public void clearLinkedEntities(final int id) {
final LinkedEntityStorage storage = linkedEntityStorage(id);
public void clearLinkedEntities(final LinkedEntityStorage storage) {
if (storage != null && storage.entities() != null) {
for (final int entity : storage.entities()) {
generatedEntities.remove(entity);
}
storage.remove(user());
generatedEntities.remove(id);
}
}
public LinkedEntityStorage linkedEntityStorage(final int id) {
public @Nullable LinkedEntityStorage linkedEntityStorage(final int id) {
final TrackedEntity entity = entity(id);
if (entity != null && entity.hasData()) {
return entity.data().get(LinkedEntityStorage.class);
}
return null;
return entity != null ? entity.get(LinkedEntityStorage.class) : null;
}
private int nextEntityId() {
final int entityId = -ThreadLocalRandom.current().nextInt(10_000);
if (generatedEntities.contains(entityId)) {
final int entityId = ThreadLocalRandom.current().nextInt(-1_000_000, -1);
if (!generatedEntities.add(entityId)) {
return nextEntityId();
}
return entityId;
@@ -24,12 +24,15 @@ 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.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LinkedEntityStorage extends EntityPositionStorage implements StorableObject {
private int[] entities;
private int[] passengers;
private Integer vehicleId;
public int[] entities() {
public int @Nullable [] entities() {
return entities;
}
@@ -37,10 +40,31 @@ public class LinkedEntityStorage extends EntityPositionStorage implements Storab
this.entities = entities;
}
public int @Nullable [] passengers() {
return passengers;
}
public void setPassengers(final int... passengers) {
this.passengers = passengers;
}
public @Nullable Integer vehicleId() {
return vehicleId;
}
public boolean isVehicle(final int vehicleId) {
return this.vehicleId != null && this.vehicleId == vehicleId;
}
public void setVehicleId(@Nullable final Integer vehicleId) {
this.vehicleId = vehicleId;
}
public void remove(final UserConnection connection) {
final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_19_3.REMOVE_ENTITIES, connection);
wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, entities);
wrapper.send(Protocol1_19_4To1_19_3.class);
entities = null;
}
}
@@ -24,21 +24,14 @@ import com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.ParticleMappings;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.minecraft.item.Item;
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_18;
import com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;
import com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;
import com.viaversion.viaversion.rewriter.RecipeRewriter;
import com.viaversion.viaversion.util.MathUtil;
public final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<ClientboundPackets1_19, ServerboundPackets1_17, Protocol1_19To1_18_2> {
@@ -187,6 +180,7 @@ public final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<Cli
final int identifier = item.identifier();
item = super.handleItemToClient(connection, item);
enchantmentRewriter.handleToClient(item);
if (identifier != 834) {
return item;
@@ -205,8 +199,6 @@ public final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<Cli
lodestonePosTag.putInt("X", position.x());
lodestonePosTag.putInt("Y", position.y());
lodestonePosTag.putInt("Z", position.z());
enchantmentRewriter.handleToClient(item);
return item;
}
@@ -28,7 +28,7 @@ import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.EntityTracker1
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;
import com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.StoredPainting;
import com.viaversion.viaversion.api.data.ParticleMappings;
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;
import com.viaversion.viaversion.api.minecraft.Particle;
@@ -49,12 +49,6 @@ import com.viaversion.viaversion.util.TagUtil;
public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPackets1_19, Protocol1_19To1_18_2> {
private static final IntSet WIDE_PAINTINGS = IntOpenHashSet.of(
7, 8, 9, 10, 11,
14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25
);
public EntityPacketRewriter1_19(final Protocol1_19To1_18_2 protocol) {
super(protocol);
}
@@ -79,8 +73,10 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
handler(wrapper -> {
final byte headYaw = wrapper.read(Types.BYTE);
int data = wrapper.read(Types.VAR_INT);
final EntityType entityType = trackAndMapEntity(wrapper);
if (entityType.isOrHasParent(EntityTypes1_19.LIVING_ENTITY)) {
final int entityId = wrapper.get(Types.VAR_INT, 0);
final int typeId = wrapper.get(Types.VAR_INT, 1);
final TrackedEntity entity = trackAndRewrite(wrapper, typeId, entityId);
if (entity.entityType().isOrHasParent(EntityTypes1_19.LIVING_ENTITY)) {
wrapper.write(Types.BYTE, headYaw);
// Switch pitch and yaw position
@@ -91,20 +87,18 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
wrapper.setPacketType(ClientboundPackets1_18.ADD_MOB);
return;
} else if (entityType == EntityTypes1_19.PAINTING) {
} else if (entity.entityType() == EntityTypes1_19.PAINTING) {
wrapper.cancel();
// The entity has been tracked, now we wait for the entity data packet
final int entityId = wrapper.get(Types.VAR_INT, 0);
final StoredEntityData entityData = tracker(wrapper.user()).entityData(entityId);
final int x = wrapper.get(Types.DOUBLE, 0).intValue();
final int y = wrapper.get(Types.DOUBLE, 1).intValue();
final int z = wrapper.get(Types.DOUBLE, 2).intValue();
final BlockPosition position = new BlockPosition(x, y, z);
entityData.put(new StoredPainting(entityId, wrapper.get(Types.UUID, 0), position, data));
entity.put(new StoredPainting(entityId, wrapper.get(Types.UUID, 0), position, data));
return;
}
if (entityType == EntityTypes1_19.FALLING_BLOCK) {
if (entity.entityType() == EntityTypes1_19.FALLING_BLOCK) {
data = protocol.getMappingData().getNewBlockStateId(data);
}
wrapper.write(Types.INT, data);
@@ -122,19 +116,70 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
final double y = wrapper.read(Types.DOUBLE);
final double z = wrapper.read(Types.DOUBLE);
final StoredEntityData entityData = tracker(wrapper.user()).entityDataIfPresent(entityId);
final StoredPainting storedPainting = entityData != null ? entityData.get(StoredPainting.class) : null;
// Presumably there is a more correct way of fixing this? Only north and east looking paintings with a width of >1 seem to be extra special
if (storedPainting != null && (storedPainting.direction() == 2 || storedPainting.direction() == 3)
&& WIDE_PAINTINGS.contains(storedPainting.type())) {
wrapper.write(Types.DOUBLE, storedPainting.direction() == 2 ? x : x + 1);
wrapper.write(Types.DOUBLE, y + 1);
wrapper.write(Types.DOUBLE, storedPainting.direction() == 3 ? z : z + 1);
} else {
final TrackedEntity entity = tracker(wrapper.user()).entity(entityId);
final StoredPainting storedPainting = entity != null ? entity.get(StoredPainting.class) : null;
if (storedPainting == null) {
wrapper.write(Types.DOUBLE, x + 1);
wrapper.write(Types.DOUBLE, y + 1);
wrapper.write(Types.DOUBLE, z + 1);
return;
}
// In 1.18 and below, the teleport position is the painting entity's position.
// In 1.19+, it is the block position the painting is attached to.
// The game calculates the entity's position as follows.
// See PaintingVariants in 1.19
double width = 0, height = 0;
int type = storedPainting.type();
if (type <= 6) {
width = 16;
height = 16;
} else if (type <= 11) {
width = 32;
height = 16;
} else if (type <= 13) {
width = 16;
height = 32;
} else if (type <= 19 || (type >= 25 && type <= 28)) {
width = 32;
height = 32;
} else if (type == 20) {
width = 64;
height = 32;
} else if (type <= 23) {
width = 64;
height = 64;
} else if (type == 24 || type == 29) {
width = 64;
height = 48;
}
// See HangingEntity#recalculateBoundingBox
double resX = x + 0.5, resY = y + 0.5, resZ = z + 0.5;
final double againstWall = -0.46875;
switch (storedPainting.direction()) {
case 2 -> resZ -= againstWall; // North -z
case 0 -> resZ += againstWall; // South +z
case 1 -> resX -= againstWall; // West -x
case 3 -> resX += againstWall; // East +x
}
// Counter-clockwise
final double horizontalOffset = width % 32 == 0 ? 0.5 : 0.0;
final double verticalOffset = height % 32 == 0 ? 0.5 : 0.0;
resY += verticalOffset;
switch (storedPainting.direction()) {
case 3 -> resZ -= horizontalOffset; // East -> North -z
case 1 -> resZ += horizontalOffset; // West -> South +z
case 2 -> resX -= horizontalOffset; // North -> West -x
case 0 -> resX += horizontalOffset; // South -> East +x
}
wrapper.write(Types.DOUBLE, resX);
wrapper.write(Types.DOUBLE, resY);
wrapper.write(Types.DOUBLE, resZ);
});
protocol.registerClientbound(ClientboundPackets1_19.UPDATE_MOB_EFFECT, new PacketHandlers() {
@@ -361,11 +406,19 @@ public final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPa
filter().type(EntityTypes1_19.PAINTING).index(8).handler((event, data) -> {
event.cancel();
final StoredEntityData entityData = tracker(event.user()).entityDataIfPresent(event.entityId());
final StoredPainting storedPainting = entityData != null ? entityData.get(StoredPainting.class) : null;
final TrackedEntity entity = tracker(event.user()).entity(event.entityId());
final StoredPainting storedPainting = entity != null ? entity.get(StoredPainting.class) : null;
if (storedPainting != null) {
final int type = data.value();
int type = data.value();
storedPainting.setType(type);
type = switch (type) {
case 29 -> 25; // kong painting changed ID
case 25 -> 19; // earth -> wither
case 26 -> 16; // wind -> stage
case 27 -> 18; // water -> skull_and_roses
case 28 -> 14; // fire -> match
default -> type;
};
final PacketWrapper packet = PacketWrapper.create(ClientboundPackets1_18.ADD_PAINTING, event.user());
packet.write(Types.VAR_INT, storedPainting.entityId());
@@ -18,10 +18,12 @@
package com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;
import com.viaversion.viaversion.libs.fastutil.ints.IntList;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class EntityTracker1_19 extends EntityTrackerBase {
@@ -33,10 +35,10 @@ public final class EntityTracker1_19 extends EntityTrackerBase {
}
@Override
public void removeEntity(final int id) {
super.removeEntity(id);
public @Nullable TrackedEntity removeEntity(final int id) {
this.affectedByBlindness.rem(id);
this.affectedByDarkness.rem(id);
return super.removeEntity(id);
}
public IntList getAffectedByBlindness() {
@@ -79,26 +79,30 @@ public final class EntityPacketRewriter1_20_2 extends EntityRewriter<Clientbound
final byte pitch = wrapper.read(Types.BYTE);
wrapper.passthrough(Types.BYTE); // Yaw
wrapper.write(Types.BYTE, pitch);
wrapper.read(Types.BYTE); // Head yaw
final byte headYaw = wrapper.read(Types.BYTE); // Head yaw
wrapper.read(Types.VAR_INT); // Data
final short velocityX = wrapper.read(Types.SHORT);
final short velocityY = wrapper.read(Types.SHORT);
final short velocityZ = wrapper.read(Types.SHORT);
if (velocityX == 0 && velocityY == 0 && velocityZ == 0) {
return;
}
// Follow up with velocity packet
wrapper.send(Protocol1_20_2To1_20.class);
wrapper.cancel();
final PacketWrapper velocityPacket = wrapper.create(ClientboundPackets1_19_4.SET_ENTITY_MOTION);
velocityPacket.write(Types.VAR_INT, entityId);
velocityPacket.write(Types.SHORT, velocityX);
velocityPacket.write(Types.SHORT, velocityY);
velocityPacket.write(Types.SHORT, velocityZ);
velocityPacket.send(Protocol1_20_2To1_20.class);
final PacketWrapper headRotation = wrapper.create(ClientboundPackets1_19_4.ROTATE_HEAD);
headRotation.write(Types.VAR_INT, entityId);
headRotation.write(Types.BYTE, headYaw);
headRotation.send(Protocol1_20_2To1_20.class);
// Follow up with velocity packet
if (velocityX != 0 || velocityY != 0 || velocityZ != 0) {
final PacketWrapper velocityPacket = wrapper.create(ClientboundPackets1_19_4.SET_ENTITY_MOTION);
velocityPacket.write(Types.VAR_INT, entityId);
velocityPacket.write(Types.SHORT, velocityX);
velocityPacket.write(Types.SHORT, velocityY);
velocityPacket.write(Types.SHORT, velocityZ);
velocityPacket.send(Protocol1_20_2To1_20.class);
}
});
}
});
@@ -84,7 +84,7 @@ public final class Protocol1_20_5To1_20_3 extends BackwardsProtocol<ClientboundP
protected void registerPackets() {
super.registerPackets();
tagRewriter.addEmptyTag(RegistryType.ITEM, "minecraft:axolotl_tempt_items");
tagRewriter.renameTag(RegistryType.ITEM, "axolotl_food", "axolotl_tempt_items");
replaceClientbound(ClientboundConfigurationPackets1_20_5.UPDATE_TAGS, wrapper -> {
// Send off registry data first, needed for tags
sendRegistryData(wrapper.user());
@@ -38,8 +38,8 @@ final class Types1_20_3 implements VersionedTypesHolder {
}
@Override
public Type<Item[]> itemArray() {
return Types.ITEM1_20_2_ARRAY;
public ArrayType<Item> itemArray() {
return (ArrayType<Item>) Types.ITEM1_20_2_ARRAY;
}
@Override
@@ -53,7 +53,7 @@ final class Types1_20_3 implements VersionedTypesHolder {
}
@Override
public Type<Item[]> itemTemplateArray() {
public ArrayType<Item> itemTemplateArray() {
return itemArray();
}
Loaded 100 of 196 files, more files were not shown because too many files have changed in this diff. Show more