Compare 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
NotAlexNoyle 45508ddf4d Use live TrueOG Network config file 2026-04-01 12:42:16 -04:00
NotAlexNoyle 47535748c4 Add eclipse plugin to gradle 2026-03-18 12:09:49 -04:00
17 changed files with 141 additions and 99 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
+7 -4
View File
@@ -10,13 +10,16 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # 6.1.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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: 17
+1
View File
@@ -2,6 +2,7 @@ import de.florianreuth.baseproject.*
plugins {
`java-library`
eclipse
id("io.papermc.hangar-publish-plugin")
id("com.modrinth.minotaur")
id("de.florianreuth.baseproject")
@@ -511,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();
@@ -529,9 +535,6 @@ 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 int entityId = tracker.getHologramIdWithExtra(wrapper.get(Types.VAR_INT, 0));
final PlayerSessionStorage position = wrapper.user().get(PlayerSessionStorage.class);
@@ -541,6 +544,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 +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);
});
}
});
@@ -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) {
@@ -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;
}
@@ -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) {
@@ -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);
@@ -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
+1 -1
View File
@@ -7,7 +7,7 @@ org.gradle.configuration-cache=true
project_jvm_version=17
project_group=com.viaversion
project_version=4.1.0-SNAPSHOT
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=florianreuth
Binary file not shown.
+4 -2
View File
@@ -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=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/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
+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%