Compare commits

..
Author SHA1 Message Date
Florian Reuth 667493a671 Codebase cleanup
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-20 21:11:21 +02:00
82 changed files with 834 additions and 2299 deletions

No files matched your search

+1 -9
View File
@@ -6,17 +6,9 @@ updates:
directory: "/"
schedule:
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
interval: "monthly"
+11 -13
View File
@@ -2,31 +2,29 @@ name: Build
on: [pull_request, push, workflow_dispatch]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-26.04
timeout-minutes: 30
# Only run on PRs if the source branch is on a different repo. We do not need to run everything twice.
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-disabled: true
cache-provider: basic
- name: Set up Java
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.0
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
with:
distribution: 'temurin'
java-version: 25
java-version: 17
check-latest: true
- name: Build with Gradle
run: ./gradlew build --stacktrace
run: ./gradlew build
- name: Upload Artifacts to GitHub
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0
with:
name: Artifacts
path: build/libs/
if-no-files-found: error
+7 -11
View File
@@ -12,26 +12,22 @@ permissions:
jobs:
publish:
if: github.repository_owner == 'ViaVersion'
runs-on: ubuntu-26.04
timeout-minutes: 30
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-disabled: true
cache-provider: basic
- name: Set up Java
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.0
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
with:
distribution: 'temurin'
java-version: 25
java-version: 17
check-latest: true
- name: Build with Gradle
run: ./gradlew build --stacktrace
run: ./gradlew build
- name: Publish to Hangar
env:
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
-8
View File
@@ -1,8 +0,0 @@
plugins {
`kotlin-dsl`
}
dependencies {
implementation(libs.foojay.resolver.convention.plugin)
implementation(libs.classtokenreplacer.plugin)
}
-10
View File
@@ -1,10 +0,0 @@
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -1,39 +0,0 @@
plugins {
`java-library`
}
group = property("project_group") as String
version = property("project_version") as String
description = property("project_description") as String
if (project == rootProject) {
(findProperty("project_name") as String?)?.let { base.archivesName = it }
}
// The Kotlin plugin picks up the Java toolchain, so no Kotlin specific configuration is needed
val jvmVersion = (property("jvm_version") as String).toInt()
java {
toolchain.languageVersion = JavaLanguageVersion.of(jvmVersion)
sourceCompatibility = JavaVersion.toVersion(jvmVersion)
targetCompatibility = JavaVersion.toVersion(jvmVersion)
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
}
tasks.withType<Javadoc>().configureEach {
options.encoding = "UTF-8"
(options as StandardJavadocDocletOptions).apply {
addBooleanOption("Xdoclint:none", true)
quiet()
}
}
tasks.jar {
val projectName = project.name
from("LICENSE") {
rename { "LICENSE_$projectName" }
}
}
@@ -1,60 +0,0 @@
plugins {
`java-library`
`maven-publish`
signing
}
val projectName = property("project_name") as String
val ownerId = property("publish_owner_id") as String
val ownerName = property("publish_owner_name") as String
val ownerMail = property("publish_owner_mail") as String
val githubAccount = findProperty("publish_github_account") as String? ?: ownerId
val licenseName = findProperty("publish_license") as String? ?: "Apache-2.0"
val githubRepository = "github.com/$githubAccount/$projectName"
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
groupId = project.group.toString()
artifactId = project.name
version = project.version.toString()
from(components["java"])
pom {
name = projectName
// Lazy so that a description set in the build script body is picked up
description = provider { project.description }
url = "https://$githubRepository"
licenses {
license {
name = licenseName
url = "https://$githubRepository/blob/main/LICENSE"
}
}
developers {
developer {
id = ownerId
name = ownerName
email = ownerMail
}
}
scm {
connection = "scm:git:git://$githubRepository.git"
developerConnection = "scm:git:ssh://$githubRepository.git"
url = "https://$githubRepository"
}
}
}
}
}
signing {
isRequired = false
sign(publishing.publications)
}
@@ -1,14 +0,0 @@
plugins {
id("org.gradle.toolchains.foojay-resolver-convention")
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
dependencyResolutionManagement {
// Repositories live in the settings only, a project or plugin declaring its own fails the build
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
mavenCentral()
maven("https://maven.florianreuth.de/releases")
}
}
@@ -1,16 +0,0 @@
plugins {
java
}
val embeddedDependencies = configurations.create("embeddedDependencies") {
isCanBeResolved = true
isCanBeConsumed = true
}
tasks.jar {
dependsOn(embeddedDependencies)
from({ embeddedDependencies.map { zipTree(it) } }) {
exclude("META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA")
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
@@ -1,59 +0,0 @@
package de.florianreuth.baseproject
import org.gradle.api.Project
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.ValueSource
import org.gradle.api.provider.ValueSourceParameters
import org.gradle.process.ExecOperations
import java.io.ByteArrayOutputStream
import javax.inject.Inject
/**
* Short hash of the latest commit, or "unknown" if Git is unavailable.
*/
fun Project.latestCommitHash(): String = runGitCommand(listOf("rev-parse", "--short", "HEAD"))
/**
* Message of the latest commit, or "unknown" if Git is unavailable.
*/
fun Project.latestCommitMessage(): String = runGitCommand(listOf("log", "-1", "--pretty=%B"))
/**
* Name of the current branch, or "unknown" if Git is unavailable.
*/
fun Project.branchName(): String = runGitCommand(listOf("rev-parse", "--abbrev-ref", "HEAD"))
/**
* Runs a Git command through a configuration-cache compatible [ValueSource].
*
* @param args the arguments passed to `git`
* @return the trimmed output, or "unknown" if the command fails or prints nothing
*/
fun Project.runGitCommand(args: List<String>): String {
return providers.of(GitCommand::class.java) { parameters.args.set(args) }.getOrNull() ?: "unknown"
}
abstract class GitCommand : ValueSource<String, GitCommand.GitCommandParameters> {
@get:Inject
abstract val execOperations: ExecOperations
interface GitCommandParameters : ValueSourceParameters {
val args: ListProperty<String>
}
override fun obtain(): String? {
try {
val output = ByteArrayOutputStream()
execOperations.exec {
commandLine = listOf("git") + parameters.args.get()
standardOutput = output
isIgnoreExitValue = true
}
return output.toString(Charsets.UTF_8).trim().takeIf { it.isNotBlank() }
} catch (_: Exception) {
return null
}
}
}
@@ -1,78 +0,0 @@
/*
* This file is part of BaseProject - https://github.com/florianreuth/BaseProject
* Copyright (C) 2024-2026 Florian Reuth <git@florianreuth.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.florianreuth.baseproject
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
/**
* Includes `<rootProject.name>-<name>` for every name, located in the `<name>` folder.
*
* @param names the subproject folder names
*/
fun Settings.setupViaSubprojects(vararg names: String) {
names.forEach { name ->
include("${rootProject.name}-$name")
project(":${rootProject.name}-$name").projectDir = settingsDir.resolve(name)
}
}
/**
* Version, channel and changelog of a Modrinth / Hangar upload of a ViaVersion project.
*
* @property isRelease whether the project version has no pre-release suffix
* @property isMainBranch whether the current Git branch is the main branch
* @property version the project version, suffixed with the GitHub run number for non-releases
* @property changelog a link to the GitHub releases for releases, otherwise [commitChangelog]
* @property commitChangelog a link to and the message of the latest commit
*/
class ViaRelease(
val isRelease: Boolean,
val isMainBranch: Boolean,
val version: String,
val changelog: String,
val commitChangelog: String
) {
val modrinthVersionType: String
get() = if (isRelease) "release" else if (isMainBranch) "beta" else "alpha"
val hangarChannel: String
get() = if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha"
}
/**
* Collects the release information from the project version and Git; the repository is `ViaVersion/<project_name>`.
*
* @param mainBranch the name of the branch releases are published from
* @return the release information of the current build
*/
fun Project.viaRelease(mainBranch: String): ViaRelease {
val baseVersion = version.toString()
val isRelease = !baseVersion.contains('-')
val repository = "https://github.com/ViaVersion/${property("project_name")}"
val commitHash = latestCommitHash()
val commitChangelog = "[$commitHash]($repository/commit/$commitHash) ${latestCommitMessage()}"
return ViaRelease(
isRelease = isRelease,
isMainBranch = branchName() == mainBranch,
version = if (isRelease) baseVersion else baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER"),
changelog = if (isRelease) "See [GitHub]($repository) for release notes." else commitChangelog,
commitChangelog = commitChangelog
)
}
@@ -1,20 +0,0 @@
import de.florianreuth.baseproject.latestCommitHash
import net.raphimc.classtokenreplacer.extension.ClassTokenReplacerExtension
plugins {
java
id("net.raphimc.class-token-replacer")
}
val implVersion = "git-${property("project_name")}-${project.version}:${latestCommitHash()}"
sourceSets.configureEach {
extensions.getByType<ClassTokenReplacerExtension>().apply {
property("\${version}", project.version)
property("\${impl_version}", implVersion)
}
}
// With replaceInPlace the plugin only finalizes classes with the replacement, so the jar could pack unreplaced classes
tasks.jar {
dependsOn("replaceTokens")
}
@@ -1,19 +0,0 @@
plugins {
`maven-publish`
}
publishing {
repositories {
maven {
name = "Via"
url = uri("https://repo.viaversion.com/")
credentials {
username = findProperty("ViaUsername") as String?
password = findProperty("ViaPassword") as String?
}
authentication {
create<BasicAuthentication>("basic")
}
}
}
}
@@ -1,20 +0,0 @@
plugins {
java
id("base.java")
id("via.maven_publish")
}
val libs = project.the<VersionCatalogsExtension>().named("libs")
dependencies {
compileOnly(libs.findLibrary("viaversion").get())
compileOnly(libs.findLibrary("viabackwards").get())
}
tasks.processResources {
val projectVersion = project.version
val projectDescription = project.description
filesMatching(listOf("plugin.yml", "fabric.mod.json", "META-INF/sponge_plugins.json")) {
expand(mapOf("version" to projectVersion, "description" to projectDescription))
}
}
@@ -1,8 +0,0 @@
plugins {
id("publishing.viaversion")
}
// base.maven_publish reads these when applied, so it is applied only after they are set
extra["publish_github_account"] = "ViaVersion"
extra["publish_license"] = "GPL-3.0"
apply(plugin = "base.maven_publish")
@@ -1,47 +0,0 @@
// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy
// For further instructions see the ViaProxy repository README
plugins {
java
}
val viaProxy = configurations.create("viaProxy")
dependencies {
addProvider<MinimalExternalModuleDependency, ExternalModuleDependency>(
viaProxy.name,
project.the<VersionCatalogsExtension>().named("libs").findLibrary("viaproxy").get()
) {
isTransitive = false
}
}
val prepareViaProxyFiles by tasks.registering(Copy::class) {
dependsOn(project.tasks.jar)
from(project.tasks.jar.get().archiveFile.get().asFile)
into(layout.projectDirectory.dir("run/jars"))
val projectName = project.name
rename { "${projectName}.jar" }
}
val cleanupViaProxyFiles by tasks.registering(Delete::class) {
delete(
layout.projectDirectory.file("run/jars/${project.name}.jar"),
layout.projectDirectory.dir("run/logs")
)
}
tasks.register<JavaExec>("runViaProxy") {
dependsOn(prepareViaProxyFiles)
finalizedBy(cleanupViaProxyFiles)
mainClass.set("net.raphimc.viaproxy.ViaProxy")
classpath = viaProxy
workingDir = layout.projectDirectory.dir("run").asFile
jvmArgs = listOf("-DskipUpdateCheck")
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
}
}
+66 -20
View File
@@ -1,20 +1,55 @@
import de.florianreuth.baseproject.viaRelease
import de.florianreuth.baseproject.*
plugins {
`java-library`
alias(libs.plugins.hangar.publish)
alias(libs.plugins.minotaur)
id("base.java")
id("via.maven_publish")
id("configuration.embedded_dependencies")
id("io.papermc.hangar-publish-plugin")
id("com.modrinth.minotaur")
id("de.florianreuth.baseproject")
}
allprojects {
setupProject()
setupViaPublishing()
repositories {
mavenCentral()
maven("https://repo.viaversion.com")
maven("https://repo.papermc.io/repository/maven-public")
maven("https://maven.fabricmc.net")
}
}
subprojects {
dependencies {
compileOnly("com.viaversion:viaversion:5.7.2")
compileOnly("com.viaversion:viabackwards:5.7.2")
}
tasks {
processResources {
val projectVersion = project.version
val projectDescription = project.description
filesMatching(listOf("plugin.yml", "fabric.mod.json", "META-INF/sponge_plugins.json")) {
expand(mapOf("version" to projectVersion, "description" to projectDescription))
}
}
}
}
base {
archivesName.set("ViaRewind")
}
val shade = configureShadedDependencies(false) // Only shade, don't add them as dependency
dependencies {
embeddedDependencies(projects.viarewindCommon)
embeddedDependencies(projects.viarewindBukkit)
embeddedDependencies(projects.viarewindFabric)
embeddedDependencies(projects.viarewindSponge)
embeddedDependencies(projects.viarewindVelocity)
subprojects.forEach {
shade(it)
}
}
tasks {
@@ -25,18 +60,29 @@ tasks {
}
}
val release = viaRelease("master")
if (!release.isRelease || release.isMainBranch) { // Only publish releases from the main branch
val branch = branchName()
val baseVersion = version as String
val isRelease = !baseVersion.contains('-')
val isMainBranch = branch == "master"
if (!isRelease || isMainBranch) { // Only publish releases from the main branch
val suffixedVersion = if (isRelease) baseVersion else baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER")
val changelogContent = if (isRelease) {
"See [GitHub](https://github.com/ViaVersion/ViaRewind) for release notes."
} else {
val commitHash = latestCommitHash()
"[$commitHash](https://github.com/ViaVersion/ViaRewind/commit/$commitHash) ${latestCommitMessage()}"
}
modrinth {
val mcVersions: List<String> = (property("minecraft_versions") as String)
.split(",")
.map { it.trim() }
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("viarewind")
versionType.set(release.modrinthVersionType)
versionNumber.set(release.version)
versionName.set(release.version)
changelog.set(release.changelog)
versionType.set(if (isRelease) "release" else if (isMainBranch) "beta" else "alpha")
versionNumber.set(suffixedVersion)
versionName.set(suffixedVersion)
changelog.set(changelogContent)
uploadFile.set(tasks.jar.flatMap { it.archiveFile })
gameVersions.set(mcVersions)
loaders.add("fabric")
@@ -57,10 +103,10 @@ if (!release.isRelease || release.isMainBranch) { // Only publish releases from
hangarPublish {
publications.register("plugin") {
version.set(release.version)
version.set(suffixedVersion)
id.set("ViaRewind")
channel.set(release.hangarChannel)
changelog.set(release.changelog)
channel.set(if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha")
changelog.set(changelogContent)
apiKey.set(System.getenv("HANGAR_TOKEN"))
platforms {
paper {
+2 -6
View File
@@ -1,8 +1,4 @@
plugins {
id("via.addon_subproject")
}
dependencies {
compileOnly(projects.viarewindCommon)
compileOnly(libs.spigot.api)
compileOnly(project(":viarewind-common"))
compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
}
+54 -6
View File
@@ -1,11 +1,59 @@
import de.florianreuth.baseproject.latestCommitHash
plugins {
id("via.addon_subproject")
id("extra.fill_build_constants")
id("via.run_viaproxy")
id("net.raphimc.class-token-replacer")
}
val viaProxy: Configuration by configurations.creating
dependencies {
compileOnly(libs.netty.all)
compileOnly(libs.guava)
compileOnly(libs.velocity.native)
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.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) {
dependsOn(project.tasks.jar)
from(project.tasks.jar.get().archiveFile.get().asFile)
into(layout.projectDirectory.dir("run/jars"))
val projectName = project.name
rename { "${projectName}.jar" }
}
val cleanupViaProxyFiles by tasks.registering(Delete::class) {
delete(
layout.projectDirectory.file("run/jars/${project.name}.jar"),
layout.projectDirectory.dir("run/logs")
)
}
tasks.register<JavaExec>("runViaProxy") {
dependsOn(prepareViaProxyFiles)
finalizedBy(cleanupViaProxyFiles)
mainClass.set("net.raphimc.viaproxy.ViaProxy")
classpath = viaProxy
workingDir = layout.projectDirectory.dir("run").asFile
jvmArgs = listOf("-DskipUpdateCheck")
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
}
}
@@ -55,8 +55,8 @@ public class ViaRewindConfig extends Config implements com.viaversion.viarewind.
cooldownIndicator = CooldownIndicator.valueOf(getString("cooldown-indicator", "TITLE").toUpperCase());
replaceAdventureMode = getBoolean("replace-adventure", false);
replaceParticles = getBoolean("replace-particles", false);
maxBookPages = getInt("max-book-pages", 100);
maxBookPageSize = getInt("max-book-page-length", 5000);
maxBookPages = getInt("end-book-pages", 100);
maxBookPageSize = getInt("end-book-page-length", 5000);
emulateWorldBorder = getBoolean("emulate-world-border", true);
alwaysShowOriginalMobName = getBoolean("always-show-original-mob-name", true);
worldBorderParticle = getString("world-border-particle", "fireworksSpark");
@@ -54,14 +54,14 @@ public interface ViaRewindConfig extends Config {
/**
* Max amount of pages for written books before a client gets kicked
*
* @return the max amount of pages
* @return the end amount of pages
*/
int getMaxBookPages();
/**
* Max amount of characters in the json (!) string of a book page before a client gets kicked
*
* @return the max amount of characters
* @return the end amount of characters
*/
int getMaxBookPageSize();
@@ -91,13 +91,4 @@ public interface ViaRewindPlatform {
return "decompress";
}
/**
* Returns whether the platform's compression handler also writes the packet length. Only used in the 1.8 -> 1.7 protocol.
*
* @return true if the compression handler writes the packet length
*/
default boolean compressHandlerWritesLength() {
return false;
}
}
@@ -1,157 +0,0 @@
/*
* 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);
}
}
}
@@ -20,13 +20,9 @@ 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 {
public final class RewindMappingData extends BackwardsMappingData {
public RewindMappingData(String unmappedVersion, String mappedVersion) {
super(unmappedVersion, mappedVersion);
@@ -42,8 +38,4 @@ public class RewindMappingData extends BackwardsMappingData {
return RewindMappingDataLoader.INSTANCE.loadNBTFromDir(name);
}
@Override
protected FullMappings createFullMappings(MappingDataLoader.IdentifiersPair identifiersPair, Mappings mappings) {
return new LegacyFullStringMappingsBase(identifiersPair, mappings);
}
}
@@ -22,7 +22,7 @@ import com.viaversion.viarewind.ViaRewind;
import java.io.File;
import java.util.logging.Logger;
public class RewindMappingDataLoader extends BackwardsMappingDataLoader {
public final class RewindMappingDataLoader extends BackwardsMappingDataLoader {
public static final RewindMappingDataLoader INSTANCE = new RewindMappingDataLoader();
@@ -30,7 +30,6 @@ public class RewindMappingDataLoader extends BackwardsMappingDataLoader {
super(RewindMappingDataLoader.class, "assets/viarewind/data/");
}
@Override
public Logger getLogger() {
return ViaRewind.getPlatform().getLogger();
@@ -40,4 +39,5 @@ public class RewindMappingDataLoader extends BackwardsMappingDataLoader {
public File getDataFolder() {
return ViaRewind.getPlatform().getDataFolder();
}
}
@@ -23,6 +23,7 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
public enum EntityDataTypes1_7_6_10 implements EntityDataType {
BYTE(0, Types.BYTE),
SHORT(1, Types.SHORT),
INT(2, Types.INT),
@@ -34,12 +35,12 @@ public enum EntityDataTypes1_7_6_10 implements EntityDataType {
private final int typeID;
private final Type<?> type;
EntityDataTypes1_7_6_10(int typeID, Type<?> type) {
EntityDataTypes1_7_6_10(final int typeID, final Type<?> type) {
this.typeID = typeID;
this.type = type;
}
public static EntityDataTypes1_7_6_10 byId(int id) {
public static EntityDataTypes1_7_6_10 byId(final int id) {
return values()[id];
}
@@ -52,4 +53,5 @@ public enum EntityDataTypes1_7_6_10 implements EntityDataType {
public Type<?> type() {
return this.type;
}
}
@@ -17,20 +17,8 @@
*/
package com.viaversion.viarewind.api.minecraft.math;
public class AABB {
Vector3d min;
Vector3d max;
import com.viaversion.viaversion.api.minecraft.Vector3d;
public AABB(Vector3d min, Vector3d max) {
this.min = min;
this.max = max;
}
public record Box(Vector3d start, Vector3d end) {
public Vector3d getMin() {
return this.min;
}
public Vector3d getMax() {
return this.max;
}
}
@@ -17,42 +17,58 @@
*/
package com.viaversion.viarewind.api.minecraft.math;
public class RayTracing {
import com.viaversion.viaversion.api.minecraft.Vector3d;
public static Vector3d trace(Ray3d ray, AABB aabb, double distance) {
Vector3d invDir = new Vector3d(1f / ray.dir.x, 1f / ray.dir.y, 1f / ray.dir.z);
public final class RayTracing {
boolean signDirX = invDir.x < 0;
boolean signDirY = invDir.y < 0;
boolean signDirZ = invDir.z < 0;
public static Vector3d trace(final Box ray, final Box box, final double distance) {
final Vector3d invDir = new Vector3d(1f / ray.end().x(), 1f / ray.end().y(), 1f / ray.end().z());
Vector3d bbox = signDirX ? aabb.max : aabb.min;
double tmin = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirX ? aabb.min : aabb.max;
double tmax = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirY ? aabb.max : aabb.min;
double tymin = (bbox.y - ray.start.y) * invDir.y;
bbox = signDirY ? aabb.min : aabb.max;
double tymax = (bbox.y - ray.start.y) * invDir.y;
final boolean signDirX = invDir.x() < 0;
final boolean signDirY = invDir.y() < 0;
final boolean signDirZ = invDir.z() < 0;
if (tmin > tymax || tymin > tmax) return null;
Vector3d boundingBox = signDirX ? box.end() : box.start();
double tmin = (boundingBox.x() - ray.start().x()) * invDir.x();
boundingBox = signDirX ? box.start() : box.end();
double tmax = (boundingBox.x() - ray.start().x()) * invDir.x();
boundingBox = signDirY ? box.end() : box.start();
final double tymin = (boundingBox.y() - ray.start().y()) * invDir.y();
boundingBox = signDirY ? box.start() : box.end();
final double tymax = (boundingBox.y() - ray.start().y()) * invDir.y();
if (tymin > tmin) tmin = tymin;
if (tmin > tymax || tymin > tmax) {
return null;
}
if (tymax < tmax) tmax = tymax;
if (tymin > tmin) {
tmin = tymin;
}
if (tymax < tmax) {
tmax = tymax;
}
bbox = signDirZ ? aabb.max : aabb.min;
double tzmin = (bbox.z - ray.start.z) * invDir.z;
bbox = signDirZ ? aabb.min : aabb.max;
double tzmax = (bbox.z - ray.start.z) * invDir.z;
boundingBox = signDirZ ? box.end() : box.start();
double tzmin = (boundingBox.z() - ray.start().z()) * invDir.z();
boundingBox = signDirZ ? box.start() : box.end();
double tzmax = (boundingBox.z() - ray.start().z()) * invDir.z();
if (tmin > tzmax || tzmin > tmax) return null;
if (tmin > tzmax || tzmin > tmax) {
return null;
}
if (tzmin > tmin) tmin = tzmin;
if (tzmin > tmin) {
tmin = tzmin;
}
if (tzmax < tmax) {
tmax = tzmax;
}
if (tzmax < tmax) tmax = tzmax;
if (tmin <= distance && tmax > 0) {
// ray.start().clone().add(ray.dir().clone().normalize().multiply(tmin))
}
return tmin <= distance && tmax > 0 ? ray.start.clone().add(ray.dir.clone().normalize().multiply(tmin)) : null;
return null;
}
}
@@ -17,13 +17,17 @@
*/
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(EntityTracker1_9 tracker, int entityId, int relX, int relY, int relZ) {
public static Vector[] calculateRelativeMoves(UserConnection user, int entityId, int relX, int relY, int relZ) {
EntityTracker1_9 tracker = user.getEntityTracker(Protocol1_9To1_8.class);
int x;
int y;
int z;
@@ -1,128 +0,0 @@
/*
* 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.minecraft.math;
import java.util.Objects;
public class Vector3d {
double x, y, z;
public Vector3d(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3d() {
}
public void set(Vector3d vec) {
this.x = vec.x;
this.y = vec.y;
this.z = vec.z;
}
public Vector3d set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
public Vector3d multiply(double a) {
this.x *= a;
this.y *= a;
this.z *= a;
return this;
}
public Vector3d add(Vector3d vec) {
this.x += vec.x;
this.y += vec.y;
this.z += vec.z;
return this;
}
public Vector3d substract(Vector3d vec) {
this.x -= vec.x;
this.y -= vec.y;
this.z -= vec.z;
return this;
}
public double length() {
return Math.sqrt(lengthSquared());
}
public double lengthSquared() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
public Vector3d normalize() {
double length = length();
multiply(1.0 / length);
return this;
}
@Override
public Vector3d clone() {
return new Vector3d(this.x, this.y, this.z);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vector3d vector3d = (Vector3d) o;
return Double.compare(vector3d.x, x) == 0 && Double.compare(vector3d.y, y) == 0 && Double.compare(vector3d.z, z) == 0;
}
@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
@Override
public String toString() {
return "Vector3d{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
}
public double getX() {
return this.x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return this.y;
}
public void setY(double y) {
this.y = y;
}
public double getZ() {
return this.z;
}
public void setZ(double z) {
this.z = z;
}
}
@@ -17,24 +17,14 @@
*/
package com.viaversion.viarewind.api.minecraft.netty;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class ForwardMessageToByteEncoder extends MessageToByteEncoder<ByteBuf> {
private final boolean writeLength;
public ForwardMessageToByteEncoder(final boolean writeLength) {
this.writeLength = writeLength;
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
if (writeLength) {
Types.VAR_INT.writePrimitive(out, msg.readableBytes());
}
out.writeBytes(msg);
}
}
@@ -17,13 +17,11 @@
*/
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.type.Type;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
@@ -65,48 +63,41 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
);
}
final ByteBuf uncompressed = buffer.alloc().buffer(totalSize);
final byte[] data = new byte[totalSize];
int offset = 0;
try {
for (int i = 0; i < chunkCount; i++) {
Chunk chunk = chunks[i];
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
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();
offset = ChunkType1_7_6.serialize(
chunk,
data,
offset,
addBitMasks[i],
anySkyLight,
biomes
);
}
buffer.writeShort(chunkCount);
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();
}
buffer.writeInt(compressedSize);
buffer.writeBoolean(anySkyLight);
buffer.writeBytes(compressedData, 0, compressedSize);
for (int i = 0; i < chunkCount; i++) {
Chunk chunk = chunks[i];
buffer.writeInt(chunk.getX());
@@ -17,15 +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.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight.LIGHT_LENGTH;
@@ -51,41 +49,33 @@ public class ChunkType1_7_6 extends Type<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 byte[] data = new byte[size];
serialize(chunk, data, 0, addBitmask, hasSkyLight, biomes);
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeBoolean(chunk.isFullChunk());
buffer.writeShort(bitmask);
buffer.writeShort(addBitmask);
final Deflater deflater = new Deflater();
byte[] compressedData;
int compressedSize;
try {
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);
deflater.setInput(data, 0, data.length);
deflater.finish();
compressedData = new byte[data.length];
compressedSize = deflater.deflate(compressedData);
} finally {
uncompressed.release();
deflater.end();
}
buffer.writeInt(compressedSize);
buffer.writeBytes(compressedData, 0, compressedSize);
}
public static void serialize(Chunk chunk, ByteBuf output, int addBitmask, boolean writeSkyLight, boolean biomes) {
public static int serialize(Chunk chunk, byte[] output, int offset, int addBitmask, boolean writeSkyLight, boolean biomes) {
final ChunkSection[] sections = chunk.getSections();
final int bitmask = chunk.getBitmask();
@@ -95,7 +85,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
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);
output[offset++] = (byte) ((block >> 4) & 0xFF);
}
}
}
@@ -107,7 +97,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
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));
output[offset++] = (byte) (meta1 | (meta2 << 4));
}
}
}
@@ -115,7 +105,8 @@ public class ChunkType1_7_6 extends Type<Chunk> {
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final byte[] blockLight = sections[i].getLight().getBlockLight();
output.writeBytes(blockLight);
System.arraycopy(blockLight, 0, output, offset, LIGHT_LENGTH);
offset += LIGHT_LENGTH;
}
}
@@ -124,11 +115,9 @@ public class ChunkType1_7_6 extends Type<Chunk> {
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);
System.arraycopy(skyLight, 0, output, offset, LIGHT_LENGTH);
}
offset += LIGHT_LENGTH;
}
}
}
@@ -141,7 +130,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
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));
output[offset++] = (byte) (add1 | (add2 << 4));
}
}
}
@@ -150,9 +139,11 @@ public class ChunkType1_7_6 extends Type<Chunk> {
if (biomes && chunk.getBiomeData() != null) {
final int[] biomeData = chunk.getBiomeData();
for (int biome : biomeData) {
output.writeByte(biome);
output[offset++] = (byte) biome;
}
}
return offset;
}
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
@@ -38,7 +38,6 @@ 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;
@@ -123,16 +122,14 @@ public class Protocol1_8To1_7_6_10 extends BackwardsProtocol<ClientboundPackets1
@Override
public void init(UserConnection connection) {
final ProtocolStorables storables = connection.storables(this);
storables.setEntityTracker(new EntityTracker1_8(connection));
storables.setClientWorld(new ClientWorld());
connection.addEntityTracker(this.getClass(), new EntityTracker1_8(connection));
connection.addClientWorld(this.getClass(), new ClientWorld());
connection.put(new InventoryTracker(connection));
connection.put(new PlayerSessionStorage(connection));
connection.put(new GameProfileStorage(connection));
connection.put(new ScoreboardTracker(connection));
connection.put(new CompressionStatusTracker(connection));
connection.put(new WorldBorderEmulator(connection));
}
@Override
@@ -26,18 +26,6 @@ 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());
}
@@ -59,28 +47,8 @@ public class ChatNewlineRewriter {
}
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));
results.addAll(splitChatComponentByNewline(child, inheritedStyle));
}
return results;
}
@@ -92,7 +60,13 @@ public class ChatNewlineRewriter {
JsonObject obj = element.getAsJsonObject();
// Build the style for this node: inherit first, then override with this object's style
JsonObject currentStyle = mergeStyle(inheritedStyle, obj);
JsonObject currentStyle = inheritedStyle.deepCopy();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
String key = entry.getKey();
if (!"text".equals(key) && !"extra".equals(key)) {
currentStyle.add(key, entry.getValue());
}
}
// Gather components in order: base text then extra array
List<JsonElement> ordered = new ArrayList<>();
@@ -107,19 +81,12 @@ public class ChatNewlineRewriter {
// 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);
results.add(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;
@@ -160,55 +127,6 @@ public class ChatNewlineRewriter {
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();
@@ -221,14 +139,11 @@ public class ChatNewlineRewriter {
return first;
}
JsonObject merged = new JsonObject();
merged.addProperty("text", "");
JsonArray extra = new JsonArray();
for (JsonObject linePart : lineParts) {
extra.add(linePart);
for (int i = 1; i < lineParts.size(); i++) {
extra.add(lineParts.get(i));
}
merged.add("extra", extra);
return merged;
first.add("extra", extra);
return first;
}
}
@@ -18,14 +18,14 @@
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
import com.viaversion.viarewind.api.minecraft.math.AABB;
import com.viaversion.viarewind.api.minecraft.math.Vector3d;
import com.viaversion.viarewind.api.minecraft.math.Box;
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.Vector3d;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_8;
@@ -328,14 +328,14 @@ public class VirtualHologramEntity {
}
}
public AABB getBoundingBox() {
public Box getBoundingBox() {
final double width = this.small ? 0.25 : 0.5;
final double height = this.small ? 0.9875 : 1.975;
final Vector3d min = new Vector3d(this.locX - width / 2, this.locY, this.locZ - width / 2);
final Vector3d max = new Vector3d(this.locX + width / 2, this.locY + height, this.locZ + width / 2);
return new AABB(min, max);
return new Box(min, max);
}
private int additionalEntityId() {
@@ -20,6 +20,7 @@ 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 {
@@ -27,6 +28,23 @@ public abstract class CompressionHandlerProvider implements Provider {
public abstract void onTransformPacket(UserConnection user);
/**
* Creates the compression encoder
*
* @param compressor A nullable object that may present a native backend for compression (if available)
* @param threshold The compression threshold
* @return The encoder
*/
public abstract ChannelHandler getEncoder(Object compressor, int threshold);
/**
* Creates the compression decoder
*
* @param compressor A nullable object that may present a native backend for compression (if available)
* @param threshold The compression threshold
* @return The decoder
*/
public abstract ChannelHandler getDecoder(Object compressor, int threshold);
public boolean isRemoveCompression(UserConnection user) {
return user.get(CompressionStatusTracker.class).removeCompression;
@@ -17,15 +17,18 @@
*/
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.DataFormatException;
import java.util.zip.Inflater;
public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
private final Inflater inflater = new Inflater();
private int threshold;
@@ -55,12 +58,25 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
}
ByteBuf output = ctx.alloc().buffer(outLength);
inflate(ctx, in, outLength, out);
}
protected void inflate(final ChannelHandlerContext ctx, final ByteBuf source, final int outLength, final List<Object> out) throws DataFormatException {
ByteBuf temp = source;
if (!source.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
} else {
source.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(outLength, outLength);
try {
ThreadLocalCompressionProvider.inflate(in, output, outLength);
this.inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
output.writerIndex(output.writerIndex() + this.inflater.inflate(output.array(), output.arrayOffset(), outLength));
out.add(output.retain());
} finally {
output.release();
temp.release();
this.inflater.reset();
}
}
@@ -17,13 +17,16 @@
*/
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.DataFormatException;
import java.util.zip.Deflater;
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
private final Deflater deflater = new Deflater();
private int threshold;
@@ -45,7 +48,32 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
Types.VAR_INT.writePrimitive(out, frameLength);
ThreadLocalCompressionProvider.deflate(in, out);
deflate(in, out);
}
protected void deflate(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 {
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()));
}
destination.writeBytes(output);
} finally {
output.release();
temp.release();
this.deflater.reset();
}
}
}
@@ -21,12 +21,28 @@ import com.viaversion.viarewind.ViaRewind;
import com.viaversion.viarewind.api.minecraft.netty.EmptyChannelHandler;
import com.viaversion.viarewind.api.minecraft.netty.ForwardMessageToByteEncoder;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity.VelocityCompressionDecoder;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity.VelocityCompressionEncoder;
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 {
// Use Velocity's compression backend if ViaRewind is loaded on a Velocity server or a custom server software
// is shipping them in the classpath.
private static boolean velocityNatives;
static {
try {
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
velocityNatives = true;
} catch (final ClassNotFoundException ignored) {
velocityNatives = false;
}
}
@Override
public void setCompressionThreshold(UserConnection user, int threshold) {
final ChannelPipeline pipeline = user.getChannel().pipeline();
@@ -35,12 +51,17 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
return;
}
Object compressor = null;
if (velocityNatives) {
compressor = com.velocitypowered.natives.util.Natives.compress.get().create(-1);
}
final String compressHandlerName = ViaRewind.getPlatform().compressHandlerName();
final CompressionEncoder encoder = (CompressionEncoder) pipeline.get(compressHandlerName);
if (encoder != null) {
encoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, new CompressionEncoder(threshold));
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, getEncoder(compressor, threshold));
}
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
@@ -48,7 +69,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
if (decoder != null) {
decoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, new CompressionDecoder(threshold));
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(compressor, threshold));
}
}
@@ -66,7 +87,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
if (compressor != null) { // We can neutralize the effect of compressor to the client
pipeline.replace(decompressor, decompressor, new EmptyChannelHandler());
pipeline.replace(compressor, compressor, new ForwardMessageToByteEncoder(ViaRewind.getPlatform().compressHandlerWritesLength()));
pipeline.replace(compressor, compressor, new ForwardMessageToByteEncoder());
} else {
throw new IllegalStateException("Couldn't remove compression for 1.7!");
}
@@ -75,5 +96,22 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
}
}
@Override
public ChannelHandler getEncoder(final Object compressor, int threshold) {
if (velocityNatives) {
return new VelocityCompressionEncoder(threshold, (com.velocitypowered.natives.compression.VelocityCompressor) compressor);
} else {
return new CompressionEncoder(threshold);
}
}
@Override
public ChannelHandler getDecoder(final Object compressor, int threshold) {
if (velocityNatives) {
return new VelocityCompressionDecoder(threshold, (com.velocitypowered.natives.compression.VelocityCompressor) compressor);
} else {
return new CompressionDecoder(threshold);
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.provider.compression.velocity;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.CompressionDecoder;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import java.util.List;
import java.util.zip.DataFormatException;
public final class VelocityCompressionDecoder extends CompressionDecoder {
private final VelocityCompressor compressor;
public VelocityCompressionDecoder(final int threshold, final VelocityCompressor compressor) {
super(threshold);
this.compressor = compressor;
}
@Override
protected void inflate(final ChannelHandlerContext ctx, final ByteBuf source, final int outLength, final List<Object> out) throws DataFormatException {
ByteBuf output = ctx.alloc().buffer(outLength);
try {
this.compressor.inflate(source, output, outLength);
out.add(output.retain());
} finally {
output.release();
}
}
}
@@ -15,22 +15,25 @@
* 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.math;
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity;
public class Ray3d {
Vector3d start;
Vector3d dir;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.CompressionEncoder;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
public Ray3d(Vector3d start, Vector3d dir) {
this.start = start;
this.dir = dir;
public final class VelocityCompressionEncoder extends CompressionEncoder {
private final VelocityCompressor compressor;
public VelocityCompressionEncoder(final int threshold, final VelocityCompressor compressor) {
super(threshold);
this.compressor = compressor;
}
public Vector3d getStart() {
return this.start;
@Override
protected void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
this.compressor.deflate(source, destination);
}
public Vector3d getDir() {
return this.dir;
}
}
@@ -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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final UUID userId = wrapper.user().getProtocolInfo().getUuid();
final Item[] items = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, 0);
@@ -76,7 +76,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
}
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
tracker.addPlayer(entityId, wrapper.user().getProtocolInfo().getUuid());
tracker.setClientEntityGameMode(wrapper.get(Types.UNSIGNED_BYTE, 0));
@@ -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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
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());
@@ -354,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final int id = wrapper.get(Types.INT, 0);
final int limit = tracker.clientEntityId() == id ? 3 : 4;
@@ -364,7 +364,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
});
handler(wrapper -> {
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final short slot = wrapper.get(Types.SHORT, 0);
final UUID uuid = tracker.getPlayerUUID(wrapper.get(Types.INT, 0));
if (uuid == null) {
@@ -396,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(protocol).removeEntity(wrapper.get(Types.INT, 0)));
handler(wrapper -> wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class).removeEntity(wrapper.get(Types.INT, 0)));
}
});
@@ -424,7 +424,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
read(Types.BOOLEAN); // On ground
handler(wrapper -> {
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
if (hologram != null) {
@@ -448,7 +448,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
read(Types.BOOLEAN); // On ground
handler(wrapper -> {
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
if (hologram != null) {
@@ -474,7 +474,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
read(Types.BOOLEAN); // On ground
handler(wrapper -> {
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
if (hologram != null) {
@@ -506,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.entityType(entityId) == EntityTypes1_8.EntityType.ABSTRACT_MINECART) { // TODO | Realign all entities?
int y = wrapper.get(Types.INT, 2);
y += 12;
@@ -537,7 +537,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
map(Types.BYTE); // Head yaw
handler(wrapper -> {
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
if (hologram != null) {
@@ -559,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final int passenger = wrapper.get(Types.INT, 0);
final int vehicle = wrapper.get(Types.INT, 1);
@@ -594,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.getHolograms().containsKey(wrapper.get(Types.INT, 0))) { // Don't handle properties for hologram emulation
wrapper.cancel();
return;
@@ -21,10 +21,8 @@ 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.minecraft.math.AABB;
import com.viaversion.viarewind.api.minecraft.math.Ray3d;
import com.viaversion.viarewind.api.minecraft.math.Box;
import com.viaversion.viarewind.api.minecraft.math.RayTracing;
import com.viaversion.viarewind.api.minecraft.math.Vector3d;
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_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
@@ -38,7 +36,6 @@ 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;
@@ -49,7 +46,6 @@ 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;
@@ -129,13 +125,13 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
}
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
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().storables(protocol).clientWorld();
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
if (world.setEnvironment(dimension)) {
tracker.clearEntities();
tracker.addPlayer(tracker.clientEntityId(), wrapper.user().getProtocolInfo().getUuid());
@@ -148,8 +144,8 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
return;
}
// On 1.8 clients when the player respawns it keeps all metadata and sets player back to max HP
// 1.7 clients do not copy the metadata but do set the health to max HP
// On 1.8 clients when the player respawns it keeps all metadata and sets player back to end HP
// 1.7 clients do not copy the metadata but do set the health to end HP
// to match the same client logic we remove the HP metadata as otherwise the client would be set to their old HP before respawning
final List<EntityData> entityDataList = new ArrayList<>(tracker.getEntityData());
entityDataList.removeIf(entityData -> entityData.id() == 6);
@@ -196,7 +192,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (!Objects.equals(tracker.spectatingClientEntityId, tracker.clientEntityIdOrNull())) {
wrapper.cancel();
}
@@ -222,7 +218,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (gameMode == 3 || tracker.isSpectator()) {
UUID myId = wrapper.user().getProtocolInfo().getUuid();
Item[] equipment = new Item[4];
@@ -294,7 +290,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
}
if (gamemode == 3 || gameProfile.gamemode == 3) {
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
int entityId = tracker.getPlayerEntityId(uuid);
boolean isOwnPlayer = entityId == tracker.clientEntityId();
if (entityId != -1) {
@@ -447,7 +443,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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.spectatingClientEntityId != entityId) {
tracker.setSpectating(entityId);
}
@@ -508,18 +504,12 @@ 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(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.isSpectator() && message.toLowerCase().startsWith("/stp ")) { // TODO add setting
String username = message.split(" ")[1];
final GameProfileStorage storage = wrapper.user().get(GameProfileStorage.class);
// 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);
final GameProfileStorage.GameProfile profile = storage.get(username, true);
if (profile != null && profile.uuid != null) {
wrapper.cancel();
@@ -537,7 +527,10 @@ 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);
if (mode != 0) {
return;
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final int entityId = tracker.getHologramIdWithExtra(wrapper.get(Types.VAR_INT, 0));
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
@@ -546,25 +539,20 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
}
wrapper.set(Types.VAR_INT, 0, entityId);
if (mode != 0) {
return;
}
final AABB boundingBox = tracker.getHolograms().get(entityId).getBoundingBox();
final Box boundingBox = tracker.getHolograms().get(entityId).getBoundingBox();
Vector3d pos = new Vector3d(position.getPosX(), position.getPosY() + 1.8, position.getPosZ());
double yaw = Math.toRadians(position.yaw);
double pitch = Math.toRadians(position.pitch);
Vector3d dir = new Vector3d(-Math.cos(pitch) * Math.sin(yaw), -Math.sin(pitch), Math.cos(pitch) * Math.cos(yaw));
Ray3d ray = new Ray3d(pos, dir);
Rotation ray = new Rotation(pos, dir);
Vector3d intersection = RayTracing.trace(ray, boundingBox, 5.0);
if (intersection == null) {
return;
}
intersection.substract(boundingBox.getMin());
intersection.substract(boundingBox.start());
wrapper.set(Types.VAR_INT, 1, 2);
wrapper.write(Types.FLOAT, (float) intersection.getX());
@@ -618,16 +606,6 @@ 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);
});
}
});
@@ -638,16 +616,6 @@ 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)));
}
});
@@ -720,7 +688,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
if (!unmount) {
return;
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.spectatingClientEntityId != tracker.clientEntityId()) {
PacketWrapper sneakPacket = PacketWrapper.create(ServerboundPackets1_8.PLAYER_COMMAND, wrapper.user());
sneakPacket.write(Types.VAR_INT, tracker.clientEntityId());
@@ -800,8 +768,6 @@ 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() {
@@ -812,27 +778,7 @@ 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); // 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
wrapper.read(Types.REMAINING_BYTES); // unused data ???
break;
}
case "MC|ItemName": {
@@ -849,28 +795,17 @@ 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);
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);
}
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);
}
}
protocol.getItemRewriter().handleItemToServer(wrapper.user(), book);
wrapper.write(Types.ITEM1_8, book);
break;
}
@@ -174,7 +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 EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final byte mode = wrapper.passthrough(Types.BYTE);
if (mode != 0 && !scoreboard.teamExists(team)) {
@@ -24,6 +24,7 @@ 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.Particles1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
import com.viaversion.viarewind.utils.ChatUtil;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
@@ -47,7 +48,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().storables(protocol).clientWorld();
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
final Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(world.getEnvironment()));
protocol.getItemRewriter().handleChunk(chunk);
@@ -224,12 +225,7 @@ public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
columnData[i + 3] = data[column + i * columns];
}
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) columnData.length); // data length
mapData.write(new FixedByteArrayType(columnData.length), columnData); // data
mapData.send(Protocol1_8To1_7_6_10.class);
sendMapItemData(wrapper.user(), id, columnData);
}
}
@@ -242,12 +238,7 @@ public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
iconData[i * 3 + 3] = icons[i * 4 + 2];
}
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) iconData.length); // data length
mapData.write(new FixedByteArrayType(iconData.length), iconData); // data
mapData.send(Protocol1_8To1_7_6_10.class);
sendMapItemData(wrapper.user(), id, iconData);
}
// Update scale
@@ -259,35 +250,55 @@ public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
mapData.send(Protocol1_8To1_7_6_10.class);
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // position
map(Types.UNSIGNED_BYTE); // action
map(Types.NAMED_COMPOUND_TAG, RewindTypes.COMPRESSED_NBT); // nbt
}
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_ENTITY_DATA, wrapper -> {
wrapper.passthroughAndMap(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // Position
wrapper.passthrough(Types.UNSIGNED_BYTE); // Action
wrapper.passthroughAndMap(Types.NAMED_COMPOUND_TAG, RewindTypes.COMPRESSED_NBT); // Tag
});
protocol.cancelClientbound(ClientboundPackets1_8.CHANGE_DIFFICULTY);
protocol.registerClientbound(ClientboundPackets1_8.SET_BORDER, null, wrapper -> {
final WorldBorderEmulator emulator = wrapper.user().get(WorldBorderEmulator.class);
wrapper.cancel();
final int action = wrapper.read(Types.VAR_INT);
if (action == 3) { // initialize
final double x = wrapper.read(Types.DOUBLE);
final double z = wrapper.read(Types.DOUBLE);
final double oldRadius = wrapper.read(Types.DOUBLE);
final double newRadius = wrapper.read(Types.DOUBLE);
final long speed = wrapper.read(Types.VAR_LONG);
wrapper.user().put(new WorldBorderEmulator(wrapper.user(), x, z, oldRadius, newRadius, speed));
}
final WorldBorderEmulator emulator = wrapper.user().get(WorldBorderEmulator.class);
if (emulator == null) {
return;
}
if (action == 0) { // set size
emulator.setSize(wrapper.read(Types.DOUBLE)); // radius
} else if (action == 1) { // lerp size
emulator.lerpSize(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), wrapper.read(Types.VAR_LONG)); // old radius, new radius, speed
final double radius = wrapper.read(Types.DOUBLE);
emulator.setSize(radius);
} else if (action == 1) { // delta size
final double oldRadius = wrapper.read(Types.DOUBLE);
final double newRadius = wrapper.read(Types.DOUBLE);
final long speed = wrapper.read(Types.VAR_LONG);
emulator.updateDeltaTime(oldRadius, newRadius, speed);
} else if (action == 2) { // set center
emulator.setCenter(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE)); // x, z
} else if (action == 3) { // initialize
emulator.init(
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // x, z
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // old radius, new radius
wrapper.read(Types.VAR_LONG) // speed
);
final double x = wrapper.read(Types.DOUBLE);
final double z = wrapper.read(Types.DOUBLE);
emulator.setCenter(x, z);
}
});
}
private void sendMapItemData(final UserConnection connection, final int id, final byte[] columnData) {
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, connection);
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) columnData.length); // data length
mapData.write(new FixedByteArrayType(columnData.length), columnData); // data
mapData.send(Protocol1_8To1_7_6_10.class);
}
}
@@ -22,7 +22,6 @@ 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;
@@ -69,15 +68,17 @@ public class EntityTracker1_8 extends EntityTrackerBase {
}
@Override
public TrackedEntity addEntity(int id, EntityType type) {
public void addEntity(int id, EntityType type) {
super.addEntity(id, type);
if (type == EntityTypes1_8.EntityType.ARMOR_STAND) {
holograms.put(id, new VirtualHologramEntity(user(), id));
}
return super.addEntity(id, type);
}
@Override
public TrackedEntity removeEntity(int entityId) {
public void removeEntity(int entityId) {
super.removeEntity(entityId);
final VirtualHologramEntity hologram = holograms.get(entityId);
if (hologram != null) {
hologram.deleteEntity();
@@ -94,7 +95,6 @@ public class EntityTracker1_8 extends EntityTrackerBase {
entityUUIDToId.removeInt(playerId);
user().get(PlayerSessionStorage.class).getPlayerEquipment().remove(playerId);
}
return super.removeEntity(entityId);
}
@Override
@@ -222,16 +222,17 @@ public class EntityTracker1_8 extends EntityTrackerBase {
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);
final GameProfileStorage.GameProfile profile = profileStorage.get(username, false);
if (profile == null) {
return;
}
final int entityId = getPlayerEntityId(profile.uuid);
if (entityId == -1) {
return;
}
checkNametagVisibility(entityId);
}
private boolean isPlayerNametagHidden(final int entityId) {
@@ -17,10 +17,6 @@
*/
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;
@@ -38,7 +34,6 @@ 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);
@@ -47,7 +42,6 @@ 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;
}
@@ -55,9 +49,19 @@ public class GameProfileStorage extends StoredObject {
return properties.get(uuid);
}
public List<GameProfile> get(String name, boolean ignoreCase) {
public GameProfile get(String name, boolean ignoreCase) {
if (ignoreCase) name = name.toLowerCase();
return nameToProfile.get(name);
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;
}
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
@@ -77,11 +81,7 @@ public class GameProfileStorage extends StoredObject {
}
public GameProfile remove(UUID uuid) {
GameProfile removedProfile = properties.remove(uuid);
if (removedProfile != null) {
nameToProfile.get(removedProfile.name).remove(removedProfile);
}
return removedProfile;
return properties.remove(uuid);
}
@@ -30,7 +30,8 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
public class ScoreboardTracker extends StoredObject {
public final class ScoreboardTracker extends StoredObject {
private final HashMap<String, List<String>> teams = new HashMap<>();
private final HashSet<String> objectives = new HashSet<>();
private final HashMap<String, ScoreTeam> scoreTeams = new HashMap<>();
@@ -40,76 +41,74 @@ public class ScoreboardTracker extends StoredObject {
private final HashMap<String, String> teamNameTagVisibilities = new HashMap<>();
private String colorIndependentSidebar;
public ScoreboardTracker(UserConnection user) {
public ScoreboardTracker(final UserConnection user) {
super(user);
}
public void addPlayerToTeam(String player, String team) {
public void addPlayerToTeam(final String player, final String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>()).add(player);
}
public void setTeamColor(String team, Byte color) {
public void setTeamColor(final String team, final Byte color) {
teamColors.put(team, color);
}
public Optional<Byte> getTeamColor(String team) {
public Optional<Byte> getTeamColor(final String team) {
return Optional.ofNullable(teamColors.get(team));
}
public void addTeam(String team) {
public void addTeam(final String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>());
}
public void removeTeam(String team) {
public void removeTeam(final String team) {
teams.remove(team);
scoreTeams.remove(team);
teamColors.remove(team);
teamNameTagVisibilities.remove(team);
}
public boolean teamExists(String team) {
public boolean teamExists(final String team) {
return teams.containsKey(team);
}
public void removePlayerFromTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
if (teamPlayers != null) teamPlayers.remove(player);
final List<String> teamPlayers = teams.get(team);
if (teamPlayers != null) {
teamPlayers.remove(player);
}
}
public boolean isPlayerInTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
public boolean isPlayerInTeam(final String player, final String team) {
final List<String> teamPlayers = teams.get(team);
return teamPlayers != null && teamPlayers.contains(player);
}
public boolean isPlayerInTeam(String player) {
for (List<String> teamPlayers : teams.values()) {
if (teamPlayers.contains(player)) return true;
}
return false;
public Optional<Byte> getPlayerTeamColor(final String player) {
final Optional<String> team = getTeam(player);
return team.flatMap(this::getTeamColor);
}
public Optional<Byte> getPlayerTeamColor(String player) {
Optional<String> team = getTeam(player);
return team.isPresent() ? getTeamColor(team.get()) : Optional.empty();
}
public Optional<String> getTeam(String player) {
for (Map.Entry<String, List<String>> entry : teams.entrySet())
if (entry.getValue().contains(player))
public Optional<String> getTeam(final String player) {
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
if (entry.getValue().contains(player)) {
return Optional.of(entry.getKey());
}
}
return Optional.empty();
}
public void setTeamNameTagVisibility(String team, String visibility) {
public void setTeamNameTagVisibility(final String team, final String visibility) {
teamNameTagVisibilities.put(team, visibility);
}
public String getTeamNameTagVisibility(String team) {
public String getTeamNameTagVisibility(final String team) {
return teamNameTagVisibilities.getOrDefault(team, "always");
}
public boolean isNametagHidden(String username) {
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
public boolean isNametagHidden(final String username) {
for (final Map.Entry<String, List<String>> entry : teams.entrySet()) {
if (entry.getValue().contains(username)) {
return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always"));
}
@@ -117,15 +116,15 @@ public class ScoreboardTracker extends StoredObject {
return false;
}
public List<String> getTeamMembers(String team) {
public List<String> getTeamMembers(final String team) {
return teams.getOrDefault(team, new ArrayList<>());
}
public void addObjective(String name) {
public void addObjective(final String name) {
objectives.add(name);
}
public void removeObjective(String name) {
public void removeObjective(final String name) {
objectives.remove(name);
colorDependentSidebar.values().remove(name);
if (name.equals(colorIndependentSidebar)) {
@@ -133,58 +132,63 @@ public class ScoreboardTracker extends StoredObject {
}
}
public boolean objectiveExists(String name) {
public boolean objectiveExists(final String name) {
return objectives.contains(name);
}
public String sendTeamForScore(String score) {
if (score.length() <= 16) return score;
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
// 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;
}
public String sendTeamForScore(final String score) {
if (score.length() <= 16) {
return score;
}
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);
}
name = score.substring(i, i + l);
}
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);
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, user());
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;
}
public String removeTeamForScore(String score) {
ScoreTeam scoreTeam = scoreTeams.remove(score);
if (scoreTeam == null) return score;
public String removeTeamForScore(final String score) {
final ScoreTeam scoreTeam = scoreTeams.remove(score);
if (scoreTeam == null) {
return score;
}
scoreTeamNames.remove(scoreTeam.name);
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, user());
teamPacket.write(Types.STRING, scoreTeam.name);
teamPacket.write(Types.BYTE, (byte) 1);
teamPacket.send(Protocol1_8To1_7_6_10.class);
@@ -196,7 +200,7 @@ public class ScoreboardTracker extends StoredObject {
return this.colorIndependentSidebar;
}
public void setColorIndependentSidebar(String colorIndependentSidebar) {
public void setColorIndependentSidebar(final String colorIndependentSidebar) {
this.colorIndependentSidebar = colorIndependentSidebar;
}
@@ -204,15 +208,6 @@ public class ScoreboardTracker extends StoredObject {
return this.colorDependentSidebar;
}
private static class ScoreTeam {
private final String prefix;
private final String suffix;
private final String name;
public ScoreTeam(String name, String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
this.name = name;
}
private record ScoreTeam(String name, String prefix, String suffix) {
}
}
@@ -19,63 +19,54 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.util.MathUtil;
public final class WorldBorderEmulator extends StoredObject {
public class WorldBorderEmulator extends StoredObject {
private double x, z;
private double oldDiameter, newDiameter;
private long lerpTime;
private long lerpStartTime;
private long deltaTime;
private long deltaStartTime;
private boolean init = false;
public WorldBorderEmulator(UserConnection user) {
public WorldBorderEmulator(UserConnection user, final double x, final double z, final double oldDiameter, final double newDiameter, final long deltaTime) {
super(user);
}
public void init(double x, double z, double oldDiameter, double newDiameter, long lerpTime) {
this.x = x;
this.z = z;
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.lerpTime = lerpTime;
init = true;
this.deltaTime = deltaTime;
}
public void setCenter(double x, double z) {
public void setCenter(final double x, final double z) {
this.x = x;
this.z = z;
}
public void lerpSize(double oldDiameter, double newDiameter, long lerpTime) {
public void updateDeltaTime(final double oldDiameter, final double newDiameter, final long deltaTime) {
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.lerpTime = lerpTime;
this.lerpStartTime = System.currentTimeMillis();
this.deltaTime = deltaTime;
this.deltaStartTime = System.currentTimeMillis();
}
public double getSize() {
if (lerpTime == 0) {
if (deltaTime == 0) {
return newDiameter;
}
double percent = ((double) (System.currentTimeMillis() - lerpStartTime) / (double) (lerpTime));
// Clamp value
if (percent > 1.0D) percent = 1.0d;
else if (percent < 0.0D) percent = 0.0d;
return oldDiameter + (newDiameter - oldDiameter) * percent;
final double percent = ((double) (System.currentTimeMillis() - deltaStartTime) / (double) (deltaTime));
return oldDiameter + (newDiameter - oldDiameter) * MathUtil.clamp(percent, 0D, 1D);
}
public void setSize(double size) {
public void setSize(final double size) {
this.oldDiameter = size;
this.newDiameter = size;
this.lerpTime = 0;
this.deltaTime = 0;
}
public double getX() {
@@ -86,10 +77,6 @@ public class WorldBorderEmulator extends StoredObject {
return z;
}
public boolean isInit() {
return init;
}
public enum Side {
NORTH(0, -1),
EAST(1, 0),
@@ -105,3 +92,4 @@ public class WorldBorderEmulator extends StoredObject {
}
}
}
@@ -35,9 +35,6 @@ 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);
@@ -26,17 +26,14 @@ import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.PlayerPacketRewriter
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;
@@ -54,7 +51,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) Math.round(inputValue * 32.0D);
return (int) (inputValue * 32.0D);
}
};
public static final ValueTransformer<Float, Byte> DEGREES_TO_ANGLE = new ValueTransformer<>(Types.BYTE) {
@@ -64,7 +61,7 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
}
};
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9", "1.8");
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9.4", "1.8");
private final BlockItemPacketRewriter1_9 itemRewriter = new BlockItemPacketRewriter1_9(this);
private final EntityPacketRewriter1_9 entityRewriter = new EntityPacketRewriter1_9(this);
@@ -83,18 +80,15 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
@Override
public void init(UserConnection connection) {
final ProtocolStorables storables = connection.storables(this);
storables.setEntityTracker(new EntityTracker1_9(connection));
storables.setClientWorld(new ClientWorld());
connection.addEntityTracker(this.getClass(), new EntityTracker1_9(connection));
connection.addClientWorld(this.getClass(), 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
@@ -18,23 +18,21 @@
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 {
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 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;
private final UserConnection user;
private boolean visible;
public TitleCooldownVisualization(UserConnection user) {
this.user = user;
@@ -42,71 +40,28 @@ public class TitleCooldownVisualization implements CooldownVisualization {
@Override
public void show(double progress) throws Exception {
final LastTitle title = this.user.get(LastTitle.class);
if (this.visible && title.isVisible()) {
this.restoreTitle(title);
return;
}
final String text = CooldownVisualization.buildProgressText("˙", progress);
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;
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);
});
}
@Override
public void hide() throws Exception {
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;
sendTitlePacket(ACTION_HIDE, wrapper -> {
});
}
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;
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);
}
}
@@ -1,165 +0,0 @@
/*
* 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;
}
}
}
@@ -19,7 +19,6 @@ 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;
@@ -27,11 +26,8 @@ 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;
@@ -63,26 +59,8 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
@Override
protected void registerPackets() {
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));
}
});
registerBlockChange(ClientboundPackets1_9.BLOCK_UPDATE);
registerMultiBlockChange(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE);
registerSetCreativeModeSlot(ServerboundPackets1_8.SET_CREATIVE_MODE_SLOT);
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_CLOSE, wrapper -> {
@@ -100,12 +78,8 @@ 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);
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
tracker.put(windowId, windowType);
tracker.openWindow(windowId, windowType, slotCount);
wrapper.user().get(WindowTracker.class).put(windowId, windowType);
});
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_CONTENT, wrapper -> {
@@ -141,49 +115,10 @@ 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")) {
@@ -259,7 +194,6 @@ 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();
@@ -346,32 +280,6 @@ 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;
}
@@ -396,8 +304,7 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
item.setData((short) 0);
}
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.identifier() == 373 && (tag == null || !tag.contains("Potion"))) { // Potions
if (item.data() >= 16384) {
item.setIdentifier(438);
item.setData((short) (item.data() - 8192));
@@ -415,9 +322,6 @@ 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");
@@ -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().storables(protocol).clientWorld();
final ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
clientWorld.setEnvironment(dimension);
});
protocol.registerClientbound(ClientboundPackets1_9.SET_ENTITY_DATA, wrapper -> {
@@ -134,24 +134,11 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
}
}
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) {
if (data > 0) {
wrapper.passthrough(Types.SHORT); // Velocity x
wrapper.passthrough(Types.SHORT); // Velocity y
wrapper.passthrough(Types.SHORT); // Velocity z
} else {
final short velocityX = wrapper.read(Types.SHORT);
final short velocityY = wrapper.read(Types.SHORT);
final short velocityZ = wrapper.read(Types.SHORT);
@@ -177,7 +164,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(protocol).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
});
}
});
@@ -192,7 +179,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(protocol).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
});
}
});
@@ -229,7 +216,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(protocol).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
});
}
});
@@ -272,7 +259,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(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
@@ -298,7 +285,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(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
@@ -308,7 +295,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(protocol).entityType(entityId);
com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
if (type == EntityTypes1_9.EntityType.BOAT) {
yaw -= 64;
wrapper.set(Types.BYTE, 3, yaw);
@@ -330,7 +317,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(protocol).entityType(entityId);
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
if (type == EntityTypes1_9.EntityType.BOAT) {
byte yaw = wrapper.read(Types.BYTE);
yaw -= 64;
@@ -342,7 +329,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
@Override
public void register() {
handler(wrapper -> {
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
if (vehicle == null) {
wrapper.cancel();
@@ -373,7 +360,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
}
final int entityId = wrapper.get(Types.VAR_INT, 0);
final EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
final EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
if (type == EntityTypes1_9.EntityType.BOAT) {
byte yaw = wrapper.get(Types.BYTE, 0);
yaw -= 64;
@@ -392,7 +379,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
if (effectId > 23) { // Throw away new effects
wrapper.cancel();
}
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
if (effectId == 25 && entityId == tracker.clientEntityId()) {
wrapper.user().get(LevitationStorage.class).setActive(false);
}
@@ -412,11 +399,9 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
public void register() {
map(Types.VAR_INT); // Entity id
handler(wrapper -> {
// 1.8 clients write their own equipment to armorInventory[slot] without the -1 offset,
// shifting armor up one slot; the window 0 set slot packets already carry it.
final int entityId = wrapper.get(Types.VAR_INT, 0);
// todo check if this is correct for the own player
int slot = wrapper.read(Types.VAR_INT);
if (slot == 1 || entityId == wrapper.user().getEntityTracker(protocol).clientEntityId()) {
if (slot == 1) {
wrapper.cancel();
} else if (slot > 1) {
slot -= 1;
@@ -431,7 +416,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(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
final int vehicle = wrapper.read(Types.VAR_INT);
final IntList oldPassengers = tracker.getPassengers(vehicle);
@@ -459,19 +444,6 @@ 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() {
@@ -485,19 +457,7 @@ 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(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;
}
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
if (tracker.entityType(entityId) == EntityTypes1_9.EntityType.BOAT) {
byte yaw = wrapper.get(Types.BYTE, 0);
yaw -= 64;
@@ -515,7 +475,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(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
final boolean player = entityId == tracker.clientEntityId();
// Throw away new attributes and track attack speed
@@ -562,7 +522,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
if (effectId > 23) { // Throw away new effects
wrapper.cancel();
}
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
if (effectId == 25 && entityId == tracker.clientEntityId()) {
final LevitationStorage levitation = wrapper.user().get(LevitationStorage.class);
levitation.setActive(true);
@@ -575,62 +535,16 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
protected void registerRewrites() {
// Handle new entities
mapEntityTypeWithData(EntityTypes1_9.EntityType.SHULKER, EntityTypes1_9.EntityType.MAGMA_CUBE).plainName();
mapObjectType(EntityTypes1_9.ObjectType.SHULKER_BULLET, EntityTypes1_9.ObjectType.WITHER_SKULL, -1).plainName();
mapEntityTypeWithData(EntityTypes1_9.EntityType.SHULKER_BULLET, EntityTypes1_9.EntityType.WITCH).plainName();
// Entity data rewrite
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) {
@@ -640,10 +554,8 @@ 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 (handActive) {
if ((((byte) entityData.value()) & 1 << HAND_ACTIVE_BIT) != 0) {
status = (byte) (status | 1 << STATUS_USE_BIT);
} else {
status = (byte) (status & ~(1 << STATUS_USE_BIT));
@@ -21,14 +21,13 @@ 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;
@@ -44,6 +43,7 @@ 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;
@@ -53,12 +53,6 @@ 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) {
@@ -113,26 +107,6 @@ 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() {
@@ -159,7 +133,8 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
map(Types.BYTE); // Relative arguments
handler(wrapper -> {
final PlayerPositionTracker pos = wrapper.user().get(PlayerPositionTracker.class);
final int teleportId = wrapper.read(Types.VAR_INT);
pos.setConfirmId(wrapper.read(Types.VAR_INT));
byte flags = wrapper.get(Types.BYTE, 0);
double x = wrapper.get(Types.DOUBLE, 0);
@@ -168,11 +143,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
float yaw = wrapper.get(Types.FLOAT, 0);
float pitch = wrapper.get(Types.FLOAT, 1);
// 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));
wrapper.set(Types.BYTE, 0, (byte) 0);
if (flags != 0) {
if ((flags & 0x01) != 0) {
@@ -188,17 +159,18 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
wrapper.set(Types.DOUBLE, 2, z);
}
if ((flags & 0x08) != 0) {
yaw += pos.getYaw(); // tracked estimate only; the wire keeps the delta
yaw += pos.getYaw();
wrapper.set(Types.FLOAT, 0, yaw);
}
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);
});
}
});
@@ -208,11 +180,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
public void register() {
map(Types.INT); // Dimension
handler(wrapper -> {
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
final int dimension = wrapper.get(Types.INT, 0);
if (world.setEnvironment(dimension)) {
final EntityTracker tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
tracker.clearEntities();
wrapper.user().get(BossBarStorage.class).reset();
}
@@ -231,7 +203,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
return;
}
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
final int entityId = wrapper.passthrough(Types.VAR_INT);
wrapper.passthrough(Types.INT); // Killer id
@@ -301,7 +273,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(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
if (tracker.isInsideVehicle(tracker.clientEntityId())) {
wrapper.cancel();
}
@@ -319,7 +291,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
handler(wrapper -> {
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
storage.sendAnimations();
if (storage.hasPendingTeleports()) {
if (storage.getConfirmId() != -1) {
return;
}
@@ -339,7 +311,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
handler(wrapper -> {
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
storage.sendAnimations();
if (storage.hasPendingTeleports()) {
if (storage.getConfirmId() != -1) {
return;
}
@@ -361,26 +333,13 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
storage.sendAnimations();
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) {
if (storage.getConfirmId() != -1) {
if (storage.getPosX() == x && storage.getPosY() == y && storage.getPosZ() == z && storage.getYaw() == yaw && storage.getPitch() == pitch) {
final PacketWrapper confirmTeleport = PacketWrapper.create(ServerboundPackets1_9.ACCEPT_TELEPORTATION, wrapper.user());
confirmTeleport.write(Types.VAR_INT, teleport.id());
confirmTeleport.write(Types.VAR_INT, storage.getConfirmId());
confirmTeleport.sendToServer(Protocol1_9To1_8.class);
if (teleport.viewRelative()) {
storage.setYaw(yaw);
storage.setPitch(pitch);
}
storage.confirmTeleport();
storage.setConfirmId(-1);
}
} else {
storage.setPos(x, y, z);
@@ -471,7 +430,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(protocol);
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
if (vehicle != null && tracker.entityType(vehicle) == EntityTypes1_9.EntityType.BOAT) {
@@ -521,7 +480,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(protocol).clientEntityId());
updateSkin.write(Types.VAR_INT, wrapper.user().getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
final List<EntityData> entityData = new ArrayList<>();
entityData.add(new EntityData(10, EntityDataTypes1_8.BYTE, (byte) flags));
@@ -565,39 +524,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
pageTag.setValue(ChatUtil.jsonToLegacy(value));
}
} else if (channel.equals("MC|AdvCdm")) {
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
}
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
}
});
}
@@ -22,9 +22,7 @@ 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;
@@ -59,12 +57,6 @@ 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");
@@ -100,12 +92,9 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
String name = wrapper.get(Types.STRING, 0);
name = protocol.getMappingData().getMappedNamedSound(name);
if (name == null) {
return;
}
if (!name.isEmpty()) {
wrapper.set(Types.STRING, 0, name);
} else {
wrapper.cancel();
} else {
wrapper.set(Types.STRING, 0, name);
}
});
read(Types.VAR_INT); // Sound category
@@ -121,12 +110,10 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
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<>()));
});
@@ -137,24 +124,9 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
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;
@@ -295,18 +267,4 @@ 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,52 +0,0 @@
/*
* 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();
}
}
}
}
@@ -18,7 +18,6 @@
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;
@@ -28,8 +27,6 @@ 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;
@@ -38,24 +35,20 @@ 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 TrackedEntity removeEntity(int id) {
public void 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());
return super.removeEntity(id);
super.removeEntity(id);
}
public void resetEntityOffset(final int id) {
@@ -95,33 +88,4 @@ 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);
}
}
}
@@ -1,107 +0,0 @@
/*
* 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;
}
}
@@ -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,23 +84,12 @@ public class PlayerPositionTracker implements StorableObject {
this.onGround = onGround;
}
public boolean hasPendingTeleports() {
return !this.pendingTeleports.isEmpty();
public int getConfirmId() {
return this.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) {
public void setConfirmId(int confirmId) {
this.confirmId = confirmId;
}
}
@@ -38,9 +38,6 @@ 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);
}
@@ -55,11 +52,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(": ").formatted(TextFormatting.DARK_GRAY))
.append(new StringComponent(amount + " ").formatted(TextFormatting.DARK_RED))
.append(new TranslationComponent("item.blazePowder.name").formatted(TextFormatting.DARK_RED));
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));
openWindow.write(Types.COMPONENT, TextComponentSerializer.V1_8.serializeJson(title));
openWindow.write(Types.UNSIGNED_BYTE, (short) 420);
@@ -86,36 +83,6 @@ 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) {
@@ -26,11 +26,7 @@ public class CooldownIndicatorTask implements Runnable {
@Override
public void run() {
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
final CooldownStorage cooldown = connection.get(CooldownStorage.class);
if (cooldown == null) {
continue;
}
cooldown.tick(connection);
connection.get(CooldownStorage.class).tick(connection);
}
}
}
@@ -36,15 +36,12 @@ 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.storables(Protocol1_9To1_8.class).entityTracker().clientEntityId());
velocityPacket.write(Types.VAR_INT, connection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
velocityPacket.write(Types.SHORT, (short) 0);
velocityPacket.write(Types.SHORT, (short) ((levitation.getAmplifier() + 1) * 360));
velocityPacket.write(Types.SHORT, (short) 0);
+3 -7
View File
@@ -1,9 +1,5 @@
plugins {
id("via.addon_subproject")
}
dependencies {
compileOnly(projects.viarewindCommon)
compileOnly(libs.fabric.loader)
compileOnly(libs.log4j.api)
compileOnly(project(":viarewind-common"))
compileOnly("net.fabricmc:fabric-loader:0.11.3")
compileOnly("org.apache.logging.log4j:log4j-api:2.17.1")
}
+1 -1
View File
@@ -24,7 +24,7 @@
},
"depends": {
"viafabric": ">=0.4.14",
"viabackwards": ">=5.12.0"
"viabackwards": ">=5.7.2"
},
"custom": {
"modmenu:api": true,
+7 -8
View File
@@ -4,18 +4,17 @@ org.gradle.parallel=true
org.gradle.configuration-cache=true
# Project Details
jvm_version=17
project_jvm_version=17
project_group=com.viaversion
project_name=ViaRewind
project_version=4.2.1-SNAPSHOT
project_version=4.1.0-SNAPSHOT
project_description=ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.
publish_owner_id=florianreuth
publish_owner_name=Florian Reuth
publish_owner_mail=git@florianreuth.de
publishing_dev_id=florianreuth
publishing_dev_name=Florian Reuth
publishing_dev_mail=git@florianreuth.de
# Modrinth/Hangar Publishing
minecraft_versions=26.3, 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8
minecraft_version_range=1.8-26.3
minecraft_versions=26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8
minecraft_version_range=1.8-26.1.2
velocity_version=3.4-3.5
-21
View File
@@ -1,21 +0,0 @@
[versions]
viaversion = "5.12.0"
velocity = "3.4.0"
[libraries]
foojay-resolver-convention-plugin = { module = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin", version = "1.0.0" }
classtokenreplacer-plugin = { module = "net.raphimc.class-token-replacer:net.raphimc.class-token-replacer.gradle.plugin", version = "1.1.7" }
viaproxy = { module = "net.raphimc:ViaProxy", version = "[3.0.0,4.0.0)" }
viaversion = { module = "com.viaversion:viaversion", version.ref = "viaversion" }
viabackwards = { module = "com.viaversion:viabackwards", version.ref = "viaversion" }
netty-all = { module = "io.netty:netty-all", version = "4.0.20.Final" }
guava = { module = "com.google.guava:guava", version = "17.0" }
velocity-native = { module = "com.velocitypowered:velocity-native", version = "3.4.0-SNAPSHOT" }
spigot-api = { module = "org.spigotmc:spigot-api", version = "1.12.2-R0.1-SNAPSHOT" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version = "0.11.3" }
log4j-api = { module = "org.apache.logging.log4j:log4j-api", version = "2.17.1" }
velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
[plugins]
hangar-publish = { id = "io.papermc.hangar-publish-plugin", version = "0.1.4" }
minotaur = { id = "com.modrinth.minotaur", version = "2.+" }
Binary file not shown.
+2 -4
View File
@@ -1,10 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bafd5ce9cfaea0fbccfdc8439a1ac42fbd4cd9c89dc9a988228d8a2639a58e6c
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+3 -3
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# gradlew start up script for POSIX generated by Gradle.
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh gradlew
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Vendored
+33 -52
View File
@@ -19,39 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem gradlew startup script for Windows
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Catch executions from older scripts and ensure they exit cleanly.
@rem This can be removed once we can be reasonably confident that few people
@rem will be migrating directly to this new wrapper.
goto afterSafetyNet
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto exitWithErrorLevel
:afterSafetyNet
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -72,14 +45,13 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
1>&2 echo.
1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
1>&2 echo.
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
1>&2 echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -87,26 +59,35 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
1>&2 echo.
1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
1>&2 echo.
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
1>&2 echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
goto fail
:execute
@rem Setup the command line
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
@rem This label must not be changed. We rely on old scripts being able to jump to this point.
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+23 -13
View File
@@ -1,21 +1,31 @@
import de.florianreuth.baseproject.setupViaSubprojects
pluginManagement {
includeBuild("build-logic")
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.florianreuth.de/releases")
}
plugins {
id("de.florianreuth.baseproject") version "2.0.2"
id("io.papermc.hangar-publish-plugin") version "0.1.4"
id("net.raphimc.class-token-replacer") version "1.1.7"
id("com.modrinth.minotaur") version "2.+"
}
}
plugins {
id("base.settings")
}
dependencyResolutionManagement {
repositories {
maven("https://repo.viaversion.com")
maven("https://repo.papermc.io/repository/maven-public")
maven("https://maven.fabricmc.net")
}
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "viarewind"
setupViaSubprojects("common", "bukkit", "fabric", "sponge", "velocity")
setupViaSubproject("common")
setupViaSubproject("bukkit")
setupViaSubproject("fabric")
setupViaSubproject("sponge")
setupViaSubproject("velocity")
fun setupViaSubproject(name: String) {
include("viarewind-$name")
project(":viarewind-$name").projectDir = file(name)
}
+1 -5
View File
@@ -1,7 +1,3 @@
plugins {
id("via.addon_subproject")
}
dependencies {
compileOnly(projects.viarewindCommon)
compileOnly(project(":viarewind-common"))
}
@@ -36,11 +36,11 @@
},
{
"id": "viaversion",
"version": "[5.12.0,)"
"version": "[5.7.2,)"
},
{
"id": "viabackwards",
"version": "[5.12.0,)"
"version": "[5.7.2,)"
}
]
},
+2 -7
View File
@@ -1,9 +1,4 @@
plugins {
id("via.addon_subproject")
}
dependencies {
compileOnly(projects.viarewindCommon)
compileOnly(libs.velocity.api)
annotationProcessor(libs.velocity.api)
compileOnly(project(":viarewind-common"))
compileOnly("com.velocitypowered:velocity-api:3.4.0")?.also { annotationProcessor(it) }
}
@@ -79,9 +79,4 @@ public class VelocityPlugin implements ViaRewindPlatform {
return "compression-decoder";
}
@Override
public boolean compressHandlerWritesLength() {
return true;
}
}