mirror of
https://github.com/ViaVersion/ViaRewind.git
synced 2026-11-04 17:00:20 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8e4746053 | ||
|
|
82a3ee84e0 | ||
|
|
9df4d11f8c | ||
|
|
c92916a265 | ||
|
|
dc04defeb6 | ||
|
|
c7edeba340 | ||
|
|
d232bd020f | ||
|
|
2525d42666 | ||
|
|
4d51a62410 | ||
|
|
b177e048dd | ||
|
|
7577279fcb | ||
|
|
e3211f99ef | ||
|
|
d562f9745a | ||
|
|
7339df9fdb | ||
|
|
d98d5bed9b | ||
|
|
786529efbe | ||
|
|
71546a7699 | ||
|
|
9e7c853a5d | ||
|
|
315a1a0fab | ||
|
|
7a2898e44a | ||
|
|
5783fa98e9 | ||
|
|
77d9d9143e | ||
|
|
ede1920686 | ||
|
|
189249478f | ||
|
|
6c19760af6 | ||
|
|
69f8213445 | ||
|
|
f95871bdc5 | ||
|
|
7d6c75ad31 | ||
|
|
0bb7be88ff | ||
|
|
4b7264c944 | ||
|
|
db6afa5b1b | ||
|
|
02f55edb0c | ||
|
|
5ef900a61e | ||
|
|
4fe7e35f6b | ||
|
|
1af08ced41 | ||
|
|
fd1d3ed221 | ||
|
|
1d6ef8833f | ||
|
|
f2e2f18511 | ||
|
|
2adf7757f0 | ||
|
|
4dd3de2ce9 | ||
|
|
ddbdbdb1cc | ||
|
|
28b4998912 | ||
|
|
f9b0fd5c5a | ||
|
|
a676368588 | ||
|
|
966231ad1c | ||
|
|
d73c2198ea | ||
|
|
6f319932be | ||
|
|
f172de2742 | ||
|
|
ca25da9445 | ||
|
|
ea548dfd91 | ||
|
|
d0561f2e19 | ||
|
|
2c8189a447 | ||
|
|
48c9e45bcf | ||
|
|
ecb4f4bf00 | ||
|
|
6ea9377622 | ||
|
|
b789fdebb7 | ||
|
|
e7b0606e18 | ||
|
|
bf490c3805 | ||
|
|
7ca7078141 | ||
|
|
170af55f91 | ||
|
|
00d82f9f2f | ||
|
|
79ef613c15 | ||
|
|
e2fcbafc70 | ||
|
|
9ae46145e5 | ||
|
|
76f90b68b8 | ||
|
|
32a2ee6bfa | ||
|
|
04878b7e34 | ||
|
|
adccdc4b41 | ||
|
|
0accea66f2 | ||
|
|
65257be3ee | ||
|
|
880e879dce | ||
|
|
0ccc6a0923 | ||
|
|
7bff361e14 | ||
|
|
c2c28473cc | ||
|
|
048d502998 | ||
|
|
e0c1f53115 | ||
|
|
e5313a466b | ||
|
|
e30633e367 | ||
|
|
a443dd5d11 | ||
|
|
3820426bce | ||
|
|
031ae32f27 | ||
|
|
e83616139e | ||
|
|
7325b733d6 | ||
|
|
e64501eae8 | ||
|
|
2d99607c35 | ||
|
|
4d159189c0 | ||
|
|
2076294856 | ||
|
|
49f7bd22be | ||
|
|
298605874d | ||
|
|
35b94f1878 | ||
|
|
37955059c4 | ||
|
|
af4306bc81 | ||
|
|
98ca00f625 | ||
|
|
598804432b | ||
|
|
92bae0a5c4 | ||
|
|
d50b6a2f89 | ||
|
|
54cc085717 | ||
|
|
bd0d4c31b2 | ||
|
|
705c88f4b3 | ||
|
|
7306a795b9 | ||
|
|
dfdfad4bfc | ||
|
|
c87654848d | ||
|
|
08a7077d10 | ||
|
|
73afaaf493 | ||
|
|
f0f8038b18 | ||
|
|
540c0d0d47 | ||
|
|
d00b0c8afe | ||
|
|
b34385c09e | ||
|
|
ff47845df1 | ||
|
|
fc37b9c0ba | ||
|
|
e3baafa2a2 | ||
|
|
11914da37e | ||
|
|
30fa58af81 | ||
|
|
52b6aed005 | ||
|
|
9d74706e84 | ||
|
|
7d5ad45ffc | ||
|
|
bd3593e15a | ||
|
|
835fb5de08 | ||
|
|
8b2769f19b | ||
|
|
4da08b5a35 | ||
|
|
7da8e62a39 | ||
|
|
31c62f96b9 | ||
|
|
0f866d8b50 | ||
|
|
32d265116a | ||
|
|
b7d1197be3 | ||
|
|
d4363b67b6 |
No files matched your search
+2
-2
@@ -1,2 +1,2 @@
|
||||
github: FlorianMichael
|
||||
custom: [ 'https://florianmichael.de/donate', 'https://creeper123123321.github.io/#donate' ]
|
||||
github: florianreuth
|
||||
custom: [ 'https://florianreuth.de/donate', 'https://creeper123123321.github.io/#donate' ]
|
||||
+14
-2
@@ -1,10 +1,22 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gradle"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "github-actions"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "monthly"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -1,21 +1,32 @@
|
||||
name: Build
|
||||
on: [ pull_request, push, workflow_dispatch ]
|
||||
|
||||
on: [pull_request, push, workflow_dispatch]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
|
||||
with:
|
||||
cache-disabled: true
|
||||
cache-provider: basic
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
check-latest: true
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew build --stacktrace
|
||||
- name: Upload Artifacts to GitHub
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: Artifacts
|
||||
path: build/libs/
|
||||
if-no-files-found: error
|
||||
@@ -4,26 +4,34 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: github.repository_owner == 'ViaVersion'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
|
||||
with:
|
||||
cache-disabled: true
|
||||
cache-provider: basic
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
check-latest: true
|
||||
- name: Build
|
||||
run: ./gradlew build
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build --stacktrace
|
||||
- name: Publish to Hangar
|
||||
env:
|
||||
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
|
||||
|
||||
+4
-2
@@ -13,6 +13,8 @@ classes/
|
||||
# Idea
|
||||
|
||||
.idea/
|
||||
!.idea/copyright/*
|
||||
!.idea/scopes/*
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
@@ -27,8 +29,8 @@ bin/
|
||||
|
||||
# macOS
|
||||
|
||||
*.DS_Store
|
||||
DS_Store
|
||||
|
||||
# Misc
|
||||
|
||||
run/
|
||||
run/
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a **security vulnerability**, please **do not** open a public issue. Instead, use one of the following *
|
||||
*confidential contact methods**:
|
||||
If you discover a **security vulnerability**, please **do not** open a public issue. Instead, use one of the following
|
||||
**confidential contact methods**:
|
||||
|
||||
* **Email:** [security@viaversion.com](mailto:security@viaversion.com)
|
||||
* **Discord:** Join our [Discord server](https://discord.gg/viaversion) and visit
|
||||
|
||||
+24
-11
@@ -1,10 +1,17 @@
|
||||
import de.florianmichael.baseproject.*
|
||||
import de.florianreuth.baseproject.core.configureEmbeddedDependencies
|
||||
import de.florianreuth.baseproject.integration.branchName
|
||||
import de.florianreuth.baseproject.integration.latestCommitHash
|
||||
import de.florianreuth.baseproject.integration.latestCommitMessage
|
||||
import de.florianreuth.baseproject.setupProject
|
||||
import de.florianreuth.baseproject.setupViaPublishing
|
||||
import net.raphimc.classtokenreplacer.extension.ClassTokenReplacerExtension
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.papermc.hangar-publish-plugin")
|
||||
id("com.modrinth.minotaur")
|
||||
id("de.florianmichael.baseproject.BaseProject")
|
||||
id("de.florianreuth.baseproject")
|
||||
id("net.raphimc.class-token-replacer") apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -13,7 +20,6 @@ allprojects {
|
||||
setupViaPublishing()
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.viaversion.com")
|
||||
maven("https://repo.papermc.io/repository/maven-public")
|
||||
maven("https://maven.fabricmc.net")
|
||||
@@ -21,11 +27,22 @@ allprojects {
|
||||
|
||||
}
|
||||
|
||||
val commitHash = latestCommitHash()
|
||||
|
||||
subprojects {
|
||||
|
||||
apply(plugin = "net.raphimc.class-token-replacer")
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.viaversion:viaversion:5.4.0")
|
||||
compileOnly("com.viaversion:viabackwards:5.4.0")
|
||||
compileOnly("com.viaversion:viaversion:5.12.0")
|
||||
compileOnly("com.viaversion:viabackwards:5.12.0")
|
||||
}
|
||||
|
||||
extensions.getByType<SourceSetContainer>().configureEach {
|
||||
extensions.getByType(ClassTokenReplacerExtension::class.java).apply {
|
||||
property("\${version}", project.version)
|
||||
property("\${impl_version}", "git-ViaRewind-${project.version}:${commitHash}")
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -33,18 +50,14 @@ subprojects {
|
||||
val projectVersion = project.version
|
||||
val projectDescription = project.description
|
||||
filesMatching(listOf("plugin.yml", "fabric.mod.json", "META-INF/sponge_plugins.json")) {
|
||||
expand("version" to projectVersion, "description" to projectDescription)
|
||||
expand(mapOf("version" to projectVersion, "description" to projectDescription))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("ViaRewind")
|
||||
}
|
||||
|
||||
val shade = configureShadedDependencies()
|
||||
val shade = configureEmbeddedDependencies()
|
||||
|
||||
dependencies {
|
||||
subprojects.forEach {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -18,8 +18,11 @@
|
||||
package com.viaversion.viarewind;
|
||||
|
||||
import com.viaversion.viarewind.api.ViaRewindPlatform;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
|
||||
import com.viaversion.viarewind.provider.BukkitInventoryProvider;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import java.io.File;
|
||||
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class BukkitPlugin extends JavaPlugin implements ViaRewindPlatform {
|
||||
@@ -27,4 +30,10 @@ public class BukkitPlugin extends JavaPlugin implements ViaRewindPlatform {
|
||||
public BukkitPlugin() {
|
||||
Via.getManager().addEnableListener(() -> this.init(new File(getDataFolder(), "config.yml")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
final ViaProviders providers = Via.getManager().getProviders();
|
||||
providers.use(InventoryProvider.class, new BukkitInventoryProvider());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.provider;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public final class BukkitInventoryProvider extends InventoryProvider {
|
||||
|
||||
@Override
|
||||
public boolean hasElytra(final UserConnection connection) {
|
||||
final Player player = Bukkit.getPlayer(connection.getProtocolInfo().getUuid());
|
||||
final ItemStack chestplate = player.getInventory().getChestplate();
|
||||
return chestplate != null && chestplate.getType() == Material.ELYTRA;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
name: ViaRewind
|
||||
main: com.viaversion.viarewind.BukkitPlugin
|
||||
authors: [ FlorianMichael/EnZaXD, Gerrygames, creeper123123321 ]
|
||||
authors: [ EnZaXD, Gerrygames, creeper123123321 ]
|
||||
version: ${version}
|
||||
description: ${description}
|
||||
website: https://viaversion.com/rewind
|
||||
|
||||
+2
-16
@@ -1,29 +1,15 @@
|
||||
import de.florianmichael.baseproject.latestCommitHash
|
||||
|
||||
plugins {
|
||||
id("net.raphimc.class-token-replacer")
|
||||
}
|
||||
|
||||
val viaProxy: Configuration by configurations.creating
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.netty:netty-all:4.0.20.Final")
|
||||
compileOnly("com.google.guava:guava:17.0")
|
||||
compileOnly("com.velocitypowered:velocity-native:3.4.0-SNAPSHOT")
|
||||
|
||||
viaProxy("net.raphimc:ViaProxy:3.4.5-SNAPSHOT") {
|
||||
viaProxy("net.raphimc:ViaProxy:[3.0.0,4.0.0)") {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.configureEach {
|
||||
classTokenReplacer {
|
||||
replaceInPlace.set(true)
|
||||
|
||||
property("\${version}", project.version)
|
||||
property("\${impl_version}", "git-ViaRewind-${project.version}:${latestCommitHash()}")
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind;
|
||||
|
||||
import com.viaversion.viarewind.api.ViaRewindPlatform;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ViaRewindPlatformImpl implements ViaRewindPlatform {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public ViaRewindPlatformImpl() {
|
||||
logger = Via.getPlatform().createLogger("ViaRewind");
|
||||
init(new File(getDataFolder(), "viarewind.yml"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return Via.getPlatform().getDataFolder();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -36,11 +36,14 @@ public interface ViaRewindPlatform {
|
||||
String VERSION = "${version}";
|
||||
String IMPL_VERSION = "${impl_version}";
|
||||
|
||||
default void init(final File configFile) {
|
||||
init(new ViaRewindConfig(configFile, getLogger()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize ViaRewind
|
||||
*/
|
||||
default void init(final File configFile) {
|
||||
ViaRewindConfig config = new ViaRewindConfig(configFile, getLogger());
|
||||
default void init(final com.viaversion.viarewind.api.ViaRewindConfig config) {
|
||||
config.reload();
|
||||
Via.getManager().getConfigurationProvider().register(config);
|
||||
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.api.compression;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* A ThreadLocal-based compression provider that either uses Velocity's native compression
|
||||
* or falls back to Java's built-in compression (Deflater/Inflater).
|
||||
*/
|
||||
public final class ThreadLocalCompressionProvider {
|
||||
|
||||
private static final boolean VELOCITY_NATIVES_AVAILABLE;
|
||||
|
||||
static {
|
||||
boolean velocityAvailable = false;
|
||||
try {
|
||||
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
|
||||
velocityAvailable = true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
VELOCITY_NATIVES_AVAILABLE = velocityAvailable;
|
||||
}
|
||||
|
||||
// ThreadLocal for Java's Deflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Deflater> JAVA_DEFLATER = ThreadLocal.withInitial(Deflater::new);
|
||||
|
||||
// ThreadLocal for Java's Inflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Inflater> JAVA_INFLATER = ThreadLocal.withInitial(Inflater::new);
|
||||
|
||||
private ThreadLocalCompressionProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing uncompressed data
|
||||
* @param destination the destination buffer to write compressed data to
|
||||
* @throws DataFormatException if compression fails
|
||||
*/
|
||||
public static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
deflateVelocity(source, destination);
|
||||
} else {
|
||||
deflateJava(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decompresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing compressed data
|
||||
* @param destination the destination buffer to write decompressed data to
|
||||
* @param expectedSize the expected size of the decompressed data
|
||||
* @throws DataFormatException if decompression fails
|
||||
*/
|
||||
public static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
inflateVelocity(source, destination, expectedSize);
|
||||
} else {
|
||||
inflateJava(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Java native implementation
|
||||
|
||||
private static void deflateJava(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
try {
|
||||
final Deflater deflater = JAVA_DEFLATER.get();
|
||||
deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
deflater.finish();
|
||||
|
||||
while (!deflater.finished()) {
|
||||
output.ensureWritable(4096);
|
||||
output.writerIndex(output.writerIndex() + deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
|
||||
}
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_DEFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void inflateJava(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(expectedSize, expectedSize);
|
||||
try {
|
||||
final Inflater inflater = JAVA_INFLATER.get();
|
||||
inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + inflater.inflate(output.array(), output.arrayOffset(), expectedSize));
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_INFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity native implementation
|
||||
private static void deflateVelocity(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
VelocityHolder.deflate(source, destination);
|
||||
}
|
||||
|
||||
private static void inflateVelocity(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
VelocityHolder.inflate(source, destination, expectedSize);
|
||||
}
|
||||
|
||||
private static final class VelocityHolder {
|
||||
|
||||
private static final ThreadLocal<com.velocitypowered.natives.compression.VelocityCompressor> COMPRESSOR =
|
||||
ThreadLocal.withInitial(() -> com.velocitypowered.natives.util.Natives.compress.get().create(-1));
|
||||
|
||||
static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
COMPRESSOR.get().deflate(source, destination);
|
||||
}
|
||||
|
||||
static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
COMPRESSOR.get().inflate(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -20,6 +20,10 @@ package com.viaversion.viarewind.api.data;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
|
||||
import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viaversion.api.data.FullMappings;
|
||||
import com.viaversion.viaversion.api.data.LegacyFullStringMappingsBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import com.viaversion.viaversion.api.data.Mappings;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class RewindMappingData extends BackwardsMappingData {
|
||||
@@ -37,4 +41,9 @@ public class RewindMappingData extends BackwardsMappingData {
|
||||
protected CompoundTag readMappingsFile(String name) {
|
||||
return RewindMappingDataLoader.INSTANCE.loadNBTFromDir(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FullMappings createFullMappings(MappingDataLoader.IdentifiersPair identifiersPair, Mappings mappings) {
|
||||
return new LegacyFullStringMappingsBase(identifiersPair, mappings);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.api.minecraft;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
|
||||
|
||||
public class ExtendedBlockStorage {
|
||||
private final byte[] blockLSBArray = new byte[ChunkSection.SIZE];
|
||||
|
||||
private final NibbleArray blockMetadataArray = new NibbleArray(this.blockLSBArray.length);
|
||||
private final NibbleArray blockLightArray = new NibbleArray(this.blockLSBArray.length);
|
||||
|
||||
private NibbleArray blockMSBArray;
|
||||
private NibbleArray skyLightArray;
|
||||
|
||||
public ExtendedBlockStorage(final boolean skylight) {
|
||||
if (skylight) {
|
||||
this.skyLightArray = new NibbleArray(this.blockLSBArray.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlockId(final int x, final int y, final int z, final int value) {
|
||||
this.blockLSBArray[ChunkSection.index(x, y, z)] = (byte) (value & 255);
|
||||
if (value > 255) {
|
||||
this.getOrCreateBlockMSBArray().set(x, y, z, (value & 0xF00) >> 8);
|
||||
} else if (this.blockMSBArray != null) {
|
||||
this.blockMSBArray.set(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlockMetadata(final int x, final int y, final int z, final int value) {
|
||||
this.blockMetadataArray.set(x, y, z, value);
|
||||
}
|
||||
|
||||
public boolean hasBlockMSBArray() {
|
||||
return this.blockMSBArray != null;
|
||||
}
|
||||
|
||||
public byte[] getBlockLSBArray() {
|
||||
return this.blockLSBArray;
|
||||
}
|
||||
|
||||
public NibbleArray getOrCreateBlockMSBArray() {
|
||||
if (this.blockMSBArray == null) {
|
||||
return this.blockMSBArray = new NibbleArray(this.blockLSBArray.length);
|
||||
}
|
||||
return this.blockMSBArray;
|
||||
}
|
||||
|
||||
public NibbleArray getBlockMetadataArray() {
|
||||
return this.blockMetadataArray;
|
||||
}
|
||||
|
||||
public NibbleArray getBlockLightArray() {
|
||||
return this.blockLightArray;
|
||||
}
|
||||
|
||||
public NibbleArray getSkyLightArray() {
|
||||
return this.skyLightArray;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+2
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,17 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.minecraft.math;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Vector;
|
||||
|
||||
@Deprecated // ?????????
|
||||
public class RelativeMoveUtil {
|
||||
|
||||
public static Vector[] calculateRelativeMoves(UserConnection user, int entityId, int relX, int relY, int relZ) {
|
||||
EntityTracker1_9 tracker = user.getEntityTracker(Protocol1_9To1_8.class);
|
||||
|
||||
public static Vector[] calculateRelativeMoves(EntityTracker1_9 tracker, int entityId, int relX, int relY, int relZ) {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -28,7 +28,6 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.StringType;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,7 +35,6 @@ import java.util.List;
|
||||
*/
|
||||
public class RewindTypes {
|
||||
|
||||
public static final Type<String> SIGN_STRING = new StringType(384);
|
||||
public static final Type<int[]> INT_ARRAY = new IntArrayType(); // Integer array with byte as length indicator
|
||||
|
||||
public static final Type<BlockPosition> SHORT_POSITION = new PositionVarYType<>(Types.SHORT, value -> (short) value);
|
||||
|
||||
+71
-63
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,12 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.type.chunk;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
|
||||
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
|
||||
@@ -38,73 +39,80 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf byteBuf, Chunk[] chunks) {
|
||||
public void write(ByteBuf buffer, Chunk[] chunks) {
|
||||
final int chunkCount = chunks.length;
|
||||
final int[] chunkX = new int[chunkCount];
|
||||
final int[] chunkZ = new int[chunkCount];
|
||||
final short[] primaryBitMask = new short[chunkCount];
|
||||
final short[] additionalBitMask = new short[chunkCount];
|
||||
final int[] addBitMasks = new int[chunkCount];
|
||||
int totalSize = 0;
|
||||
boolean anySkyLight = false;
|
||||
|
||||
final byte[][] dataArrays = new byte[chunkCount][];
|
||||
int dataSize = 0;
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
final Chunk chunk = chunks[i];
|
||||
Pair<byte[], Short> chunkData;
|
||||
try {
|
||||
chunkData = ChunkType1_7_6.serialize(chunk);
|
||||
final byte[] data = chunkData.key();
|
||||
dataArrays[i] = data;
|
||||
dataSize += data.length;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to serialize chunk", e);
|
||||
}
|
||||
chunkX[i] = chunk.getX();
|
||||
chunkZ[i] = chunk.getZ();
|
||||
primaryBitMask[i] = (short) chunk.getBitmask();
|
||||
additionalBitMask[i] = chunkData.value();
|
||||
}
|
||||
|
||||
final byte[] data = new byte[dataSize];
|
||||
int destPos = 0;
|
||||
for (final byte[] array : dataArrays) {
|
||||
System.arraycopy(array, 0, data, destPos, array.length);
|
||||
destPos += array.length;
|
||||
}
|
||||
|
||||
final Deflater deflater = new Deflater();
|
||||
byte[] compressedData;
|
||||
int compressedSize;
|
||||
try {
|
||||
deflater.setInput(data, 0, data.length);
|
||||
deflater.finish();
|
||||
compressedData = new byte[data.length];
|
||||
compressedSize = deflater.deflate(compressedData);
|
||||
} finally {
|
||||
deflater.end();
|
||||
}
|
||||
|
||||
byteBuf.writeShort(chunkCount);
|
||||
byteBuf.writeInt(compressedSize);
|
||||
|
||||
boolean skyLight = false;
|
||||
for (Chunk chunk : chunks) {
|
||||
for (ChunkSection section : chunk.getSections()) {
|
||||
if (section != null && section.getLight().hasSkyLight()) {
|
||||
skyLight = true;
|
||||
break;
|
||||
}
|
||||
if (ChunkType1_7_6.hasSkyLight(chunk)) {
|
||||
anySkyLight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
byteBuf.writeBoolean(skyLight); // hasSkyLight
|
||||
byteBuf.writeBytes(compressedData, 0, compressedSize);
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
addBitMasks[i] = ChunkType1_7_6.getAddBitMask(chunk);
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
totalSize += ChunkType1_7_6.calcSize(
|
||||
chunk.getBitmask(),
|
||||
addBitMasks[i],
|
||||
anySkyLight,
|
||||
biomes
|
||||
);
|
||||
}
|
||||
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(totalSize);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
ChunkType1_7_6.serialize(
|
||||
chunk,
|
||||
uncompressed,
|
||||
addBitMasks[i],
|
||||
anySkyLight,
|
||||
biomes
|
||||
);
|
||||
}
|
||||
|
||||
buffer.writeShort(chunkCount);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
buffer.writeBoolean(anySkyLight);
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress bulk chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
uncompressed.release();
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
byteBuf.writeInt(chunkX[i]);
|
||||
byteBuf.writeInt(chunkZ[i]);
|
||||
byteBuf.writeShort(primaryBitMask[i]);
|
||||
byteBuf.writeShort(additionalBitMask[i]);
|
||||
Chunk chunk = chunks[i];
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.writeShort(chunk.getBitmask());
|
||||
buffer.writeShort(addBitMasks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+159
-132
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,15 +17,15 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.type.chunk;
|
||||
|
||||
import com.viaversion.viarewind.api.minecraft.ExtendedBlockStorage;
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight.LIGHT_LENGTH;
|
||||
@@ -38,143 +38,170 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
super(Chunk.class);
|
||||
}
|
||||
|
||||
public static Pair<byte[], Short> serialize(final Chunk chunk) throws IOException {
|
||||
final ExtendedBlockStorage[] storageArrays = new ExtendedBlockStorage[16];
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
final ChunkSection section = chunk.getSections()[i];
|
||||
if (section != null) {
|
||||
final ExtendedBlockStorage storage = storageArrays[i] = new ExtendedBlockStorage(section.getLight().hasSkyLight());
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
final int flatBlock = section.palette(PaletteType.BLOCKS).idAt(x, y, z);
|
||||
storage.setBlockId(x, y, z, flatBlock >> 4);
|
||||
storage.setBlockMetadata(x, y, z, flatBlock & 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
storage.getBlockLightArray().setHandle(section.getLight().getBlockLight());
|
||||
if (section.getLight().hasSkyLight()) {
|
||||
storage.getSkyLightArray().setHandle(section.getLight().getSkyLight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
final int totalSize = calculateSize(storageArrays, chunk.getBitmask(), biomes);
|
||||
|
||||
final byte[] output = new byte[totalSize];
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((chunk.getBitmask() & 1 << i) != 0) {
|
||||
final byte[] blockLSBArray = storageArrays[i].getBlockLSBArray();
|
||||
System.arraycopy(blockLSBArray, 0, output, index, blockLSBArray.length);
|
||||
index += blockLSBArray.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((chunk.getBitmask() & 1 << i) != 0) {
|
||||
final byte[] blockMetadataArray = storageArrays[i].getBlockMetadataArray().getHandle();
|
||||
System.arraycopy(blockMetadataArray, 0, output, index, blockMetadataArray.length);
|
||||
index += blockMetadataArray.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((chunk.getBitmask() & 1 << i) != 0) {
|
||||
final byte[] blockLightArray = storageArrays[i].getBlockLightArray().getHandle();
|
||||
System.arraycopy(blockLightArray, 0, output, index, blockLightArray.length);
|
||||
index += blockLightArray.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].getSkyLightArray() != null) {
|
||||
final byte[] skyLightArray = storageArrays[i].getSkyLightArray().getHandle();
|
||||
System.arraycopy(skyLightArray, 0, output, index, skyLightArray.length);
|
||||
index += skyLightArray.length;
|
||||
}
|
||||
}
|
||||
|
||||
short additionalBitMask = 0;
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].hasBlockMSBArray()) {
|
||||
additionalBitMask |= (short) (1 << i);
|
||||
final byte[] blockMSBArray = storageArrays[i].getOrCreateBlockMSBArray().getHandle();
|
||||
System.arraycopy(blockMSBArray, 0, output, index, blockMSBArray.length);
|
||||
index += blockMSBArray.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (biomes) {
|
||||
for (int biome : chunk.getBiomeData()) {
|
||||
output[index++] = (byte) biome;
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(output, additionalBitMask);
|
||||
}
|
||||
|
||||
private static int calculateSize(final ExtendedBlockStorage[] storageArrays, final int bitmask, final boolean biomes) {
|
||||
int totalSize = 0;
|
||||
for (int i = 0; i < storageArrays.length; i++) {
|
||||
if ((bitmask & 1 << i) != 0) {
|
||||
totalSize += SIZE; // Block lsb array
|
||||
totalSize += SIZE / 2; // Block metadata array
|
||||
totalSize += LIGHT_LENGTH; // Block light array
|
||||
|
||||
if (storageArrays[i].getSkyLightArray() != null) {
|
||||
totalSize += LIGHT_LENGTH;
|
||||
}
|
||||
|
||||
if (storageArrays[i].hasBlockMSBArray()) {
|
||||
totalSize += SIZE / 2; // Block msb array
|
||||
}
|
||||
}
|
||||
}
|
||||
if (biomes) {
|
||||
totalSize += 256;
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk read(ByteBuf byteBuf) {
|
||||
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf output, Chunk chunk) {
|
||||
Pair<byte[], Short> chunkData;
|
||||
try {
|
||||
chunkData = serialize(chunk);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to serialize chunk", e);
|
||||
}
|
||||
final byte[] data = chunkData.key();
|
||||
final short additionalBitMask = chunkData.value();
|
||||
public void write(ByteBuf buffer, Chunk chunk) {
|
||||
final int bitmask = chunk.getBitmask();
|
||||
final int addBitmask = getAddBitMask(chunk);
|
||||
final boolean hasSkyLight = hasSkyLight(chunk);
|
||||
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
final int size = calcSize(bitmask, addBitmask, hasSkyLight, biomes);
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(size);
|
||||
|
||||
final Deflater deflater = new Deflater();
|
||||
byte[] compressedData;
|
||||
int compressedSize;
|
||||
try {
|
||||
deflater.setInput(data, 0, data.length);
|
||||
deflater.finish();
|
||||
compressedData = new byte[data.length];
|
||||
compressedSize = deflater.deflate(compressedData);
|
||||
serialize(chunk, uncompressed, addBitmask, hasSkyLight, biomes);
|
||||
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.writeBoolean(chunk.isFullChunk());
|
||||
buffer.writeShort(bitmask);
|
||||
buffer.writeShort(addBitmask);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
deflater.end();
|
||||
uncompressed.release();
|
||||
}
|
||||
|
||||
output.writeInt(chunk.getX());
|
||||
output.writeInt(chunk.getZ());
|
||||
output.writeBoolean(chunk.isFullChunk());
|
||||
output.writeShort(chunk.getBitmask());
|
||||
output.writeShort(additionalBitMask);
|
||||
output.writeInt(compressedSize);
|
||||
output.writeBytes(compressedData, 0, compressedSize);
|
||||
}
|
||||
|
||||
public static void serialize(Chunk chunk, ByteBuf output, int addBitmask, boolean writeSkyLight, boolean biomes) {
|
||||
final ChunkSection[] sections = chunk.getSections();
|
||||
final int bitmask = chunk.getBitmask();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
final ChunkSection section = sections[i];
|
||||
final DataPalette palette = section.palette(PaletteType.BLOCKS);
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
final int block = palette.idAt(j);
|
||||
output.writeByte((block >> 4) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
final ChunkSection section = sections[i];
|
||||
final DataPalette palette = section.palette(PaletteType.BLOCKS);
|
||||
for (int j = 0; j < ChunkSection.SIZE; j += 2) {
|
||||
final int meta1 = palette.idAt(j) & 0xF;
|
||||
final int meta2 = palette.idAt(j + 1) & 0xF;
|
||||
output.writeByte(meta1 | (meta2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
final byte[] blockLight = sections[i].getLight().getBlockLight();
|
||||
output.writeBytes(blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
if (writeSkyLight) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
if (sections[i].getLight().hasSkyLight()) {
|
||||
final byte[] skyLight = sections[i].getLight().getSkyLight();
|
||||
output.writeBytes(skyLight);
|
||||
} else {
|
||||
// Write empty skylight data
|
||||
output.writeZero(LIGHT_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addBitmask != 0) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0 && (addBitmask & (1 << i)) != 0) {
|
||||
final ChunkSection section = sections[i];
|
||||
final DataPalette palette = section.palette(PaletteType.BLOCKS);
|
||||
for (int j = 0; j < SIZE; j += 2) {
|
||||
final int add1 = (palette.idAt(j) >> 12) & 0xF;
|
||||
final int add2 = (palette.idAt(j + 1) >> 12) & 0xF;
|
||||
output.writeByte(add1 | (add2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (biomes && chunk.getBiomeData() != null) {
|
||||
final int[] biomeData = chunk.getBiomeData();
|
||||
for (int biome : biomeData) {
|
||||
output.writeByte(biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
|
||||
int size = 0;
|
||||
int sections = Integer.bitCount(bitmask);
|
||||
|
||||
size += sections * SIZE;
|
||||
size += sections * (SIZE / 2);
|
||||
size += sections * LIGHT_LENGTH;
|
||||
|
||||
if (hasSkyLight) {
|
||||
size += sections * LIGHT_LENGTH;
|
||||
}
|
||||
|
||||
if (addBitmask != 0) {
|
||||
size += Integer.bitCount(addBitmask) * (SIZE / 2);
|
||||
}
|
||||
|
||||
if (biomes) {
|
||||
size += 256;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public static int getAddBitMask(Chunk chunk) {
|
||||
int addBitMask = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((chunk.getBitmask() & (1 << i)) != 0) {
|
||||
final ChunkSection section = chunk.getSections()[i];
|
||||
final DataPalette palette = section.palette(PaletteType.BLOCKS);
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
final int id = palette.idAt(j);
|
||||
if ((id >> 12) != 0) {
|
||||
addBitMask |= (1 << i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return addBitMask;
|
||||
}
|
||||
|
||||
public static boolean hasSkyLight(Chunk chunk) {
|
||||
for (ChunkSection section : chunk.getSections()) {
|
||||
if (section != null && section.getLight().hasSkyLight()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -38,6 +38,7 @@ import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracke
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.task.WorldBorderUpdateTask;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.ProtocolStorables;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
||||
@@ -122,8 +123,9 @@ public class Protocol1_8To1_7_6_10 extends BackwardsProtocol<ClientboundPackets1
|
||||
|
||||
@Override
|
||||
public void init(UserConnection connection) {
|
||||
connection.addEntityTracker(this.getClass(), new EntityTracker1_8(connection));
|
||||
connection.addClientWorld(this.getClass(), new ClientWorld());
|
||||
final ProtocolStorables storables = connection.storables(this);
|
||||
storables.setEntityTracker(new EntityTracker1_8(connection));
|
||||
storables.setClientWorld(new ClientWorld());
|
||||
|
||||
connection.put(new InventoryTracker(connection));
|
||||
connection.put(new PlayerSessionStorage(connection));
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.protocol.v1_8to1_7_6_10.data;
|
||||
|
||||
import com.viaversion.viaversion.libs.gson.JsonArray;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
import com.viaversion.viaversion.libs.gson.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChatNewlineRewriter {
|
||||
private static final String[] STYLE_KEYS = {
|
||||
"color",
|
||||
"bold",
|
||||
"italic",
|
||||
"underlined",
|
||||
"strikethrough",
|
||||
"obfuscated",
|
||||
"clickEvent",
|
||||
"hoverEvent",
|
||||
"insertion"
|
||||
};
|
||||
|
||||
public static List<JsonObject> splitChatComponentByNewline(JsonElement element) {
|
||||
return splitChatComponentByNewline(element, new JsonObject());
|
||||
}
|
||||
|
||||
private static List<JsonObject> splitChatComponentByNewline(JsonElement element, JsonObject inheritedStyle) {
|
||||
List<JsonObject> results = new ArrayList<>();
|
||||
if (element == null || element.isJsonNull()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
if (element.isJsonPrimitive()) {
|
||||
String text = element.getAsString();
|
||||
for (String part : text.split("\n", -1)) {
|
||||
JsonObject obj = inheritedStyle.deepCopy();
|
||||
obj.addProperty("text", part);
|
||||
results.add(obj);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
if (element.isJsonArray()) {
|
||||
List<JsonObject> currentLine = new ArrayList<>();
|
||||
JsonObject siblingStyle = inheritedStyle;
|
||||
boolean foundRoot = false;
|
||||
|
||||
for (JsonElement child : element.getAsJsonArray()) {
|
||||
List<JsonObject> splitParts = splitChatComponentByNewline(child, siblingStyle);
|
||||
for (int i = 0; i < splitParts.size(); i++) {
|
||||
currentLine.add(splitParts.get(i));
|
||||
if (i < splitParts.size() - 1) {
|
||||
results.add(mergeLine(currentLine));
|
||||
currentLine.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundRoot && child != null && !child.isJsonNull()) {
|
||||
siblingStyle = rootStyle(inheritedStyle, child);
|
||||
foundRoot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentLine.isEmpty()) {
|
||||
results.add(mergeLine(currentLine));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
if (!element.isJsonObject()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
JsonObject obj = element.getAsJsonObject();
|
||||
|
||||
// Build the style for this node: inherit first, then override with this object's style
|
||||
JsonObject currentStyle = mergeStyle(inheritedStyle, obj);
|
||||
|
||||
// Gather components in order: base text then extra array
|
||||
List<JsonElement> ordered = new ArrayList<>();
|
||||
if (obj.has("text")) {
|
||||
ordered.add(obj.get("text"));
|
||||
}
|
||||
if (obj.has("extra") && obj.get("extra").isJsonArray()) {
|
||||
for (JsonElement extra : obj.getAsJsonArray("extra")) {
|
||||
ordered.add(extra);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no text/extra, return a single object with the style
|
||||
if (ordered.isEmpty()) {
|
||||
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
|
||||
results.add(baseComponent != null ? baseComponent : currentStyle);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Build lines by concatenating split pieces
|
||||
List<JsonObject> currentLine = new ArrayList<>();
|
||||
if (!obj.has("text")) {
|
||||
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
|
||||
if (baseComponent != null) {
|
||||
currentLine.add(baseComponent);
|
||||
}
|
||||
}
|
||||
for (JsonElement part : ordered) {
|
||||
if (part == null || part.isJsonNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part.isJsonPrimitive()) {
|
||||
String text = part.getAsString();
|
||||
String[] split = text.split("\n", -1);
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
JsonObject piece = currentStyle.deepCopy();
|
||||
piece.addProperty("text", split[i]);
|
||||
currentLine.add(piece);
|
||||
|
||||
if (i < split.length - 1) {
|
||||
results.add(mergeLine(currentLine));
|
||||
currentLine.clear();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part.isJsonObject() || part.isJsonArray()) {
|
||||
List<JsonObject> splitParts = splitChatComponentByNewline(part, currentStyle);
|
||||
for (int i = 0; i < splitParts.size(); i++) {
|
||||
currentLine.add(splitParts.get(i));
|
||||
if (i < splitParts.size() - 1) {
|
||||
results.add(mergeLine(currentLine));
|
||||
currentLine.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentLine.isEmpty()) {
|
||||
results.add(mergeLine(currentLine));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static JsonObject rootStyle(JsonObject inheritedStyle, JsonElement element) {
|
||||
if (element == null || element.isJsonNull()) {
|
||||
return inheritedStyle;
|
||||
}
|
||||
if (element.isJsonObject()) {
|
||||
return mergeStyle(inheritedStyle, element.getAsJsonObject());
|
||||
}
|
||||
if (element.isJsonArray()) {
|
||||
for (JsonElement child : element.getAsJsonArray()) {
|
||||
if (child != null && !child.isJsonNull()) {
|
||||
return rootStyle(inheritedStyle, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return inheritedStyle;
|
||||
}
|
||||
|
||||
private static JsonObject mergeStyle(JsonObject inheritedStyle, JsonObject obj) {
|
||||
JsonObject currentStyle = inheritedStyle.deepCopy();
|
||||
for (String key : STYLE_KEYS) {
|
||||
if (obj.has(key)) {
|
||||
currentStyle.add(key, obj.get(key).deepCopy());
|
||||
}
|
||||
}
|
||||
return currentStyle;
|
||||
}
|
||||
|
||||
private static JsonObject copyComponentWithoutExtra(JsonObject obj, JsonObject currentStyle) {
|
||||
JsonObject component = currentStyle.deepCopy();
|
||||
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if ("extra".equals(key) || isStyleKey(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
component.add(key, entry.getValue().deepCopy());
|
||||
}
|
||||
return component.size() == currentStyle.size() ? null : component;
|
||||
}
|
||||
|
||||
private static boolean isStyleKey(String key) {
|
||||
for (String styleKey : STYLE_KEYS) {
|
||||
if (styleKey.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JsonObject mergeLine(List<JsonObject> lineParts) {
|
||||
if (lineParts.isEmpty()) {
|
||||
JsonObject empty = new JsonObject();
|
||||
empty.addProperty("text", "");
|
||||
return empty;
|
||||
}
|
||||
|
||||
JsonObject first = lineParts.get(0);
|
||||
if (lineParts.size() == 1) {
|
||||
return first;
|
||||
}
|
||||
|
||||
JsonObject merged = new JsonObject();
|
||||
merged.addProperty("text", "");
|
||||
|
||||
JsonArray extra = new JsonArray();
|
||||
for (JsonObject linePart : lineParts) {
|
||||
extra.add(linePart);
|
||||
}
|
||||
merged.add("extra", extra);
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+31
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -24,6 +24,7 @@ import com.viaversion.viarewind.api.type.RewindTypes;
|
||||
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
@@ -46,6 +47,8 @@ public class VirtualHologramEntity {
|
||||
private String name = null;
|
||||
private float yaw, pitch;
|
||||
private float headYaw;
|
||||
private byte flags = 0;
|
||||
private byte armorStandFlags = 0;
|
||||
private boolean small = false;
|
||||
private boolean marker = false;
|
||||
private boolean sneaking = false;
|
||||
@@ -99,8 +102,6 @@ public class VirtualHologramEntity {
|
||||
}
|
||||
|
||||
// Filter armor stand data to calculate emulation
|
||||
byte flags = 0;
|
||||
byte armorStandFlags = 0;
|
||||
for (EntityData entityData : entityDataTracker) {
|
||||
if (entityData.id() == 0 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
|
||||
flags = ((Number) entityData.getValue()).byteValue();
|
||||
@@ -120,7 +121,7 @@ public class VirtualHologramEntity {
|
||||
gravity = (armorStandFlags & 0x02) == 0;
|
||||
|
||||
State prevState = currentState;
|
||||
if (invisible && name != null) {
|
||||
if (invisible && name != null && alwaysShowNametag == 1) {
|
||||
currentState = State.HOLOGRAM;
|
||||
} else if (gravity) {
|
||||
currentState = State.ZOMBIE;
|
||||
@@ -167,6 +168,18 @@ public class VirtualHologramEntity {
|
||||
} else {
|
||||
return baseOffset + (0.9875 * 2);
|
||||
}
|
||||
} else if (currentState == State.ZOMBIE_NO_GRAVITY) {
|
||||
// 1.7 ride stack offset sum: Squid (0.95 * 0.75) + Zombie (1.8 * 0.75) = 2.0625
|
||||
// By starting at -2.0625, we zero out the 1.7 passenger height so we can apply 1.8's offsets.
|
||||
double baseOffset = -2.0625;
|
||||
|
||||
if (marker) {
|
||||
// 1.8 Marker ArmorStand height is 0.0 -> mounted offset 0.0
|
||||
return baseOffset;
|
||||
} else {
|
||||
// 1.8 Normal ArmorStand height is 1.975 -> mounted offset 1.975 * 0.75 = 1.48125
|
||||
return baseOffset + 1.48125;
|
||||
}
|
||||
} else {
|
||||
return -0.4;
|
||||
}
|
||||
@@ -291,6 +304,13 @@ public class VirtualHologramEntity {
|
||||
this.entityIds = entityIds;
|
||||
}
|
||||
|
||||
for (int extraId : entityIds) {
|
||||
if (extraId != entityId) {
|
||||
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).setExtraHologramId(entityId, extraId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sendEntityDataUpdate(entityRewriter);
|
||||
if (entityIds == null) {
|
||||
return;
|
||||
@@ -326,6 +346,13 @@ public class VirtualHologramEntity {
|
||||
if (entityIds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int extraId : entityIds) {
|
||||
if (extraId != entityId) {
|
||||
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).removeExtraHologramId(extraId);
|
||||
}
|
||||
}
|
||||
|
||||
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user);
|
||||
despawn.write(Types.BYTE, (byte) entityIds.length);
|
||||
for (int id : entityIds) {
|
||||
|
||||
+1
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -20,7 +20,6 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.CompressionStatusTracker;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.platform.providers.Provider;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
public abstract class CompressionHandlerProvider implements Provider {
|
||||
|
||||
@@ -28,9 +27,6 @@ public abstract class CompressionHandlerProvider implements Provider {
|
||||
|
||||
public abstract void onTransformPacket(UserConnection user);
|
||||
|
||||
public abstract ChannelHandler getEncoder(int threshold);
|
||||
|
||||
public abstract ChannelHandler getDecoder(int threshold);
|
||||
|
||||
public boolean isRemoveCompression(UserConnection user) {
|
||||
return user.get(CompressionStatusTracker.class).removeCompression;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+8
-16
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,17 +17,15 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import java.util.List;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private final Inflater inflater = new Inflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -41,7 +39,9 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (!in.isReadable()) return;
|
||||
if (!in.isReadable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int outLength = Types.VAR_INT.readPrimitive(in);
|
||||
if (outLength == 0) {
|
||||
@@ -55,21 +55,13 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
|
||||
}
|
||||
|
||||
ByteBuf temp = in;
|
||||
if (!in.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
|
||||
} else {
|
||||
in.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(outLength, outLength);
|
||||
ByteBuf output = ctx.alloc().buffer(outLength);
|
||||
try {
|
||||
this.inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + this.inflater.inflate(output.array(), output.arrayOffset(), outLength));
|
||||
ThreadLocalCompressionProvider.inflate(in, output, outLength);
|
||||
out.add(output.retain());
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
this.inflater.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+4
-26
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,15 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
private final Deflater deflater = new Deflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -47,27 +45,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
Types.VAR_INT.writePrimitive(out, frameLength);
|
||||
|
||||
ByteBuf temp = in;
|
||||
if (!in.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
|
||||
} else {
|
||||
in.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
try {
|
||||
this.deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
deflater.finish();
|
||||
|
||||
while (!deflater.finished()) {
|
||||
output.ensureWritable(4096);
|
||||
output.writerIndex(output.writerIndex() + this.deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
|
||||
}
|
||||
out.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
this.deflater.reset();
|
||||
}
|
||||
ThreadLocalCompressionProvider.deflate(in, out);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-12
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -23,7 +23,6 @@ import com.viaversion.viarewind.api.minecraft.netty.ForwardMessageToByteEncoder;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
public class TrackingCompressionHandlerProvider extends CompressionHandlerProvider {
|
||||
@@ -41,7 +40,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
if (encoder != null) {
|
||||
encoder.setThreshold(threshold);
|
||||
} else {
|
||||
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, getEncoder(threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, new CompressionEncoder(threshold));
|
||||
}
|
||||
|
||||
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
|
||||
@@ -49,7 +48,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
if (decoder != null) {
|
||||
decoder.setThreshold(threshold);
|
||||
} else {
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, new CompressionDecoder(threshold));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,13 +75,5 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler getEncoder(int threshold) {
|
||||
return new CompressionEncoder(threshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler getDecoder(int threshold) {
|
||||
return new CompressionDecoder(threshold);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -113,7 +113,7 @@ public class BlockItemPacketRewriter1_8 extends VRBlockItemRewriter<ClientboundP
|
||||
final Item item = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0);
|
||||
playerSession.setPlayerEquipment(wrapper.user().getProtocolInfo().getUuid(), item, 8 - slot);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isSpectator()) { // Spectator mode didn't exist in 1.7.10
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public class BlockItemPacketRewriter1_8 extends VRBlockItemRewriter<ClientboundP
|
||||
if (windowId != 0) {
|
||||
return;
|
||||
}
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final UUID userId = wrapper.user().getProtocolInfo().getUuid();
|
||||
|
||||
final Item[] items = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, 0);
|
||||
|
||||
+20
-15
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -76,7 +76,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
}
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.addPlayer(entityId, wrapper.user().getProtocolInfo().getUuid());
|
||||
|
||||
tracker.setClientEntityGameMode(wrapper.get(Types.UNSIGNED_BYTE, 0));
|
||||
@@ -237,7 +237,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
break;
|
||||
}
|
||||
wrapper.set(RewindTypes.INT_POSITION, 0, new BlockPosition(position.x() + modX, position.y(), position.z() + modZ));
|
||||
addTrackedEntity(wrapper, entityId, EntityTypes1_8.EntityType.PAINTING);
|
||||
tracker(wrapper.user()).addEntity(entityId, EntityTypes1_8.EntityType.PAINTING);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -275,7 +275,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
final byte headYaw = wrapper.get(Types.BYTE, 2);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(entityId);
|
||||
|
||||
hologram.setPosition(x / 32.0, y / 32.0, z / 32.0);
|
||||
@@ -314,7 +314,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (gameProfile != null && gameProfile.gamemode == 3) { // Spectator mode
|
||||
for (short i = 0; i < 5; i++) {
|
||||
final PacketWrapper entityEquipment = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_EQUIPPED_ITEM, wrapper.user());
|
||||
@@ -337,6 +337,11 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.ENTITY_DATA_LIST1_8, RewindTypes.ENTITY_DATA_LIST1_7); // Entity data
|
||||
|
||||
handler(getTrackerAndDataHandler(RewindTypes.ENTITY_DATA_LIST1_7, EntityTypes1_8.EntityType.PLAYER));
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
final EntityTracker1_8 tracker = tracker(wrapper.user());
|
||||
tracker.checkNametagVisibility(entityId);
|
||||
});
|
||||
}
|
||||
});
|
||||
protocol.registerClientbound(ClientboundPackets1_8.SET_EQUIPPED_ITEM, new PacketHandlers() {
|
||||
@@ -349,7 +354,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
handler(wrapper -> protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0)));
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int id = wrapper.get(Types.INT, 0);
|
||||
|
||||
final int limit = tracker.clientEntityId() == id ? 3 : 4;
|
||||
@@ -359,7 +364,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
});
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final short slot = wrapper.get(Types.SHORT, 0);
|
||||
final UUID uuid = tracker.getPlayerUUID(wrapper.get(Types.INT, 0));
|
||||
if (uuid == null) {
|
||||
@@ -391,7 +396,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.VAR_INT, Types.INT); // Collected Entity id
|
||||
map(Types.VAR_INT, Types.INT); // Collector Entity id
|
||||
|
||||
handler(wrapper -> wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class).removeEntity(wrapper.get(Types.INT, 0)));
|
||||
handler(wrapper -> wrapper.user().getEntityTracker(protocol).removeEntity(wrapper.get(Types.INT, 0)));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -419,7 +424,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
read(Types.BOOLEAN); // On ground
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -443,7 +448,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
read(Types.BOOLEAN); // On ground
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -469,7 +474,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
read(Types.BOOLEAN); // On ground
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -501,7 +506,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.INT, 0);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.entityType(entityId) == EntityTypes1_8.EntityType.ABSTRACT_MINECART) { // TODO | Realign all entities?
|
||||
int y = wrapper.get(Types.INT, 2);
|
||||
y += 12;
|
||||
@@ -532,7 +537,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.BYTE); // Head yaw
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -554,7 +559,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
handler(wrapper -> {
|
||||
final boolean leash = wrapper.get(Types.BOOLEAN, 0);
|
||||
if (!leash) {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final int passenger = wrapper.get(Types.INT, 0);
|
||||
final int vehicle = wrapper.get(Types.INT, 1);
|
||||
@@ -589,7 +594,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
public void register() {
|
||||
map(Types.VAR_INT, Types.INT); // Entity id
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.getHolograms().containsKey(wrapper.get(Types.INT, 0))) { // Don't handle properties for hologram emulation
|
||||
wrapper.cancel();
|
||||
return;
|
||||
|
||||
+108
-27
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -30,6 +30,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPa
|
||||
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.ChatItemRewriter;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.ChatNewlineRewriter;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.TitleRenderProvider;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.GameProfileStorage;
|
||||
@@ -37,6 +38,7 @@ import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.InventoryTracker
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.PlayerSessionStorage;
|
||||
import com.viaversion.viarewind.utils.ChatUtil;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
import com.viaversion.viaversion.api.minecraft.GameProfile;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
@@ -46,6 +48,8 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
import com.viaversion.viaversion.api.rewriter.RewriterBase;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
import com.viaversion.viaversion.libs.gson.JsonObject;
|
||||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
|
||||
import com.viaversion.viaversion.util.ComponentUtil;
|
||||
@@ -70,13 +74,27 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
@Override
|
||||
public void register() {
|
||||
map(Types.COMPONENT); // Chat message
|
||||
|
||||
handler(wrapper -> {
|
||||
final JsonElement json = wrapper.get(Types.COMPONENT, 0);
|
||||
ChatItemRewriter.toClient(protocol, wrapper.user(), json);
|
||||
|
||||
final int position = wrapper.read(Types.BYTE);
|
||||
|
||||
if (position == 2) { // Above hotbar
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
ChatItemRewriter.toClient(protocol, wrapper.user(), json);
|
||||
List<JsonObject> splitComponents = ChatNewlineRewriter.splitChatComponentByNewline(json);
|
||||
|
||||
if (splitComponents.size() > 1) {
|
||||
wrapper.cancel();
|
||||
|
||||
for (JsonObject split : splitComponents) {
|
||||
PacketWrapper newWrapper = wrapper.create(ClientboundPackets1_7_2_5.CHAT);
|
||||
newWrapper.write(Types.COMPONENT, split);
|
||||
newWrapper.send(Protocol1_8To1_7_6_10.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -111,13 +129,13 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
}
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.setClientEntityGameMode(wrapper.get(Types.UNSIGNED_BYTE, 1));
|
||||
|
||||
final int dimension = wrapper.get(Types.INT, 0);
|
||||
|
||||
// Clear entities on dimension change and re-track player
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
if (world.setEnvironment(dimension)) {
|
||||
tracker.clearEntities();
|
||||
tracker.addPlayer(tracker.clientEntityId(), wrapper.user().getProtocolInfo().getUuid());
|
||||
@@ -178,7 +196,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
|
||||
wrapper.write(Types.BOOLEAN, playerSession.onGround);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (!Objects.equals(tracker.spectatingClientEntityId, tracker.clientEntityIdOrNull())) {
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -204,7 +222,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
if (wrapper.get(Types.UNSIGNED_BYTE, 0) != 3) return; // Change game mode
|
||||
int gameMode = wrapper.get(Types.FLOAT, 0).intValue();
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (gameMode == 3 || tracker.isSpectator()) {
|
||||
UUID myId = wrapper.user().getProtocolInfo().getUuid();
|
||||
Item[] equipment = new Item[4];
|
||||
@@ -276,7 +294,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
|
||||
if (gamemode == 3 || gameProfile.gamemode == 3) {
|
||||
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
int entityId = tracker.getPlayerEntityId(uuid);
|
||||
boolean isOwnPlayer = entityId == tracker.clientEntityId();
|
||||
if (entityId != -1) {
|
||||
@@ -429,7 +447,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
wrapper.cancel();
|
||||
final int entityId = wrapper.read(Types.VAR_INT);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.spectatingClientEntityId != entityId) {
|
||||
tracker.setSpectating(entityId);
|
||||
}
|
||||
@@ -490,12 +508,18 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
|
||||
protocol.registerServerbound(ServerboundPackets1_7_2_5.CHAT, wrapper -> {
|
||||
final String message = wrapper.passthrough(Types.STRING);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isSpectator() && message.toLowerCase().startsWith("/stp ")) { // TODO add setting
|
||||
String username = message.split(" ")[1];
|
||||
final GameProfileStorage storage = wrapper.user().get(GameProfileStorage.class);
|
||||
|
||||
final GameProfileStorage.GameProfile profile = storage.get(username, true);
|
||||
// Multiple players with the same name may exist. This will only represent
|
||||
// fake players so it's fine to just pick the first one.
|
||||
List<GameProfileStorage.GameProfile> profiles = storage.get(username, true);
|
||||
if (profiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final GameProfileStorage.GameProfile profile = profiles.get(0);
|
||||
if (profile != null && profile.uuid != null) {
|
||||
wrapper.cancel();
|
||||
|
||||
@@ -513,16 +537,20 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
map(Types.BYTE, Types.VAR_INT); // Mode
|
||||
handler(wrapper -> {
|
||||
final int mode = wrapper.get(Types.VAR_INT, 1);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int entityId = tracker.getHologramIdWithExtra(wrapper.get(Types.VAR_INT, 0));
|
||||
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
|
||||
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.set(Types.VAR_INT, 0, entityId);
|
||||
|
||||
if (mode != 0) {
|
||||
return;
|
||||
}
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
|
||||
|
||||
if (!tracker.getHolograms().containsKey(entityId)) {
|
||||
return;
|
||||
}
|
||||
final AABB boundingBox = tracker.getHolograms().get(entityId).getBoundingBox();
|
||||
|
||||
Vector3d pos = new Vector3d(position.getPosX(), position.getPosY() + 1.8, position.getPosZ());
|
||||
@@ -590,6 +618,16 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
protected void register() {
|
||||
map(Types.VAR_INT); // Status
|
||||
map(RewindTypes.U_BYTE_POSITION, Types.BLOCK_POSITION1_8); // Position
|
||||
handler(wrapper -> {
|
||||
// 1.7 uses 255 as "no face"
|
||||
// 1.8 changed this to only be in the range EnumFacing range (0-5) and uses DOWN when no face is used
|
||||
// This properly rewrites invalid values to the 1.8 equivalent
|
||||
int face = wrapper.read(Types.UNSIGNED_BYTE);
|
||||
if (face > 5) {
|
||||
face = 0;
|
||||
}
|
||||
wrapper.write(Types.UNSIGNED_BYTE, (short) face);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -600,6 +638,16 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
map(Types.BYTE); // Direction
|
||||
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Item
|
||||
|
||||
handler(wrapper -> {
|
||||
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
|
||||
final byte direction = wrapper.get(Types.BYTE, 0);
|
||||
// 1.7 serializes the Y component as an unsigned byte, turning the
|
||||
// use-in-air position from (-1, -1, -1) into (-1, 255, -1).
|
||||
// we change that back to -1 for Y like 1.8 clients
|
||||
if (position.x() == -1 && position.y() == 255 && position.z() == -1 && direction == -1) {
|
||||
wrapper.set(Types.BLOCK_POSITION1_8, 0, new BlockPosition(-1, -1, -1));
|
||||
}
|
||||
});
|
||||
handler(wrapper -> protocol.getItemRewriter().handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
|
||||
}
|
||||
});
|
||||
@@ -672,7 +720,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
if (!unmount) {
|
||||
return;
|
||||
}
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.spectatingClientEntityId != tracker.clientEntityId()) {
|
||||
PacketWrapper sneakPacket = PacketWrapper.create(ServerboundPackets1_8.PLAYER_COMMAND, wrapper.user());
|
||||
sneakPacket.write(Types.VAR_INT, tracker.clientEntityId());
|
||||
@@ -752,6 +800,8 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
});
|
||||
|
||||
// 1.7.10 custom payload constructor (String, ByteBuf) uses byteBuf.array(), so it sends the whole backing array capacity, not only the written bytes
|
||||
// After reading the proper packet data we discard the remaining bytes
|
||||
protocol.registerServerbound(ServerboundPackets1_7_2_5.CUSTOM_PAYLOAD, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -762,7 +812,27 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
switch (channel) {
|
||||
case "MC|TrSel": {
|
||||
wrapper.passthrough(Types.INT);
|
||||
wrapper.read(Types.REMAINING_BYTES); // unused data ???
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|AdvCdm": {
|
||||
final byte commandBlockType = wrapper.passthrough(Types.BYTE);
|
||||
if (commandBlockType == 0) { // Block
|
||||
wrapper.passthrough(Types.INT); // X
|
||||
wrapper.passthrough(Types.INT); // Y
|
||||
wrapper.passthrough(Types.INT); // Z
|
||||
} else if (commandBlockType == 1) { // Minecart
|
||||
wrapper.passthrough(Types.INT); // Entity id
|
||||
}
|
||||
wrapper.passthrough(Types.STRING); // Command
|
||||
wrapper.write(Types.BOOLEAN, true); // Track output, added in 1.8, default to true
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|Beacon": {
|
||||
wrapper.passthrough(Types.INT); // Primary effect
|
||||
wrapper.passthrough(Types.INT); // Secondary effect
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|ItemName": {
|
||||
@@ -779,17 +849,28 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
case "MC|BEdit":
|
||||
case "MC|BSign": {
|
||||
final boolean signBook = channel.equals("MC|BSign");
|
||||
final Item book = wrapper.read(RewindTypes.COMPRESSED_NBT_ITEM);
|
||||
CompoundTag tag = book.tag();
|
||||
if (tag != null && tag.contains("pages")) {
|
||||
ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = ComponentUtil.legacyToJsonString(value);
|
||||
page.setValue(value);
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
|
||||
if (book == null) {
|
||||
wrapper.write(Types.ITEM1_8, null);
|
||||
break;
|
||||
}
|
||||
|
||||
final CompoundTag tag = book.tag();
|
||||
if (signBook && tag != null && tag.contains("pages")) {
|
||||
final ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
if (pages != null) {
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
final StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = new JsonPrimitive(value).toString();
|
||||
page.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
protocol.getItemRewriter().handleItemToServer(wrapper.user(), book);
|
||||
wrapper.write(Types.ITEM1_8, book);
|
||||
break;
|
||||
}
|
||||
|
||||
+21
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
@@ -173,6 +174,7 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
}
|
||||
|
||||
final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final byte mode = wrapper.passthrough(Types.BYTE);
|
||||
if (mode != 0 && !scoreboard.teamExists(team)) {
|
||||
@@ -190,6 +192,10 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
if (mode == 0) {
|
||||
scoreboard.addTeam(team);
|
||||
} else if (mode == 1) {
|
||||
// Team removed, nametag visibility might have changed
|
||||
for (final String member : scoreboard.getTeamMembers(team)) {
|
||||
tracker.checkNametagVisbility(member);
|
||||
}
|
||||
scoreboard.removeTeam(team);
|
||||
}
|
||||
|
||||
@@ -198,7 +204,9 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
wrapper.passthrough(Types.STRING); // Prefix
|
||||
wrapper.passthrough(Types.STRING); // Suffix
|
||||
wrapper.passthrough(Types.BYTE); // Friendly fire
|
||||
wrapper.read(Types.STRING); // Name tag visibility
|
||||
final String nameTagVisibility = wrapper.read(Types.STRING);
|
||||
final String previousVisibility = scoreboard.getTeamNameTagVisibility(team);
|
||||
scoreboard.setTeamNameTagVisibility(team, nameTagVisibility);
|
||||
byte color = wrapper.read(Types.BYTE);
|
||||
if (mode == 2 && scoreboard.getTeamColor(team).get() != color) {
|
||||
final String sidebar = scoreboard.getColorDependentSidebar().get(color);
|
||||
@@ -209,6 +217,13 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
|
||||
}
|
||||
scoreboard.setTeamColor(team, color);
|
||||
|
||||
// Re-evaluate nametag visibility for all team members when visibility changes
|
||||
if (mode == 2 && !nameTagVisibility.equals(previousVisibility)) {
|
||||
for (final String member : scoreboard.getTeamMembers(team)) {
|
||||
tracker.checkNametagVisbility(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode == 0 || mode == 3 || mode == 4) {
|
||||
byte color = scoreboard.getTeamColor(team).get();
|
||||
@@ -223,6 +238,8 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
continue;
|
||||
}
|
||||
scoreboard.removePlayerFromTeam(entry, team);
|
||||
// Player left team, nametag visibility may change
|
||||
tracker.checkNametagVisbility(entry);
|
||||
if (entry.equals(username)) {
|
||||
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
|
||||
sidebarPacket.write(Types.BYTE, (byte) 1);
|
||||
@@ -231,6 +248,8 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
|
||||
}
|
||||
} else {
|
||||
scoreboard.addPlayerToTeam(entry, team);
|
||||
// Player joined team, nametag visibility may change
|
||||
tracker.checkNametagVisbility(entry);
|
||||
if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) {
|
||||
final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
|
||||
displayObjective.write(Types.BYTE, (byte) 1);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -47,7 +47,7 @@ public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_CHUNK, wrapper -> {
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
final Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(world.getEnvironment()));
|
||||
protocol.getItemRewriter().handleChunk(chunk);
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+127
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -22,6 +22,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPa
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.VirtualHologramEntity;
|
||||
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_8;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
@@ -37,18 +38,26 @@ import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
|
||||
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
|
||||
import com.viaversion.viarewind.api.type.RewindTypes;
|
||||
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
|
||||
|
||||
public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
|
||||
private final Int2ObjectMap<VirtualHologramEntity> holograms = new Int2ObjectArrayMap<>();
|
||||
private final Int2IntMap extraHologramIds = new Int2IntArrayMap();
|
||||
private final Int2IntMap vehicles = new Int2IntArrayMap();
|
||||
private final Int2ObjectMap<UUID> entityIdToUUID = new Int2ObjectArrayMap<>();
|
||||
private final Object2IntMap<UUID> entityUUIDToId = new Object2IntOpenHashMap<>();
|
||||
private final Int2IntMap playerNametagHiderEntities = new Int2IntArrayMap();
|
||||
|
||||
private final List<EntityData> entityData = new ArrayList<>();
|
||||
|
||||
@@ -60,35 +69,41 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntity(int id, EntityType type) {
|
||||
super.addEntity(id, type);
|
||||
public TrackedEntity addEntity(int id, EntityType type) {
|
||||
if (type == EntityTypes1_8.EntityType.ARMOR_STAND) {
|
||||
holograms.put(id, new VirtualHologramEntity(user(), id));
|
||||
}
|
||||
return super.addEntity(id, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int entityId) {
|
||||
super.removeEntity(entityId);
|
||||
|
||||
public TrackedEntity removeEntity(int entityId) {
|
||||
final VirtualHologramEntity hologram = holograms.get(entityId);
|
||||
if (hologram != null) {
|
||||
hologram.deleteEntity();
|
||||
holograms.remove(entityId);
|
||||
}
|
||||
|
||||
if (playerNametagHiderEntities.containsKey(entityId)) {
|
||||
despawnNametagHiderEntity(entityId);
|
||||
}
|
||||
|
||||
if (entityIdToUUID.containsKey(entityId)) {
|
||||
final UUID playerId = entityIdToUUID.remove(entityId);
|
||||
|
||||
entityUUIDToId.removeInt(playerId);
|
||||
user().get(PlayerSessionStorage.class).getPlayerEquipment().remove(playerId);
|
||||
}
|
||||
return super.removeEntity(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearEntities() {
|
||||
super.clearEntities();
|
||||
holograms.clear();
|
||||
extraHologramIds.clear();
|
||||
vehicles.clear();
|
||||
playerNametagHiderEntities.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,6 +170,11 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
} else {
|
||||
vehicles.put(vehicleId, passengerId);
|
||||
}
|
||||
|
||||
// Re-evaluate nametag visibility when a player entity's passenger changes
|
||||
if (vehicleId != -1 && entityIdToUUID.containsKey(vehicleId)) {
|
||||
checkNametagVisibility(vehicleId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void attachEntity(final int target) {
|
||||
@@ -185,10 +205,111 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
}
|
||||
}
|
||||
|
||||
public void checkNametagVisibility(final int entityId) {
|
||||
if (!entityIdToUUID.containsKey(entityId)) {
|
||||
return;
|
||||
}
|
||||
final boolean shouldHide = isPlayerNametagHidden(entityId);
|
||||
final boolean hasServerPassenger = getPassenger(entityId) != -1;
|
||||
final boolean hasSkull = playerNametagHiderEntities.containsKey(entityId);
|
||||
|
||||
if (shouldHide && !hasServerPassenger && !hasSkull) {
|
||||
spawnNametagHiderEntity(entityId);
|
||||
} else if ((!shouldHide || hasServerPassenger) && hasSkull) {
|
||||
despawnNametagHiderEntity(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkNametagVisbility(final String username) {
|
||||
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
|
||||
final List<GameProfileStorage.GameProfile> profiles = profileStorage.get(username, false);
|
||||
|
||||
for (GameProfileStorage.GameProfile profile : profiles) {
|
||||
final int entityId = getPlayerEntityId(profile.uuid);
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNametagVisibility(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPlayerNametagHidden(final int entityId) {
|
||||
final UUID uuid = entityIdToUUID.get(entityId);
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
|
||||
final GameProfileStorage.GameProfile profile = profileStorage.get(uuid);
|
||||
if (profile == null) {
|
||||
return false;
|
||||
}
|
||||
return user().get(ScoreboardTracker.class).isNametagHidden(profile.name);
|
||||
}
|
||||
|
||||
private int getNametagHiderEntityId(final int playerEntityId) {
|
||||
return Integer.MAX_VALUE - 32000 - playerEntityId;
|
||||
}
|
||||
|
||||
private void spawnNametagHiderEntity(final int playerEntityId) {
|
||||
final int entityId = getNametagHiderEntityId(playerEntityId);
|
||||
playerNametagHiderEntities.put(playerEntityId, entityId);
|
||||
|
||||
final List<EntityData> mobData = new ArrayList<>();
|
||||
mobData.add(new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0x20));
|
||||
mobData.add(new EntityData(16, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
|
||||
|
||||
final PacketWrapper spawnMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user());
|
||||
spawnMob.write(Types.VAR_INT, entityId);
|
||||
spawnMob.write(Types.UNSIGNED_BYTE, (short) EntityTypes1_8.EntityType.MAGMA_CUBE.getId());
|
||||
spawnMob.write(Types.INT, 0); // X
|
||||
spawnMob.write(Types.INT, 0); // Y
|
||||
spawnMob.write(Types.INT, 0); // Z
|
||||
spawnMob.write(Types.BYTE, (byte) 0); // Yaw
|
||||
spawnMob.write(Types.BYTE, (byte) 0); // Pitch
|
||||
spawnMob.write(Types.BYTE, (byte) 0); // Head yaw
|
||||
spawnMob.write(Types.SHORT, (short) 0); // Velocity x
|
||||
spawnMob.write(Types.SHORT, (short) 0); // Velocity y
|
||||
spawnMob.write(Types.SHORT, (short) 0); // Velocity z
|
||||
spawnMob.write(RewindTypes.ENTITY_DATA_LIST1_7, mobData);
|
||||
spawnMob.scheduleSend(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user());
|
||||
attach.write(Types.INT, entityId);
|
||||
attach.write(Types.INT, playerEntityId);
|
||||
attach.write(Types.BOOLEAN, false);
|
||||
attach.scheduleSend(Protocol1_8To1_7_6_10.class);
|
||||
}
|
||||
|
||||
private void despawnNametagHiderEntity(final int playerEntityId) {
|
||||
if (!playerNametagHiderEntities.containsKey(playerEntityId)) return;
|
||||
final int mobId = playerNametagHiderEntities.remove(playerEntityId);
|
||||
|
||||
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user());
|
||||
despawn.write(Types.BYTE, (byte) 1);
|
||||
despawn.write(Types.INT, mobId);
|
||||
despawn.scheduleSend(Protocol1_8To1_7_6_10.class);
|
||||
}
|
||||
|
||||
public Int2ObjectMap<VirtualHologramEntity> getHolograms() {
|
||||
return holograms;
|
||||
}
|
||||
|
||||
public void setExtraHologramId(final int entityId, final int extraId) {
|
||||
extraHologramIds.put(extraId, entityId);
|
||||
}
|
||||
|
||||
public void removeExtraHologramId(int extraId) {
|
||||
extraHologramIds.remove(extraId);
|
||||
}
|
||||
|
||||
public int getHologramIdWithExtra(final int id) {
|
||||
if (holograms.containsKey(id)) {
|
||||
return id;
|
||||
}
|
||||
return extraHologramIds.getOrDefault(id, -1);
|
||||
}
|
||||
|
||||
public boolean isSpectator() {
|
||||
return clientEntityGameMode == 3;
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
@@ -34,6 +38,7 @@ import java.util.UUID;
|
||||
|
||||
public class GameProfileStorage extends StoredObject {
|
||||
private final Map<UUID, GameProfile> properties = new HashMap<>();
|
||||
private final ListMultimap<String, GameProfile> nameToProfile = ArrayListMultimap.create();
|
||||
|
||||
public GameProfileStorage(UserConnection user) {
|
||||
super(user);
|
||||
@@ -42,6 +47,7 @@ public class GameProfileStorage extends StoredObject {
|
||||
public GameProfile put(UUID uuid, String name, String displayName, int ping, int gamemode) {
|
||||
GameProfile gameProfile = new GameProfile(uuid, name, displayName, ping, gamemode);
|
||||
properties.put(uuid, gameProfile);
|
||||
nameToProfile.put(name, gameProfile);
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
@@ -49,19 +55,9 @@ public class GameProfileStorage extends StoredObject {
|
||||
return properties.get(uuid);
|
||||
}
|
||||
|
||||
public GameProfile get(String name, boolean ignoreCase) {
|
||||
public List<GameProfile> get(String name, boolean ignoreCase) {
|
||||
if (ignoreCase) name = name.toLowerCase();
|
||||
|
||||
for (GameProfile profile : properties.values()) {
|
||||
if (profile.name == null) continue;
|
||||
|
||||
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
|
||||
|
||||
if (n.equals(name)) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return nameToProfile.get(name);
|
||||
}
|
||||
|
||||
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
|
||||
@@ -81,7 +77,11 @@ public class GameProfileStorage extends StoredObject {
|
||||
}
|
||||
|
||||
public GameProfile remove(UUID uuid) {
|
||||
return properties.remove(uuid);
|
||||
GameProfile removedProfile = properties.remove(uuid);
|
||||
if (removedProfile != null) {
|
||||
nameToProfile.get(removedProfile.name).remove(removedProfile);
|
||||
}
|
||||
return removedProfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+58
-29
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -37,6 +37,7 @@ public class ScoreboardTracker extends StoredObject {
|
||||
private final HashMap<String, Byte> teamColors = new HashMap<>();
|
||||
private final HashSet<String> scoreTeamNames = new HashSet<>();
|
||||
private final HashMap<Byte, String> colorDependentSidebar = new HashMap<>();
|
||||
private final HashMap<String, String> teamNameTagVisibilities = new HashMap<>();
|
||||
private String colorIndependentSidebar;
|
||||
|
||||
public ScoreboardTracker(UserConnection user) {
|
||||
@@ -63,6 +64,7 @@ public class ScoreboardTracker extends StoredObject {
|
||||
teams.remove(team);
|
||||
scoreTeams.remove(team);
|
||||
teamColors.remove(team);
|
||||
teamNameTagVisibilities.remove(team);
|
||||
}
|
||||
|
||||
public boolean teamExists(String team) {
|
||||
@@ -98,6 +100,27 @@ public class ScoreboardTracker extends StoredObject {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void setTeamNameTagVisibility(String team, String visibility) {
|
||||
teamNameTagVisibilities.put(team, visibility);
|
||||
}
|
||||
|
||||
public String getTeamNameTagVisibility(String team) {
|
||||
return teamNameTagVisibilities.getOrDefault(team, "always");
|
||||
}
|
||||
|
||||
public boolean isNametagHidden(String username) {
|
||||
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
|
||||
if (entry.getValue().contains(username)) {
|
||||
return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always"));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<String> getTeamMembers(String team) {
|
||||
return teams.getOrDefault(team, new ArrayList<>());
|
||||
}
|
||||
|
||||
public void addObjective(String name) {
|
||||
objectives.add(name);
|
||||
}
|
||||
@@ -117,37 +140,43 @@ public class ScoreboardTracker extends StoredObject {
|
||||
public String sendTeamForScore(String score) {
|
||||
if (score.length() <= 16) return score;
|
||||
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
|
||||
int l = 16;
|
||||
int i = Math.min(16, score.length() - 16);
|
||||
String name = score.substring(i, i + l);
|
||||
while (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
i--;
|
||||
while (score.length() - l - i > 16) {
|
||||
l--;
|
||||
if (l < 1) return score;
|
||||
i = Math.min(16, score.length() - l);
|
||||
|
||||
// Find the longest window score[i, i+l) that:
|
||||
// - is not already claimed as a team name
|
||||
// - keeps prefix (score[0,i)) at most 16 chars i <= 16
|
||||
// - keeps suffix (score[i+l, end)) at most 16 chars score.length()-l-i <= 16
|
||||
for (int l = 16; l >= 1; l--) {
|
||||
final int iMax = Math.min(16, score.length() - l);
|
||||
final int iMin = Math.max(0, score.length() - l - 16);
|
||||
for (int i = iMax; i >= iMin; i--) {
|
||||
final String name = score.substring(i, i + l);
|
||||
if (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String prefix = score.substring(0, i);
|
||||
final String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
final ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
}
|
||||
name = score.substring(i, i + l);
|
||||
}
|
||||
String prefix = score.substring(0, i);
|
||||
String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
return score;
|
||||
}
|
||||
|
||||
public String removeTeamForScore(String score) {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -35,6 +35,9 @@ public class WorldBorderUpdateTask implements Runnable {
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final WorldBorderEmulator worldBorderEmulatorTracker = connection.get(WorldBorderEmulator.class);
|
||||
if (worldBorderEmulatorTracker == null) {
|
||||
continue;
|
||||
}
|
||||
if (!worldBorderEmulatorTracker.isInit()) continue;
|
||||
|
||||
final PlayerSessionStorage playerSession = connection.get(PlayerSessionStorage.class);
|
||||
|
||||
+16
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -18,21 +18,25 @@
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8;
|
||||
|
||||
import com.viaversion.viabackwards.api.BackwardsProtocol;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.RewindMappingData1_8;
|
||||
import com.viaversion.viarewind.api.data.RewindMappingData;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.BlockItemPacketRewriter1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.EntityPacketRewriter1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.PlayerPacketRewriter1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.WorldPacketRewriter1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BlockPlaceDestroyTracker;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BossBarStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CooldownStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LevitationStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.PlayerPositionTracker;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.task.CooldownIndicatorTask;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.task.LevitationUpdateTask;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.ProtocolStorables;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
||||
@@ -50,7 +54,7 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
public static final ValueTransformer<Double, Integer> DOUBLE_TO_INT_TIMES_32 = new ValueTransformer<>(Types.INT) {
|
||||
@Override
|
||||
public Integer transform(PacketWrapper wrapper, Double inputValue) {
|
||||
return (int) (inputValue * 32.0D);
|
||||
return (int) Math.round(inputValue * 32.0D);
|
||||
}
|
||||
};
|
||||
public static final ValueTransformer<Float, Byte> DEGREES_TO_ANGLE = new ValueTransformer<>(Types.BYTE) {
|
||||
@@ -60,7 +64,7 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
}
|
||||
};
|
||||
|
||||
public static final RewindMappingData1_8 MAPPINGS = new RewindMappingData1_8();
|
||||
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9", "1.8");
|
||||
|
||||
private final BlockItemPacketRewriter1_9 itemRewriter = new BlockItemPacketRewriter1_9(this);
|
||||
private final EntityPacketRewriter1_9 entityRewriter = new EntityPacketRewriter1_9(this);
|
||||
@@ -79,25 +83,30 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
|
||||
@Override
|
||||
public void init(UserConnection connection) {
|
||||
connection.addEntityTracker(this.getClass(), new EntityTracker1_9(connection));
|
||||
connection.addClientWorld(this.getClass(), new ClientWorld());
|
||||
final ProtocolStorables storables = connection.storables(this);
|
||||
storables.setEntityTracker(new EntityTracker1_9(connection));
|
||||
storables.setClientWorld(new ClientWorld());
|
||||
|
||||
connection.put(new WindowTracker(connection));
|
||||
connection.put(new LevitationStorage());
|
||||
connection.put(new PlayerPositionTracker());
|
||||
connection.put(new CooldownStorage());
|
||||
connection.put(new LastTitle());
|
||||
connection.put(new BlockPlaceDestroyTracker());
|
||||
connection.put(new BossBarStorage(connection));
|
||||
connection.put(new CommandBlockStateStorage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ViaProviders providers) {
|
||||
providers.register(InventoryProvider.class, new InventoryProvider());
|
||||
|
||||
Via.getManager().getScheduler().scheduleRepeating(new LevitationUpdateTask(), 0L, 50L, TimeUnit.MILLISECONDS);
|
||||
Via.getManager().getScheduler().scheduleRepeating(new CooldownIndicatorTask(), 0L, 50L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RewindMappingData1_8 getMappingData() {
|
||||
public RewindMappingData getMappingData() {
|
||||
return MAPPINGS;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+66
-21
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -18,21 +18,23 @@
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TitleCooldownVisualization implements CooldownVisualization {
|
||||
|
||||
private static final int ACTION_SET_TITLE = 0;
|
||||
private static final int ACTION_SET_SUBTITLE = 1;
|
||||
private static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
|
||||
private static final int ACTION_HIDE = 3;
|
||||
public static final int ACTION_SET_TITLE = 0;
|
||||
public static final int ACTION_SET_SUBTITLE = 1;
|
||||
public static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
|
||||
public static final int ACTION_HIDE = 3;
|
||||
public static final int ACTION_RESET = 4;
|
||||
|
||||
private final UserConnection user;
|
||||
private boolean visible;
|
||||
|
||||
public TitleCooldownVisualization(UserConnection user) {
|
||||
this.user = user;
|
||||
@@ -40,28 +42,71 @@ public class TitleCooldownVisualization implements CooldownVisualization {
|
||||
|
||||
@Override
|
||||
public void show(double progress) throws Exception {
|
||||
final String text = CooldownVisualization.buildProgressText("˙", progress);
|
||||
final LastTitle title = this.user.get(LastTitle.class);
|
||||
if (this.visible && title.isVisible()) {
|
||||
this.restoreTitle(title);
|
||||
return;
|
||||
}
|
||||
|
||||
sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive("")));
|
||||
sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(text)));
|
||||
sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> {
|
||||
wrapper.write(Types.INT, 0);
|
||||
wrapper.write(Types.INT, 2);
|
||||
wrapper.write(Types.INT, 5);
|
||||
});
|
||||
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
|
||||
setTitle.write(Types.COMPONENT, new JsonPrimitive(""));
|
||||
setTitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
|
||||
setSubtitle.write(Types.COMPONENT, new JsonPrimitive(CooldownVisualization.buildProgressText("˙", progress)));
|
||||
setSubtitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
|
||||
setTitleTimes.write(Types.INT, 0);
|
||||
setTitleTimes.write(Types.INT, 2);
|
||||
setTitleTimes.write(Types.INT, 5);
|
||||
setTitleTimes.send(Protocol1_9To1_8.class);
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() throws Exception {
|
||||
sendTitlePacket(ACTION_HIDE, wrapper -> {
|
||||
});
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LastTitle title = this.user.get(LastTitle.class);
|
||||
if (title.isVisible()) {
|
||||
this.restoreTitle(title);
|
||||
return;
|
||||
}
|
||||
|
||||
final PacketWrapper hideTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
hideTitle.write(Types.VAR_INT, ACTION_HIDE);
|
||||
hideTitle.send(Protocol1_9To1_8.class);
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
private void sendTitlePacket(int action, Consumer<PacketWrapper> writer) {
|
||||
final PacketWrapper title = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
title.write(Types.VAR_INT, action);
|
||||
writer.accept(title);
|
||||
title.scheduleSend(Protocol1_9To1_8.class);
|
||||
private void restoreTitle(final LastTitle title) {
|
||||
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
|
||||
final int[] times = title.remainingTimes();
|
||||
setTitleTimes.write(Types.INT, times[0]);
|
||||
setTitleTimes.write(Types.INT, times[1]);
|
||||
setTitleTimes.write(Types.INT, times[2]);
|
||||
setTitleTimes.send(Protocol1_9To1_8.class);
|
||||
|
||||
if (title.subtitle() != null) {
|
||||
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
|
||||
setSubtitle.write(Types.COMPONENT, title.subtitle());
|
||||
setSubtitle.send(Protocol1_9To1_8.class);
|
||||
}
|
||||
|
||||
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
|
||||
setTitle.write(Types.COMPONENT, title.title());
|
||||
setTitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.protocol.v1_9to1_8.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.NumberTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
|
||||
public final class CommandBlockState {
|
||||
|
||||
public static final int COMMAND_BLOCK_COMMAND_LIMIT = 32500;
|
||||
|
||||
private static final String IMPULSE = "IMPULSE";
|
||||
private static final String REPEAT = "REPEAT";
|
||||
private static final String CHAIN = "CHAIN";
|
||||
private static final String CONDITIONAL = "CONDITIONAL";
|
||||
private static final String UNCONDITIONAL = "UNCONDITIONAL";
|
||||
private static final String ALWAYS_ACTIVE = "ALWAYSACTIVE";
|
||||
private static final String REDSTONE = "REDSTONE";
|
||||
|
||||
private CommandBlockState() {
|
||||
}
|
||||
|
||||
public static boolean isCommandBlock(final int blockState) {
|
||||
return isImpulse(blockState) || isRepeat(blockState) || isChain(blockState);
|
||||
}
|
||||
|
||||
public static void decorateCommand(final CompoundTag tag, final int blockState) {
|
||||
if (tag == null || !isCommandBlock(blockState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final StringTag commandTag = tag.getStringTag("Command");
|
||||
if (commandTag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Mode mode = mode(blockState);
|
||||
final boolean conditional = (blockState & 8) != 0;
|
||||
final boolean automatic = booleanTag(tag, "auto");
|
||||
if (mode == Mode.REDSTONE && !conditional && !automatic) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String prefix = mode.prefix + ' ' + (conditional ? CONDITIONAL : UNCONDITIONAL) + ' ' + (automatic ? ALWAYS_ACTIVE : REDSTONE) + ' ';
|
||||
final String command = commandTag.getValue();
|
||||
if (!parseDecoratedCommand(command).prefixed() && prefix.length() + command.length() <= COMMAND_BLOCK_COMMAND_LIMIT) {
|
||||
commandTag.setValue(prefix + command);
|
||||
}
|
||||
}
|
||||
|
||||
public static ParsedCommand parseDecoratedCommand(final String command) {
|
||||
final int first = command.indexOf(' ');
|
||||
if (first == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
final int second = command.indexOf(' ', first + 1);
|
||||
if (second == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
final int third = command.indexOf(' ', second + 1);
|
||||
if (third == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final Mode mode = Mode.fromPrefix(command.substring(0, first));
|
||||
if (mode == null) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final String conditionalToken = command.substring(first + 1, second);
|
||||
final boolean conditional;
|
||||
if (CONDITIONAL.equalsIgnoreCase(conditionalToken)) {
|
||||
conditional = true;
|
||||
} else if (UNCONDITIONAL.equalsIgnoreCase(conditionalToken)) {
|
||||
conditional = false;
|
||||
} else {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final String automaticToken = command.substring(second + 1, third);
|
||||
final boolean automatic;
|
||||
if (ALWAYS_ACTIVE.equalsIgnoreCase(automaticToken)) {
|
||||
automatic = true;
|
||||
} else if (REDSTONE.equalsIgnoreCase(automaticToken)) {
|
||||
automatic = false;
|
||||
} else {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
return new ParsedCommand(command.substring(third + 1), mode.serverName, conditional, automatic, true);
|
||||
}
|
||||
|
||||
private static boolean booleanTag(final CompoundTag tag, final String key) {
|
||||
final NumberTag numberTag = tag.getNumberTag(key);
|
||||
return numberTag != null && numberTag.asByte() != 0;
|
||||
}
|
||||
|
||||
private static Mode mode(final int blockState) {
|
||||
if (isRepeat(blockState)) {
|
||||
return Mode.AUTO;
|
||||
}
|
||||
if (isChain(blockState)) {
|
||||
return Mode.SEQUENCE;
|
||||
}
|
||||
return Mode.REDSTONE;
|
||||
}
|
||||
|
||||
private static boolean isImpulse(final int blockState) {
|
||||
return blockState >= 2192 && blockState <= 2207;
|
||||
}
|
||||
|
||||
private static boolean isRepeat(final int blockState) {
|
||||
return blockState >= 3360 && blockState <= 3375;
|
||||
}
|
||||
|
||||
private static boolean isChain(final int blockState) {
|
||||
return blockState >= 3376 && blockState <= 3391;
|
||||
}
|
||||
|
||||
public record ParsedCommand(String command, String mode, boolean conditional, boolean automatic, boolean prefixed) {
|
||||
|
||||
private static ParsedCommand defaultState(final String command) {
|
||||
return new ParsedCommand(command, Mode.REDSTONE.serverName, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Mode {
|
||||
REDSTONE(IMPULSE, "REDSTONE"),
|
||||
AUTO(REPEAT, "AUTO"),
|
||||
SEQUENCE(CHAIN, "SEQUENCE");
|
||||
|
||||
private final String prefix;
|
||||
private final String serverName;
|
||||
|
||||
Mode(final String prefix, final String serverName) {
|
||||
this.prefix = prefix;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
private static Mode fromPrefix(final String prefix) {
|
||||
for (final Mode mode : values()) {
|
||||
if (mode.prefix.equalsIgnoreCase(prefix)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viarewind.api.data.RewindMappingDataLoader;
|
||||
import com.viaversion.viaversion.libs.fastutil.objects.ObjectArrayList;
|
||||
import com.viaversion.viaversion.libs.fastutil.objects.ObjectList;
|
||||
import com.viaversion.viaversion.libs.gson.JsonArray;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
|
||||
public final class RewindMappingData1_8 extends com.viaversion.viarewind.api.data.RewindMappingData {
|
||||
|
||||
private final ObjectList<String> sounds = new ObjectArrayList<>();
|
||||
|
||||
public RewindMappingData1_8() {
|
||||
super("1.9.4", "1.8");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadExtras(CompoundTag data) {
|
||||
super.loadExtras(data);
|
||||
|
||||
final JsonArray sounds = RewindMappingDataLoader.INSTANCE.loadData("sounds-1.9.json").getAsJsonArray("sounds");
|
||||
for (final JsonElement sound : sounds) {
|
||||
this.sounds.add(sound.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public String soundName(final int soundId) {
|
||||
return sounds.get(soundId);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.protocol.v1_9to1_8.provider;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.platform.providers.Provider;
|
||||
|
||||
public class InventoryProvider implements Provider {
|
||||
|
||||
public boolean hasElytra(final UserConnection connection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+101
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteTag;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
@@ -26,8 +27,11 @@ import com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;
|
||||
import com.viaversion.viarewind.api.rewriter.VRBlockItemRewriter;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.PotionIdMappings1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
@@ -59,8 +63,26 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
registerBlockChange(ClientboundPackets1_9.BLOCK_UPDATE);
|
||||
registerMultiBlockChange(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE);
|
||||
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_UPDATE, wrapper -> {
|
||||
final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_8);
|
||||
final int blockState = wrapper.passthrough(Types.VAR_INT);
|
||||
wrapper.user().get(CommandBlockStateStorage.class).storeOrRemove(position, blockState);
|
||||
wrapper.set(Types.VAR_INT, 0, handleBlockId(blockState));
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE, wrapper -> {
|
||||
final int chunkX = wrapper.passthrough(Types.INT);
|
||||
final int chunkZ = wrapper.passthrough(Types.INT);
|
||||
final BlockChangeRecord[] records = wrapper.passthrough(Types.BLOCK_CHANGE_ARRAY);
|
||||
final CommandBlockStateStorage storage = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
for (BlockChangeRecord record : records) {
|
||||
final int blockState = record.getBlockId();
|
||||
final BlockPosition position = new BlockPosition((chunkX << 4) + record.getSectionX(), record.getY(), (chunkZ << 4) + record.getSectionZ());
|
||||
storage.storeOrRemove(position, blockState);
|
||||
record.setBlockId(handleBlockId(blockState));
|
||||
}
|
||||
});
|
||||
registerSetCreativeModeSlot(ServerboundPackets1_8.SET_CREATIVE_MODE_SLOT);
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_CLOSE, wrapper -> {
|
||||
@@ -78,8 +100,12 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
protocol.registerClientbound(ClientboundPackets1_9.OPEN_SCREEN, wrapper -> {
|
||||
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
|
||||
final String windowType = wrapper.passthrough(Types.STRING);
|
||||
wrapper.passthrough(Types.COMPONENT); // title
|
||||
final short slotCount = wrapper.passthrough(Types.UNSIGNED_BYTE);
|
||||
|
||||
wrapper.user().get(WindowTracker.class).put(windowId, windowType);
|
||||
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
|
||||
tracker.put(windowId, windowType);
|
||||
tracker.openWindow(windowId, windowType, slotCount);
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_CONTENT, wrapper -> {
|
||||
@@ -115,10 +141,49 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
final Item item = wrapper.passthrough(Types.ITEM1_8);
|
||||
|
||||
handleItemToClient(wrapper.user(), item);
|
||||
if (windowId == -2) {
|
||||
// 1.8 has no window -2 (set a player-inv slot by raw index, ignoring the open container) — retarget it.
|
||||
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
|
||||
final short openWindow = tracker.openWindowId();
|
||||
if (openWindow != 0) {
|
||||
// Foreign container open: 1.8 only applies player-inv changes through it (main=size.., hotbar=size+27..).
|
||||
final int size = tracker.openWindowSize();
|
||||
final int containerSlot;
|
||||
if (slot >= 0 && slot <= 8) {
|
||||
containerSlot = size + 27 + slot;
|
||||
} else if (slot >= 9 && slot <= 35) {
|
||||
containerSlot = size + slot - 9;
|
||||
} else { // armor/offhand: no slot in a foreign container
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
wrapper.set(Types.BYTE, 0, (byte) openWindow);
|
||||
wrapper.set(Types.SHORT, 0, (short) containerSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise window 0 with the menu slot.
|
||||
final int windowSlot;
|
||||
if (slot >= 0 && slot <= 8) {
|
||||
windowSlot = slot + 36;
|
||||
} else if (slot >= 9 && slot <= 35) {
|
||||
windowSlot = slot;
|
||||
} else if (slot >= 36 && slot <= 39) {
|
||||
windowSlot = 44 - slot;
|
||||
} else { // offhand/body/saddle: no 1.8 equivalent
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
wrapper.set(Types.BYTE, 0, (byte) 0);
|
||||
wrapper.set(Types.SHORT, 0, (short) windowSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (windowId == 0 && slot == 45) {
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);
|
||||
final String windowType = windowTracker.get(windowId);
|
||||
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
|
||||
@@ -194,6 +259,7 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
@Override
|
||||
public Item handleItemToClient(UserConnection connection, Item item) {
|
||||
if (item == null) return null;
|
||||
final int originalId = item.identifier();
|
||||
super.handleItemToClient(connection, item);
|
||||
|
||||
CompoundTag tag = item.tag();
|
||||
@@ -280,6 +346,32 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
});
|
||||
}
|
||||
|
||||
// Colors the fake leather armor for an elytra gray
|
||||
if (originalId == 443) {
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
CompoundTag display = tag.getCompoundTag("display");
|
||||
if (display == null) {
|
||||
tag.put("display", display = new CompoundTag());
|
||||
}
|
||||
display.put("color", new IntTag(0x737373)); // Gray
|
||||
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
|
||||
}
|
||||
|
||||
// Makes the fake banner for a shield brown if it has no banner patterns
|
||||
if (originalId == 442) {
|
||||
final CompoundTag blockEntityTag = tag == null ? null : tag.getCompoundTag("BlockEntityTag");
|
||||
final ListTag<CompoundTag> patterns = blockEntityTag == null ? null : blockEntityTag.getListTag("Patterns", CompoundTag.class);
|
||||
if (patterns == null || patterns.isEmpty()) {
|
||||
item.setData((short) 3); // Brown
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
tag.put(nbtTagName() + "|noData", new ByteTag(true));
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -304,7 +396,8 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
item.setData((short) 0);
|
||||
}
|
||||
|
||||
if (item.identifier() == 373 && (tag == null || !tag.contains("Potion"))) { // Potions
|
||||
if (item.identifier() == 373) { // Potions
|
||||
// Restore the splash id even when a Potion tag is present (e.g. items re-sent by a creative mode client)
|
||||
if (item.data() >= 16384) {
|
||||
item.setIdentifier(438);
|
||||
item.setData((short) (item.data() - 8192));
|
||||
@@ -322,6 +415,9 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
if (tag == null) {
|
||||
return item;
|
||||
}
|
||||
if (tag.remove(nbtTagName() + "|noData") != null) {
|
||||
item.setData((short) 0);
|
||||
}
|
||||
final Tag noDisplayTag = tag.remove(nbtTagName() + "|noDisplay");
|
||||
if (noDisplayTag != null) {
|
||||
tag.remove("display");
|
||||
|
||||
+111
-25
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -68,7 +68,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int dimension = wrapper.passthrough(Types.BYTE);
|
||||
|
||||
trackPlayer(wrapper.user(), entityId);
|
||||
final ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
|
||||
final ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
|
||||
clientWorld.setEnvironment(dimension);
|
||||
});
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_ENTITY_DATA, wrapper -> {
|
||||
@@ -118,7 +118,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
wrapper.set(Types.BYTE, 1, yaw);
|
||||
|
||||
int y = wrapper.get(Types.INT, 1);
|
||||
y += 10;
|
||||
y += 6;
|
||||
wrapper.set(Types.INT, 1, y);
|
||||
}
|
||||
|
||||
@@ -134,11 +134,24 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
}
|
||||
|
||||
if (data > 0) {
|
||||
wrapper.passthrough(Types.SHORT); // Velocity x
|
||||
wrapper.passthrough(Types.SHORT); // Velocity y
|
||||
wrapper.passthrough(Types.SHORT); // Velocity z
|
||||
} else {
|
||||
if (type.is(EntityTypes1_9.EntityType.POTION) && data > 0) {
|
||||
// 1.8 clients read the potion type from the object data, but newer versions only
|
||||
// send it in the entity data - the next packet. Held until then, see handleEntityData
|
||||
wrapper.cancel();
|
||||
final int x = wrapper.get(Types.INT, 0);
|
||||
final int y = wrapper.get(Types.INT, 1);
|
||||
final int z = wrapper.get(Types.INT, 2);
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 2);
|
||||
final short velocityX = wrapper.passthrough(Types.SHORT);
|
||||
final short velocityY = wrapper.passthrough(Types.SHORT);
|
||||
final short velocityZ = wrapper.passthrough(Types.SHORT);
|
||||
final EntityTracker1_9 tracker = tracker(wrapper.user());
|
||||
tracker.getPendingPotions().put(entityId, new EntityTracker1_9.PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data <= 0) {
|
||||
final short velocityX = wrapper.read(Types.SHORT);
|
||||
final short velocityY = wrapper.read(Types.SHORT);
|
||||
final short velocityZ = wrapper.read(Types.SHORT);
|
||||
@@ -164,7 +177,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.SHORT); // Count
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -179,7 +192,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.DOUBLE, Protocol1_9To1_8.DOUBLE_TO_INT_TIMES_32); // Z
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -216,7 +229,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.BYTE, Types.UNSIGNED_BYTE); // Direction
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -259,7 +272,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int deltaY = wrapper.read(Types.SHORT);
|
||||
final int deltaZ = wrapper.read(Types.SHORT);
|
||||
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
|
||||
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
|
||||
@@ -285,7 +298,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int deltaY = wrapper.read(Types.SHORT);
|
||||
final int deltaZ = wrapper.read(Types.SHORT);
|
||||
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
|
||||
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
|
||||
@@ -295,7 +308,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final byte pitch = wrapper.passthrough(Types.BYTE);
|
||||
final boolean onGround = wrapper.passthrough(Types.BOOLEAN);
|
||||
|
||||
com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
yaw -= 64;
|
||||
wrapper.set(Types.BYTE, 3, yaw);
|
||||
@@ -317,7 +330,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.MOVE_ENTITY_ROT, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.read(Types.BYTE);
|
||||
yaw -= 64;
|
||||
@@ -329,7 +342,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
|
||||
if (vehicle == null) {
|
||||
wrapper.cancel();
|
||||
@@ -360,13 +373,13 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
final EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
final EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
yaw -= 64;
|
||||
wrapper.set(Types.BYTE, 0, yaw);
|
||||
int y = wrapper.get(Types.INT, 1);
|
||||
y += 10;
|
||||
y += 6;
|
||||
wrapper.set(Types.INT, 1, y);
|
||||
}
|
||||
});
|
||||
@@ -379,7 +392,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
if (effectId > 23) { // Throw away new effects
|
||||
wrapper.cancel();
|
||||
}
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (effectId == 25 && entityId == tracker.clientEntityId()) {
|
||||
wrapper.user().get(LevitationStorage.class).setActive(false);
|
||||
}
|
||||
@@ -416,7 +429,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_PASSENGERS, null, wrapper -> {
|
||||
wrapper.cancel();
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int vehicle = wrapper.read(Types.VAR_INT);
|
||||
final IntList oldPassengers = tracker.getPassengers(vehicle);
|
||||
|
||||
@@ -444,6 +457,19 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_ENTITY_MOTION, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
final EntityTracker1_9 tracker = tracker(wrapper.user());
|
||||
final EntityTracker1_9.PendingPotionEntity pending = tracker.getPendingPotions().get(entityId);
|
||||
if (pending != null) {
|
||||
wrapper.cancel();
|
||||
final short velocityX = wrapper.read(Types.SHORT);
|
||||
final short velocityY = wrapper.read(Types.SHORT);
|
||||
final short velocityZ = wrapper.read(Types.SHORT);
|
||||
tracker.getPendingPotions().put(entityId, pending.withVelocity(velocityX, velocityY, velocityZ));
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.TELEPORT_ENTITY, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -457,14 +483,26 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final EntityTracker1_9.PendingPotionEntity pending = tracker.getPendingPotions().get(entityId);
|
||||
if (pending != null) {
|
||||
wrapper.cancel();
|
||||
final int x = wrapper.get(Types.INT, 0);
|
||||
final int y = wrapper.get(Types.INT, 1);
|
||||
final int z = wrapper.get(Types.INT, 2);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
tracker.getPendingPotions().put(entityId, pending.withPosition(x, y, z, pitch, yaw));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracker.entityType(entityId) == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
yaw -= 64;
|
||||
wrapper.set(Types.BYTE, 0, yaw);
|
||||
|
||||
int y = wrapper.get(Types.INT, 1);
|
||||
y += 10;
|
||||
y += 6;
|
||||
wrapper.set(Types.INT, 1, y);
|
||||
}
|
||||
tracker.resetEntityOffset(entityId);
|
||||
@@ -475,7 +513,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
protocol.registerClientbound(ClientboundPackets1_9.UPDATE_ATTRIBUTES, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final boolean player = entityId == tracker.clientEntityId();
|
||||
|
||||
// Throw away new attributes and track attack speed
|
||||
@@ -522,7 +560,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
if (effectId > 23) { // Throw away new effects
|
||||
wrapper.cancel();
|
||||
}
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (effectId == 25 && entityId == tracker.clientEntityId()) {
|
||||
final LevitationStorage levitation = wrapper.user().get(LevitationStorage.class);
|
||||
levitation.setActive(true);
|
||||
@@ -541,10 +579,56 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
filter().handler(this::handleEntityData);
|
||||
}
|
||||
|
||||
private void sendDelayedPotionSpawn(final EntityDataHandlerEvent event, final EntityTracker1_9 tracker, final Item item) {
|
||||
final EntityTracker1_9.PendingPotionEntity spawn = tracker.getPendingPotions().remove(event.entityId());
|
||||
if (spawn == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Item converted = protocol.getItemRewriter().handleItemToClient(event.user(), item);
|
||||
short data = converted == null ? 0 : converted.data();
|
||||
if (data <= 0) {
|
||||
data = 16384; // Unmapped potion, at least display a splash bottle
|
||||
}
|
||||
|
||||
final PacketWrapper addEntity = PacketWrapper.create(ClientboundPackets1_8.ADD_ENTITY, event.user());
|
||||
addEntity.write(Types.VAR_INT, event.entityId());
|
||||
addEntity.write(Types.BYTE, (byte) EntityTypes1_9.ObjectType.POTION.getId());
|
||||
addEntity.write(Types.INT, spawn.x());
|
||||
addEntity.write(Types.INT, spawn.y());
|
||||
addEntity.write(Types.INT, spawn.z());
|
||||
addEntity.write(Types.BYTE, spawn.pitch());
|
||||
addEntity.write(Types.BYTE, spawn.yaw());
|
||||
addEntity.write(Types.INT, (int) data);
|
||||
addEntity.write(Types.SHORT, spawn.velocityX());
|
||||
addEntity.write(Types.SHORT, spawn.velocityY());
|
||||
addEntity.write(Types.SHORT, spawn.velocityZ());
|
||||
addEntity.send(Protocol1_9To1_8.class); // Before the entity data is sent
|
||||
|
||||
// 1.8 clients never apply a potion spawn's velocity - vanilla servers send it in a separate
|
||||
// velocity packet after the spawn, so do the same
|
||||
final PacketWrapper setEntityMotion = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_MOTION, event.user());
|
||||
setEntityMotion.write(Types.VAR_INT, event.entityId());
|
||||
setEntityMotion.write(Types.SHORT, spawn.velocityX());
|
||||
setEntityMotion.write(Types.SHORT, spawn.velocityY());
|
||||
setEntityMotion.write(Types.SHORT, spawn.velocityZ());
|
||||
setEntityMotion.send(Protocol1_9To1_8.class);
|
||||
}
|
||||
|
||||
private void handleEntityData(EntityDataHandlerEvent event, EntityData entityData) {
|
||||
final EntityTracker1_9 tracker = tracker(event.user());
|
||||
if (event.entityType() == EntityTypes1_9.EntityType.POTION && entityData.value() instanceof Item potionItem) {
|
||||
// No 1.8 equivalent; supplies the object data of the held-back spawn. Matched by value type as the index is version dependent
|
||||
this.sendDelayedPotionSpawn(event, tracker, potionItem);
|
||||
event.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityData.id() == EntityDataIndex1_9.ENTITY_STATUS.getIndex()) {
|
||||
tracker.getStatus().put(event.entityId(), (Byte) entityData.value());
|
||||
if (tracker.isHandActive(event.entityId())) {
|
||||
entityData.setValue((byte) ((byte) entityData.value() | 1 << STATUS_USE_BIT));
|
||||
}
|
||||
}
|
||||
final EntityDataIndex1_9 metaIndex = EntityDataIndex1_8.searchIndex(event.entityType(), entityData.id());
|
||||
if (metaIndex == null) {
|
||||
@@ -554,8 +638,10 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
if (metaIndex.getOldType() == null || metaIndex.getNewType() == null) {
|
||||
if (metaIndex == EntityDataIndex1_9.PLAYER_HAND) { // Player eating/aiming/drinking
|
||||
final boolean handActive = (((byte) entityData.value()) & 1 << HAND_ACTIVE_BIT) != 0;
|
||||
tracker.setHandActive(event.entityId(), handActive);
|
||||
byte status = (byte) tracker.getStatus().getOrDefault(event.entityId(), 0);
|
||||
if ((((byte) entityData.value()) & 1 << HAND_ACTIVE_BIT) != 0) {
|
||||
if (handActive) {
|
||||
status = (byte) (status | 1 << STATUS_USE_BIT);
|
||||
} else {
|
||||
status = (byte) (status & ~(1 << STATUS_USE_BIT));
|
||||
|
||||
+100
-24
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -21,14 +21,17 @@ import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viarewind.api.type.RewindTypes;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BlockPlaceDestroyTracker;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BossBarStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CooldownStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.PlayerPositionTracker;
|
||||
import com.viaversion.viarewind.utils.ChatUtil;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.data.entity.EntityTracker;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
@@ -41,7 +44,6 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
import com.viaversion.viaversion.api.rewriter.RewriterBase;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
import com.viaversion.viaversion.libs.gson.JsonParser;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
|
||||
@@ -51,6 +53,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_HIDE;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_RESET;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_SUBTITLE;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TIMES_AND_DISPLAY;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TITLE;
|
||||
|
||||
public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
public PlayerPacketRewriter1_9(Protocol1_9To1_8 protocol) {
|
||||
@@ -105,6 +113,26 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
protocol.cancelClientbound(ClientboundPackets1_9.COOLDOWN);
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_TITLES, wrapper -> {
|
||||
final int action = wrapper.passthrough(Types.VAR_INT);
|
||||
if (action == ACTION_SET_TITLE) {
|
||||
final JsonElement title = wrapper.passthrough(Types.COMPONENT);
|
||||
wrapper.user().get(LastTitle.class).setTitle(title);
|
||||
} else if (action == ACTION_SET_SUBTITLE) {
|
||||
final JsonElement subtitle = wrapper.passthrough(Types.COMPONENT);
|
||||
wrapper.user().get(LastTitle.class).setSubtitle(subtitle);
|
||||
} else if (action == ACTION_SET_TIMES_AND_DISPLAY) {
|
||||
final int fadeIn = wrapper.passthrough(Types.INT);
|
||||
final int stay = wrapper.passthrough(Types.INT);
|
||||
final int fadeOut = wrapper.passthrough(Types.INT);
|
||||
wrapper.user().get(LastTitle.class).setTimes(fadeIn, stay, fadeOut);
|
||||
} else if (action == ACTION_HIDE) {
|
||||
wrapper.user().get(LastTitle.class).hide();
|
||||
} else if (action == ACTION_RESET) {
|
||||
wrapper.user().get(LastTitle.class).reset();
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_PAYLOAD, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -131,8 +159,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.BYTE); // Relative arguments
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker pos = wrapper.user().get(PlayerPositionTracker.class);
|
||||
|
||||
pos.setConfirmId(wrapper.read(Types.VAR_INT));
|
||||
final int teleportId = wrapper.read(Types.VAR_INT);
|
||||
|
||||
byte flags = wrapper.get(Types.BYTE, 0);
|
||||
double x = wrapper.get(Types.DOUBLE, 0);
|
||||
@@ -141,7 +168,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
float yaw = wrapper.get(Types.FLOAT, 0);
|
||||
float pitch = wrapper.get(Types.FLOAT, 1);
|
||||
|
||||
wrapper.set(Types.BYTE, 0, (byte) 0);
|
||||
// 1.8 supports the rotation-relative flags (0x08/0x10) natively, and resolving them here
|
||||
// would snap the camera to the tracked rotation, which lags the client's live look. Keep
|
||||
// them on the wire and only resolve the position bits (native 1.8 servers use zero-delta
|
||||
// rotation-relative teleports to leave the camera untouched).
|
||||
wrapper.set(Types.BYTE, 0, (byte) (flags & 0x18));
|
||||
|
||||
if (flags != 0) {
|
||||
if ((flags & 0x01) != 0) {
|
||||
@@ -157,18 +188,17 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
wrapper.set(Types.DOUBLE, 2, z);
|
||||
}
|
||||
if ((flags & 0x08) != 0) {
|
||||
yaw += pos.getYaw();
|
||||
wrapper.set(Types.FLOAT, 0, yaw);
|
||||
yaw += pos.getYaw(); // tracked estimate only; the wire keeps the delta
|
||||
}
|
||||
if ((flags & 0x10) != 0) {
|
||||
pitch += pos.getPitch();
|
||||
wrapper.set(Types.FLOAT, 1, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
pos.setPos(x, y, z);
|
||||
pos.setYaw(yaw);
|
||||
pos.setPitch(pitch);
|
||||
pos.queueTeleport(teleportId, x, y, z, pos.getYaw(), pos.getPitch(), (flags & 0x18) != 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -178,11 +208,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
public void register() {
|
||||
map(Types.INT); // Dimension
|
||||
handler(wrapper -> {
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
final int dimension = wrapper.get(Types.INT, 0);
|
||||
|
||||
if (world.setEnvironment(dimension)) {
|
||||
final EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.clearEntities();
|
||||
wrapper.user().get(BossBarStorage.class).reset();
|
||||
}
|
||||
@@ -201,7 +231,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
return;
|
||||
}
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
wrapper.passthrough(Types.INT); // Killer id
|
||||
|
||||
@@ -271,7 +301,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
wrapper.user().get(PlayerPositionTracker.class).sendAnimations();
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isInsideVehicle(tracker.clientEntityId())) {
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -289,7 +319,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.hasPendingTeleports()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,7 +339,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.hasPendingTeleports()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,13 +361,26 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.getPosX() == x && storage.getPosY() == y && storage.getPosZ() == z && storage.getYaw() == yaw && storage.getPitch() == pitch) {
|
||||
final PlayerPositionTracker.PendingTeleport teleport = storage.peekTeleport();
|
||||
if (teleport != null) {
|
||||
// 1.7 uses resynced hitbox minY for y however this can have small floating point error due to the calculations it does to get there on teleport confirm
|
||||
// This Y error gets propagated from 1.7 to 1.8 to 1.9 which causes teleport confirmation to not properly be detected
|
||||
// This fixes it similarly to how anticheats detect teleports: https://github.com/GrimAnticheat/Grim/blob/67aa3a9483a9b2a6987d594092697b4104c781f0/common/src/main/java/ac/grim/grimac/manager/SetbackTeleportUtil.java#L315
|
||||
boolean closeEnoughY = Math.abs(teleport.y() - y) <= 1e-7;
|
||||
// A view-relative teleport keeps its rotation deltas on the wire, so the client applies them
|
||||
// to its own live look and echoes that - match on position only and adopt the echoed rotation
|
||||
final boolean rotationMatches = teleport.viewRelative() || (teleport.yaw() == yaw && teleport.pitch() == pitch);
|
||||
|
||||
if (teleport.x() == x && closeEnoughY && teleport.z() == z && rotationMatches) {
|
||||
final PacketWrapper confirmTeleport = PacketWrapper.create(ServerboundPackets1_9.ACCEPT_TELEPORTATION, wrapper.user());
|
||||
confirmTeleport.write(Types.VAR_INT, storage.getConfirmId());
|
||||
confirmTeleport.write(Types.VAR_INT, teleport.id());
|
||||
confirmTeleport.sendToServer(Protocol1_9To1_8.class);
|
||||
|
||||
storage.setConfirmId(-1);
|
||||
if (teleport.viewRelative()) {
|
||||
storage.setYaw(yaw);
|
||||
storage.setPitch(pitch);
|
||||
}
|
||||
storage.confirmTeleport();
|
||||
}
|
||||
} else {
|
||||
storage.setPos(x, y, z);
|
||||
@@ -409,10 +452,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.VAR_INT); // Action parameter
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker tracker = wrapper.user().get(PlayerPositionTracker.class);
|
||||
final InventoryProvider provider = Via.getManager().getProviders().get(InventoryProvider.class);
|
||||
final int action = wrapper.get(Types.VAR_INT, 1);
|
||||
if (action == 6) { // Jump with horse
|
||||
wrapper.set(Types.VAR_INT, 1, 7);
|
||||
} else if (action == 0 && !tracker.isOnGround()) { // Start sneaking
|
||||
} else if (action == 0 && provider.hasElytra(wrapper.user()) && !tracker.isOnGround()) { // Start sneaking
|
||||
final PacketWrapper elytra = PacketWrapper.create(ServerboundPackets1_9.PLAYER_COMMAND, wrapper.user());
|
||||
elytra.write(Types.VAR_INT, wrapper.get(Types.VAR_INT, 0));
|
||||
elytra.write(Types.VAR_INT, 8);
|
||||
@@ -427,7 +471,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
final float sideways = wrapper.passthrough(Types.FLOAT);
|
||||
final float forward = wrapper.passthrough(Types.FLOAT);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
|
||||
|
||||
if (vehicle != null && tracker.entityType(vehicle) == EntityTypes1_9.EntityType.BOAT) {
|
||||
@@ -444,7 +488,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.BLOCK_POSITION1_8); // Position
|
||||
handler(wrapper -> {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final JsonElement json = JsonParser.parseString(wrapper.read(RewindTypes.SIGN_STRING));
|
||||
final JsonElement json = wrapper.read(Types.COMPONENT);
|
||||
if (!json.isJsonPrimitive()) {
|
||||
throw new IllegalStateException("Invalid sign text: " + StringUtil.forLogging(json));
|
||||
}
|
||||
@@ -477,7 +521,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
final short flags = wrapper.get(Types.UNSIGNED_BYTE, 0);
|
||||
|
||||
final PacketWrapper updateSkin = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_DATA, wrapper.user());
|
||||
updateSkin.write(Types.VAR_INT, wrapper.user().getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
|
||||
updateSkin.write(Types.VAR_INT, wrapper.user().getEntityTracker(protocol).clientEntityId());
|
||||
|
||||
final List<EntityData> entityData = new ArrayList<>();
|
||||
entityData.add(new EntityData(10, EntityDataTypes1_8.BYTE, (byte) flags));
|
||||
@@ -521,7 +565,39 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
pageTag.setValue(ChatUtil.jsonToLegacy(value));
|
||||
}
|
||||
} else if (channel.equals("MC|AdvCdm")) {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
final byte type = wrapper.read(Types.BYTE);
|
||||
if (type == 0) {
|
||||
final int x = wrapper.read(Types.INT);
|
||||
final int y = wrapper.read(Types.INT);
|
||||
final int z = wrapper.read(Types.INT);
|
||||
final CommandBlockState.ParsedCommand command = CommandBlockState.parseDecoratedCommand(wrapper.read(Types.STRING));
|
||||
final boolean trackOutput = wrapper.read(Types.BOOLEAN);
|
||||
if (command.prefixed()) {
|
||||
wrapper.set(Types.STRING, 0, "MC|AutoCmd");
|
||||
wrapper.write(Types.INT, x);
|
||||
wrapper.write(Types.INT, y);
|
||||
wrapper.write(Types.INT, z);
|
||||
wrapper.write(Types.STRING, command.command());
|
||||
wrapper.write(Types.BOOLEAN, trackOutput);
|
||||
wrapper.write(Types.STRING, command.mode());
|
||||
wrapper.write(Types.BOOLEAN, command.conditional());
|
||||
wrapper.write(Types.BOOLEAN, command.automatic());
|
||||
} else {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
wrapper.write(Types.BYTE, type);
|
||||
wrapper.write(Types.INT, x);
|
||||
wrapper.write(Types.INT, y);
|
||||
wrapper.write(Types.INT, z);
|
||||
wrapper.write(Types.STRING, command.command());
|
||||
wrapper.write(Types.BOOLEAN, trackOutput);
|
||||
}
|
||||
} else {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
wrapper.write(Types.BYTE, type);
|
||||
wrapper.passthrough(Types.INT); // Entity id
|
||||
wrapper.passthrough(Types.STRING); // Command
|
||||
wrapper.passthrough(Types.BOOLEAN); // Track output
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+50
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -22,7 +22,9 @@ import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.EffectIdMappings1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.Environment;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
|
||||
@@ -57,6 +59,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.NAMED_COMPOUND_TAG); // Tag
|
||||
handler(wrapper -> {
|
||||
final CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);
|
||||
final short action = wrapper.get(Types.UNSIGNED_BYTE, 0);
|
||||
|
||||
if (action == 2) {
|
||||
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
|
||||
CommandBlockState.decorateCommand(tag, wrapper.user().get(CommandBlockStateStorage.class).state(position));
|
||||
}
|
||||
|
||||
if (tag.remove("SpawnData") instanceof CompoundTag spawnData) {
|
||||
final Tag id = spawnData.remove("id");
|
||||
@@ -92,9 +100,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
String name = wrapper.get(Types.STRING, 0);
|
||||
name = protocol.getMappingData().getMappedNamedSound(name);
|
||||
if (name == null) {
|
||||
wrapper.cancel();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (!name.isEmpty()) {
|
||||
wrapper.set(Types.STRING, 0, name);
|
||||
} else {
|
||||
wrapper.cancel();
|
||||
}
|
||||
});
|
||||
read(Types.VAR_INT); // Sound category
|
||||
@@ -110,10 +121,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
|
||||
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
|
||||
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
final int chunkX = wrapper.read(Types.INT);
|
||||
final int chunkZ = wrapper.read(Types.INT);
|
||||
commandBlockStates.unloadChunk(chunkX, chunkZ);
|
||||
|
||||
wrapper.write(ChunkType1_8.forEnvironment(environment), new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], null, new ArrayList<>()));
|
||||
});
|
||||
@@ -124,9 +137,24 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
|
||||
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
|
||||
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
Chunk chunk = wrapper.read(ChunkType1_9_1.forEnvironment(environment));
|
||||
final Chunk originalChunk = chunk;
|
||||
|
||||
chunk.getBlockEntities().forEach(nbt -> {
|
||||
if (!nbt.contains("x") || !nbt.contains("y") || !nbt.contains("z")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BlockPosition position = new BlockPosition((int) nbt.get("x").getValue(), (int) nbt.get("y").getValue(), (int) nbt.get("z").getValue());
|
||||
final int blockState = blockStateAt(originalChunk, position);
|
||||
if (CommandBlockState.isCommandBlock(blockState)) {
|
||||
commandBlockStates.storeOrRemove(position, blockState);
|
||||
CommandBlockState.decorateCommand(nbt, blockState);
|
||||
}
|
||||
});
|
||||
|
||||
for (ChunkSection section : chunk.getSections()) {
|
||||
if (section == null) continue;
|
||||
@@ -251,11 +279,11 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final int soundId = wrapper.read(Types.VAR_INT);
|
||||
final String soundName = protocol.getMappingData().soundName(soundId);
|
||||
if (soundName == null) {
|
||||
final int mappedSoundId = protocol.getMappingData().getFullSoundMappings().getNewId(soundId);
|
||||
if (mappedSoundId == -1) {
|
||||
wrapper.cancel();
|
||||
} else {
|
||||
wrapper.write(Types.STRING, protocol.getMappingData().getMappedNamedSound(soundName));
|
||||
wrapper.write(Types.STRING, protocol.getMappingData().getFullSoundMappings().mappedIdentifier(mappedSoundId));
|
||||
}
|
||||
});
|
||||
read(Types.VAR_INT); // Sound category
|
||||
@@ -267,4 +295,18 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int blockStateAt(final Chunk chunk, final BlockPosition position) {
|
||||
final int sectionY = position.y() >> 4;
|
||||
if (sectionY < 0 || sectionY >= chunk.getSections().length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final ChunkSection section = chunk.getSections()[sectionY];
|
||||
if (section == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return section.palette(PaletteType.BLOCKS).idAt(position.x() & 0xF, position.y() & 0xF, position.z() & 0xF);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class CommandBlockStateStorage implements StorableObject {
|
||||
|
||||
private final Map<BlockPosition, Integer> commandBlockStates = new HashMap<>();
|
||||
|
||||
public void storeOrRemove(final BlockPosition position, final int blockState) {
|
||||
if (CommandBlockState.isCommandBlock(blockState)) {
|
||||
commandBlockStates.put(position, blockState);
|
||||
} else {
|
||||
commandBlockStates.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
public int state(final BlockPosition position) {
|
||||
return commandBlockStates.getOrDefault(position, -1);
|
||||
}
|
||||
|
||||
public void unloadChunk(final int chunkX, final int chunkZ) {
|
||||
final Iterator<BlockPosition> iterator = commandBlockStates.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final BlockPosition position = iterator.next();
|
||||
if (position.x() >> 4 == chunkX && position.z() >> 4 == chunkZ) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+39
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.Vector;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
|
||||
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
|
||||
@@ -27,6 +28,8 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntList;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -35,20 +38,24 @@ public class EntityTracker1_9 extends EntityTrackerBase {
|
||||
private final Int2ObjectMap<IntList> vehicles = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<Vector> offsets = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2IntMap status = new Int2IntOpenHashMap();
|
||||
private final IntSet handActive = new IntOpenHashSet();
|
||||
private final Int2ObjectMap<PendingPotionEntity> pendingPotions = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public EntityTracker1_9(UserConnection connection) {
|
||||
super(connection, EntityTypes1_9.EntityType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int id) {
|
||||
public TrackedEntity removeEntity(int id) {
|
||||
vehicles.remove(id);
|
||||
offsets.remove(id);
|
||||
status.remove(id);
|
||||
handActive.remove(id);
|
||||
pendingPotions.remove(id);
|
||||
|
||||
vehicles.forEach((vehicle, passengers) -> passengers.rem(id));
|
||||
vehicles.int2ObjectEntrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
super.removeEntity(id);
|
||||
return super.removeEntity(id);
|
||||
}
|
||||
|
||||
public void resetEntityOffset(final int id) {
|
||||
@@ -88,4 +95,33 @@ public class EntityTracker1_9 extends EntityTrackerBase {
|
||||
public Int2IntMap getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean isHandActive(final int id) {
|
||||
return handActive.contains(id);
|
||||
}
|
||||
|
||||
public void setHandActive(final int id, final boolean active) {
|
||||
if (active) {
|
||||
handActive.add(id);
|
||||
} else {
|
||||
handActive.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Int2ObjectMap<PendingPotionEntity> getPendingPotions() {
|
||||
return pendingPotions;
|
||||
}
|
||||
|
||||
public record PendingPotionEntity(int x, int y, int z, byte pitch, byte yaw, short velocityX, short velocityY, short velocityZ) {
|
||||
|
||||
public PendingPotionEntity withPosition(final int x, final int y, final int z, final byte pitch, final byte yaw) {
|
||||
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
|
||||
}
|
||||
|
||||
public PendingPotionEntity withVelocity(final short velocityX, final short velocityY, final short velocityZ) {
|
||||
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-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.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
|
||||
public final class LastTitle implements StorableObject {
|
||||
|
||||
private JsonElement title;
|
||||
private JsonElement subtitle;
|
||||
|
||||
private long visibleSince;
|
||||
private long visibleUntil;
|
||||
|
||||
private int fadeIn;
|
||||
private int stay;
|
||||
private int fadeOut;
|
||||
|
||||
public LastTitle() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public void setTitle(final JsonElement title) {
|
||||
this.title = title;
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
this.visibleSince = now;
|
||||
this.visibleUntil = now + titleLength();
|
||||
}
|
||||
|
||||
public void setSubtitle(final JsonElement subtitle) {
|
||||
this.subtitle = subtitle;
|
||||
}
|
||||
|
||||
public void setTimes(final int fadeIn, final int stay, final int fadeOut) {
|
||||
this.fadeIn = fadeIn;
|
||||
this.stay = stay;
|
||||
this.fadeOut = fadeOut;
|
||||
|
||||
if (this.visibleUntil > System.currentTimeMillis()) {
|
||||
final long now = System.currentTimeMillis();
|
||||
this.visibleSince = now;
|
||||
this.visibleUntil = now + titleLength();
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
this.visibleSince = 0;
|
||||
this.visibleUntil = 0;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.hide();
|
||||
|
||||
this.fadeIn = 10;
|
||||
this.stay = 70;
|
||||
this.fadeOut = 20;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return this.title != null && this.visibleUntil > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public JsonElement title() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public JsonElement subtitle() {
|
||||
return this.subtitle;
|
||||
}
|
||||
|
||||
public int[] remainingTimes() {
|
||||
final int fullLength = Math.max(0, this.fadeIn + this.stay + this.fadeOut);
|
||||
final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - this.visibleSince) / 50L));
|
||||
if (elapsed < this.fadeIn) {
|
||||
return new int[]{this.fadeIn - elapsed, this.stay, this.fadeOut};
|
||||
}
|
||||
|
||||
final int titleEnd = this.fadeIn + this.stay;
|
||||
if (elapsed < titleEnd) {
|
||||
return new int[]{0, titleEnd - elapsed, this.fadeOut};
|
||||
}
|
||||
|
||||
return new int[]{0, 0, fullLength - elapsed};
|
||||
}
|
||||
|
||||
private long titleLength() {
|
||||
return Math.max(0, this.fadeIn + this.stay + this.fadeOut) * 50L;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
|
||||
+17
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -26,10 +26,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
public class PlayerPositionTracker implements StorableObject {
|
||||
|
||||
private final Queue<PacketWrapper> animations = new ConcurrentLinkedQueue<>();
|
||||
private final Queue<PendingTeleport> pendingTeleports = new ConcurrentLinkedQueue<>();
|
||||
private double posX, posY, posZ;
|
||||
private float yaw, pitch;
|
||||
private boolean onGround;
|
||||
private int confirmId = -1;
|
||||
|
||||
public void setPos(double x, double y, double z) {
|
||||
this.posX = x;
|
||||
@@ -84,12 +84,23 @@ public class PlayerPositionTracker implements StorableObject {
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public int getConfirmId() {
|
||||
return this.confirmId;
|
||||
public boolean hasPendingTeleports() {
|
||||
return !this.pendingTeleports.isEmpty();
|
||||
}
|
||||
|
||||
public void setConfirmId(int confirmId) {
|
||||
this.confirmId = confirmId;
|
||||
public void queueTeleport(final int id, final double x, final double y, final double z, final float yaw, final float pitch, final boolean viewRelative) {
|
||||
this.pendingTeleports.add(new PendingTeleport(id, x, y, z, yaw, pitch, viewRelative));
|
||||
}
|
||||
|
||||
public PendingTeleport peekTeleport() {
|
||||
return this.pendingTeleports.peek();
|
||||
}
|
||||
|
||||
public void confirmTeleport() {
|
||||
this.pendingTeleports.poll();
|
||||
}
|
||||
|
||||
public record PendingTeleport(int id, double x, double y, double z, float yaw, float pitch, boolean viewRelative) {
|
||||
}
|
||||
|
||||
}
|
||||
+39
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -38,6 +38,9 @@ public class WindowTracker extends StoredObject {
|
||||
private final HashMap<Short, Item[]> brewingItems = new HashMap<>();
|
||||
private final Map<Short, Short> enchantmentProperties = new HashMap<>();
|
||||
|
||||
private short openWindowId = 0;
|
||||
private int openWindowSize = 0; // Translated client container slots, excluding the player inventory
|
||||
|
||||
public WindowTracker(UserConnection user) {
|
||||
super(user);
|
||||
}
|
||||
@@ -52,11 +55,11 @@ public class WindowTracker extends StoredObject {
|
||||
openWindow.write(Types.UNSIGNED_BYTE, windowId);
|
||||
openWindow.write(Types.STRING, "minecraft:brewing_stand");
|
||||
|
||||
TextComponent title = new StringComponent().
|
||||
append(new TranslationComponent("container.brewing")).
|
||||
append(new StringComponent(": " + TextFormatting.DARK_GRAY)).
|
||||
append(new StringComponent(amount + " " + TextFormatting.DARK_RED)).
|
||||
append(new TranslationComponent("item.blazePowder.name", TextFormatting.DARK_RED));
|
||||
TextComponent title = new StringComponent()
|
||||
.append(new TranslationComponent("container.brewing"))
|
||||
.append(new StringComponent(": ").formatted(TextFormatting.DARK_GRAY))
|
||||
.append(new StringComponent(amount + " ").formatted(TextFormatting.DARK_RED))
|
||||
.append(new TranslationComponent("item.blazePowder.name").formatted(TextFormatting.DARK_RED));
|
||||
|
||||
openWindow.write(Types.COMPONENT, TextComponentSerializer.V1_8.serializeJson(title));
|
||||
openWindow.write(Types.UNSIGNED_BYTE, (short) 420);
|
||||
@@ -83,6 +86,36 @@ public class WindowTracker extends StoredObject {
|
||||
public void remove(short windowId) {
|
||||
types.remove(windowId);
|
||||
brewingItems.remove(windowId);
|
||||
if (windowId == openWindowId) {
|
||||
openWindowId = 0;
|
||||
openWindowSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void openWindow(final short windowId, final String windowType, final int slotCount) {
|
||||
openWindowId = windowId;
|
||||
|
||||
// The client only uses the packet's slot count when constructing dynamic inventories.
|
||||
// Fixed menus construct their own slots; use the translated 1.8 layout for those.
|
||||
openWindowSize = switch (windowType) {
|
||||
case "minecraft:furnace", "minecraft:villager", "minecraft:anvil" -> 3;
|
||||
case "minecraft:brewing_stand" -> 4;
|
||||
case "minecraft:beacon" -> 1;
|
||||
case "minecraft:dispenser", "minecraft:dropper" -> 9;
|
||||
case "minecraft:enchanting_table" -> 2;
|
||||
case "minecraft:crafting_table" -> 10;
|
||||
case "minecraft:hopper" -> slotCount;
|
||||
case "EntityHorse" -> slotCount > 2 ? 17 : 2;
|
||||
default -> slotCount / 9 * 9;
|
||||
};
|
||||
}
|
||||
|
||||
public short openWindowId() {
|
||||
return openWindowId;
|
||||
}
|
||||
|
||||
public int openWindowSize() {
|
||||
return openWindowSize;
|
||||
}
|
||||
|
||||
public Item[] getBrewingItems(short windowId) {
|
||||
|
||||
+6
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -26,7 +26,11 @@ public class CooldownIndicatorTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
connection.get(CooldownStorage.class).tick(connection);
|
||||
final CooldownStorage cooldown = connection.get(CooldownStorage.class);
|
||||
if (cooldown == null) {
|
||||
continue;
|
||||
}
|
||||
cooldown.tick(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -36,12 +36,15 @@ public class LevitationUpdateTask implements Runnable {
|
||||
}
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final LevitationStorage levitation = connection.get(LevitationStorage.class);
|
||||
if (levitation == null) {
|
||||
continue;
|
||||
}
|
||||
if (!levitation.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final PacketWrapper velocityPacket = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_MOTION, connection);
|
||||
velocityPacket.write(Types.VAR_INT, connection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
|
||||
velocityPacket.write(Types.VAR_INT, connection.storables(Protocol1_9To1_8.class).entityTracker().clientEntityId());
|
||||
velocityPacket.write(Types.SHORT, (short) 0);
|
||||
velocityPacket.write(Types.SHORT, (short) ((levitation.getAmplifier() + 1) * 360));
|
||||
velocityPacket.write(Types.SHORT, (short) 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2025 ViaVersion and contributors
|
||||
* Copyright (C) 2018-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
|
||||
@@ -49,7 +49,7 @@ public class ChatUtil {
|
||||
try {
|
||||
return jsonToLegacy(JsonParser.parseString(json));
|
||||
} catch (Exception e) {
|
||||
if (!Via.getConfig().isSuppressConversionWarnings()) {
|
||||
if (Via.getConfig().logTextComponentConversionErrors()) {
|
||||
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Could not convert component to legacy text: " + json, e);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class ChatUtil {
|
||||
}
|
||||
return legacy;
|
||||
} catch (Exception ex) {
|
||||
if (!Via.getConfig().isSuppressConversionWarnings()) {
|
||||
if (Via.getConfig().logTextComponentConversionErrors()) {
|
||||
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Could not convert component to legacy text: " + component, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
},
|
||||
"180": {
|
||||
"id": "128",
|
||||
"data": "-1",
|
||||
"name": "1.8 Red Sandstone Stairs"
|
||||
},
|
||||
"181": {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user