Compare commits

...
52 Commits
Author SHA1 Message Date
NotAlexNoyle 007799734f Exclude CODEOWNERS from language statistics 2026-10-28 08:31:41 -04:00
NotAlexNoyle 2a2cfd20a5 Add CODEOWNERS (one staff approval) 2026-10-28 08:31:41 -04:00
NotAlexNoyle 197288a47e normalize client animations 2026-08-09 16:32:33 -04:00
NotAlexNoyle 44e478ebe7 Merge branch 'ViaVersion:master' into master 2026-05-26 08:38:28 -04:00
dependabot[bot] ea548dfd91 [ci skip] Bump gradle-wrapper from 9.4.1 to 9.5.0 (#681)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.1 to 9.5.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.1...v9.5.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 20:48:44 +02:00
Florian Reuth d0561f2e19 ViaRewind 4.1.1 2026-05-05 20:42:51 +02:00
Beanes 2c8189a447 Fix edge case when server sends a useless packet (#679) 2026-04-28 23:06:11 +02:00
Beanes 48c9e45bcf Also rewrite extra entity ids for other interaction modes (#678) 2026-04-26 16:56:26 +02:00
Gabik21 ecb4f4bf00 Fix nametag visibility duplicate names edge case (#675) 2026-04-24 21:34:24 +02:00
Kichura 6ea9377622 [ci skip] Fix version comments, Unsubscribe from proprietary caching tier. (#677) 2026-04-24 21:34:05 +02:00
Beanes b789fdebb7 Fix set score packet crashing (#674) 2026-04-22 15:16:00 +02:00
Beanes e7b0606e18 Rewrite "no face" in digging packet to a valid 1.8 value (#673) 2026-04-22 13:43:35 +02:00
NotAlexNoyle 14991b61e7 Merge branch 'ViaVersion:master' into master 2026-04-20 16:17:40 -04:00
NotAlexNoyle bf490c3805 Fix brewing stand titles for 1.8 clients on 1.9+ servers (#672) 2026-04-20 22:10:04 +02:00
Florian Reuth 7ca7078141 Start 4.1.1 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-20 22:09:12 +02:00
NotAlexNoyle 9c72c68d11 Merge branch 'ViaVersion:master' into master 2026-04-20 15:32:16 -04:00
NotAlexNoyle 91643b8a53 Fix brewing stand title display for 1.8 clients on 1.9+ servers 2026-04-20 15:31:44 -04:00
Florian Reuth 170af55f91 ViaRewind 4.1.0
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-20 21:15:13 +02:00
dependabot[bot] 00d82f9f2f [ci skip] Bump actions/upload-artifact from 7.0.0 to 7.0.1 (#671)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 21:13:24 +02:00
Beanes 79ef613c15 Implement invisible name tag tracking in 1.8->1.7 (#666)
Fixes https://github.com/ViaVersion/ViaRewind/issues/518
Fixes https://github.com/ViaVersion/ViaRewind/issues/364
2026-04-19 21:58:18 +02:00
Beanes e2fcbafc70 Improve hologram emulation in 1.8->1.7 (#667)
* Fix metadata flags not saving cross packets syncing

* Fix marker zombie no gravity offsets

* Improve interaction by rewriting "extra" entity ids of holograms to normal hologram id
2026-04-19 17:40:18 +02:00
dependabot[bot] 9ae46145e5 [ci skip] Bump gradle/actions from 5.0.2 to 6.1.0 (#669)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.2 to 6.1.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/0723195856401067f7a2779048b490ace7a47d7c...50e97c2cd7a37755bbfafc9c5b7cafaece252f6e)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-19 17:27:54 +02:00
dependabot[bot] 76f90b68b8 Bump de.florianreuth.baseproject from 2.0.1 to 2.0.2 (#665)
Bumps de.florianreuth.baseproject from 2.0.1 to 2.0.2.

---
updated-dependencies:
- dependency-name: de.florianreuth.baseproject
  dependency-version: 2.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 17:59:11 +02:00
Florian Reuth 32a2ee6bfa 26.1.2
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-09 17:54:37 +02:00
Florian Reuth 04878b7e34 26.1.1
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-02 20:50:46 +02:00
NotAlexNoyle 45508ddf4d Use live TrueOG Network config file 2026-04-01 12:42:16 -04:00
Florian Reuth adccdc4b41 26.1
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-27 17:39:20 +01:00
dependabot[bot] 0accea66f2 Bump gradle-wrapper from 9.4.0 to 9.4.1 (#662)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.0 to 9.4.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.0...v9.4.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-26 16:11:29 +01:00
NotAlexNoyle 47535748c4 Add eclipse plugin to gradle 2026-03-18 12:09:49 -04:00
dependabot[bot] 65257be3ee Bump actions/upload-artifact from 6.0.0 to 7.0.0 (#658)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:54 +01:00
dependabot[bot] 880e879dce Bump gradle/actions from 5.0.1 to 5.0.2 (#659)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/f29f5a9d7b09a7c6b29859002d29d24e1674c884...0723195856401067f7a2779048b490ace7a47d7c)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 5.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:46 +01:00
dependabot[bot] 0ccc6a0923 Bump gradle-wrapper from 9.3.1 to 9.4.0 (#660)
Bumps gradle-wrapper from 9.3.1 to 9.4.0.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:40 +01:00
dependabot[bot] 7bff361e14 Bump de.florianreuth.baseproject from 2.0.0 to 2.0.1 (#657)
Bumps de.florianreuth.baseproject from 2.0.0 to 2.0.1.

---
updated-dependencies:
- dependency-name: de.florianreuth.baseproject
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 20:34:07 +01:00
Florian Reuth c2c28473cc Start 4.1.0 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-01 18:10:12 +01:00
Florian Reuth 048d502998 ViaRewind 4.0.15
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-01 18:05:34 +01:00
Beanes e0c1f53115 Manually split newlines in chat messages in 1.8->1.7 (#655) 2026-02-16 17:55:48 +01:00
dependabot[bot] e5313a466b Bump gradle/actions from 5.0.0 to 5.0.1 (#654)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2...f29f5a9d7b09a7c6b29859002d29d24e1674c884)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 5.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 15:20:14 +01:00
dependabot[bot] e30633e367 Bump gradle-wrapper from 9.3.0 to 9.3.1 (#653)
Bumps gradle-wrapper from 9.3.0 to 9.3.1.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-07 16:04:17 +01:00
Florian Reuth a443dd5d11 Update my name
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-02-07 16:00:19 +01:00
dependabot[bot] 3820426bce Bump actions/setup-java from 5.1.0 to 5.2.0 (#651)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/f2beeb24e141e01a676f977032f5a29d81c9e27e...be666c2fcd27ec809703dec50e508c2fdc7f6654)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 16:32:51 +01:00
Beanes 031ae32f27 Only set hologram state if name tag is visible (#652) 2026-02-03 16:32:38 +01:00
FlorianMichael e83616139e Update supported Velocity versions
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-31 20:16:07 +01:00
dependabot[bot] 7325b733d6 Bump actions/checkout from 6.0.1 to 6.0.2 (#650)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 10:57:27 +01:00
Alessandro e64501eae8 Fix red sandstone stairs rotation in 1.8->1.7 (#649) 2026-01-25 17:09:32 +01:00
FlorianMichael 2d99607c35 Start 4.0.15 cycle
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 16:31:36 +01:00
FlorianMichael 4d159189c0 ViaRewind 4.0.14
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 16:19:42 +01:00
FlorianMichael 2076294856 Gradle 9.3.0
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 13:36:11 +01:00
FlorianMichael 49f7bd22be Add default impl class 2026-01-17 14:19:49 +01:00
FlorianMichael 298605874d Remove special sign component type 2026-01-10 00:39:23 +01:00
BeanesandFlorianMichael 35b94f1878 Use velocity native compression if available (#647)
* Optimize 1.7 compression by using velocity-native compressor

* Rewrite 1.7 chunk serializing to reduce allocations

* Don't include velocity native as shaded dependency, add fallback and only use velocity compression if available

* Change chunk compression back to default java as chunks are serialized on heap byte arrays so native compression has no use

* Move velocity only code in custom decoder/encoder overrides

* Remove debug

* Move compression into non static context

---------

Co-authored-by: FlorianMichael <git@florianmichael.de>
2026-01-06 03:25:48 +01:00
FlorianMichael 37955059c4 Don't require config impl class in ViaRewindPlatform#init 2026-01-04 13:01:44 +01:00
FlorianMichael af4306bc81 Start 4.0.14 cycle 2025-12-29 15:08:09 +01:00
44 changed files with 921 additions and 439 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+2 -2
View File
@@ -1,2 +1,2 @@
github: FlorianMichael
custom: [ 'https://florianmichael.de/donate', 'https://creeper123123321.github.io/#donate' ]
github: florianreuth
custom: [ 'https://florianreuth.de/donate', 'https://creeper123123321.github.io/#donate' ]
+7 -4
View File
@@ -10,13 +10,16 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # 5.0.0
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # 5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: 17
@@ -24,7 +27,7 @@ jobs:
- name: Build with Gradle
run: ./gradlew build
- name: Upload Artifacts to GitHub
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # 6.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: Artifacts
path: build/libs/
+6 -3
View File
@@ -15,13 +15,16 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # 5.0.0
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # 5.1.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: 17
+5 -4
View File
@@ -1,10 +1,11 @@
import de.florianmichael.baseproject.*
import de.florianreuth.baseproject.*
plugins {
`java-library`
eclipse
id("io.papermc.hangar-publish-plugin")
id("com.modrinth.minotaur")
id("de.florianmichael.baseproject.BaseProject")
id("de.florianreuth.baseproject")
}
allprojects {
@@ -24,8 +25,8 @@ allprojects {
subprojects {
dependencies {
compileOnly("com.viaversion:viaversion:5.7.0")
compileOnly("com.viaversion:viabackwards:5.7.0")
compileOnly("com.viaversion:viaversion:5.7.2")
compileOnly("com.viaversion:viabackwards:5.7.2")
}
tasks {
+1 -1
View File
@@ -1,6 +1,6 @@
name: ViaRewind
main: com.viaversion.viarewind.BukkitPlugin
authors: [ FlorianMichael/EnZaXD, Gerrygames, creeper123123321 ]
authors: [ EnZaXD, Gerrygames, creeper123123321 ]
version: ${version}
description: ${description}
website: https://viaversion.com/rewind
+2 -1
View File
@@ -1,4 +1,4 @@
import de.florianmichael.baseproject.latestCommitHash
import de.florianreuth.baseproject.latestCommitHash
plugins {
id("net.raphimc.class-token-replacer")
@@ -9,6 +9,7 @@ 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
@@ -0,0 +1,43 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind;
import com.viaversion.viarewind.api.ViaRewindPlatform;
import com.viaversion.viaversion.api.Via;
import java.io.File;
import java.util.logging.Logger;
public class ViaRewindPlatformImpl implements ViaRewindPlatform {
private final Logger logger;
public ViaRewindPlatformImpl() {
logger = Via.getPlatform().createLogger("ViaRewind");
init(new File(getDataFolder(), "viarewind.yml"));
}
@Override
public Logger getLogger() {
return logger;
}
@Override
public File getDataFolder() {
return Via.getPlatform().getDataFolder();
}
}
@@ -43,7 +43,7 @@ public interface ViaRewindPlatform {
/**
* Initialize ViaRewind
*/
default void init(final ViaRewindConfig config) {
default void init(final com.viaversion.viarewind.api.ViaRewindConfig config) {
config.reload();
Via.getManager().getConfigurationProvider().register(config);
@@ -1,77 +0,0 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.api.minecraft;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
public class ExtendedBlockStorage {
private final byte[] blockLSBArray = new byte[ChunkSection.SIZE];
private final NibbleArray blockMetadataArray = new NibbleArray(this.blockLSBArray.length);
private final NibbleArray blockLightArray = new NibbleArray(this.blockLSBArray.length);
private NibbleArray blockMSBArray;
private NibbleArray skyLightArray;
public ExtendedBlockStorage(final boolean skylight) {
if (skylight) {
this.skyLightArray = new NibbleArray(this.blockLSBArray.length);
}
}
public void setBlockId(final int x, final int y, final int z, final int value) {
this.blockLSBArray[ChunkSection.index(x, y, z)] = (byte) (value & 255);
if (value > 255) {
this.getOrCreateBlockMSBArray().set(x, y, z, (value & 0xF00) >> 8);
} else if (this.blockMSBArray != null) {
this.blockMSBArray.set(x, y, z, 0);
}
}
public void setBlockMetadata(final int x, final int y, final int z, final int value) {
this.blockMetadataArray.set(x, y, z, value);
}
public boolean hasBlockMSBArray() {
return this.blockMSBArray != null;
}
public byte[] getBlockLSBArray() {
return this.blockLSBArray;
}
public NibbleArray getOrCreateBlockMSBArray() {
if (this.blockMSBArray == null) {
return this.blockMSBArray = new NibbleArray(this.blockLSBArray.length);
}
return this.blockMSBArray;
}
public NibbleArray getBlockMetadataArray() {
return this.blockMetadataArray;
}
public NibbleArray getBlockLightArray() {
return this.blockLightArray;
}
public NibbleArray getSkyLightArray() {
return this.skyLightArray;
}
}
@@ -28,7 +28,6 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.gson.JsonElement;
import java.util.List;
/**
@@ -36,7 +35,6 @@ import java.util.List;
*/
public class RewindTypes {
public static final Type<JsonElement> SIGN_COMPONENT = new SignComponentType();
public static final Type<int[]> INT_ARRAY = new IntArrayType(); // Integer array with byte as length indicator
public static final Type<BlockPosition> SHORT_POSITION = new PositionVarYType<>(Types.SHORT, value -> (short) value);
@@ -1,51 +0,0 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.api.type;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.ComponentType;
import com.viaversion.viaversion.api.type.types.StringType;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonParser;
import com.viaversion.viaversion.libs.gson.JsonSyntaxException;
import io.netty.buffer.ByteBuf;
public class SignComponentType extends ComponentType {
public static final Type<String> STRING = new StringType(384);
@Override
public JsonElement read(final ByteBuf buffer) {
final String string = STRING.read(buffer);
try {
return JsonParser.parseString(string);
} catch (final JsonSyntaxException e) {
if (Via.getManager().isDebug()) {
Via.getPlatform().getLogger().severe("Error when trying to parse json: " + string);
}
throw e;
}
}
@Override
public void write(final ByteBuf buffer, final JsonElement value) {
STRING.write(buffer, value.toString());
}
}
@@ -18,10 +18,9 @@
package com.viaversion.viarewind.api.type.chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import java.util.zip.Deflater;
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
@@ -38,40 +37,51 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
}
@Override
public void write(ByteBuf byteBuf, Chunk[] chunks) {
public void write(ByteBuf buffer, Chunk[] chunks) {
final int chunkCount = chunks.length;
final int[] chunkX = new int[chunkCount];
final int[] chunkZ = new int[chunkCount];
final short[] primaryBitMask = new short[chunkCount];
final short[] additionalBitMask = new short[chunkCount];
final int[] addBitMasks = new int[chunkCount];
int totalSize = 0;
boolean anySkyLight = false;
final byte[][] dataArrays = new byte[chunkCount][];
int dataSize = 0;
for (Chunk chunk : chunks) {
if (ChunkType1_7_6.hasSkyLight(chunk)) {
anySkyLight = true;
break;
}
}
for (int i = 0; i < chunkCount; i++) {
final Chunk chunk = chunks[i];
Pair<byte[], Short> chunkData;
try {
chunkData = ChunkType1_7_6.serialize(chunk);
final byte[] data = chunkData.key();
dataArrays[i] = data;
dataSize += data.length;
} catch (Exception e) {
throw new RuntimeException("Unable to serialize chunk", e);
}
chunkX[i] = chunk.getX();
chunkZ[i] = chunk.getZ();
primaryBitMask[i] = (short) chunk.getBitmask();
additionalBitMask[i] = chunkData.value();
Chunk chunk = chunks[i];
addBitMasks[i] = ChunkType1_7_6.getAddBitMask(chunk);
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
totalSize += ChunkType1_7_6.calcSize(
chunk.getBitmask(),
addBitMasks[i],
anySkyLight,
biomes
);
}
final byte[] data = new byte[dataSize];
int destPos = 0;
for (final byte[] array : dataArrays) {
System.arraycopy(array, 0, data, destPos, array.length);
destPos += array.length;
final byte[] data = new byte[totalSize];
int offset = 0;
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;
@@ -84,27 +94,16 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
deflater.end();
}
byteBuf.writeShort(chunkCount);
byteBuf.writeInt(compressedSize);
boolean skyLight = false;
for (Chunk chunk : chunks) {
for (ChunkSection section : chunk.getSections()) {
if (section != null && section.getLight().hasSkyLight()) {
skyLight = true;
break;
}
}
}
byteBuf.writeBoolean(skyLight); // hasSkyLight
byteBuf.writeBytes(compressedData, 0, compressedSize);
buffer.writeInt(compressedSize);
buffer.writeBoolean(anySkyLight);
buffer.writeBytes(compressedData, 0, compressedSize);
for (int i = 0; i < chunkCount; i++) {
byteBuf.writeInt(chunkX[i]);
byteBuf.writeInt(chunkZ[i]);
byteBuf.writeShort(primaryBitMask[i]);
byteBuf.writeShort(additionalBitMask[i]);
Chunk chunk = chunks[i];
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeShort(chunk.getBitmask());
buffer.writeShort(addBitMasks[i]);
}
}
}
@@ -17,14 +17,12 @@
*/
package com.viaversion.viarewind.api.type.chunk;
import com.viaversion.viarewind.api.minecraft.ExtendedBlockStorage;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.util.zip.Deflater;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
@@ -38,123 +36,28 @@ public class ChunkType1_7_6 extends Type<Chunk> {
super(Chunk.class);
}
public static Pair<byte[], Short> serialize(final Chunk chunk) throws IOException {
final ExtendedBlockStorage[] storageArrays = new ExtendedBlockStorage[16];
for (int i = 0; i < storageArrays.length; i++) {
final ChunkSection section = chunk.getSections()[i];
if (section != null) {
final ExtendedBlockStorage storage = storageArrays[i] = new ExtendedBlockStorage(section.getLight().hasSkyLight());
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
final int flatBlock = section.palette(PaletteType.BLOCKS).idAt(x, y, z);
storage.setBlockId(x, y, z, flatBlock >> 4);
storage.setBlockMetadata(x, y, z, flatBlock & 15);
}
}
}
storage.getBlockLightArray().setHandle(section.getLight().getBlockLight());
if (section.getLight().hasSkyLight()) {
storage.getSkyLightArray().setHandle(section.getLight().getSkyLight());
}
}
}
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
final int totalSize = calculateSize(storageArrays, chunk.getBitmask(), biomes);
final byte[] output = new byte[totalSize];
int index = 0;
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
final byte[] blockLSBArray = storageArrays[i].getBlockLSBArray();
System.arraycopy(blockLSBArray, 0, output, index, blockLSBArray.length);
index += blockLSBArray.length;
}
}
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
final byte[] blockMetadataArray = storageArrays[i].getBlockMetadataArray().getHandle();
System.arraycopy(blockMetadataArray, 0, output, index, blockMetadataArray.length);
index += blockMetadataArray.length;
}
}
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
final byte[] blockLightArray = storageArrays[i].getBlockLightArray().getHandle();
System.arraycopy(blockLightArray, 0, output, index, blockLightArray.length);
index += blockLightArray.length;
}
}
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].getSkyLightArray() != null) {
final byte[] skyLightArray = storageArrays[i].getSkyLightArray().getHandle();
System.arraycopy(skyLightArray, 0, output, index, skyLightArray.length);
index += skyLightArray.length;
}
}
short additionalBitMask = 0;
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].hasBlockMSBArray()) {
additionalBitMask |= (short) (1 << i);
final byte[] blockMSBArray = storageArrays[i].getOrCreateBlockMSBArray().getHandle();
System.arraycopy(blockMSBArray, 0, output, index, blockMSBArray.length);
index += blockMSBArray.length;
}
}
if (biomes) {
for (int biome : chunk.getBiomeData()) {
output[index++] = (byte) biome;
}
}
return new Pair<>(output, additionalBitMask);
}
private static int calculateSize(final ExtendedBlockStorage[] storageArrays, final int bitmask, final boolean biomes) {
int totalSize = 0;
for (int i = 0; i < storageArrays.length; i++) {
if ((bitmask & 1 << i) != 0) {
totalSize += SIZE; // Block lsb array
totalSize += SIZE / 2; // Block metadata array
totalSize += LIGHT_LENGTH; // Block light array
if (storageArrays[i].getSkyLightArray() != null) {
totalSize += LIGHT_LENGTH;
}
if (storageArrays[i].hasBlockMSBArray()) {
totalSize += SIZE / 2; // Block msb array
}
}
}
if (biomes) {
totalSize += 256;
}
return totalSize;
}
@Override
public Chunk read(ByteBuf byteBuf) {
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
}
@Override
public void write(ByteBuf output, Chunk chunk) {
Pair<byte[], Short> chunkData;
try {
chunkData = serialize(chunk);
} catch (IOException e) {
throw new RuntimeException("Unable to serialize chunk", e);
}
final byte[] data = chunkData.key();
final short additionalBitMask = chunkData.value();
public void write(ByteBuf buffer, Chunk chunk) {
final int bitmask = chunk.getBitmask();
final int addBitmask = getAddBitMask(chunk);
final boolean hasSkyLight = hasSkyLight(chunk);
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
final int size = calcSize(bitmask, addBitmask, hasSkyLight, biomes);
final byte[] data = new byte[size];
serialize(chunk, data, 0, addBitmask, hasSkyLight, biomes);
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeBoolean(chunk.isFullChunk());
buffer.writeShort(bitmask);
buffer.writeShort(addBitmask);
final Deflater deflater = new Deflater();
byte[] compressedData;
@@ -168,13 +71,128 @@ public class ChunkType1_7_6 extends Type<Chunk> {
deflater.end();
}
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
output.writeBoolean(chunk.isFullChunk());
output.writeShort(chunk.getBitmask());
output.writeShort(additionalBitMask);
output.writeInt(compressedSize);
output.writeBytes(compressedData, 0, compressedSize);
buffer.writeInt(compressedSize);
buffer.writeBytes(compressedData, 0, compressedSize);
}
public static int serialize(Chunk chunk, byte[] output, int offset, int addBitmask, boolean writeSkyLight, boolean biomes) {
final ChunkSection[] sections = chunk.getSections();
final int bitmask = chunk.getBitmask();
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j++) {
final int block = palette.idAt(j);
output[offset++] = (byte) ((block >> 4) & 0xFF);
}
}
}
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < ChunkSection.SIZE; j += 2) {
final int meta1 = palette.idAt(j) & 0xF;
final int meta2 = palette.idAt(j + 1) & 0xF;
output[offset++] = (byte) (meta1 | (meta2 << 4));
}
}
}
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;
}
}
if (writeSkyLight) {
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
if (sections[i].getLight().hasSkyLight()) {
final byte[] skyLight = sections[i].getLight().getSkyLight();
System.arraycopy(skyLight, 0, output, offset, LIGHT_LENGTH);
}
offset += LIGHT_LENGTH;
}
}
}
if (addBitmask != 0) {
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0 && (addBitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j += 2) {
final int add1 = (palette.idAt(j) >> 12) & 0xF;
final int add2 = (palette.idAt(j + 1) >> 12) & 0xF;
output[offset++] = (byte) (add1 | (add2 << 4));
}
}
}
}
if (biomes && chunk.getBiomeData() != null) {
final int[] biomeData = chunk.getBiomeData();
for (int biome : biomeData) {
output[offset++] = (byte) biome;
}
}
return offset;
}
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
int size = 0;
int sections = Integer.bitCount(bitmask);
size += sections * SIZE;
size += sections * (SIZE / 2);
size += sections * LIGHT_LENGTH;
if (hasSkyLight) {
size += sections * LIGHT_LENGTH;
}
if (addBitmask != 0) {
size += Integer.bitCount(addBitmask) * (SIZE / 2);
}
if (biomes) {
size += 256;
}
return size;
}
public static int getAddBitMask(Chunk chunk) {
int addBitMask = 0;
for (int i = 0; i < 16; i++) {
if ((chunk.getBitmask() & (1 << i)) != 0) {
final ChunkSection section = chunk.getSections()[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j++) {
final int id = palette.idAt(j);
if ((id >> 12) != 0) {
addBitMask |= (1 << i);
break;
}
}
}
}
return addBitMask;
}
public static boolean hasSkyLight(Chunk chunk) {
for (ChunkSection section : chunk.getSections()) {
if (section != null && section.getLight().hasSkyLight()) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,149 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import com.viaversion.viaversion.libs.gson.JsonArray;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ChatNewlineRewriter {
public static List<JsonObject> splitChatComponentByNewline(JsonElement element) {
return splitChatComponentByNewline(element, new JsonObject());
}
private static List<JsonObject> splitChatComponentByNewline(JsonElement element, JsonObject inheritedStyle) {
List<JsonObject> results = new ArrayList<>();
if (element == null || element.isJsonNull()) {
return results;
}
if (element.isJsonPrimitive()) {
String text = element.getAsString();
for (String part : text.split("\n", -1)) {
JsonObject obj = inheritedStyle.deepCopy();
obj.addProperty("text", part);
results.add(obj);
}
return results;
}
if (element.isJsonArray()) {
for (JsonElement child : element.getAsJsonArray()) {
results.addAll(splitChatComponentByNewline(child, inheritedStyle));
}
return results;
}
if (!element.isJsonObject()) {
return results;
}
JsonObject obj = element.getAsJsonObject();
// Build the style for this node: inherit first, then override with this object's style
JsonObject currentStyle = inheritedStyle.deepCopy();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
String key = entry.getKey();
if (!"text".equals(key) && !"extra".equals(key)) {
currentStyle.add(key, entry.getValue());
}
}
// Gather components in order: base text then extra array
List<JsonElement> ordered = new ArrayList<>();
if (obj.has("text")) {
ordered.add(obj.get("text"));
}
if (obj.has("extra") && obj.get("extra").isJsonArray()) {
for (JsonElement extra : obj.getAsJsonArray("extra")) {
ordered.add(extra);
}
}
// If there is no text/extra, return a single object with the style
if (ordered.isEmpty()) {
results.add(currentStyle);
return results;
}
// Build lines by concatenating split pieces
List<JsonObject> currentLine = new ArrayList<>();
for (JsonElement part : ordered) {
if (part == null || part.isJsonNull()) {
continue;
}
if (part.isJsonPrimitive()) {
String text = part.getAsString();
String[] split = text.split("\n", -1);
for (int i = 0; i < split.length; i++) {
JsonObject piece = currentStyle.deepCopy();
piece.addProperty("text", split[i]);
currentLine.add(piece);
if (i < split.length - 1) {
results.add(mergeLine(currentLine));
currentLine.clear();
}
}
continue;
}
if (part.isJsonObject() || part.isJsonArray()) {
List<JsonObject> splitParts = splitChatComponentByNewline(part, currentStyle);
for (int i = 0; i < splitParts.size(); i++) {
currentLine.add(splitParts.get(i));
if (i < splitParts.size() - 1) {
results.add(mergeLine(currentLine));
currentLine.clear();
}
}
}
}
if (!currentLine.isEmpty()) {
results.add(mergeLine(currentLine));
}
return results;
}
private static JsonObject mergeLine(List<JsonObject> lineParts) {
if (lineParts.isEmpty()) {
JsonObject empty = new JsonObject();
empty.addProperty("text", "");
return empty;
}
JsonObject first = lineParts.get(0);
if (lineParts.size() == 1) {
return first;
}
JsonArray extra = new JsonArray();
for (int i = 1; i < lineParts.size(); i++) {
extra.add(lineParts.get(i));
}
first.add("extra", extra);
return first;
}
}
@@ -24,6 +24,7 @@ import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -46,6 +47,8 @@ public class VirtualHologramEntity {
private String name = null;
private float yaw, pitch;
private float headYaw;
private byte flags = 0;
private byte armorStandFlags = 0;
private boolean small = false;
private boolean marker = false;
private boolean sneaking = false;
@@ -99,8 +102,6 @@ public class VirtualHologramEntity {
}
// Filter armor stand data to calculate emulation
byte flags = 0;
byte armorStandFlags = 0;
for (EntityData entityData : entityDataTracker) {
if (entityData.id() == 0 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
flags = ((Number) entityData.getValue()).byteValue();
@@ -120,7 +121,7 @@ public class VirtualHologramEntity {
gravity = (armorStandFlags & 0x02) == 0;
State prevState = currentState;
if (invisible && name != null) {
if (invisible && name != null && alwaysShowNametag == 1) {
currentState = State.HOLOGRAM;
} else if (gravity) {
currentState = State.ZOMBIE;
@@ -167,6 +168,18 @@ public class VirtualHologramEntity {
} else {
return baseOffset + (0.9875 * 2);
}
} else if (currentState == State.ZOMBIE_NO_GRAVITY) {
// 1.7 ride stack offset sum: Squid (0.95 * 0.75) + Zombie (1.8 * 0.75) = 2.0625
// By starting at -2.0625, we zero out the 1.7 passenger height so we can apply 1.8's offsets.
double baseOffset = -2.0625;
if (marker) {
// 1.8 Marker ArmorStand height is 0.0 -> mounted offset 0.0
return baseOffset;
} else {
// 1.8 Normal ArmorStand height is 1.975 -> mounted offset 1.975 * 0.75 = 1.48125
return baseOffset + 1.48125;
}
} else {
return -0.4;
}
@@ -291,6 +304,13 @@ public class VirtualHologramEntity {
this.entityIds = entityIds;
}
for (int extraId : entityIds) {
if (extraId != entityId) {
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).setExtraHologramId(entityId, extraId);
}
}
sendEntityDataUpdate(entityRewriter);
if (entityIds == null) {
return;
@@ -326,6 +346,13 @@ public class VirtualHologramEntity {
if (entityIds == null) {
return;
}
for (int extraId : entityIds) {
if (extraId != entityId) {
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).removeExtraHologramId(extraId);
}
}
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user);
despawn.write(Types.BYTE, (byte) entityIds.length);
for (int id : entityIds) {
@@ -28,9 +28,23 @@ public abstract class CompressionHandlerProvider implements Provider {
public abstract void onTransformPacket(UserConnection user);
public abstract ChannelHandler getEncoder(int threshold);
/**
* 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);
public abstract ChannelHandler getDecoder(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;
@@ -24,6 +24,7 @@ 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> {
@@ -41,7 +42,9 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (!in.isReadable()) return;
if (!in.isReadable()) {
return;
}
int outLength = Types.VAR_INT.readPrimitive(in);
if (outLength == 0) {
@@ -55,11 +58,15 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
}
ByteBuf temp = in;
if (!in.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
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 {
in.retain();
source.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(outLength, outLength);
try {
@@ -72,4 +79,5 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
this.inflater.reset();
}
}
}
@@ -22,6 +22,7 @@ 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> {
@@ -48,11 +49,15 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
Types.VAR_INT.writePrimitive(out, frameLength);
ByteBuf temp = in;
if (!in.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
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 {
in.retain();
source.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
try {
@@ -63,11 +68,12 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
output.ensureWritable(4096);
output.writerIndex(output.writerIndex() + this.deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
}
out.writeBytes(output);
destination.writeBytes(output);
} finally {
output.release();
temp.release();
this.deflater.reset();
}
}
}
@@ -21,6 +21,8 @@ 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;
@@ -28,6 +30,19 @@ 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();
@@ -36,12 +51,17 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
return;
}
Object compressor = null;
if (velocityNatives) {
compressor = com.velocitypowered.natives.util.Natives.compress.get().create(-1);
}
final String compressHandlerName = ViaRewind.getPlatform().compressHandlerName();
final CompressionEncoder encoder = (CompressionEncoder) pipeline.get(compressHandlerName);
if (encoder != null) {
encoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, getEncoder(threshold));
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, getEncoder(compressor, threshold));
}
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
@@ -49,7 +69,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
if (decoder != null) {
decoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(threshold));
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(compressor, threshold));
}
}
@@ -77,12 +97,21 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
}
@Override
public ChannelHandler getEncoder(int threshold) {
return new CompressionEncoder(threshold);
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(int threshold) {
return new CompressionDecoder(threshold);
public ChannelHandler getDecoder(final Object compressor, int threshold) {
if (velocityNatives) {
return new VelocityCompressionDecoder(threshold, (com.velocitypowered.natives.compression.VelocityCompressor) compressor);
} else {
return new CompressionDecoder(threshold);
}
}
}
@@ -0,0 +1,47 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.CompressionDecoder;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import java.util.List;
import java.util.zip.DataFormatException;
public final class VelocityCompressionDecoder extends CompressionDecoder {
private final VelocityCompressor compressor;
public VelocityCompressionDecoder(final int threshold, final VelocityCompressor compressor) {
super(threshold);
this.compressor = compressor;
}
@Override
protected void inflate(final ChannelHandlerContext ctx, final ByteBuf source, final int outLength, final List<Object> out) throws DataFormatException {
ByteBuf output = ctx.alloc().buffer(outLength);
try {
this.compressor.inflate(source, output, outLength);
out.add(output.retain());
} finally {
output.release();
}
}
}
@@ -0,0 +1,39 @@
/*
* 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);
}
}
@@ -337,6 +337,11 @@ public class EntityPacketRewriter1_8 extends VREntityRewriter<ClientboundPackets
map(Types.ENTITY_DATA_LIST1_8, RewindTypes.ENTITY_DATA_LIST1_7); // Entity data
handler(getTrackerAndDataHandler(RewindTypes.ENTITY_DATA_LIST1_7, EntityTypes1_8.EntityType.PLAYER));
handler(wrapper -> {
final int entityId = wrapper.get(Types.VAR_INT, 0);
final EntityTracker1_8 tracker = tracker(wrapper.user());
tracker.checkNametagVisibility(entityId);
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.SET_EQUIPPED_ITEM, new PacketHandlers() {
@@ -30,6 +30,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPa
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.ChatItemRewriter;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.ChatNewlineRewriter;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.TitleRenderProvider;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.GameProfileStorage;
@@ -46,6 +47,7 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
import com.viaversion.viaversion.util.ComponentUtil;
@@ -70,13 +72,27 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
@Override
public void register() {
map(Types.COMPONENT); // Chat message
handler(wrapper -> {
final JsonElement json = wrapper.get(Types.COMPONENT, 0);
ChatItemRewriter.toClient(protocol, wrapper.user(), json);
final int position = wrapper.read(Types.BYTE);
if (position == 2) { // Above hotbar
wrapper.cancel();
return;
}
ChatItemRewriter.toClient(protocol, wrapper.user(), json);
List<JsonObject> splitComponents = ChatNewlineRewriter.splitChatComponentByNewline(json);
if (splitComponents.size() > 1) {
wrapper.cancel();
for (JsonObject split : splitComponents) {
PacketWrapper newWrapper = wrapper.create(ClientboundPackets1_7_2_5.CHAT);
newWrapper.write(Types.COMPONENT, split);
newWrapper.send(Protocol1_8To1_7_6_10.class);
}
}
});
}
@@ -495,7 +511,13 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
String username = message.split(" ")[1];
final GameProfileStorage storage = wrapper.user().get(GameProfileStorage.class);
final GameProfileStorage.GameProfile profile = storage.get(username, true);
// Multiple players with the same name may exist. This will only represent
// fake players so it's fine to just pick the first one.
List<GameProfileStorage.GameProfile> profiles = storage.get(username, true);
if (profiles.isEmpty()) {
return;
}
final GameProfileStorage.GameProfile profile = profiles.get(0);
if (profile != null && profile.uuid != null) {
wrapper.cancel();
@@ -513,16 +535,20 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
map(Types.BYTE, Types.VAR_INT); // Mode
handler(wrapper -> {
final int mode = wrapper.get(Types.VAR_INT, 1);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final int entityId = tracker.getHologramIdWithExtra(wrapper.get(Types.VAR_INT, 0));
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
if (entityId == -1) {
return;
}
wrapper.set(Types.VAR_INT, 0, entityId);
if (mode != 0) {
return;
}
final int entityId = wrapper.get(Types.VAR_INT, 0);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
if (!tracker.getHolograms().containsKey(entityId)) {
return;
}
final AABB boundingBox = tracker.getHolograms().get(entityId).getBoundingBox();
Vector3d pos = new Vector3d(position.getPosX(), position.getPosY() + 1.8, position.getPosZ());
@@ -590,6 +616,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);
});
}
});
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -173,6 +174,7 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
}
final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final byte mode = wrapper.passthrough(Types.BYTE);
if (mode != 0 && !scoreboard.teamExists(team)) {
@@ -190,6 +192,10 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
if (mode == 0) {
scoreboard.addTeam(team);
} else if (mode == 1) {
// Team removed, nametag visibility might have changed
for (final String member : scoreboard.getTeamMembers(team)) {
tracker.checkNametagVisbility(member);
}
scoreboard.removeTeam(team);
}
@@ -198,7 +204,9 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
wrapper.passthrough(Types.STRING); // Prefix
wrapper.passthrough(Types.STRING); // Suffix
wrapper.passthrough(Types.BYTE); // Friendly fire
wrapper.read(Types.STRING); // Name tag visibility
final String nameTagVisibility = wrapper.read(Types.STRING);
final String previousVisibility = scoreboard.getTeamNameTagVisibility(team);
scoreboard.setTeamNameTagVisibility(team, nameTagVisibility);
byte color = wrapper.read(Types.BYTE);
if (mode == 2 && scoreboard.getTeamColor(team).get() != color) {
final String sidebar = scoreboard.getColorDependentSidebar().get(color);
@@ -209,6 +217,13 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
scoreboard.setTeamColor(team, color);
// Re-evaluate nametag visibility for all team members when visibility changes
if (mode == 2 && !nameTagVisibility.equals(previousVisibility)) {
for (final String member : scoreboard.getTeamMembers(team)) {
tracker.checkNametagVisbility(member);
}
}
}
if (mode == 0 || mode == 3 || mode == 4) {
byte color = scoreboard.getTeamColor(team).get();
@@ -223,6 +238,8 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
continue;
}
scoreboard.removePlayerFromTeam(entry, team);
// Player left team, nametag visibility may change
tracker.checkNametagVisbility(entry);
if (entry.equals(username)) {
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
@@ -231,6 +248,8 @@ public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6
}
} else {
scoreboard.addPlayerToTeam(entry, team);
// Player joined team, nametag visibility may change
tracker.checkNametagVisbility(entry);
if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) {
final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
displayObjective.write(Types.BYTE, (byte) 1);
@@ -37,18 +37,26 @@ import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
public class EntityTracker1_8 extends EntityTrackerBase {
private final Int2ObjectMap<VirtualHologramEntity> holograms = new Int2ObjectArrayMap<>();
private final Int2IntMap extraHologramIds = new Int2IntArrayMap();
private final Int2IntMap vehicles = new Int2IntArrayMap();
private final Int2ObjectMap<UUID> entityIdToUUID = new Int2ObjectArrayMap<>();
private final Object2IntMap<UUID> entityUUIDToId = new Object2IntOpenHashMap<>();
private final Int2IntMap playerNametagHiderEntities = new Int2IntArrayMap();
private final List<EntityData> entityData = new ArrayList<>();
@@ -77,6 +85,10 @@ public class EntityTracker1_8 extends EntityTrackerBase {
holograms.remove(entityId);
}
if (playerNametagHiderEntities.containsKey(entityId)) {
despawnNametagHiderEntity(entityId);
}
if (entityIdToUUID.containsKey(entityId)) {
final UUID playerId = entityIdToUUID.remove(entityId);
@@ -88,7 +100,10 @@ public class EntityTracker1_8 extends EntityTrackerBase {
@Override
public void clearEntities() {
super.clearEntities();
holograms.clear();
extraHologramIds.clear();
vehicles.clear();
playerNametagHiderEntities.clear();
}
@Override
@@ -155,6 +170,11 @@ public class EntityTracker1_8 extends EntityTrackerBase {
} else {
vehicles.put(vehicleId, passengerId);
}
// Re-evaluate nametag visibility when a player entity's passenger changes
if (vehicleId != -1 && entityIdToUUID.containsKey(vehicleId)) {
checkNametagVisibility(vehicleId);
}
}
protected void attachEntity(final int target) {
@@ -185,10 +205,111 @@ public class EntityTracker1_8 extends EntityTrackerBase {
}
}
public void checkNametagVisibility(final int entityId) {
if (!entityIdToUUID.containsKey(entityId)) {
return;
}
final boolean shouldHide = isPlayerNametagHidden(entityId);
final boolean hasServerPassenger = getPassenger(entityId) != -1;
final boolean hasSkull = playerNametagHiderEntities.containsKey(entityId);
if (shouldHide && !hasServerPassenger && !hasSkull) {
spawnNametagHiderEntity(entityId);
} else if ((!shouldHide || hasServerPassenger) && hasSkull) {
despawnNametagHiderEntity(entityId);
}
}
public void checkNametagVisbility(final String username) {
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
final List<GameProfileStorage.GameProfile> profiles = profileStorage.get(username, false);
for (GameProfileStorage.GameProfile profile : profiles) {
final int entityId = getPlayerEntityId(profile.uuid);
if (entityId == -1) {
return;
}
checkNametagVisibility(entityId);
}
}
private boolean isPlayerNametagHidden(final int entityId) {
final UUID uuid = entityIdToUUID.get(entityId);
if (uuid == null) {
return false;
}
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
final GameProfileStorage.GameProfile profile = profileStorage.get(uuid);
if (profile == null) {
return false;
}
return user().get(ScoreboardTracker.class).isNametagHidden(profile.name);
}
private int getNametagHiderEntityId(final int playerEntityId) {
return Integer.MAX_VALUE - 32000 - playerEntityId;
}
private void spawnNametagHiderEntity(final int playerEntityId) {
final int entityId = getNametagHiderEntityId(playerEntityId);
playerNametagHiderEntities.put(playerEntityId, entityId);
final List<EntityData> mobData = new ArrayList<>();
mobData.add(new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0x20));
mobData.add(new EntityData(16, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
final PacketWrapper spawnMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user());
spawnMob.write(Types.VAR_INT, entityId);
spawnMob.write(Types.UNSIGNED_BYTE, (short) EntityTypes1_8.EntityType.MAGMA_CUBE.getId());
spawnMob.write(Types.INT, 0); // X
spawnMob.write(Types.INT, 0); // Y
spawnMob.write(Types.INT, 0); // Z
spawnMob.write(Types.BYTE, (byte) 0); // Yaw
spawnMob.write(Types.BYTE, (byte) 0); // Pitch
spawnMob.write(Types.BYTE, (byte) 0); // Head yaw
spawnMob.write(Types.SHORT, (short) 0); // Velocity x
spawnMob.write(Types.SHORT, (short) 0); // Velocity y
spawnMob.write(Types.SHORT, (short) 0); // Velocity z
spawnMob.write(RewindTypes.ENTITY_DATA_LIST1_7, mobData);
spawnMob.scheduleSend(Protocol1_8To1_7_6_10.class);
final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user());
attach.write(Types.INT, entityId);
attach.write(Types.INT, playerEntityId);
attach.write(Types.BOOLEAN, false);
attach.scheduleSend(Protocol1_8To1_7_6_10.class);
}
private void despawnNametagHiderEntity(final int playerEntityId) {
if (!playerNametagHiderEntities.containsKey(playerEntityId)) return;
final int mobId = playerNametagHiderEntities.remove(playerEntityId);
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user());
despawn.write(Types.BYTE, (byte) 1);
despawn.write(Types.INT, mobId);
despawn.scheduleSend(Protocol1_8To1_7_6_10.class);
}
public Int2ObjectMap<VirtualHologramEntity> getHolograms() {
return holograms;
}
public void setExtraHologramId(final int entityId, final int extraId) {
extraHologramIds.put(extraId, entityId);
}
public void removeExtraHologramId(int extraId) {
extraHologramIds.remove(extraId);
}
public int getHologramIdWithExtra(final int id) {
if (holograms.containsKey(id)) {
return id;
}
return extraHologramIds.getOrDefault(id, -1);
}
public boolean isSpectator() {
return clientEntityGameMode == 3;
}
@@ -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;
}
@@ -37,6 +37,7 @@ public class ScoreboardTracker extends StoredObject {
private final HashMap<String, Byte> teamColors = new HashMap<>();
private final HashSet<String> scoreTeamNames = new HashSet<>();
private final HashMap<Byte, String> colorDependentSidebar = new HashMap<>();
private final HashMap<String, String> teamNameTagVisibilities = new HashMap<>();
private String colorIndependentSidebar;
public ScoreboardTracker(UserConnection user) {
@@ -63,6 +64,7 @@ public class ScoreboardTracker extends StoredObject {
teams.remove(team);
scoreTeams.remove(team);
teamColors.remove(team);
teamNameTagVisibilities.remove(team);
}
public boolean teamExists(String team) {
@@ -98,6 +100,27 @@ public class ScoreboardTracker extends StoredObject {
return Optional.empty();
}
public void setTeamNameTagVisibility(String team, String visibility) {
teamNameTagVisibilities.put(team, visibility);
}
public String getTeamNameTagVisibility(String team) {
return teamNameTagVisibilities.getOrDefault(team, "always");
}
public boolean isNametagHidden(String username) {
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
if (entry.getValue().contains(username)) {
return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always"));
}
}
return false;
}
public List<String> getTeamMembers(String team) {
return teams.getOrDefault(team, new ArrayList<>());
}
public void addObjective(String name) {
objectives.add(name);
}
@@ -117,37 +140,43 @@ public class ScoreboardTracker extends StoredObject {
public String sendTeamForScore(String score) {
if (score.length() <= 16) return score;
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
int l = 16;
int i = Math.min(16, score.length() - 16);
String name = score.substring(i, i + l);
while (scoreTeamNames.contains(name) || teams.containsKey(name)) {
i--;
while (score.length() - l - i > 16) {
l--;
if (l < 1) return score;
i = Math.min(16, score.length() - l);
// Find the longest window score[i, i+l) that:
// - is not already claimed as a team name
// - keeps prefix (score[0,i)) at most 16 chars i <= 16
// - keeps suffix (score[i+l, end)) at most 16 chars score.length()-l-i <= 16
for (int l = 16; l >= 1; l--) {
final int iMax = Math.min(16, score.length() - l);
final int iMin = Math.max(0, score.length() - l - 16);
for (int i = iMax; i >= iMin; i--) {
final String name = score.substring(i, i + l);
if (scoreTeamNames.contains(name) || teams.containsKey(name)) {
continue;
}
final String prefix = score.substring(0, i);
final String suffix = i + l >= score.length() ? "" : score.substring(i + l);
final ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
scoreTeams.put(score, scoreTeam);
scoreTeamNames.add(name);
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, name);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.STRING, "ViaRewind");
teamPacket.write(Types.STRING, prefix);
teamPacket.write(Types.STRING, suffix);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.SHORT, (short) 1);
teamPacket.write(Types.STRING, name);
teamPacket.send(Protocol1_8To1_7_6_10.class);
return name;
}
name = score.substring(i, i + l);
}
String prefix = score.substring(0, i);
String suffix = i + l >= score.length() ? "" : score.substring(i + l);
ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
scoreTeams.put(score, scoreTeam);
scoreTeamNames.add(name);
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, name);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.STRING, "ViaRewind");
teamPacket.write(Types.STRING, prefix);
teamPacket.write(Types.STRING, suffix);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.SHORT, (short) 1);
teamPacket.write(Types.STRING, name);
teamPacket.send(Protocol1_8To1_7_6_10.class);
return name;
return score;
}
public String removeTeamForScore(String score) {
@@ -253,6 +253,23 @@ public class EntityPacketRewriter1_9 extends VREntityRewriter<ClientboundPackets
}
});
protocol.registerClientbound(ClientboundPackets1_9.ANIMATE, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Entity id
map(Types.UNSIGNED_BYTE); // Animation
handler(wrapper -> {
final short animation = wrapper.get(Types.UNSIGNED_BYTE, 0);
// Action 1 is a hurt duplicate of entity status 2 (only ViaBackwards manufactures it)
// and makes 1.8 clients re-flash mid-window; action 3 is offhand swing on 1.9+ but
// eat food on 1.8. Cancel both, matching ViaVersion's reverse-direction handling.
if (animation == 1 || animation == 3) {
wrapper.cancel();
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.MOVE_ENTITY_POS, wrapper -> {
final int entityId = wrapper.passthrough(Types.VAR_INT);
final int deltaX = wrapper.read(Types.SHORT);
@@ -447,7 +447,7 @@ public class PlayerPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
map(Types.BLOCK_POSITION1_8); // Position
handler(wrapper -> {
for (int i = 0; i < 4; i++) {
final JsonElement json = wrapper.read(RewindTypes.SIGN_COMPONENT);
final JsonElement json = wrapper.read(Types.COMPONENT);
if (!json.isJsonPrimitive()) {
throw new IllegalStateException("Invalid sign text: " + StringUtil.forLogging(json));
}
@@ -52,11 +52,11 @@ public class WindowTracker extends StoredObject {
openWindow.write(Types.UNSIGNED_BYTE, windowId);
openWindow.write(Types.STRING, "minecraft:brewing_stand");
TextComponent title = new StringComponent().
append(new TranslationComponent("container.brewing")).
append(new StringComponent(": " + 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);
@@ -5,7 +5,7 @@
# ONLY DISABLE IF YOU HAVE 1.9 COOLDOWN DISABLED ON YOUR SERVER
# 1.8 PLAYERS MAY ASK WHY PVP IS NOT WORKING OTHERWISE.
# REQUIRES A SERVER RESTART TO TAKE EFFECT
cooldown-indicator: 'TITLE'
cooldown-indicator: DISABLED
#
# Replaces Adventure mode with Survival mode for 1.7.x clients
# Enable this option if your server is using the 'CanDestroy'
@@ -14,31 +14,31 @@ replace-adventure: false
#
# Whether similar ones should replace 1.9 particles in
# 1.8 and lower
replace-particles: false
replace-particles: true
#
# Max number of pages for written books before a client gets kicked
max-book-pages: 100
max-book-pages: 500
#
# Max number of characters in the json (!) string of a book page before a client gets kicked
max-book-page-length: 5000
#
# Whether to emulate the 1.8+ world border for 1.7.x clients
emulate-world-border: true
emulate-world-border: false
#
# Always shows a mapped mob's original name, and not only when hovering over it with the cursor.
always-show-original-mob-name: true
#
# The particle to show the world border for the 1.8+ world border for 1.7.x clients
world-border-particle: 'fireworksSpark'
world-border-particle: fireworksSpark
#
# If enabled, 1.8 players on 1.9+ servers can use /offhand to switch items between their main hand and offhand.
enable-offhand: true
enable-offhand: false
#
# Allows defining the offhand command
offhand-command: '/offhand'
offhand-command: /offhand
#
# If enabled, 1.8 players on 1.9+ servers will also experience the levitation effect by sending velocity packets.
emulate-levitation-effect: true
#
# If enabled, 1.8 players will handle the player combat packet added in 1.9 by showing the custom death message above the hotbar.
handle-player-combat-packet: true
handle-player-combat-packet: false
@@ -126,6 +126,7 @@
},
"180": {
"id": "128",
"data": "-1",
"name": "1.8 Red Sandstone Stairs"
},
"181": {
+2 -2
View File
@@ -13,7 +13,7 @@
"icon": "assets/viarewind/textures/squarelogo.png",
"environment": "*",
"authors": [
"FlorianMichael/EnZaXD",
"Florian Reuth (EnZaXD)",
"Gerrygames",
"creeper123123321"
],
@@ -24,7 +24,7 @@
},
"depends": {
"viafabric": ">=0.4.14",
"viabackwards": ">=5.7.0"
"viabackwards": ">=5.7.2"
},
"custom": {
"modmenu:api": true,
+7 -7
View File
@@ -7,14 +7,14 @@ org.gradle.configuration-cache=true
project_jvm_version=17
project_group=com.viaversion
project_version=4.0.13
project_version=4.1.1
project_description=ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.
publishing_dev_id=FlorianMichael
publishing_dev_name=EnZaXD
publishing_dev_mail=git@florianmichael.de
publishing_dev_id=florianreuth
publishing_dev_name=Florian Reuth
publishing_dev_mail=git@florianreuth.de
# Modrinth/Hangar Publishing
minecraft_versions=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-1.21.11
velocity_version=3.3
minecraft_versions=26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8
minecraft_version_range=1.8-26.1.2
velocity_version=3.4-3.5
Binary file not shown.
+4 -2
View File
@@ -1,8 +1,10 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+1 -1
View File
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/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
+10 -21
View File
@@ -23,8 +23,8 @@
@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
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ 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
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ 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
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@@ -73,21 +73,10 @@ goto fail
@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 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
: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
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+2 -1
View File
@@ -2,10 +2,11 @@ pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.florianreuth.de/releases")
}
plugins {
id("de.florianmichael.baseproject.BaseProject") version "1.2.6"
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.+"
@@ -13,7 +13,7 @@
},
"contributors": [
{
"name": "FlorianMichael/EnZaXD",
"name": "Florian Reuth (EnZaXD)",
"description": "Author"
},
{
@@ -36,11 +36,11 @@
},
{
"id": "viaversion",
"version": "[5.7.0,)"
"version": "[5.7.2,)"
},
{
"id": "viabackwards",
"version": "[5.7.0,)"
"version": "[5.7.2,)"
}
]
},
+1 -1
View File
@@ -1,4 +1,4 @@
dependencies {
compileOnly(project(":viarewind-common"))
compileOnly("com.velocitypowered:velocity-api:3.1.1")?.also { annotationProcessor(it) }
compileOnly("com.velocitypowered:velocity-api:3.4.0")?.also { annotationProcessor(it) }
}
@@ -34,7 +34,7 @@ import java.util.logging.Logger;
@Plugin(id = "viarewind",
name = "ViaRewind",
version = ViaRewindPlatform.VERSION,
authors = {"FlorianMichael/EnZaXD", "Gerrygames", "creeper123123321"},
authors = {"EnZaXD", "Gerrygames", "creeper123123321"},
description = "ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.",
dependencies = {
@Dependency(id = "viaversion"),
@@ -52,6 +52,7 @@ public class VelocityPlugin implements ViaRewindPlatform {
@DataDirectory
private Path configPath;
@SuppressWarnings("deprecation")
@Subscribe(order = PostOrder.LATE)
public void onProxyStart(ProxyInitializeEvent e) {
this.logger = new LoggerWrapper(loggerSlf4j);