Compare commits

...
111 Commits
Author SHA1 Message Date
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
Florian Reuth 59abf8bd18 Fix tag updating in 1.21.6->1.21.5 (#1277) 2026-06-15 11:17:05 +02:00
Term4 33d3b1db9c Round instead of truncating in toLegacyVelocity (#1272) 2026-06-12 17:28:11 +02:00
Nassim Jahnke 5255394ed6 Track mid-game spectator gamemode change in 26.1->1.21.11
Fixes #1267
2026-06-07 12:49:00 +02:00
Nassim Jahnke c5713f915d Move velocity explosion packet out of bounds in 1.21.2->1.21 2026-06-07 12:37:08 +02:00
Nassim Jahnke e651e1cef9 Update VV usage 2026-05-25 09:33:52 +02:00
Gubis Zsombor Dániel c3f6271a0d Fix NPE in BlockRewriter1_20_2 when block entity has null tag (#1262) 2026-05-19 10:21:03 +02:00
RK_01 1508cd2820 [ci skip] Remove redundant types (#1259) 2026-05-08 19:07:16 +02:00
Nassim Jahnke bc7f3607c6 Track gamemode in 1.20.5 protocol 2026-05-08 19:05:55 +02:00
Nassim Jahnke 95d53b7f01 5.9.1 Release 2026-05-04 20:31:09 +02:00
Nassim Jahnke c4ac1b0560 Add hashed item shortcut in 1.21.5->1.21.4 2026-05-03 16:00:40 +02:00
Nassim Jahnke 8ee6391ff9 Remove ChunkLightStorage reflection 2026-05-03 12:21:56 +02:00
Florian Reuth 7700afb479 Move configuration state tracking into shared registrations (#1249) 2026-05-02 09:49:45 +02:00
Nassim Jahnke 5361c019f2 Fix shouldered parrot tracking in 1.21.9->1.21.7
Fixes #1257
2026-04-27 15:50:53 +02:00
Nassim Jahnke 041f7f933b Fix toggled advance_time handling
Fixes #1256
2026-04-27 15:40:18 +02:00
Kichura 48ad4e4aa3 [ci skip] Update action files (#1253) 2026-04-26 16:19:27 +02:00
Nassim Jahnke 358288f8f7 [ci skip] Add agents.md file 2026-04-26 16:16:51 +02:00
Beanes e46cf56e52 Cancel non-player bed sleep data in 1.14->1.13 (#1252) 2026-04-22 20:03:45 +02:00
BT ab9d1d26f3 Use TagRewriter in 1.14->1.13.2 (#1251) 2026-04-21 10:58:10 +02:00
Nassim Jahnke 65b2c30c7e Remove copper golem data in 1.21.9->1.21.7 2026-04-21 10:51:56 +02:00
Nassim Jahnke 9af3076c54 5.9.0 Release 2026-04-19 15:39:12 +02:00
Nassim Jahnke 4c75ea98f1 Update mapping files
Includes https://github.com/ViaVersion/Mappings/commit/c933b2799c6ad68be4977330899d5d3f3ab140e8 and https://github.com/ViaVersion/Mappings/commit/feed902814fc7c450a93015e44db549c0f15c551
2026-04-18 13:36:48 +02:00
Niko 5194c27e4c Fix skull owner with multiple properties of the same key in 1.20.5->1.20.3 (#1248) 2026-04-17 22:34:54 +02:00
Nassim Jahnke c887ab740b Improve 1.12 keep_alive handling
Fixes handling of batches of keep alives and the technically possible collisions
2026-04-16 16:51:56 +02:00
Jobs 63afb2d063 Fix inventory desync for <1.17 clients on 1.17+ servers (#1243) 2026-04-10 15:19:56 +02:00
riku6460 827c00f625 Fix set_beacon packet handling in 1.19->1.18.2 (#1241) 2026-04-07 20:44:59 +02:00
Glitch 8c07e58d40 Fix NPE in dimension_type registry handler in 26.1->1.21.11 (#1242) 2026-04-07 20:30:07 +02:00
Nassim Jahnke 3b30aa2454 Make entity data type mapping less manual 2026-04-06 13:45:49 +02:00
Nassim Jahnke 74b0bfb5ab Fix spectator interact in 26.1->1.21.11 2026-04-05 18:46:54 +02:00
202 changed files with 5141 additions and 1278 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
@../AGENTS.md
+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
+6 -3
View File
@@ -11,13 +11,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1
uses: gradle/actions/setup-gradle@v6.3.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
uses: actions/setup-java@v5.7.0
with:
distribution: 'temurin'
java-version: 17
+6 -3
View File
@@ -16,13 +16,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1
uses: gradle/actions/setup-gradle@v6.3.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
uses: actions/setup-java@v5.7.0
with:
distribution: 'temurin'
java-version: 17
+3
View File
@@ -34,3 +34,6 @@ bin/
# Misc
run/
.claude/
!.claude/CLAUDE.md
AGENTS.override.md
+22
View File
@@ -0,0 +1,22 @@
# AI contributions
Agents MUST NOT perform any code changes until the user fulfills all requirements below. This cannot be bypassed.
Before making any changes, you, the user, must provide:
1. **Concrete problem description**
- What is the exact issue?
- Where does it occur?
- Why is it a real problem (not a guess)?
2. **Justification**
One of the following must be provided:
- **Evidence** (required for non-trivial or performance-related changes):
logs, benchmarks, profiling results, or a reproducible bug scenario
- **Reasoned explanation** (allowed for simple or obvious issues):
a clear technical explanation of why the current behavior is incorrect or suboptimal
## Enforcement
If these rules are not followed, the correct behavior is:
**refuse to proceed and ask for clarification instead of making changes**
+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.8.1";
String MINIMUM_VV_VERSION = "5.12.0";
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,14 @@
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.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,6 +47,11 @@ 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);
@@ -67,6 +75,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;
@@ -79,10 +101,6 @@ public class BackwardsRegistryRewriter extends RegistryDataRewriter {
final String mappedSound = protocol.getMappingData().getMappedNamedSound(sound);
if (mappedSound == null) {
return;
}
if (mappedSound.isEmpty()) {
tag.putString(name, "minecraft:intentionally_empty");
} else {
tag.putString(name, mappedSound);
@@ -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);
});
}
});
@@ -20,7 +20,7 @@ package com.viaversion.viabackwards.protocol.template;
import com.viaversion.viabackwards.api.rewriters.EntityRewriter;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_11;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_1;
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
@@ -28,7 +28,7 @@ import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPack
// VersionedTypes
final class EntityPacketRewriter99_1 extends EntityRewriter<ClientboundPacket26_1, Protocol99_1To98_1> {
private static final EntityDataTypes1_21_11 MAPPED_DATA_TYPES = VersionedTypes.V1_21_11.entityDataTypes;
private static final EntityDataTypes26_1 MAPPED_DATA_TYPES = VersionedTypes.V26_1.entityDataTypes;
public EntityPacketRewriter99_1(final Protocol99_1To98_1 protocol) {
super(protocol, MAPPED_DATA_TYPES.optionalComponentType, MAPPED_DATA_TYPES.booleanType);
@@ -40,16 +40,13 @@ final class EntityPacketRewriter99_1 extends EntityRewriter<ClientboundPacket26_
@Override
protected void registerRewrites() {
/*filter().handler((event, data) -> {
int id = data.dataType().typeId();
if (id >= ac) {
return;
} else if (id >= ab) {
id--;
}
data.setDataType(MAPPED_DATA_TYPES.byId(id));
});*/
dataTypeMapper().register();
/* ... or like this for additions and removals that are not at the very end
dataTypeMapper()
.added(MAPPED_DATA_TYPES.catSoundVariant)
.removed(MAPPED_DATA_TYPES.cowSoundVariant)
.skip(MAPPED_DATA_TYPES.pigSoundVariant) // if neither removed nor added, but the value type has to be changed separately
.register();*/
registerEntityDataTypeHandler1_20_3(
MAPPED_DATA_TYPES.itemType,
@@ -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));
addItemHasher(connection, 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());
@@ -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;
@@ -147,7 +147,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);
@@ -295,9 +295,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 +305,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;
@@ -21,7 +21,6 @@ package com.viaversion.viabackwards.protocol.v1_12_2to1_12_1;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.storage.KeepAliveTracker;
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.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;
import com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;
@@ -34,32 +33,20 @@ public class Protocol1_12_2To1_12_1 extends BackwardsProtocol<ClientboundPackets
@Override
protected void registerPackets() {
registerClientbound(ClientboundPackets1_12_1.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
handler(packetWrapper -> {
Long keepAlive = packetWrapper.read(Types.LONG);
packetWrapper.user().get(KeepAliveTracker.class).setKeepAlive(keepAlive);
packetWrapper.write(Types.VAR_INT, keepAlive.hashCode());
});
}
registerClientbound(ClientboundPackets1_12_1.KEEP_ALIVE, wrapper -> {
long keepAlive = wrapper.read(Types.LONG);
int id = wrapper.user().get(KeepAliveTracker.class).track(keepAlive);
wrapper.write(Types.VAR_INT, id);
});
registerServerbound(ServerboundPackets1_12_1.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
handler(packetWrapper -> {
int keepAlive = packetWrapper.read(Types.VAR_INT);
long realKeepAlive = packetWrapper.user().get(KeepAliveTracker.class).getKeepAlive();
if (keepAlive != Long.hashCode(realKeepAlive)) {
packetWrapper.cancel(); // Wrong data, cancel packet
return;
}
packetWrapper.write(Types.LONG, realKeepAlive);
// Reset KeepAliveTracker (to prevent sending same valid value in a row causing a timeout)
packetWrapper.user().get(KeepAliveTracker.class).setKeepAlive(Integer.MAX_VALUE);
});
registerServerbound(ServerboundPackets1_12_1.KEEP_ALIVE, wrapper -> {
int keepAlive = wrapper.read(Types.VAR_INT);
Long realKeepAlive = wrapper.user().get(KeepAliveTracker.class).consume(keepAlive);
if (realKeepAlive == null) {
wrapper.cancel(); // stale/bad
return;
}
wrapper.write(Types.LONG, realKeepAlive);
});
}
@@ -19,20 +19,22 @@
package com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public class KeepAliveTracker implements StorableObject {
private long keepAlive = Integer.MAX_VALUE;
public long getKeepAlive() {
return keepAlive;
private final Map<Integer, Long> pending = new HashMap<>();
private int nextId;
public int track(long original) {
int id = nextId++;
pending.put(id, original);
return id;
}
public void setKeepAlive(long keepAlive) {
this.keepAlive = keepAlive;
}
@Override
public String toString() {
return "KeepAliveTracker{" + "keepAlive=" + keepAlive + '}';
public @Nullable Long consume(int id) {
return pending.remove(id);
}
}
@@ -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));
}
@@ -131,7 +131,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);
@@ -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;
}
}
@@ -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));
@@ -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,9 +27,10 @@ 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;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
@@ -39,6 +40,7 @@ import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPacke
import com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ParticleRewriter;
import com.viaversion.viaversion.rewriter.TagRewriter;
import com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;
public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_13, ServerboundPackets1_14, ServerboundPackets1_13> {
@@ -49,6 +51,7 @@ public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_
private final BlockRewriter<ClientboundPackets1_14> blockRewriter = BlockRewriter.legacy(this);
private final ParticleRewriter<ClientboundPackets1_14> particleRewriter = new ParticleRewriter<>(this);
private final JsonNBTComponentRewriter<ClientboundPackets1_14> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);
private final TagRewriter<ClientboundPackets1_14> tagRewriter = new TagRewriter<>(this);
public Protocol1_14To1_13_2() {
super(ClientboundPackets1_14.class, ClientboundPackets1_13.class, ServerboundPackets1_14.class, ServerboundPackets1_13.class);
@@ -66,35 +69,9 @@ public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_
cancelClientbound(ClientboundPackets1_14.SET_CHUNK_CACHE_RADIUS);
registerClientbound(ClientboundPackets1_14.UPDATE_TAGS, wrapper -> {
int blockTagsSize = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < blockTagsSize; i++) {
wrapper.passthrough(Types.STRING);
int[] blockIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);
for (int j = 0; j < blockIds.length; j++) {
int id = blockIds[j];
// Ignore new blocktags
int blockId = MAPPINGS.getNewBlockId(id);
blockIds[j] = blockId;
}
}
int itemTagsSize = wrapper.passthrough(Types.VAR_INT);
for (int i = 0; i < itemTagsSize; i++) {
wrapper.passthrough(Types.STRING);
int[] itemIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);
for (int j = 0; j < itemIds.length; j++) {
int itemId = itemIds[j];
// Ignore new itemtags
int oldId = MAPPINGS.getItemMappings().getNewId(itemId);
itemIds[j] = oldId;
}
}
int fluidTagsSize = wrapper.passthrough(Types.VAR_INT); // fluid tags
for (int i = 0; i < fluidTagsSize; i++) {
wrapper.passthrough(Types.STRING);
wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);
}
tagRewriter.handle(wrapper, RegistryType.BLOCK);
tagRewriter.handle(wrapper, RegistryType.ITEM);
tagRewriter.handle(wrapper, RegistryType.FLUID);
// Eat entity tags
int entityTagsSize = wrapper.read(Types.VAR_INT);
@@ -144,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();
});
}
@@ -155,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
@@ -194,4 +172,9 @@ public class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_
public JsonNBTComponentRewriter<ClientboundPackets1_14> getComponentRewriter() {
return translatableRewriter;
}
@Override
public TagRewriter<ClientboundPackets1_14> getTagRewriter() {
return tagRewriter;
}
}
@@ -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);
@@ -450,15 +458,13 @@ public class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPa
if (index == 12) {
BlockPosition position = (BlockPosition) data.getValue();
if (position != null) {
// Use bed
PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.PLAYER_SLEEP, null, event.user());
wrapper.write(Types.VAR_INT, event.entityId());
wrapper.write(Types.BLOCK_POSITION1_8, position);
// Only players can sleep in 1.13
if (event.entityType() == EntityTypes1_14.PLAYER) {
PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.PLAYER_SLEEP, null, event.user());
wrapper.write(Types.VAR_INT, event.entityId());
wrapper.write(Types.BLOCK_POSITION1_8, position);
try {
wrapper.scheduleSend(Protocol1_14To1_13_2.class);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@@ -478,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);
}
});
@@ -521,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,27 +17,19 @@
*/
package com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.viaversion.viaversion.libs.fastutil.longs.Long2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
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];
private static Constructor<?> fastUtilLongObjectHashMap;
private final Map<Long, ChunkLight> storedLight = createLongObjectMap();
private final Long2ObjectMap<ChunkLight> storedLight = new Long2ObjectOpenHashMap<>();
static {
Arrays.fill(FULL_LIGHT, (byte) 0xFF);
Arrays.fill(EMPTY_LIGHT, (byte) 0x0);
try {
fastUtilLongObjectHashMap = Class.forName("it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap").getConstructor();
} catch (ClassNotFoundException | NoSuchMethodException ignored) {
}
}
public void setStoredLight(byte[][] skyLight, byte[][] blockLight, int x, int z) {
@@ -60,17 +52,6 @@ public class ChunkLightStorage implements StorableObject {
return ((x & 0x3FFFFFFL) << 38) | (z & 0x3FFFFFFL);
}
private Map<Long, ChunkLight> createLongObjectMap() {
if (fastUtilLongObjectHashMap != null) {
try {
return (Map<Long, ChunkLight>) fastUtilLongObjectHashMap.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
return new HashMap<>();
}
public record ChunkLight(byte[][] skyLight, byte[][] blockLight) {
@Override
@@ -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,25 +56,25 @@ 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));
// Carried item - should work without adding it to the array above
// 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,
@@ -82,16 +82,26 @@ public final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPac
// for a subsequent drag
lastCursorItem.setLastCursorItem(carried);
// In 1.17.1+, the carried/cursor item is part of CONTAINER_SET_CONTENT,
// but <1.17 clients don't have this field. Send it as a separate
// CONTAINER_SET_SLOT packet so the client's cursor state is properly synced.
// This fixes inventory desyncs when the server cancels InventoryClickEvents.
PacketWrapper cursorPacket = wrapper.create(ClientboundPackets1_17.CONTAINER_SET_SLOT);
cursorPacket.write(Types.BYTE, (byte) -1); // Window ID: -1 for cursor
cursorPacket.write(Types.SHORT, (short) -1); // Slot: -1 for cursor
cursorPacket.write(Types.ITEM1_13_2, carried);
cursorPacket.send(Protocol1_17_1To1_17.class);
}
});
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);
});
@@ -148,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,7 +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;
@@ -83,10 +88,30 @@ public final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<Cli
int mode = wrapper.passthrough(Types.VAR_INT); // Mode
Item clicked = handleItemToServer(wrapper.user(), wrapper.read(Types.ITEM1_13_2)); // Clicked item
// The 1.17 client would check the entire inventory for changes before -> after a click and send the changed slots here
wrapper.write(Types.VAR_INT, 0); // Empty array of slot+item
// The 1.17 client would check the entire inventory for changes before -> after a click
// and send the changed slots here. We include the clicked slot so the server
// detects any desync (e.g. cancelled InventoryClickEvent) and sends corrections.
if (slot >= 0 && clicked != null) {
wrapper.write(Types.VAR_INT, 1); // One modified slot
wrapper.write(Types.SHORT, slot); // The clicked slot
wrapper.write(Types.ITEM1_13_2, (Item) null); // Predicted: empty (item picked up)
} else {
wrapper.write(Types.VAR_INT, 0); // Empty array of slot+item
}
PlayerLastCursorItem state = wrapper.user().get(PlayerLastCursorItem.class);
// Non-PICKUP modes (shift-click, swap, throw, drag, etc.) affect multiple slots
// 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) {
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);
}
}
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
@@ -135,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));
@@ -209,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);
@@ -325,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;
}
}
@@ -103,8 +103,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());
}
});
@@ -70,8 +70,6 @@ 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);
public static final ByteArrayType.OptionalByteArrayType OPTIONAL_SIGNATURE_BYTES_TYPE = new ByteArrayType.OptionalByteArrayType(256);
public static final ByteArrayType SIGNATURE_BYTES_TYPE = new ByteArrayType(256);
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);
@@ -227,9 +225,9 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
throw new RuntimeException(e);
}
wrapper.write(Protocol1_19_3To1_19_1.OPTIONAL_SIGNATURE_BYTES_TYPE, signature); // Signature
wrapper.write(Types.OPTIONAL_SIGNATURE_BYTES, signature); // Signature
} else {
wrapper.write(Protocol1_19_3To1_19_1.OPTIONAL_SIGNATURE_BYTES_TYPE, null); // Signature
wrapper.write(Types.OPTIONAL_SIGNATURE_BYTES, null); // Signature
}
//TODO is this fine (probably not)? same for chat_command
@@ -273,7 +271,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
}
wrapper.write(Types.STRING, argument.key());
wrapper.write(Protocol1_19_3To1_19_1.SIGNATURE_BYTES_TYPE, signature);
wrapper.write(Types.SIGNATURE_BYTES, signature);
}
} else {
wrapper.write(Types.VAR_INT, 0); // No signatures
@@ -293,7 +291,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
public void register() {
read(Types.UUID); // Sender
read(Types.VAR_INT); // Index
read(OPTIONAL_SIGNATURE_BYTES_TYPE); // Signature
read(Types.OPTIONAL_SIGNATURE_BYTES); // Signature
handler(wrapper -> {
final String plainContent = wrapper.read(Types.STRING);
wrapper.read(Types.LONG); // Timestamp
@@ -302,7 +300,7 @@ public final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundP
for (int i = 0; i < lastSeen; i++) {
final int index = wrapper.read(Types.VAR_INT);
if (index == 0) {
wrapper.read(SIGNATURE_BYTES_TYPE);
wrapper.read(Types.SIGNATURE_BYTES);
}
}
@@ -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;
}
}
@@ -159,7 +159,7 @@ public final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<Cli
protocol.registerServerbound(ServerboundPackets1_17.SET_BEACON, wrapper -> {
final int primaryEffect = wrapper.read(Types.VAR_INT);
if (primaryEffect != -1) {
if (primaryEffect > 0) {
wrapper.write(Types.BOOLEAN, true);
wrapper.write(Types.VAR_INT, primaryEffect);
} else {
@@ -167,7 +167,7 @@ public final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<Cli
}
final int secondaryEffect = wrapper.read(Types.VAR_INT);
if (secondaryEffect != -1) {
if (secondaryEffect > 0) {
wrapper.write(Types.BOOLEAN, true);
wrapper.write(Types.VAR_INT, secondaryEffect);
} else {
@@ -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;
@@ -79,8 +79,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 +93,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,8 +122,8 @@ 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;
final TrackedEntity entity = tracker(wrapper.user()).entity(entityId);
final StoredPainting storedPainting = entity != null ? entity.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())) {
@@ -361,8 +361,8 @@ 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();
storedPainting.setType(type);
@@ -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() {
@@ -195,11 +195,6 @@ public final class Protocol1_20_2To1_20 extends BackwardsProtocol<ClientboundPac
}
}
@Override
protected void registerConfigurationChangeHandlers() {
// Don't register them in the transitioning protocol
}
@Override
public void init(final UserConnection connection) {
addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_19_4.PLAYER));
@@ -41,6 +41,9 @@ public final class BlockRewriter1_20_2 extends BlockRewriter<ClientboundPackets1
@Override
public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {
final CompoundTag tag = blockEntity.tag();
if (tag == null) {
return;
}
final Tag primaryEffect = tag.remove("primary_effect");
if (primaryEffect instanceof StringTag) {
final String effectKey = Key.stripMinecraftNamespace(((StringTag) primaryEffect).getValue());
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff. Show more