mirror of
https://github.com/ViaVersion/ViaRewind.git
synced 2026-11-04 08:52:09 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6424ddb56 | ||
|
|
00bd23a639 | ||
|
|
7a6d73bd20 | ||
|
|
118fab1ba2 | ||
|
|
79ab8df038 | ||
|
|
824908a72f | ||
|
|
27136c2e3a | ||
|
|
d8e4746053 | ||
|
|
82a3ee84e0 | ||
|
|
9df4d11f8c | ||
|
|
c92916a265 | ||
|
|
dc04defeb6 | ||
|
|
c7edeba340 | ||
|
|
d232bd020f | ||
|
|
2525d42666 | ||
|
|
4d51a62410 | ||
|
|
b177e048dd | ||
|
|
7577279fcb | ||
|
|
e3211f99ef | ||
|
|
d562f9745a | ||
|
|
7339df9fdb | ||
|
|
d98d5bed9b | ||
|
|
786529efbe | ||
|
|
71546a7699 | ||
|
|
9e7c853a5d | ||
|
|
315a1a0fab | ||
|
|
7a2898e44a | ||
|
|
5783fa98e9 | ||
|
|
77d9d9143e | ||
|
|
ede1920686 | ||
|
|
189249478f | ||
|
|
6c19760af6 | ||
|
|
69f8213445 | ||
|
|
f95871bdc5 | ||
|
|
7d6c75ad31 | ||
|
|
0bb7be88ff | ||
|
|
4b7264c944 | ||
|
|
db6afa5b1b | ||
|
|
02f55edb0c | ||
|
|
5ef900a61e | ||
|
|
4fe7e35f6b | ||
|
|
1af08ced41 | ||
|
|
fd1d3ed221 | ||
|
|
1d6ef8833f | ||
|
|
f2e2f18511 | ||
|
|
2adf7757f0 | ||
|
|
4dd3de2ce9 | ||
|
|
ddbdbdb1cc | ||
|
|
28b4998912 | ||
|
|
f9b0fd5c5a | ||
|
|
a676368588 | ||
|
|
966231ad1c | ||
|
|
d73c2198ea | ||
|
|
6f319932be | ||
|
|
f172de2742 | ||
|
|
ca25da9445 | ||
|
|
ea548dfd91 | ||
|
|
d0561f2e19 | ||
|
|
2c8189a447 | ||
|
|
48c9e45bcf | ||
|
|
ecb4f4bf00 | ||
|
|
6ea9377622 | ||
|
|
b789fdebb7 | ||
|
|
e7b0606e18 | ||
|
|
bf490c3805 | ||
|
|
7ca7078141 |
No files matched your search
@@ -6,9 +6,17 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "github-actions"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
interval: "monthly"
|
||||
groups:
|
||||
all-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
+13
-11
@@ -2,29 +2,31 @@ name: Build
|
||||
on: [pull_request, push, workflow_dispatch]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# 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
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
|
||||
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
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
check-latest: true
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew build --stacktrace
|
||||
- name: Upload Artifacts to GitHub
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: Artifacts
|
||||
path: build/libs/
|
||||
if-no-files-found: error
|
||||
@@ -12,22 +12,26 @@ permissions:
|
||||
jobs:
|
||||
publish:
|
||||
if: github.repository_owner == 'ViaVersion'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
|
||||
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
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 25
|
||||
check-latest: true
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew build --stacktrace
|
||||
- name: Publish to Hangar
|
||||
env:
|
||||
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.foojay.resolver.convention.plugin)
|
||||
implementation(libs.classtokenreplacer.plugin)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
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")
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
+20
-66
@@ -1,55 +1,20 @@
|
||||
import de.florianreuth.baseproject.*
|
||||
import de.florianreuth.baseproject.viaRelease
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.papermc.hangar-publish-plugin")
|
||||
id("com.modrinth.minotaur")
|
||||
id("de.florianreuth.baseproject")
|
||||
alias(libs.plugins.hangar.publish)
|
||||
alias(libs.plugins.minotaur)
|
||||
id("base.java")
|
||||
id("via.maven_publish")
|
||||
id("configuration.embedded_dependencies")
|
||||
}
|
||||
|
||||
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 {
|
||||
subprojects.forEach {
|
||||
shade(it)
|
||||
}
|
||||
embeddedDependencies(projects.viarewindCommon)
|
||||
embeddedDependencies(projects.viarewindBukkit)
|
||||
embeddedDependencies(projects.viarewindFabric)
|
||||
embeddedDependencies(projects.viarewindSponge)
|
||||
embeddedDependencies(projects.viarewindVelocity)
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -60,29 +25,18 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
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()}"
|
||||
}
|
||||
|
||||
val release = viaRelease("master")
|
||||
if (!release.isRelease || release.isMainBranch) { // Only publish releases from the main branch
|
||||
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(if (isRelease) "release" else if (isMainBranch) "beta" else "alpha")
|
||||
versionNumber.set(suffixedVersion)
|
||||
versionName.set(suffixedVersion)
|
||||
changelog.set(changelogContent)
|
||||
versionType.set(release.modrinthVersionType)
|
||||
versionNumber.set(release.version)
|
||||
versionName.set(release.version)
|
||||
changelog.set(release.changelog)
|
||||
uploadFile.set(tasks.jar.flatMap { it.archiveFile })
|
||||
gameVersions.set(mcVersions)
|
||||
loaders.add("fabric")
|
||||
@@ -103,10 +57,10 @@ if (!isRelease || isMainBranch) { // Only publish releases from the main branch
|
||||
|
||||
hangarPublish {
|
||||
publications.register("plugin") {
|
||||
version.set(suffixedVersion)
|
||||
version.set(release.version)
|
||||
id.set("ViaRewind")
|
||||
channel.set(if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha")
|
||||
changelog.set(changelogContent)
|
||||
channel.set(release.hangarChannel)
|
||||
changelog.set(release.changelog)
|
||||
apiKey.set(System.getenv("HANGAR_TOKEN"))
|
||||
platforms {
|
||||
paper {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
dependencies {
|
||||
compileOnly(project(":viarewind-common"))
|
||||
compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
|
||||
plugins {
|
||||
id("via.addon_subproject")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(projects.viarewindCommon)
|
||||
compileOnly(libs.spigot.api)
|
||||
}
|
||||
+6
-54
@@ -1,59 +1,11 @@
|
||||
import de.florianreuth.baseproject.latestCommitHash
|
||||
|
||||
plugins {
|
||||
id("net.raphimc.class-token-replacer")
|
||||
id("via.addon_subproject")
|
||||
id("extra.fill_build_constants")
|
||||
id("via.run_viaproxy")
|
||||
}
|
||||
|
||||
val viaProxy: Configuration by configurations.creating
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.netty:netty-all:4.0.20.Final")
|
||||
compileOnly("com.google.guava:guava:17.0")
|
||||
compileOnly("com.velocitypowered:velocity-native:3.4.0-SNAPSHOT")
|
||||
|
||||
viaProxy("net.raphimc:ViaProxy:[3.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")
|
||||
}
|
||||
compileOnly(libs.netty.all)
|
||||
compileOnly(libs.guava)
|
||||
compileOnly(libs.velocity.native)
|
||||
}
|
||||
@@ -91,4 +91,13 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.api.compression;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* A ThreadLocal-based compression provider that either uses Velocity's native compression
|
||||
* or falls back to Java's built-in compression (Deflater/Inflater).
|
||||
*/
|
||||
public final class ThreadLocalCompressionProvider {
|
||||
|
||||
private static final boolean VELOCITY_NATIVES_AVAILABLE;
|
||||
|
||||
static {
|
||||
boolean velocityAvailable = false;
|
||||
try {
|
||||
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
|
||||
velocityAvailable = true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
VELOCITY_NATIVES_AVAILABLE = velocityAvailable;
|
||||
}
|
||||
|
||||
// ThreadLocal for Java's Deflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Deflater> JAVA_DEFLATER = ThreadLocal.withInitial(Deflater::new);
|
||||
|
||||
// ThreadLocal for Java's Inflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Inflater> JAVA_INFLATER = ThreadLocal.withInitial(Inflater::new);
|
||||
|
||||
private ThreadLocalCompressionProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing uncompressed data
|
||||
* @param destination the destination buffer to write compressed data to
|
||||
* @throws DataFormatException if compression fails
|
||||
*/
|
||||
public static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
deflateVelocity(source, destination);
|
||||
} else {
|
||||
deflateJava(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decompresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing compressed data
|
||||
* @param destination the destination buffer to write decompressed data to
|
||||
* @param expectedSize the expected size of the decompressed data
|
||||
* @throws DataFormatException if decompression fails
|
||||
*/
|
||||
public static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
inflateVelocity(source, destination, expectedSize);
|
||||
} else {
|
||||
inflateJava(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Java native implementation
|
||||
|
||||
private static void deflateJava(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
try {
|
||||
final Deflater deflater = JAVA_DEFLATER.get();
|
||||
deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
deflater.finish();
|
||||
|
||||
while (!deflater.finished()) {
|
||||
output.ensureWritable(4096);
|
||||
output.writerIndex(output.writerIndex() + deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
|
||||
}
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_DEFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void inflateJava(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(expectedSize, expectedSize);
|
||||
try {
|
||||
final Inflater inflater = JAVA_INFLATER.get();
|
||||
inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + inflater.inflate(output.array(), output.arrayOffset(), expectedSize));
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_INFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity native implementation
|
||||
private static void deflateVelocity(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
VelocityHolder.deflate(source, destination);
|
||||
}
|
||||
|
||||
private static void inflateVelocity(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
VelocityHolder.inflate(source, destination, expectedSize);
|
||||
}
|
||||
|
||||
private static final class VelocityHolder {
|
||||
|
||||
private static final ThreadLocal<com.velocitypowered.natives.compression.VelocityCompressor> COMPRESSOR =
|
||||
ThreadLocal.withInitial(() -> com.velocitypowered.natives.util.Natives.compress.get().create(-1));
|
||||
|
||||
static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
COMPRESSOR.get().deflate(source, destination);
|
||||
}
|
||||
|
||||
static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
COMPRESSOR.get().inflate(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ package com.viaversion.viarewind.api.data;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
|
||||
import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viaversion.api.data.FullMappings;
|
||||
import com.viaversion.viaversion.api.data.LegacyFullStringMappingsBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import com.viaversion.viaversion.api.data.Mappings;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class RewindMappingData extends BackwardsMappingData {
|
||||
@@ -37,4 +41,9 @@ public class RewindMappingData extends BackwardsMappingData {
|
||||
protected CompoundTag readMappingsFile(String name) {
|
||||
return RewindMappingDataLoader.INSTANCE.loadNBTFromDir(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FullMappings createFullMappings(MappingDataLoader.IdentifiersPair identifiersPair, Mappings mappings) {
|
||||
return new LegacyFullStringMappingsBase(identifiersPair, mappings);
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -17,17 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.minecraft.math;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Vector;
|
||||
|
||||
@Deprecated // ?????????
|
||||
public class RelativeMoveUtil {
|
||||
|
||||
public static Vector[] calculateRelativeMoves(UserConnection user, int entityId, int relX, int relY, int relZ) {
|
||||
EntityTracker1_9 tracker = user.getEntityTracker(Protocol1_9To1_8.class);
|
||||
|
||||
public static Vector[] calculateRelativeMoves(EntityTracker1_9 tracker, int entityId, int relX, int relY, int relZ) {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
+10
@@ -17,14 +17,24 @@
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
+41
-32
@@ -17,11 +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.type.Type;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
|
||||
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
|
||||
@@ -63,40 +65,47 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
);
|
||||
}
|
||||
|
||||
final byte[] data = new byte[totalSize];
|
||||
int offset = 0;
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(totalSize);
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
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();
|
||||
}
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writeBoolean(anySkyLight);
|
||||
buffer.writeBytes(compressedData, 0, compressedSize);
|
||||
ChunkType1_7_6.serialize(
|
||||
chunk,
|
||||
uncompressed,
|
||||
addBitMasks[i],
|
||||
anySkyLight,
|
||||
biomes
|
||||
);
|
||||
}
|
||||
|
||||
buffer.writeShort(chunkCount);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
buffer.writeBoolean(anySkyLight);
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress bulk chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
uncompressed.release();
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
*/
|
||||
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.Deflater;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight.LIGHT_LENGTH;
|
||||
@@ -49,33 +51,41 @@ 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 byte[] data = new byte[size];
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(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 {
|
||||
deflater.setInput(data, 0, data.length);
|
||||
deflater.finish();
|
||||
compressedData = new byte[data.length];
|
||||
compressedSize = deflater.deflate(compressedData);
|
||||
} finally {
|
||||
deflater.end();
|
||||
}
|
||||
serialize(chunk, uncompressed, addBitmask, hasSkyLight, biomes);
|
||||
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writeBytes(compressedData, 0, compressedSize);
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.writeBoolean(chunk.isFullChunk());
|
||||
buffer.writeShort(bitmask);
|
||||
buffer.writeShort(addBitmask);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
uncompressed.release();
|
||||
}
|
||||
}
|
||||
|
||||
public static int serialize(Chunk chunk, byte[] output, int offset, int addBitmask, boolean writeSkyLight, boolean biomes) {
|
||||
public static void serialize(Chunk chunk, ByteBuf output, int addBitmask, boolean writeSkyLight, boolean biomes) {
|
||||
final ChunkSection[] sections = chunk.getSections();
|
||||
final int bitmask = chunk.getBitmask();
|
||||
|
||||
@@ -85,7 +95,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[offset++] = (byte) ((block >> 4) & 0xFF);
|
||||
output.writeByte((block >> 4) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +107,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[offset++] = (byte) (meta1 | (meta2 << 4));
|
||||
output.writeByte(meta1 | (meta2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +115,7 @@ 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();
|
||||
System.arraycopy(blockLight, 0, output, offset, LIGHT_LENGTH);
|
||||
offset += LIGHT_LENGTH;
|
||||
output.writeBytes(blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +124,11 @@ 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();
|
||||
System.arraycopy(skyLight, 0, output, offset, LIGHT_LENGTH);
|
||||
output.writeBytes(skyLight);
|
||||
} else {
|
||||
// Write empty skylight data
|
||||
output.writeZero(LIGHT_LENGTH);
|
||||
}
|
||||
offset += LIGHT_LENGTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +141,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[offset++] = (byte) (add1 | (add2 << 4));
|
||||
output.writeByte(add1 | (add2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,11 +150,9 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
if (biomes && chunk.getBiomeData() != null) {
|
||||
final int[] biomeData = chunk.getBiomeData();
|
||||
for (int biome : biomeData) {
|
||||
output[offset++] = (byte) biome;
|
||||
output.writeByte(biome);
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
|
||||
|
||||
+4
-2
@@ -38,6 +38,7 @@ import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracke
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.task.WorldBorderUpdateTask;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.ProtocolStorables;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
|
||||
@@ -122,8 +123,9 @@ public class Protocol1_8To1_7_6_10 extends BackwardsProtocol<ClientboundPackets1
|
||||
|
||||
@Override
|
||||
public void init(UserConnection connection) {
|
||||
connection.addEntityTracker(this.getClass(), new EntityTracker1_8(connection));
|
||||
connection.addClientWorld(this.getClass(), new ClientWorld());
|
||||
final ProtocolStorables storables = connection.storables(this);
|
||||
storables.setEntityTracker(new EntityTracker1_8(connection));
|
||||
storables.setClientWorld(new ClientWorld());
|
||||
|
||||
connection.put(new InventoryTracker(connection));
|
||||
connection.put(new PlayerSessionStorage(connection));
|
||||
|
||||
+98
-13
@@ -26,6 +26,18 @@ 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());
|
||||
}
|
||||
@@ -47,8 +59,28 @@ public class ChatNewlineRewriter {
|
||||
}
|
||||
|
||||
if (element.isJsonArray()) {
|
||||
List<JsonObject> currentLine = new ArrayList<>();
|
||||
JsonObject siblingStyle = inheritedStyle;
|
||||
boolean foundRoot = false;
|
||||
|
||||
for (JsonElement child : element.getAsJsonArray()) {
|
||||
results.addAll(splitChatComponentByNewline(child, inheritedStyle));
|
||||
List<JsonObject> splitParts = splitChatComponentByNewline(child, siblingStyle);
|
||||
for (int i = 0; i < splitParts.size(); i++) {
|
||||
currentLine.add(splitParts.get(i));
|
||||
if (i < splitParts.size() - 1) {
|
||||
results.add(mergeLine(currentLine));
|
||||
currentLine.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundRoot && child != null && !child.isJsonNull()) {
|
||||
siblingStyle = rootStyle(inheritedStyle, child);
|
||||
foundRoot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentLine.isEmpty()) {
|
||||
results.add(mergeLine(currentLine));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -60,13 +92,7 @@ public class ChatNewlineRewriter {
|
||||
JsonObject obj = element.getAsJsonObject();
|
||||
|
||||
// Build the style for this node: inherit first, then override with this object's style
|
||||
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());
|
||||
}
|
||||
}
|
||||
JsonObject currentStyle = mergeStyle(inheritedStyle, obj);
|
||||
|
||||
// Gather components in order: base text then extra array
|
||||
List<JsonElement> ordered = new ArrayList<>();
|
||||
@@ -81,12 +107,19 @@ public class ChatNewlineRewriter {
|
||||
|
||||
// If there is no text/extra, return a single object with the style
|
||||
if (ordered.isEmpty()) {
|
||||
results.add(currentStyle);
|
||||
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
|
||||
results.add(baseComponent != null ? baseComponent : currentStyle);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Build lines by concatenating split pieces
|
||||
List<JsonObject> currentLine = new ArrayList<>();
|
||||
if (!obj.has("text")) {
|
||||
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
|
||||
if (baseComponent != null) {
|
||||
currentLine.add(baseComponent);
|
||||
}
|
||||
}
|
||||
for (JsonElement part : ordered) {
|
||||
if (part == null || part.isJsonNull()) {
|
||||
continue;
|
||||
@@ -127,6 +160,55 @@ 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();
|
||||
@@ -139,11 +221,14 @@ public class ChatNewlineRewriter {
|
||||
return first;
|
||||
}
|
||||
|
||||
JsonObject merged = new JsonObject();
|
||||
merged.addProperty("text", "");
|
||||
|
||||
JsonArray extra = new JsonArray();
|
||||
for (int i = 1; i < lineParts.size(); i++) {
|
||||
extra.add(lineParts.get(i));
|
||||
for (JsonObject linePart : lineParts) {
|
||||
extra.add(linePart);
|
||||
}
|
||||
first.add("extra", extra);
|
||||
return first;
|
||||
merged.add("extra", extra);
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
-18
@@ -20,7 +20,6 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.CompressionStatusTracker;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.platform.providers.Provider;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
public abstract class CompressionHandlerProvider implements Provider {
|
||||
|
||||
@@ -28,23 +27,6 @@ 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;
|
||||
|
||||
+3
-19
@@ -17,18 +17,15 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private final Inflater inflater = new Inflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -58,25 +55,12 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
|
||||
}
|
||||
|
||||
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);
|
||||
ByteBuf output = ctx.alloc().buffer(outLength);
|
||||
try {
|
||||
this.inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + this.inflater.inflate(output.array(), output.arrayOffset(), outLength));
|
||||
ThreadLocalCompressionProvider.inflate(in, output, outLength);
|
||||
out.add(output.retain());
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
this.inflater.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-30
@@ -17,16 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
private final Deflater deflater = new Deflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -48,32 +45,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
Types.VAR_INT.writePrimitive(out, frameLength);
|
||||
|
||||
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();
|
||||
}
|
||||
ThreadLocalCompressionProvider.deflate(in, out);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-41
@@ -21,28 +21,12 @@ 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();
|
||||
@@ -51,17 +35,12 @@ 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, getEncoder(compressor, threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, new CompressionEncoder(threshold));
|
||||
}
|
||||
|
||||
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
|
||||
@@ -69,7 +48,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
if (decoder != null) {
|
||||
decoder.setThreshold(threshold);
|
||||
} else {
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(compressor, threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, new CompressionDecoder(threshold));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +66,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());
|
||||
pipeline.replace(compressor, compressor, new ForwardMessageToByteEncoder(ViaRewind.getPlatform().compressHandlerWritesLength()));
|
||||
} else {
|
||||
throw new IllegalStateException("Couldn't remove compression for 1.7!");
|
||||
}
|
||||
@@ -96,22 +75,5 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-47
@@ -1,47 +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_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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +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_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.CompressionEncoder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
public final class VelocityCompressionEncoder extends CompressionEncoder {
|
||||
|
||||
private final VelocityCompressor compressor;
|
||||
|
||||
public VelocityCompressionEncoder(final int threshold, final VelocityCompressor compressor) {
|
||||
super(threshold);
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
this.compressor.deflate(source, destination);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -113,7 +113,7 @@ public class BlockItemPacketRewriter1_8 extends VRBlockItemRewriter<ClientboundP
|
||||
final Item item = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0);
|
||||
playerSession.setPlayerEquipment(wrapper.user().getProtocolInfo().getUuid(), item, 8 - slot);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isSpectator()) { // Spectator mode didn't exist in 1.7.10
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public class BlockItemPacketRewriter1_8 extends VRBlockItemRewriter<ClientboundP
|
||||
if (windowId != 0) {
|
||||
return;
|
||||
}
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final UUID userId = wrapper.user().getProtocolInfo().getUuid();
|
||||
|
||||
final Item[] items = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, 0);
|
||||
|
||||
+13
-13
@@ -76,7 +76,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
}
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.addPlayer(entityId, wrapper.user().getProtocolInfo().getUuid());
|
||||
|
||||
tracker.setClientEntityGameMode(wrapper.get(Types.UNSIGNED_BYTE, 0));
|
||||
@@ -275,7 +275,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
final byte headYaw = wrapper.get(Types.BYTE, 2);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(entityId);
|
||||
|
||||
hologram.setPosition(x / 32.0, y / 32.0, z / 32.0);
|
||||
@@ -314,7 +314,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (gameProfile != null && gameProfile.gamemode == 3) { // Spectator mode
|
||||
for (short i = 0; i < 5; i++) {
|
||||
final PacketWrapper entityEquipment = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_EQUIPPED_ITEM, wrapper.user());
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int id = wrapper.get(Types.INT, 0);
|
||||
|
||||
final int limit = tracker.clientEntityId() == id ? 3 : 4;
|
||||
@@ -364,7 +364,7 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
|
||||
});
|
||||
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final short slot = wrapper.get(Types.SHORT, 0);
|
||||
final UUID uuid = tracker.getPlayerUUID(wrapper.get(Types.INT, 0));
|
||||
if (uuid == null) {
|
||||
@@ -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(Protocol1_8To1_7_6_10.class).removeEntity(wrapper.get(Types.INT, 0)));
|
||||
handler(wrapper -> wrapper.user().getEntityTracker(protocol).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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.entityType(entityId) == EntityTypes1_8.EntityType.ABSTRACT_MINECART) { // TODO | Realign all entities?
|
||||
int y = wrapper.get(Types.INT, 2);
|
||||
y += 12;
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final VirtualHologramEntity hologram = tracker.getHolograms().get(wrapper.get(Types.INT, 0));
|
||||
if (hologram != null) {
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final int passenger = wrapper.get(Types.INT, 0);
|
||||
final int vehicle = wrapper.get(Types.INT, 1);
|
||||
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.getHolograms().containsKey(wrapper.get(Types.INT, 0))) { // Don't handle properties for hologram emulation
|
||||
wrapper.cancel();
|
||||
return;
|
||||
|
||||
+85
-22
@@ -38,6 +38,7 @@ import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.InventoryTracker
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.PlayerSessionStorage;
|
||||
import com.viaversion.viarewind.utils.ChatUtil;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.ClientWorld;
|
||||
import com.viaversion.viaversion.api.minecraft.GameProfile;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
@@ -48,6 +49,7 @@ 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;
|
||||
@@ -127,13 +129,13 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
}
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.setClientEntityGameMode(wrapper.get(Types.UNSIGNED_BYTE, 1));
|
||||
|
||||
final int dimension = wrapper.get(Types.INT, 0);
|
||||
|
||||
// Clear entities on dimension change and re-track player
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
if (world.setEnvironment(dimension)) {
|
||||
tracker.clearEntities();
|
||||
tracker.addPlayer(tracker.clientEntityId(), wrapper.user().getProtocolInfo().getUuid());
|
||||
@@ -194,7 +196,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
|
||||
wrapper.write(Types.BOOLEAN, playerSession.onGround);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (!Objects.equals(tracker.spectatingClientEntityId, tracker.clientEntityIdOrNull())) {
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -220,7 +222,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
if (wrapper.get(Types.UNSIGNED_BYTE, 0) != 3) return; // Change game mode
|
||||
int gameMode = wrapper.get(Types.FLOAT, 0).intValue();
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (gameMode == 3 || tracker.isSpectator()) {
|
||||
UUID myId = wrapper.user().getProtocolInfo().getUuid();
|
||||
Item[] equipment = new Item[4];
|
||||
@@ -292,7 +294,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
|
||||
if (gamemode == 3 || gameProfile.gamemode == 3) {
|
||||
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
int entityId = tracker.getPlayerEntityId(uuid);
|
||||
boolean isOwnPlayer = entityId == tracker.clientEntityId();
|
||||
if (entityId != -1) {
|
||||
@@ -445,7 +447,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
wrapper.cancel();
|
||||
final int entityId = wrapper.read(Types.VAR_INT);
|
||||
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.spectatingClientEntityId != entityId) {
|
||||
tracker.setSpectating(entityId);
|
||||
}
|
||||
@@ -506,12 +508,18 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
|
||||
protocol.registerServerbound(ServerboundPackets1_7_2_5.CHAT, wrapper -> {
|
||||
final String message = wrapper.passthrough(Types.STRING);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isSpectator() && message.toLowerCase().startsWith("/stp ")) { // TODO add setting
|
||||
String username = message.split(" ")[1];
|
||||
final GameProfileStorage storage = wrapper.user().get(GameProfileStorage.class);
|
||||
|
||||
final GameProfileStorage.GameProfile profile = storage.get(username, true);
|
||||
// Multiple players with the same name may exist. This will only represent
|
||||
// fake players so it's fine to just pick the first one.
|
||||
List<GameProfileStorage.GameProfile> profiles = storage.get(username, true);
|
||||
if (profiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final GameProfileStorage.GameProfile profile = profiles.get(0);
|
||||
if (profile != null && profile.uuid != null) {
|
||||
wrapper.cancel();
|
||||
|
||||
@@ -529,10 +537,7 @@ 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);
|
||||
if (mode != 0) {
|
||||
return;
|
||||
}
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int entityId = tracker.getHologramIdWithExtra(wrapper.get(Types.VAR_INT, 0));
|
||||
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
|
||||
|
||||
@@ -541,6 +546,11 @@ 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();
|
||||
|
||||
Vector3d pos = new Vector3d(position.getPosX(), position.getPosY() + 1.8, position.getPosZ());
|
||||
@@ -608,6 +618,16 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
protected void register() {
|
||||
map(Types.VAR_INT); // Status
|
||||
map(RewindTypes.U_BYTE_POSITION, Types.BLOCK_POSITION1_8); // Position
|
||||
handler(wrapper -> {
|
||||
// 1.7 uses 255 as "no face"
|
||||
// 1.8 changed this to only be in the range EnumFacing range (0-5) and uses DOWN when no face is used
|
||||
// This properly rewrites invalid values to the 1.8 equivalent
|
||||
int face = wrapper.read(Types.UNSIGNED_BYTE);
|
||||
if (face > 5) {
|
||||
face = 0;
|
||||
}
|
||||
wrapper.write(Types.UNSIGNED_BYTE, (short) face);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -618,6 +638,16 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
map(Types.BYTE); // Direction
|
||||
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Item
|
||||
|
||||
handler(wrapper -> {
|
||||
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
|
||||
final byte direction = wrapper.get(Types.BYTE, 0);
|
||||
// 1.7 serializes the Y component as an unsigned byte, turning the
|
||||
// use-in-air position from (-1, -1, -1) into (-1, 255, -1).
|
||||
// we change that back to -1 for Y like 1.8 clients
|
||||
if (position.x() == -1 && position.y() == 255 && position.z() == -1 && direction == -1) {
|
||||
wrapper.set(Types.BLOCK_POSITION1_8, 0, new BlockPosition(-1, -1, -1));
|
||||
}
|
||||
});
|
||||
handler(wrapper -> protocol.getItemRewriter().handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
|
||||
}
|
||||
});
|
||||
@@ -690,7 +720,7 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
if (!unmount) {
|
||||
return;
|
||||
}
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.spectatingClientEntityId != tracker.clientEntityId()) {
|
||||
PacketWrapper sneakPacket = PacketWrapper.create(ServerboundPackets1_8.PLAYER_COMMAND, wrapper.user());
|
||||
sneakPacket.write(Types.VAR_INT, tracker.clientEntityId());
|
||||
@@ -770,6 +800,8 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
});
|
||||
|
||||
// 1.7.10 custom payload constructor (String, ByteBuf) uses byteBuf.array(), so it sends the whole backing array capacity, not only the written bytes
|
||||
// After reading the proper packet data we discard the remaining bytes
|
||||
protocol.registerServerbound(ServerboundPackets1_7_2_5.CUSTOM_PAYLOAD, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -780,7 +812,27 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
switch (channel) {
|
||||
case "MC|TrSel": {
|
||||
wrapper.passthrough(Types.INT);
|
||||
wrapper.read(Types.REMAINING_BYTES); // unused data ???
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|AdvCdm": {
|
||||
final byte commandBlockType = wrapper.passthrough(Types.BYTE);
|
||||
if (commandBlockType == 0) { // Block
|
||||
wrapper.passthrough(Types.INT); // X
|
||||
wrapper.passthrough(Types.INT); // Y
|
||||
wrapper.passthrough(Types.INT); // Z
|
||||
} else if (commandBlockType == 1) { // Minecart
|
||||
wrapper.passthrough(Types.INT); // Entity id
|
||||
}
|
||||
wrapper.passthrough(Types.STRING); // Command
|
||||
wrapper.write(Types.BOOLEAN, true); // Track output, added in 1.8, default to true
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|Beacon": {
|
||||
wrapper.passthrough(Types.INT); // Primary effect
|
||||
wrapper.passthrough(Types.INT); // Secondary effect
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|ItemName": {
|
||||
@@ -797,17 +849,28 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
case "MC|BEdit":
|
||||
case "MC|BSign": {
|
||||
final boolean signBook = channel.equals("MC|BSign");
|
||||
final Item book = wrapper.read(RewindTypes.COMPRESSED_NBT_ITEM);
|
||||
CompoundTag tag = book.tag();
|
||||
if (tag != null && tag.contains("pages")) {
|
||||
ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = ComponentUtil.legacyToJsonString(value);
|
||||
page.setValue(value);
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
|
||||
if (book == null) {
|
||||
wrapper.write(Types.ITEM1_8, null);
|
||||
break;
|
||||
}
|
||||
|
||||
final CompoundTag tag = book.tag();
|
||||
if (signBook && tag != null && tag.contains("pages")) {
|
||||
final ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
if (pages != null) {
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
final StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = new JsonPrimitive(value).toString();
|
||||
page.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
protocol.getItemRewriter().handleItemToServer(wrapper.user(), book);
|
||||
wrapper.write(Types.ITEM1_8, book);
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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(Protocol1_8To1_7_6_10.class);
|
||||
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
|
||||
final byte mode = wrapper.passthrough(Types.BYTE);
|
||||
if (mode != 0 && !scoreboard.teamExists(team)) {
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_CHUNK, wrapper -> {
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
final Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(world.getEnvironment()));
|
||||
protocol.getItemRewriter().handleChunk(chunk);
|
||||
|
||||
|
||||
+13
-14
@@ -22,6 +22,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPa
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.VirtualHologramEntity;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
|
||||
@@ -68,17 +69,15 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntity(int id, EntityType type) {
|
||||
super.addEntity(id, type);
|
||||
public TrackedEntity addEntity(int id, EntityType type) {
|
||||
if (type == EntityTypes1_8.EntityType.ARMOR_STAND) {
|
||||
holograms.put(id, new VirtualHologramEntity(user(), id));
|
||||
}
|
||||
return super.addEntity(id, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int entityId) {
|
||||
super.removeEntity(entityId);
|
||||
|
||||
public TrackedEntity removeEntity(int entityId) {
|
||||
final VirtualHologramEntity hologram = holograms.get(entityId);
|
||||
if (hologram != null) {
|
||||
hologram.deleteEntity();
|
||||
@@ -95,6 +94,7 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
entityUUIDToId.removeInt(playerId);
|
||||
user().get(PlayerSessionStorage.class).getPlayerEquipment().remove(playerId);
|
||||
}
|
||||
return super.removeEntity(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,17 +222,16 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
|
||||
public void checkNametagVisbility(final String username) {
|
||||
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
|
||||
final GameProfileStorage.GameProfile profile = profileStorage.get(username, false);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
final List<GameProfileStorage.GameProfile> profiles = profileStorage.get(username, false);
|
||||
|
||||
final int entityId = getPlayerEntityId(profile.uuid);
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
for (GameProfileStorage.GameProfile profile : profiles) {
|
||||
final int entityId = getPlayerEntityId(profile.uuid);
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNametagVisibility(entityId);
|
||||
checkNametagVisibility(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPlayerNametagHidden(final int entityId) {
|
||||
|
||||
+13
-13
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
@@ -34,6 +38,7 @@ import java.util.UUID;
|
||||
|
||||
public class GameProfileStorage extends StoredObject {
|
||||
private final Map<UUID, GameProfile> properties = new HashMap<>();
|
||||
private final ListMultimap<String, GameProfile> nameToProfile = ArrayListMultimap.create();
|
||||
|
||||
public GameProfileStorage(UserConnection user) {
|
||||
super(user);
|
||||
@@ -42,6 +47,7 @@ public class GameProfileStorage extends StoredObject {
|
||||
public GameProfile put(UUID uuid, String name, String displayName, int ping, int gamemode) {
|
||||
GameProfile gameProfile = new GameProfile(uuid, name, displayName, ping, gamemode);
|
||||
properties.put(uuid, gameProfile);
|
||||
nameToProfile.put(name, gameProfile);
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
@@ -49,19 +55,9 @@ public class GameProfileStorage extends StoredObject {
|
||||
return properties.get(uuid);
|
||||
}
|
||||
|
||||
public GameProfile get(String name, boolean ignoreCase) {
|
||||
public List<GameProfile> get(String name, boolean ignoreCase) {
|
||||
if (ignoreCase) name = name.toLowerCase();
|
||||
|
||||
for (GameProfile profile : properties.values()) {
|
||||
if (profile.name == null) continue;
|
||||
|
||||
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
|
||||
|
||||
if (n.equals(name)) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return nameToProfile.get(name);
|
||||
}
|
||||
|
||||
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
|
||||
@@ -81,7 +77,11 @@ public class GameProfileStorage extends StoredObject {
|
||||
}
|
||||
|
||||
public GameProfile remove(UUID uuid) {
|
||||
return properties.remove(uuid);
|
||||
GameProfile removedProfile = properties.remove(uuid);
|
||||
if (removedProfile != null) {
|
||||
nameToProfile.get(removedProfile.name).remove(removedProfile);
|
||||
}
|
||||
return removedProfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+34
-28
@@ -140,37 +140,43 @@ public class ScoreboardTracker extends StoredObject {
|
||||
public String sendTeamForScore(String score) {
|
||||
if (score.length() <= 16) return score;
|
||||
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
|
||||
int l = 16;
|
||||
int i = Math.min(16, score.length() - 16);
|
||||
String name = score.substring(i, i + l);
|
||||
while (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
i--;
|
||||
while (score.length() - l - i > 16) {
|
||||
l--;
|
||||
if (l < 1) return score;
|
||||
i = Math.min(16, score.length() - l);
|
||||
|
||||
// Find the longest window score[i, i+l) that:
|
||||
// - is not already claimed as a team name
|
||||
// - keeps prefix (score[0,i)) at most 16 chars i <= 16
|
||||
// - keeps suffix (score[i+l, end)) at most 16 chars score.length()-l-i <= 16
|
||||
for (int l = 16; l >= 1; l--) {
|
||||
final int iMax = Math.min(16, score.length() - l);
|
||||
final int iMin = Math.max(0, score.length() - l - 16);
|
||||
for (int i = iMax; i >= iMin; i--) {
|
||||
final String name = score.substring(i, i + l);
|
||||
if (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String prefix = score.substring(0, i);
|
||||
final String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
final ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
}
|
||||
name = score.substring(i, i + l);
|
||||
}
|
||||
String prefix = score.substring(0, i);
|
||||
String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
return score;
|
||||
}
|
||||
|
||||
public String removeTeamForScore(String score) {
|
||||
|
||||
+3
@@ -35,6 +35,9 @@ public class WorldBorderUpdateTask implements Runnable {
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final WorldBorderEmulator worldBorderEmulatorTracker = connection.get(WorldBorderEmulator.class);
|
||||
if (worldBorderEmulatorTracker == null) {
|
||||
continue;
|
||||
}
|
||||
if (!worldBorderEmulatorTracker.isInit()) continue;
|
||||
|
||||
final PlayerSessionStorage playerSession = connection.get(PlayerSessionStorage.class);
|
||||
|
||||
+10
-4
@@ -26,14 +26,17 @@ 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;
|
||||
@@ -51,7 +54,7 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
public static final ValueTransformer<Double, Integer> DOUBLE_TO_INT_TIMES_32 = new ValueTransformer<>(Types.INT) {
|
||||
@Override
|
||||
public Integer transform(PacketWrapper wrapper, Double inputValue) {
|
||||
return (int) (inputValue * 32.0D);
|
||||
return (int) Math.round(inputValue * 32.0D);
|
||||
}
|
||||
};
|
||||
public static final ValueTransformer<Float, Byte> DEGREES_TO_ANGLE = new ValueTransformer<>(Types.BYTE) {
|
||||
@@ -61,7 +64,7 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
}
|
||||
};
|
||||
|
||||
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9.4", "1.8");
|
||||
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9", "1.8");
|
||||
|
||||
private final BlockItemPacketRewriter1_9 itemRewriter = new BlockItemPacketRewriter1_9(this);
|
||||
private final EntityPacketRewriter1_9 entityRewriter = new EntityPacketRewriter1_9(this);
|
||||
@@ -80,15 +83,18 @@ public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, C
|
||||
|
||||
@Override
|
||||
public void init(UserConnection connection) {
|
||||
connection.addEntityTracker(this.getClass(), new EntityTracker1_9(connection));
|
||||
connection.addClientWorld(this.getClass(), new ClientWorld());
|
||||
final ProtocolStorables storables = connection.storables(this);
|
||||
storables.setEntityTracker(new EntityTracker1_9(connection));
|
||||
storables.setClientWorld(new ClientWorld());
|
||||
|
||||
connection.put(new WindowTracker(connection));
|
||||
connection.put(new LevitationStorage());
|
||||
connection.put(new PlayerPositionTracker());
|
||||
connection.put(new CooldownStorage());
|
||||
connection.put(new LastTitle());
|
||||
connection.put(new BlockPlaceDestroyTracker());
|
||||
connection.put(new BossBarStorage(connection));
|
||||
connection.put(new CommandBlockStateStorage());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+65
-20
@@ -18,21 +18,23 @@
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TitleCooldownVisualization implements CooldownVisualization {
|
||||
|
||||
private static final int ACTION_SET_TITLE = 0;
|
||||
private static final int ACTION_SET_SUBTITLE = 1;
|
||||
private static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
|
||||
private static final int ACTION_HIDE = 3;
|
||||
public static final int ACTION_SET_TITLE = 0;
|
||||
public static final int ACTION_SET_SUBTITLE = 1;
|
||||
public static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
|
||||
public static final int ACTION_HIDE = 3;
|
||||
public static final int ACTION_RESET = 4;
|
||||
|
||||
private final UserConnection user;
|
||||
private boolean visible;
|
||||
|
||||
public TitleCooldownVisualization(UserConnection user) {
|
||||
this.user = user;
|
||||
@@ -40,28 +42,71 @@ public class TitleCooldownVisualization implements CooldownVisualization {
|
||||
|
||||
@Override
|
||||
public void show(double progress) throws Exception {
|
||||
final String text = CooldownVisualization.buildProgressText("˙", progress);
|
||||
final LastTitle title = this.user.get(LastTitle.class);
|
||||
if (this.visible && title.isVisible()) {
|
||||
this.restoreTitle(title);
|
||||
return;
|
||||
}
|
||||
|
||||
sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive("")));
|
||||
sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(text)));
|
||||
sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> {
|
||||
wrapper.write(Types.INT, 0);
|
||||
wrapper.write(Types.INT, 2);
|
||||
wrapper.write(Types.INT, 5);
|
||||
});
|
||||
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
|
||||
setTitle.write(Types.COMPONENT, new JsonPrimitive(""));
|
||||
setTitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
|
||||
setSubtitle.write(Types.COMPONENT, new JsonPrimitive(CooldownVisualization.buildProgressText("˙", progress)));
|
||||
setSubtitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
|
||||
setTitleTimes.write(Types.INT, 0);
|
||||
setTitleTimes.write(Types.INT, 2);
|
||||
setTitleTimes.write(Types.INT, 5);
|
||||
setTitleTimes.send(Protocol1_9To1_8.class);
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() throws Exception {
|
||||
sendTitlePacket(ACTION_HIDE, wrapper -> {
|
||||
});
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LastTitle title = this.user.get(LastTitle.class);
|
||||
if (title.isVisible()) {
|
||||
this.restoreTitle(title);
|
||||
return;
|
||||
}
|
||||
|
||||
final PacketWrapper hideTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
hideTitle.write(Types.VAR_INT, ACTION_HIDE);
|
||||
hideTitle.send(Protocol1_9To1_8.class);
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
private void sendTitlePacket(int action, Consumer<PacketWrapper> writer) {
|
||||
final PacketWrapper title = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
title.write(Types.VAR_INT, action);
|
||||
writer.accept(title);
|
||||
title.scheduleSend(Protocol1_9To1_8.class);
|
||||
private void restoreTitle(final LastTitle title) {
|
||||
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
|
||||
final int[] times = title.remainingTimes();
|
||||
setTitleTimes.write(Types.INT, times[0]);
|
||||
setTitleTimes.write(Types.INT, times[1]);
|
||||
setTitleTimes.write(Types.INT, times[2]);
|
||||
setTitleTimes.send(Protocol1_9To1_8.class);
|
||||
|
||||
if (title.subtitle() != null) {
|
||||
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
|
||||
setSubtitle.write(Types.COMPONENT, title.subtitle());
|
||||
setSubtitle.send(Protocol1_9To1_8.class);
|
||||
}
|
||||
|
||||
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
|
||||
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
|
||||
setTitle.write(Types.COMPONENT, title.title());
|
||||
setTitle.send(Protocol1_9To1_8.class);
|
||||
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.NumberTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
|
||||
public final class CommandBlockState {
|
||||
|
||||
public static final int COMMAND_BLOCK_COMMAND_LIMIT = 32500;
|
||||
|
||||
private static final String IMPULSE = "IMPULSE";
|
||||
private static final String REPEAT = "REPEAT";
|
||||
private static final String CHAIN = "CHAIN";
|
||||
private static final String CONDITIONAL = "CONDITIONAL";
|
||||
private static final String UNCONDITIONAL = "UNCONDITIONAL";
|
||||
private static final String ALWAYS_ACTIVE = "ALWAYSACTIVE";
|
||||
private static final String REDSTONE = "REDSTONE";
|
||||
|
||||
private CommandBlockState() {
|
||||
}
|
||||
|
||||
public static boolean isCommandBlock(final int blockState) {
|
||||
return isImpulse(blockState) || isRepeat(blockState) || isChain(blockState);
|
||||
}
|
||||
|
||||
public static void decorateCommand(final CompoundTag tag, final int blockState) {
|
||||
if (tag == null || !isCommandBlock(blockState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final StringTag commandTag = tag.getStringTag("Command");
|
||||
if (commandTag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Mode mode = mode(blockState);
|
||||
final boolean conditional = (blockState & 8) != 0;
|
||||
final boolean automatic = booleanTag(tag, "auto");
|
||||
if (mode == Mode.REDSTONE && !conditional && !automatic) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String prefix = mode.prefix + ' ' + (conditional ? CONDITIONAL : UNCONDITIONAL) + ' ' + (automatic ? ALWAYS_ACTIVE : REDSTONE) + ' ';
|
||||
final String command = commandTag.getValue();
|
||||
if (!parseDecoratedCommand(command).prefixed() && prefix.length() + command.length() <= COMMAND_BLOCK_COMMAND_LIMIT) {
|
||||
commandTag.setValue(prefix + command);
|
||||
}
|
||||
}
|
||||
|
||||
public static ParsedCommand parseDecoratedCommand(final String command) {
|
||||
final int first = command.indexOf(' ');
|
||||
if (first == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
final int second = command.indexOf(' ', first + 1);
|
||||
if (second == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
final int third = command.indexOf(' ', second + 1);
|
||||
if (third == -1) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final Mode mode = Mode.fromPrefix(command.substring(0, first));
|
||||
if (mode == null) {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final String conditionalToken = command.substring(first + 1, second);
|
||||
final boolean conditional;
|
||||
if (CONDITIONAL.equalsIgnoreCase(conditionalToken)) {
|
||||
conditional = true;
|
||||
} else if (UNCONDITIONAL.equalsIgnoreCase(conditionalToken)) {
|
||||
conditional = false;
|
||||
} else {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
final String automaticToken = command.substring(second + 1, third);
|
||||
final boolean automatic;
|
||||
if (ALWAYS_ACTIVE.equalsIgnoreCase(automaticToken)) {
|
||||
automatic = true;
|
||||
} else if (REDSTONE.equalsIgnoreCase(automaticToken)) {
|
||||
automatic = false;
|
||||
} else {
|
||||
return ParsedCommand.defaultState(command);
|
||||
}
|
||||
|
||||
return new ParsedCommand(command.substring(third + 1), mode.serverName, conditional, automatic, true);
|
||||
}
|
||||
|
||||
private static boolean booleanTag(final CompoundTag tag, final String key) {
|
||||
final NumberTag numberTag = tag.getNumberTag(key);
|
||||
return numberTag != null && numberTag.asByte() != 0;
|
||||
}
|
||||
|
||||
private static Mode mode(final int blockState) {
|
||||
if (isRepeat(blockState)) {
|
||||
return Mode.AUTO;
|
||||
}
|
||||
if (isChain(blockState)) {
|
||||
return Mode.SEQUENCE;
|
||||
}
|
||||
return Mode.REDSTONE;
|
||||
}
|
||||
|
||||
private static boolean isImpulse(final int blockState) {
|
||||
return blockState >= 2192 && blockState <= 2207;
|
||||
}
|
||||
|
||||
private static boolean isRepeat(final int blockState) {
|
||||
return blockState >= 3360 && blockState <= 3375;
|
||||
}
|
||||
|
||||
private static boolean isChain(final int blockState) {
|
||||
return blockState >= 3376 && blockState <= 3391;
|
||||
}
|
||||
|
||||
public record ParsedCommand(String command, String mode, boolean conditional, boolean automatic, boolean prefixed) {
|
||||
|
||||
private static ParsedCommand defaultState(final String command) {
|
||||
return new ParsedCommand(command, Mode.REDSTONE.serverName, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Mode {
|
||||
REDSTONE(IMPULSE, "REDSTONE"),
|
||||
AUTO(REPEAT, "AUTO"),
|
||||
SEQUENCE(CHAIN, "SEQUENCE");
|
||||
|
||||
private final String prefix;
|
||||
private final String serverName;
|
||||
|
||||
Mode(final String prefix, final String serverName) {
|
||||
this.prefix = prefix;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
private static Mode fromPrefix(final String prefix) {
|
||||
for (final Mode mode : values()) {
|
||||
if (mode.prefix.equalsIgnoreCase(prefix)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
-4
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteTag;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
@@ -26,8 +27,11 @@ import com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;
|
||||
import com.viaversion.viarewind.api.rewriter.VRBlockItemRewriter;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.PotionIdMappings1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
@@ -59,8 +63,26 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
registerBlockChange(ClientboundPackets1_9.BLOCK_UPDATE);
|
||||
registerMultiBlockChange(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE);
|
||||
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_UPDATE, wrapper -> {
|
||||
final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_8);
|
||||
final int blockState = wrapper.passthrough(Types.VAR_INT);
|
||||
wrapper.user().get(CommandBlockStateStorage.class).storeOrRemove(position, blockState);
|
||||
wrapper.set(Types.VAR_INT, 0, handleBlockId(blockState));
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE, wrapper -> {
|
||||
final int chunkX = wrapper.passthrough(Types.INT);
|
||||
final int chunkZ = wrapper.passthrough(Types.INT);
|
||||
final BlockChangeRecord[] records = wrapper.passthrough(Types.BLOCK_CHANGE_ARRAY);
|
||||
final CommandBlockStateStorage storage = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
for (BlockChangeRecord record : records) {
|
||||
final int blockState = record.getBlockId();
|
||||
final BlockPosition position = new BlockPosition((chunkX << 4) + record.getSectionX(), record.getY(), (chunkZ << 4) + record.getSectionZ());
|
||||
storage.storeOrRemove(position, blockState);
|
||||
record.setBlockId(handleBlockId(blockState));
|
||||
}
|
||||
});
|
||||
registerSetCreativeModeSlot(ServerboundPackets1_8.SET_CREATIVE_MODE_SLOT);
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_CLOSE, wrapper -> {
|
||||
@@ -78,8 +100,12 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
protocol.registerClientbound(ClientboundPackets1_9.OPEN_SCREEN, wrapper -> {
|
||||
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
|
||||
final String windowType = wrapper.passthrough(Types.STRING);
|
||||
wrapper.passthrough(Types.COMPONENT); // title
|
||||
final short slotCount = wrapper.passthrough(Types.UNSIGNED_BYTE);
|
||||
|
||||
wrapper.user().get(WindowTracker.class).put(windowId, windowType);
|
||||
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
|
||||
tracker.put(windowId, windowType);
|
||||
tracker.openWindow(windowId, windowType, slotCount);
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_CONTENT, wrapper -> {
|
||||
@@ -115,10 +141,49 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
final Item item = wrapper.passthrough(Types.ITEM1_8);
|
||||
|
||||
handleItemToClient(wrapper.user(), item);
|
||||
if (windowId == -2) {
|
||||
// 1.8 has no window -2 (set a player-inv slot by raw index, ignoring the open container) — retarget it.
|
||||
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
|
||||
final short openWindow = tracker.openWindowId();
|
||||
if (openWindow != 0) {
|
||||
// Foreign container open: 1.8 only applies player-inv changes through it (main=size.., hotbar=size+27..).
|
||||
final int size = tracker.openWindowSize();
|
||||
final int containerSlot;
|
||||
if (slot >= 0 && slot <= 8) {
|
||||
containerSlot = size + 27 + slot;
|
||||
} else if (slot >= 9 && slot <= 35) {
|
||||
containerSlot = size + slot - 9;
|
||||
} else { // armor/offhand: no slot in a foreign container
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
wrapper.set(Types.BYTE, 0, (byte) openWindow);
|
||||
wrapper.set(Types.SHORT, 0, (short) containerSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise window 0 with the menu slot.
|
||||
final int windowSlot;
|
||||
if (slot >= 0 && slot <= 8) {
|
||||
windowSlot = slot + 36;
|
||||
} else if (slot >= 9 && slot <= 35) {
|
||||
windowSlot = slot;
|
||||
} else if (slot >= 36 && slot <= 39) {
|
||||
windowSlot = 44 - slot;
|
||||
} else { // offhand/body/saddle: no 1.8 equivalent
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
wrapper.set(Types.BYTE, 0, (byte) 0);
|
||||
wrapper.set(Types.SHORT, 0, (short) windowSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (windowId == 0 && slot == 45) {
|
||||
wrapper.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);
|
||||
final String windowType = windowTracker.get(windowId);
|
||||
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
|
||||
@@ -194,6 +259,7 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
@Override
|
||||
public Item handleItemToClient(UserConnection connection, Item item) {
|
||||
if (item == null) return null;
|
||||
final int originalId = item.identifier();
|
||||
super.handleItemToClient(connection, item);
|
||||
|
||||
CompoundTag tag = item.tag();
|
||||
@@ -280,6 +346,32 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
});
|
||||
}
|
||||
|
||||
// Colors the fake leather armor for an elytra gray
|
||||
if (originalId == 443) {
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
CompoundTag display = tag.getCompoundTag("display");
|
||||
if (display == null) {
|
||||
tag.put("display", display = new CompoundTag());
|
||||
}
|
||||
display.put("color", new IntTag(0x737373)); // Gray
|
||||
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
|
||||
}
|
||||
|
||||
// Makes the fake banner for a shield brown if it has no banner patterns
|
||||
if (originalId == 442) {
|
||||
final CompoundTag blockEntityTag = tag == null ? null : tag.getCompoundTag("BlockEntityTag");
|
||||
final ListTag<CompoundTag> patterns = blockEntityTag == null ? null : blockEntityTag.getListTag("Patterns", CompoundTag.class);
|
||||
if (patterns == null || patterns.isEmpty()) {
|
||||
item.setData((short) 3); // Brown
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
tag.put(nbtTagName() + "|noData", new ByteTag(true));
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -304,7 +396,8 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
item.setData((short) 0);
|
||||
}
|
||||
|
||||
if (item.identifier() == 373 && (tag == null || !tag.contains("Potion"))) { // Potions
|
||||
if (item.identifier() == 373) { // Potions
|
||||
// Restore the splash id even when a Potion tag is present (e.g. items re-sent by a creative mode client)
|
||||
if (item.data() >= 16384) {
|
||||
item.setIdentifier(438);
|
||||
item.setData((short) (item.data() - 8192));
|
||||
@@ -322,6 +415,9 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
if (tag == null) {
|
||||
return item;
|
||||
}
|
||||
if (tag.remove(nbtTagName() + "|noData") != null) {
|
||||
item.setData((short) 0);
|
||||
}
|
||||
final Tag noDisplayTag = tag.remove(nbtTagName() + "|noDisplay");
|
||||
if (noDisplayTag != null) {
|
||||
tag.remove("display");
|
||||
|
||||
+112
-24
@@ -68,7 +68,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int dimension = wrapper.passthrough(Types.BYTE);
|
||||
|
||||
trackPlayer(wrapper.user(), entityId);
|
||||
final ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
|
||||
final ClientWorld clientWorld = wrapper.user().storables(protocol).clientWorld();
|
||||
clientWorld.setEnvironment(dimension);
|
||||
});
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_ENTITY_DATA, wrapper -> {
|
||||
@@ -134,11 +134,24 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
}
|
||||
|
||||
if (data > 0) {
|
||||
wrapper.passthrough(Types.SHORT); // Velocity x
|
||||
wrapper.passthrough(Types.SHORT); // Velocity y
|
||||
wrapper.passthrough(Types.SHORT); // Velocity z
|
||||
} else {
|
||||
if (type.is(EntityTypes1_9.EntityType.POTION) && data > 0) {
|
||||
// 1.8 clients read the potion type from the object data, but newer versions only
|
||||
// send it in the entity data - the next packet. Held until then, see handleEntityData
|
||||
wrapper.cancel();
|
||||
final int x = wrapper.get(Types.INT, 0);
|
||||
final int y = wrapper.get(Types.INT, 1);
|
||||
final int z = wrapper.get(Types.INT, 2);
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 2);
|
||||
final short velocityX = wrapper.passthrough(Types.SHORT);
|
||||
final short velocityY = wrapper.passthrough(Types.SHORT);
|
||||
final short velocityZ = wrapper.passthrough(Types.SHORT);
|
||||
final EntityTracker1_9 tracker = tracker(wrapper.user());
|
||||
tracker.getPendingPotions().put(entityId, new EntityTracker1_9.PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data <= 0) {
|
||||
final short velocityX = wrapper.read(Types.SHORT);
|
||||
final short velocityY = wrapper.read(Types.SHORT);
|
||||
final short velocityZ = wrapper.read(Types.SHORT);
|
||||
@@ -164,7 +177,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.SHORT); // Count
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.EXPERIENCE_ORB);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -179,7 +192,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.DOUBLE, Protocol1_9To1_8.DOUBLE_TO_INT_TIMES_32); // Z
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.LIGHTNING_BOLT);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -216,7 +229,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
map(Types.BYTE, Types.UNSIGNED_BYTE); // Direction
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
wrapper.user().getEntityTracker(Protocol1_9To1_8.class).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
|
||||
wrapper.user().getEntityTracker(protocol).addEntity(entityId, EntityTypes1_9.EntityType.PAINTING);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -259,7 +272,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int deltaY = wrapper.read(Types.SHORT);
|
||||
final int deltaZ = wrapper.read(Types.SHORT);
|
||||
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
|
||||
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
|
||||
@@ -285,7 +298,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final int deltaY = wrapper.read(Types.SHORT);
|
||||
final int deltaZ = wrapper.read(Types.SHORT);
|
||||
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(wrapper.user(), entityId, deltaX, deltaY, deltaZ);
|
||||
final Vector[] moves = RelativeMoveUtil.calculateRelativeMoves(tracker(wrapper.user()), entityId, deltaX, deltaY, deltaZ);
|
||||
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockX());
|
||||
wrapper.write(Types.BYTE, (byte) moves[0].blockY());
|
||||
@@ -295,7 +308,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
final byte pitch = wrapper.passthrough(Types.BYTE);
|
||||
final boolean onGround = wrapper.passthrough(Types.BOOLEAN);
|
||||
|
||||
com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
yaw -= 64;
|
||||
wrapper.set(Types.BYTE, 3, yaw);
|
||||
@@ -317,7 +330,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.MOVE_ENTITY_ROT, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
final com.viaversion.viaversion.api.minecraft.entities.EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.read(Types.BYTE);
|
||||
yaw -= 64;
|
||||
@@ -329,7 +342,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
|
||||
if (vehicle == null) {
|
||||
wrapper.cancel();
|
||||
@@ -360,7 +373,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
final EntityType type = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).entityType(entityId);
|
||||
final EntityType type = wrapper.user().getEntityTracker(protocol).entityType(entityId);
|
||||
if (type == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
yaw -= 64;
|
||||
@@ -379,7 +392,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
if (effectId > 23) { // Throw away new effects
|
||||
wrapper.cancel();
|
||||
}
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (effectId == 25 && entityId == tracker.clientEntityId()) {
|
||||
wrapper.user().get(LevitationStorage.class).setActive(false);
|
||||
}
|
||||
@@ -399,9 +412,11 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
public void register() {
|
||||
map(Types.VAR_INT); // Entity id
|
||||
handler(wrapper -> {
|
||||
// todo check if this is correct for the own player
|
||||
// 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);
|
||||
int slot = wrapper.read(Types.VAR_INT);
|
||||
if (slot == 1) {
|
||||
if (slot == 1 || entityId == wrapper.user().getEntityTracker(protocol).clientEntityId()) {
|
||||
wrapper.cancel();
|
||||
} else if (slot > 1) {
|
||||
slot -= 1;
|
||||
@@ -416,7 +431,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_PASSENGERS, null, wrapper -> {
|
||||
wrapper.cancel();
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int vehicle = wrapper.read(Types.VAR_INT);
|
||||
final IntList oldPassengers = tracker.getPassengers(vehicle);
|
||||
|
||||
@@ -444,6 +459,19 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_ENTITY_MOTION, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
final EntityTracker1_9 tracker = tracker(wrapper.user());
|
||||
final EntityTracker1_9.PendingPotionEntity pending = tracker.getPendingPotions().get(entityId);
|
||||
if (pending != null) {
|
||||
wrapper.cancel();
|
||||
final short velocityX = wrapper.read(Types.SHORT);
|
||||
final short velocityY = wrapper.read(Types.SHORT);
|
||||
final short velocityZ = wrapper.read(Types.SHORT);
|
||||
tracker.getPendingPotions().put(entityId, pending.withVelocity(velocityX, velocityY, velocityZ));
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.TELEPORT_ENTITY, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -457,7 +485,19 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
handler(wrapper -> {
|
||||
final int entityId = wrapper.get(Types.VAR_INT, 0);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final EntityTracker1_9.PendingPotionEntity pending = tracker.getPendingPotions().get(entityId);
|
||||
if (pending != null) {
|
||||
wrapper.cancel();
|
||||
final int x = wrapper.get(Types.INT, 0);
|
||||
final int y = wrapper.get(Types.INT, 1);
|
||||
final int z = wrapper.get(Types.INT, 2);
|
||||
final byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
final byte pitch = wrapper.get(Types.BYTE, 1);
|
||||
tracker.getPendingPotions().put(entityId, pending.withPosition(x, y, z, pitch, yaw));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracker.entityType(entityId) == EntityTypes1_9.EntityType.BOAT) {
|
||||
byte yaw = wrapper.get(Types.BYTE, 0);
|
||||
yaw -= 64;
|
||||
@@ -475,7 +515,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
protocol.registerClientbound(ClientboundPackets1_9.UPDATE_ATTRIBUTES, wrapper -> {
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final boolean player = entityId == tracker.clientEntityId();
|
||||
|
||||
// Throw away new attributes and track attack speed
|
||||
@@ -522,7 +562,7 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
if (effectId > 23) { // Throw away new effects
|
||||
wrapper.cancel();
|
||||
}
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (effectId == 25 && entityId == tracker.clientEntityId()) {
|
||||
final LevitationStorage levitation = wrapper.user().get(LevitationStorage.class);
|
||||
levitation.setActive(true);
|
||||
@@ -535,16 +575,62 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
protected void registerRewrites() {
|
||||
// Handle new entities
|
||||
mapEntityTypeWithData(EntityTypes1_9.EntityType.SHULKER, EntityTypes1_9.EntityType.MAGMA_CUBE).plainName();
|
||||
mapEntityTypeWithData(EntityTypes1_9.EntityType.SHULKER_BULLET, EntityTypes1_9.EntityType.WITCH).plainName();
|
||||
mapObjectType(EntityTypes1_9.ObjectType.SHULKER_BULLET, EntityTypes1_9.ObjectType.WITHER_SKULL, -1).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) {
|
||||
@@ -554,8 +640,10 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
|
||||
}
|
||||
if (metaIndex.getOldType() == null || metaIndex.getNewType() == null) {
|
||||
if (metaIndex == EntityDataIndex1_9.PLAYER_HAND) { // Player eating/aiming/drinking
|
||||
final boolean handActive = (((byte) entityData.value()) & 1 << HAND_ACTIVE_BIT) != 0;
|
||||
tracker.setHandActive(event.entityId(), handActive);
|
||||
byte status = (byte) tracker.getStatus().getOrDefault(event.entityId(), 0);
|
||||
if ((((byte) entityData.value()) & 1 << HAND_ACTIVE_BIT) != 0) {
|
||||
if (handActive) {
|
||||
status = (byte) (status | 1 << STATUS_USE_BIT);
|
||||
} else {
|
||||
status = (byte) (status & ~(1 << STATUS_USE_BIT));
|
||||
|
||||
+94
-21
@@ -21,13 +21,14 @@ 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;
|
||||
@@ -43,7 +44,6 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
|
||||
import com.viaversion.viaversion.api.rewriter.RewriterBase;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
import com.viaversion.viaversion.libs.gson.JsonParser;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
|
||||
@@ -53,6 +53,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_HIDE;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_RESET;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_SUBTITLE;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TIMES_AND_DISPLAY;
|
||||
import static com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.TitleCooldownVisualization.ACTION_SET_TITLE;
|
||||
|
||||
public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
public PlayerPacketRewriter1_9(Protocol1_9To1_8 protocol) {
|
||||
@@ -107,6 +113,26 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
protocol.cancelClientbound(ClientboundPackets1_9.COOLDOWN);
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.SET_TITLES, wrapper -> {
|
||||
final int action = wrapper.passthrough(Types.VAR_INT);
|
||||
if (action == ACTION_SET_TITLE) {
|
||||
final JsonElement title = wrapper.passthrough(Types.COMPONENT);
|
||||
wrapper.user().get(LastTitle.class).setTitle(title);
|
||||
} else if (action == ACTION_SET_SUBTITLE) {
|
||||
final JsonElement subtitle = wrapper.passthrough(Types.COMPONENT);
|
||||
wrapper.user().get(LastTitle.class).setSubtitle(subtitle);
|
||||
} else if (action == ACTION_SET_TIMES_AND_DISPLAY) {
|
||||
final int fadeIn = wrapper.passthrough(Types.INT);
|
||||
final int stay = wrapper.passthrough(Types.INT);
|
||||
final int fadeOut = wrapper.passthrough(Types.INT);
|
||||
wrapper.user().get(LastTitle.class).setTimes(fadeIn, stay, fadeOut);
|
||||
} else if (action == ACTION_HIDE) {
|
||||
wrapper.user().get(LastTitle.class).hide();
|
||||
} else if (action == ACTION_RESET) {
|
||||
wrapper.user().get(LastTitle.class).reset();
|
||||
}
|
||||
});
|
||||
|
||||
protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_PAYLOAD, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -133,8 +159,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.BYTE); // Relative arguments
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker pos = wrapper.user().get(PlayerPositionTracker.class);
|
||||
|
||||
pos.setConfirmId(wrapper.read(Types.VAR_INT));
|
||||
final int teleportId = wrapper.read(Types.VAR_INT);
|
||||
|
||||
byte flags = wrapper.get(Types.BYTE, 0);
|
||||
double x = wrapper.get(Types.DOUBLE, 0);
|
||||
@@ -143,7 +168,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
float yaw = wrapper.get(Types.FLOAT, 0);
|
||||
float pitch = wrapper.get(Types.FLOAT, 1);
|
||||
|
||||
wrapper.set(Types.BYTE, 0, (byte) 0);
|
||||
// 1.8 supports the rotation-relative flags (0x08/0x10) natively, and resolving them here
|
||||
// would snap the camera to the tracked rotation, which lags the client's live look. Keep
|
||||
// them on the wire and only resolve the position bits (native 1.8 servers use zero-delta
|
||||
// rotation-relative teleports to leave the camera untouched).
|
||||
wrapper.set(Types.BYTE, 0, (byte) (flags & 0x18));
|
||||
|
||||
if (flags != 0) {
|
||||
if ((flags & 0x01) != 0) {
|
||||
@@ -159,18 +188,17 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
wrapper.set(Types.DOUBLE, 2, z);
|
||||
}
|
||||
if ((flags & 0x08) != 0) {
|
||||
yaw += pos.getYaw();
|
||||
wrapper.set(Types.FLOAT, 0, yaw);
|
||||
yaw += pos.getYaw(); // tracked estimate only; the wire keeps the delta
|
||||
}
|
||||
if ((flags & 0x10) != 0) {
|
||||
pitch += pos.getPitch();
|
||||
wrapper.set(Types.FLOAT, 1, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
pos.setPos(x, y, z);
|
||||
pos.setYaw(yaw);
|
||||
pos.setPitch(pitch);
|
||||
pos.queueTeleport(teleportId, x, y, z, pos.getYaw(), pos.getPitch(), (flags & 0x18) != 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -180,11 +208,11 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
public void register() {
|
||||
map(Types.INT); // Dimension
|
||||
handler(wrapper -> {
|
||||
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_9To1_8.class);
|
||||
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
|
||||
final int dimension = wrapper.get(Types.INT, 0);
|
||||
|
||||
if (world.setEnvironment(dimension)) {
|
||||
final EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker tracker = wrapper.user().getEntityTracker(protocol);
|
||||
tracker.clearEntities();
|
||||
wrapper.user().get(BossBarStorage.class).reset();
|
||||
}
|
||||
@@ -203,7 +231,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
return;
|
||||
}
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final int entityId = wrapper.passthrough(Types.VAR_INT);
|
||||
wrapper.passthrough(Types.INT); // Killer id
|
||||
|
||||
@@ -273,7 +301,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
wrapper.user().get(PlayerPositionTracker.class).sendAnimations();
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
if (tracker.isInsideVehicle(tracker.clientEntityId())) {
|
||||
wrapper.cancel();
|
||||
}
|
||||
@@ -291,7 +319,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.hasPendingTeleports()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,7 +339,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
handler(wrapper -> {
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.hasPendingTeleports()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,13 +361,26 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
|
||||
final PlayerPositionTracker storage = wrapper.user().get(PlayerPositionTracker.class);
|
||||
storage.sendAnimations();
|
||||
if (storage.getConfirmId() != -1) {
|
||||
if (storage.getPosX() == x && storage.getPosY() == y && storage.getPosZ() == z && storage.getYaw() == yaw && storage.getPitch() == pitch) {
|
||||
final PlayerPositionTracker.PendingTeleport teleport = storage.peekTeleport();
|
||||
if (teleport != null) {
|
||||
// 1.7 uses resynced hitbox minY for y however this can have small floating point error due to the calculations it does to get there on teleport confirm
|
||||
// This Y error gets propagated from 1.7 to 1.8 to 1.9 which causes teleport confirmation to not properly be detected
|
||||
// This fixes it similarly to how anticheats detect teleports: https://github.com/GrimAnticheat/Grim/blob/67aa3a9483a9b2a6987d594092697b4104c781f0/common/src/main/java/ac/grim/grimac/manager/SetbackTeleportUtil.java#L315
|
||||
boolean closeEnoughY = Math.abs(teleport.y() - y) <= 1e-7;
|
||||
// A view-relative teleport keeps its rotation deltas on the wire, so the client applies them
|
||||
// to its own live look and echoes that - match on position only and adopt the echoed rotation
|
||||
final boolean rotationMatches = teleport.viewRelative() || (teleport.yaw() == yaw && teleport.pitch() == pitch);
|
||||
|
||||
if (teleport.x() == x && closeEnoughY && teleport.z() == z && rotationMatches) {
|
||||
final PacketWrapper confirmTeleport = PacketWrapper.create(ServerboundPackets1_9.ACCEPT_TELEPORTATION, wrapper.user());
|
||||
confirmTeleport.write(Types.VAR_INT, storage.getConfirmId());
|
||||
confirmTeleport.write(Types.VAR_INT, teleport.id());
|
||||
confirmTeleport.sendToServer(Protocol1_9To1_8.class);
|
||||
|
||||
storage.setConfirmId(-1);
|
||||
if (teleport.viewRelative()) {
|
||||
storage.setYaw(yaw);
|
||||
storage.setPitch(pitch);
|
||||
}
|
||||
storage.confirmTeleport();
|
||||
}
|
||||
} else {
|
||||
storage.setPos(x, y, z);
|
||||
@@ -430,7 +471,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
final float sideways = wrapper.passthrough(Types.FLOAT);
|
||||
final float forward = wrapper.passthrough(Types.FLOAT);
|
||||
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_9To1_8.class);
|
||||
final EntityTracker1_9 tracker = wrapper.user().getEntityTracker(protocol);
|
||||
final Integer vehicle = tracker.getVehicle(tracker.clientEntityId());
|
||||
|
||||
if (vehicle != null && tracker.entityType(vehicle) == EntityTypes1_9.EntityType.BOAT) {
|
||||
@@ -480,7 +521,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
final short flags = wrapper.get(Types.UNSIGNED_BYTE, 0);
|
||||
|
||||
final PacketWrapper updateSkin = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_DATA, wrapper.user());
|
||||
updateSkin.write(Types.VAR_INT, wrapper.user().getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
|
||||
updateSkin.write(Types.VAR_INT, wrapper.user().getEntityTracker(protocol).clientEntityId());
|
||||
|
||||
final List<EntityData> entityData = new ArrayList<>();
|
||||
entityData.add(new EntityData(10, EntityDataTypes1_8.BYTE, (byte) flags));
|
||||
@@ -524,7 +565,39 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
pageTag.setValue(ChatUtil.jsonToLegacy(value));
|
||||
}
|
||||
} else if (channel.equals("MC|AdvCdm")) {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
final byte type = wrapper.read(Types.BYTE);
|
||||
if (type == 0) {
|
||||
final int x = wrapper.read(Types.INT);
|
||||
final int y = wrapper.read(Types.INT);
|
||||
final int z = wrapper.read(Types.INT);
|
||||
final CommandBlockState.ParsedCommand command = CommandBlockState.parseDecoratedCommand(wrapper.read(Types.STRING));
|
||||
final boolean trackOutput = wrapper.read(Types.BOOLEAN);
|
||||
if (command.prefixed()) {
|
||||
wrapper.set(Types.STRING, 0, "MC|AutoCmd");
|
||||
wrapper.write(Types.INT, x);
|
||||
wrapper.write(Types.INT, y);
|
||||
wrapper.write(Types.INT, z);
|
||||
wrapper.write(Types.STRING, command.command());
|
||||
wrapper.write(Types.BOOLEAN, trackOutput);
|
||||
wrapper.write(Types.STRING, command.mode());
|
||||
wrapper.write(Types.BOOLEAN, command.conditional());
|
||||
wrapper.write(Types.BOOLEAN, command.automatic());
|
||||
} else {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
wrapper.write(Types.BYTE, type);
|
||||
wrapper.write(Types.INT, x);
|
||||
wrapper.write(Types.INT, y);
|
||||
wrapper.write(Types.INT, z);
|
||||
wrapper.write(Types.STRING, command.command());
|
||||
wrapper.write(Types.BOOLEAN, trackOutput);
|
||||
}
|
||||
} else {
|
||||
wrapper.set(Types.STRING, 0, "MC|AdvCmd");
|
||||
wrapper.write(Types.BYTE, type);
|
||||
wrapper.passthrough(Types.INT); // Entity id
|
||||
wrapper.passthrough(Types.STRING); // Command
|
||||
wrapper.passthrough(Types.BOOLEAN); // Track output
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+46
-4
@@ -22,7 +22,9 @@ import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.EffectIdMappings1_8;
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.Environment;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
|
||||
@@ -57,6 +59,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
map(Types.NAMED_COMPOUND_TAG); // Tag
|
||||
handler(wrapper -> {
|
||||
final CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);
|
||||
final short action = wrapper.get(Types.UNSIGNED_BYTE, 0);
|
||||
|
||||
if (action == 2) {
|
||||
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
|
||||
CommandBlockState.decorateCommand(tag, wrapper.user().get(CommandBlockStateStorage.class).state(position));
|
||||
}
|
||||
|
||||
if (tag.remove("SpawnData") instanceof CompoundTag spawnData) {
|
||||
final Tag id = spawnData.remove("id");
|
||||
@@ -92,9 +100,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
String name = wrapper.get(Types.STRING, 0);
|
||||
name = protocol.getMappingData().getMappedNamedSound(name);
|
||||
if (name == null) {
|
||||
wrapper.cancel();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (!name.isEmpty()) {
|
||||
wrapper.set(Types.STRING, 0, name);
|
||||
} else {
|
||||
wrapper.cancel();
|
||||
}
|
||||
});
|
||||
read(Types.VAR_INT); // Sound category
|
||||
@@ -110,10 +121,12 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
|
||||
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
|
||||
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
final int chunkX = wrapper.read(Types.INT);
|
||||
final int chunkZ = wrapper.read(Types.INT);
|
||||
commandBlockStates.unloadChunk(chunkX, chunkZ);
|
||||
|
||||
wrapper.write(ChunkType1_8.forEnvironment(environment), new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], null, new ArrayList<>()));
|
||||
});
|
||||
@@ -124,9 +137,24 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
@Override
|
||||
public void register() {
|
||||
handler(wrapper -> {
|
||||
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
|
||||
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
|
||||
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
|
||||
|
||||
Chunk chunk = wrapper.read(ChunkType1_9_1.forEnvironment(environment));
|
||||
final Chunk originalChunk = chunk;
|
||||
|
||||
chunk.getBlockEntities().forEach(nbt -> {
|
||||
if (!nbt.contains("x") || !nbt.contains("y") || !nbt.contains("z")) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BlockPosition position = new BlockPosition((int) nbt.get("x").getValue(), (int) nbt.get("y").getValue(), (int) nbt.get("z").getValue());
|
||||
final int blockState = blockStateAt(originalChunk, position);
|
||||
if (CommandBlockState.isCommandBlock(blockState)) {
|
||||
commandBlockStates.storeOrRemove(position, blockState);
|
||||
CommandBlockState.decorateCommand(nbt, blockState);
|
||||
}
|
||||
});
|
||||
|
||||
for (ChunkSection section : chunk.getSections()) {
|
||||
if (section == null) continue;
|
||||
@@ -267,4 +295,18 @@ public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int blockStateAt(final Chunk chunk, final BlockPosition position) {
|
||||
final int sectionY = position.y() >> 4;
|
||||
if (sectionY < 0 || sectionY >= chunk.getSections().length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final ChunkSection section = chunk.getSections()[sectionY];
|
||||
if (section == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return section.palette(PaletteType.BLOCKS).idAt(position.x() & 0xF, position.y() & 0xF, position.z() & 0xF);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class CommandBlockStateStorage implements StorableObject {
|
||||
|
||||
private final Map<BlockPosition, Integer> commandBlockStates = new HashMap<>();
|
||||
|
||||
public void storeOrRemove(final BlockPosition position, final int blockState) {
|
||||
if (CommandBlockState.isCommandBlock(blockState)) {
|
||||
commandBlockStates.put(position, blockState);
|
||||
} else {
|
||||
commandBlockStates.remove(position);
|
||||
}
|
||||
}
|
||||
|
||||
public int state(final BlockPosition position) {
|
||||
return commandBlockStates.getOrDefault(position, -1);
|
||||
}
|
||||
|
||||
public void unloadChunk(final int chunkX, final int chunkZ) {
|
||||
final Iterator<BlockPosition> iterator = commandBlockStates.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final BlockPosition position = iterator.next();
|
||||
if (position.x() >> 4 == chunkX && position.z() >> 4 == chunkZ) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-2
@@ -18,6 +18,7 @@
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.Vector;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
|
||||
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
|
||||
@@ -27,6 +28,8 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntList;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;
|
||||
import com.viaversion.viaversion.libs.fastutil.ints.IntSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -35,20 +38,24 @@ public class EntityTracker1_9 extends EntityTrackerBase {
|
||||
private final Int2ObjectMap<IntList> vehicles = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<Vector> offsets = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2IntMap status = new Int2IntOpenHashMap();
|
||||
private final IntSet handActive = new IntOpenHashSet();
|
||||
private final Int2ObjectMap<PendingPotionEntity> pendingPotions = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public EntityTracker1_9(UserConnection connection) {
|
||||
super(connection, EntityTypes1_9.EntityType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int id) {
|
||||
public TrackedEntity removeEntity(int id) {
|
||||
vehicles.remove(id);
|
||||
offsets.remove(id);
|
||||
status.remove(id);
|
||||
handActive.remove(id);
|
||||
pendingPotions.remove(id);
|
||||
|
||||
vehicles.forEach((vehicle, passengers) -> passengers.rem(id));
|
||||
vehicles.int2ObjectEntrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
super.removeEntity(id);
|
||||
return super.removeEntity(id);
|
||||
}
|
||||
|
||||
public void resetEntityOffset(final int id) {
|
||||
@@ -88,4 +95,33 @@ public class EntityTracker1_9 extends EntityTrackerBase {
|
||||
public Int2IntMap getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean isHandActive(final int id) {
|
||||
return handActive.contains(id);
|
||||
}
|
||||
|
||||
public void setHandActive(final int id, final boolean active) {
|
||||
if (active) {
|
||||
handActive.add(id);
|
||||
} else {
|
||||
handActive.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Int2ObjectMap<PendingPotionEntity> getPendingPotions() {
|
||||
return pendingPotions;
|
||||
}
|
||||
|
||||
public record PendingPotionEntity(int x, int y, int z, byte pitch, byte yaw, short velocityX, short velocityY, short velocityZ) {
|
||||
|
||||
public PendingPotionEntity withPosition(final int x, final int y, final int z, final byte pitch, final byte yaw) {
|
||||
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
|
||||
}
|
||||
|
||||
public PendingPotionEntity withVelocity(final short velocityX, final short velocityY, final short velocityZ) {
|
||||
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
|
||||
public final class LastTitle implements StorableObject {
|
||||
|
||||
private JsonElement title;
|
||||
private JsonElement subtitle;
|
||||
|
||||
private long visibleSince;
|
||||
private long visibleUntil;
|
||||
|
||||
private int fadeIn;
|
||||
private int stay;
|
||||
private int fadeOut;
|
||||
|
||||
public LastTitle() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public void setTitle(final JsonElement title) {
|
||||
this.title = title;
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
this.visibleSince = now;
|
||||
this.visibleUntil = now + titleLength();
|
||||
}
|
||||
|
||||
public void setSubtitle(final JsonElement subtitle) {
|
||||
this.subtitle = subtitle;
|
||||
}
|
||||
|
||||
public void setTimes(final int fadeIn, final int stay, final int fadeOut) {
|
||||
this.fadeIn = fadeIn;
|
||||
this.stay = stay;
|
||||
this.fadeOut = fadeOut;
|
||||
|
||||
if (this.visibleUntil > System.currentTimeMillis()) {
|
||||
final long now = System.currentTimeMillis();
|
||||
this.visibleSince = now;
|
||||
this.visibleUntil = now + titleLength();
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
this.visibleSince = 0;
|
||||
this.visibleUntil = 0;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.hide();
|
||||
|
||||
this.fadeIn = 10;
|
||||
this.stay = 70;
|
||||
this.fadeOut = 20;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return this.title != null && this.visibleUntil > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public JsonElement title() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public JsonElement subtitle() {
|
||||
return this.subtitle;
|
||||
}
|
||||
|
||||
public int[] remainingTimes() {
|
||||
final int fullLength = Math.max(0, this.fadeIn + this.stay + this.fadeOut);
|
||||
final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - this.visibleSince) / 50L));
|
||||
if (elapsed < this.fadeIn) {
|
||||
return new int[]{this.fadeIn - elapsed, this.stay, this.fadeOut};
|
||||
}
|
||||
|
||||
final int titleEnd = this.fadeIn + this.stay;
|
||||
if (elapsed < titleEnd) {
|
||||
return new int[]{0, titleEnd - elapsed, this.fadeOut};
|
||||
}
|
||||
|
||||
return new int[]{0, 0, fullLength - elapsed};
|
||||
}
|
||||
|
||||
private long titleLength() {
|
||||
return Math.max(0, this.fadeIn + this.stay + this.fadeOut) * 50L;
|
||||
}
|
||||
|
||||
}
|
||||
+16
-5
@@ -26,10 +26,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
public class PlayerPositionTracker implements StorableObject {
|
||||
|
||||
private final Queue<PacketWrapper> animations = new ConcurrentLinkedQueue<>();
|
||||
private final Queue<PendingTeleport> pendingTeleports = new ConcurrentLinkedQueue<>();
|
||||
private double posX, posY, posZ;
|
||||
private float yaw, pitch;
|
||||
private boolean onGround;
|
||||
private int confirmId = -1;
|
||||
|
||||
public void setPos(double x, double y, double z) {
|
||||
this.posX = x;
|
||||
@@ -84,12 +84,23 @@ public class PlayerPositionTracker implements StorableObject {
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public int getConfirmId() {
|
||||
return this.confirmId;
|
||||
public boolean hasPendingTeleports() {
|
||||
return !this.pendingTeleports.isEmpty();
|
||||
}
|
||||
|
||||
public void setConfirmId(int confirmId) {
|
||||
this.confirmId = confirmId;
|
||||
public void queueTeleport(final int id, final double x, final double y, final double z, final float yaw, final float pitch, final boolean viewRelative) {
|
||||
this.pendingTeleports.add(new PendingTeleport(id, x, y, z, yaw, pitch, viewRelative));
|
||||
}
|
||||
|
||||
public PendingTeleport peekTeleport() {
|
||||
return this.pendingTeleports.peek();
|
||||
}
|
||||
|
||||
public void confirmTeleport() {
|
||||
this.pendingTeleports.poll();
|
||||
}
|
||||
|
||||
public record PendingTeleport(int id, double x, double y, double z, float yaw, float pitch, boolean viewRelative) {
|
||||
}
|
||||
|
||||
}
|
||||
+38
-5
@@ -38,6 +38,9 @@ public class WindowTracker extends StoredObject {
|
||||
private final HashMap<Short, Item[]> brewingItems = new HashMap<>();
|
||||
private final Map<Short, Short> enchantmentProperties = new HashMap<>();
|
||||
|
||||
private short openWindowId = 0;
|
||||
private int openWindowSize = 0; // Translated client container slots, excluding the player inventory
|
||||
|
||||
public WindowTracker(UserConnection user) {
|
||||
super(user);
|
||||
}
|
||||
@@ -52,11 +55,11 @@ public class WindowTracker extends StoredObject {
|
||||
openWindow.write(Types.UNSIGNED_BYTE, windowId);
|
||||
openWindow.write(Types.STRING, "minecraft:brewing_stand");
|
||||
|
||||
TextComponent title = new StringComponent().
|
||||
append(new TranslationComponent("container.brewing")).
|
||||
append(new StringComponent(": " + TextFormatting.DARK_GRAY)).
|
||||
append(new StringComponent(amount + " " + TextFormatting.DARK_RED)).
|
||||
append(new TranslationComponent("item.blazePowder.name", TextFormatting.DARK_RED));
|
||||
TextComponent title = new StringComponent()
|
||||
.append(new TranslationComponent("container.brewing"))
|
||||
.append(new StringComponent(": ").formatted(TextFormatting.DARK_GRAY))
|
||||
.append(new StringComponent(amount + " ").formatted(TextFormatting.DARK_RED))
|
||||
.append(new TranslationComponent("item.blazePowder.name").formatted(TextFormatting.DARK_RED));
|
||||
|
||||
openWindow.write(Types.COMPONENT, TextComponentSerializer.V1_8.serializeJson(title));
|
||||
openWindow.write(Types.UNSIGNED_BYTE, (short) 420);
|
||||
@@ -83,6 +86,36 @@ public class WindowTracker extends StoredObject {
|
||||
public void remove(short windowId) {
|
||||
types.remove(windowId);
|
||||
brewingItems.remove(windowId);
|
||||
if (windowId == openWindowId) {
|
||||
openWindowId = 0;
|
||||
openWindowSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void openWindow(final short windowId, final String windowType, final int slotCount) {
|
||||
openWindowId = windowId;
|
||||
|
||||
// The client only uses the packet's slot count when constructing dynamic inventories.
|
||||
// Fixed menus construct their own slots; use the translated 1.8 layout for those.
|
||||
openWindowSize = switch (windowType) {
|
||||
case "minecraft:furnace", "minecraft:villager", "minecraft:anvil" -> 3;
|
||||
case "minecraft:brewing_stand" -> 4;
|
||||
case "minecraft:beacon" -> 1;
|
||||
case "minecraft:dispenser", "minecraft:dropper" -> 9;
|
||||
case "minecraft:enchanting_table" -> 2;
|
||||
case "minecraft:crafting_table" -> 10;
|
||||
case "minecraft:hopper" -> slotCount;
|
||||
case "EntityHorse" -> slotCount > 2 ? 17 : 2;
|
||||
default -> slotCount / 9 * 9;
|
||||
};
|
||||
}
|
||||
|
||||
public short openWindowId() {
|
||||
return openWindowId;
|
||||
}
|
||||
|
||||
public int openWindowSize() {
|
||||
return openWindowSize;
|
||||
}
|
||||
|
||||
public Item[] getBrewingItems(short windowId) {
|
||||
|
||||
+5
-1
@@ -26,7 +26,11 @@ public class CooldownIndicatorTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
connection.get(CooldownStorage.class).tick(connection);
|
||||
final CooldownStorage cooldown = connection.get(CooldownStorage.class);
|
||||
if (cooldown == null) {
|
||||
continue;
|
||||
}
|
||||
cooldown.tick(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -36,12 +36,15 @@ public class LevitationUpdateTask implements Runnable {
|
||||
}
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final LevitationStorage levitation = connection.get(LevitationStorage.class);
|
||||
if (levitation == null) {
|
||||
continue;
|
||||
}
|
||||
if (!levitation.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final PacketWrapper velocityPacket = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_MOTION, connection);
|
||||
velocityPacket.write(Types.VAR_INT, connection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId());
|
||||
velocityPacket.write(Types.VAR_INT, connection.storables(Protocol1_9To1_8.class).entityTracker().clientEntityId());
|
||||
velocityPacket.write(Types.SHORT, (short) 0);
|
||||
velocityPacket.write(Types.SHORT, (short) ((levitation.getAmplifier() + 1) * 360));
|
||||
velocityPacket.write(Types.SHORT, (short) 0);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,9 @@
|
||||
dependencies {
|
||||
compileOnly(project(":viarewind-common"))
|
||||
compileOnly("net.fabricmc:fabric-loader:0.11.3")
|
||||
compileOnly("org.apache.logging.log4j:log4j-api:2.17.1")
|
||||
plugins {
|
||||
id("via.addon_subproject")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(projects.viarewindCommon)
|
||||
compileOnly(libs.fabric.loader)
|
||||
compileOnly(libs.log4j.api)
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"depends": {
|
||||
"viafabric": ">=0.4.14",
|
||||
"viabackwards": ">=5.7.2"
|
||||
"viabackwards": ">=5.12.0"
|
||||
},
|
||||
"custom": {
|
||||
"modmenu:api": true,
|
||||
|
||||
+8
-7
@@ -4,17 +4,18 @@ org.gradle.parallel=true
|
||||
org.gradle.configuration-cache=true
|
||||
|
||||
# Project Details
|
||||
project_jvm_version=17
|
||||
jvm_version=17
|
||||
|
||||
project_group=com.viaversion
|
||||
project_version=4.1.0
|
||||
project_name=ViaRewind
|
||||
project_version=4.2.1-SNAPSHOT
|
||||
project_description=ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.
|
||||
|
||||
publishing_dev_id=florianreuth
|
||||
publishing_dev_name=Florian Reuth
|
||||
publishing_dev_mail=git@florianreuth.de
|
||||
publish_owner_id=florianreuth
|
||||
publish_owner_name=Florian Reuth
|
||||
publish_owner_mail=git@florianreuth.de
|
||||
|
||||
# Modrinth/Hangar Publishing
|
||||
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
|
||||
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
|
||||
velocity_version=3.4-3.5
|
||||
@@ -0,0 +1,21 @@
|
||||
[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.+" }
|
||||
Vendored
BIN
Binary file not shown.
+4
-2
@@ -1,8 +1,10 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
distributionSha256Sum=bafd5ce9cfaea0fbccfdc8439a1ac42fbd4cd9c89dc9a988228d8a2639a58e6c
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
# gradlew 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 Gradle
|
||||
# ksh gradlew
|
||||
#
|
||||
# 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/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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
+52
-33
@@ -19,12 +19,39 @@
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem gradlew startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@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
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@@ -45,13 +72,14 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
goto exitWithErrorLevel
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
@@ -59,35 +87,26 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
goto exitWithErrorLevel
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@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 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
|
||||
|
||||
: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
|
||||
@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%
|
||||
+13
-23
@@ -1,31 +1,21 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven("https://maven.florianreuth.de/releases")
|
||||
}
|
||||
import de.florianreuth.baseproject.setupViaSubprojects
|
||||
|
||||
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.+"
|
||||
}
|
||||
pluginManagement {
|
||||
includeBuild("build-logic")
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
id("base.settings")
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
maven("https://repo.viaversion.com")
|
||||
maven("https://repo.papermc.io/repository/maven-public")
|
||||
maven("https://maven.fabricmc.net")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "viarewind"
|
||||
|
||||
setupViaSubproject("common")
|
||||
setupViaSubproject("bukkit")
|
||||
setupViaSubproject("fabric")
|
||||
setupViaSubproject("sponge")
|
||||
setupViaSubproject("velocity")
|
||||
|
||||
fun setupViaSubproject(name: String) {
|
||||
include("viarewind-$name")
|
||||
project(":viarewind-$name").projectDir = file(name)
|
||||
}
|
||||
setupViaSubprojects("common", "bukkit", "fabric", "sponge", "velocity")
|
||||
@@ -1,3 +1,7 @@
|
||||
dependencies {
|
||||
compileOnly(project(":viarewind-common"))
|
||||
plugins {
|
||||
id("via.addon_subproject")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(projects.viarewindCommon)
|
||||
}
|
||||
@@ -36,11 +36,11 @@
|
||||
},
|
||||
{
|
||||
"id": "viaversion",
|
||||
"version": "[5.7.2,)"
|
||||
"version": "[5.12.0,)"
|
||||
},
|
||||
{
|
||||
"id": "viabackwards",
|
||||
"version": "[5.7.2,)"
|
||||
"version": "[5.12.0,)"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
dependencies {
|
||||
compileOnly(project(":viarewind-common"))
|
||||
compileOnly("com.velocitypowered:velocity-api:3.4.0")?.also { annotationProcessor(it) }
|
||||
plugins {
|
||||
id("via.addon_subproject")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(projects.viarewindCommon)
|
||||
compileOnly(libs.velocity.api)
|
||||
annotationProcessor(libs.velocity.api)
|
||||
}
|
||||
@@ -79,4 +79,9 @@ public class VelocityPlugin implements ViaRewindPlatform {
|
||||
return "compression-decoder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compressHandlerWritesLength() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user