mirror of
https://github.com/ViaVersion/ViaRewind.git
synced 2026-11-04 08:52:09 -05:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd3de2ce9 | ||
|
|
ddbdbdb1cc | ||
|
|
28b4998912 | ||
|
|
f9b0fd5c5a | ||
|
|
a676368588 | ||
|
|
966231ad1c | ||
|
|
d73c2198ea | ||
|
|
6f319932be | ||
|
|
f172de2742 | ||
|
|
ca25da9445 | ||
|
|
ea548dfd91 | ||
|
|
d0561f2e19 | ||
|
|
2c8189a447 | ||
|
|
48c9e45bcf | ||
|
|
ecb4f4bf00 | ||
|
|
6ea9377622 | ||
|
|
b789fdebb7 | ||
|
|
e7b0606e18 | ||
|
|
bf490c3805 | ||
|
|
7ca7078141 | ||
|
|
170af55f91 | ||
|
|
00d82f9f2f |
No files matched your search
@@ -10,13 +10,16 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
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/
|
||||
@@ -15,13 +15,16 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
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
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ allprojects {
|
||||
subprojects {
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.viaversion:viaversion:5.7.2")
|
||||
compileOnly("com.viaversion:viabackwards:5.7.2")
|
||||
compileOnly("com.viaversion:viaversion:5.10.0")
|
||||
compileOnly("com.viaversion:viabackwards:5.10.0")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.api.compression;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* A ThreadLocal-based compression provider that either uses Velocity's native compression
|
||||
* or falls back to Java's built-in compression (Deflater/Inflater).
|
||||
*/
|
||||
public final class ThreadLocalCompressionProvider {
|
||||
|
||||
private static final boolean VELOCITY_NATIVES_AVAILABLE;
|
||||
|
||||
static {
|
||||
boolean velocityAvailable = false;
|
||||
try {
|
||||
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
|
||||
velocityAvailable = true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
VELOCITY_NATIVES_AVAILABLE = velocityAvailable;
|
||||
}
|
||||
|
||||
// ThreadLocal for Java's Deflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Deflater> JAVA_DEFLATER = ThreadLocal.withInitial(Deflater::new);
|
||||
|
||||
// ThreadLocal for Java's Inflater (used when Velocity natives are not available)
|
||||
private static final ThreadLocal<Inflater> JAVA_INFLATER = ThreadLocal.withInitial(Inflater::new);
|
||||
|
||||
private ThreadLocalCompressionProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing uncompressed data
|
||||
* @param destination the destination buffer to write compressed data to
|
||||
* @throws DataFormatException if compression fails
|
||||
*/
|
||||
public static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
deflateVelocity(source, destination);
|
||||
} else {
|
||||
deflateJava(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decompresses data from the source buffer into the destination buffer.
|
||||
*
|
||||
* @param source the source buffer containing compressed data
|
||||
* @param destination the destination buffer to write decompressed data to
|
||||
* @param expectedSize the expected size of the decompressed data
|
||||
* @throws DataFormatException if decompression fails
|
||||
*/
|
||||
public static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
if (VELOCITY_NATIVES_AVAILABLE) {
|
||||
inflateVelocity(source, destination, expectedSize);
|
||||
} else {
|
||||
inflateJava(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Java native implementation
|
||||
|
||||
private static void deflateJava(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
try {
|
||||
final Deflater deflater = JAVA_DEFLATER.get();
|
||||
deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
deflater.finish();
|
||||
|
||||
while (!deflater.finished()) {
|
||||
output.ensureWritable(4096);
|
||||
output.writerIndex(output.writerIndex() + deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
|
||||
}
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_DEFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void inflateJava(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(expectedSize, expectedSize);
|
||||
try {
|
||||
final Inflater inflater = JAVA_INFLATER.get();
|
||||
inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + inflater.inflate(output.array(), output.arrayOffset(), expectedSize));
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
JAVA_INFLATER.get().reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity native implementation
|
||||
private static void deflateVelocity(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
VelocityHolder.deflate(source, destination);
|
||||
}
|
||||
|
||||
private static void inflateVelocity(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
VelocityHolder.inflate(source, destination, expectedSize);
|
||||
}
|
||||
|
||||
private static final class VelocityHolder {
|
||||
|
||||
private static final ThreadLocal<com.velocitypowered.natives.compression.VelocityCompressor> COMPRESSOR =
|
||||
ThreadLocal.withInitial(() -> com.velocitypowered.natives.util.Natives.compress.get().create(-1));
|
||||
|
||||
static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
COMPRESSOR.get().deflate(source, destination);
|
||||
}
|
||||
|
||||
static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
|
||||
COMPRESSOR.get().inflate(source, destination, expectedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-32
@@ -17,11 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.type.chunk;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
|
||||
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
|
||||
@@ -63,40 +65,47 @@ public class BulkChunkType1_7_6 extends Type<Chunk[]> {
|
||||
);
|
||||
}
|
||||
|
||||
final byte[] data = new byte[totalSize];
|
||||
int offset = 0;
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(totalSize);
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
offset = ChunkType1_7_6.serialize(
|
||||
chunk,
|
||||
data,
|
||||
offset,
|
||||
addBitMasks[i],
|
||||
anySkyLight,
|
||||
biomes
|
||||
);
|
||||
}
|
||||
|
||||
buffer.writeShort(chunkCount);
|
||||
|
||||
final Deflater deflater = new Deflater();
|
||||
byte[] compressedData;
|
||||
int compressedSize;
|
||||
try {
|
||||
deflater.setInput(data, 0, data.length);
|
||||
deflater.finish();
|
||||
compressedData = new byte[data.length];
|
||||
compressedSize = deflater.deflate(compressedData);
|
||||
} finally {
|
||||
deflater.end();
|
||||
}
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writeBoolean(anySkyLight);
|
||||
buffer.writeBytes(compressedData, 0, compressedSize);
|
||||
ChunkType1_7_6.serialize(
|
||||
chunk,
|
||||
uncompressed,
|
||||
addBitMasks[i],
|
||||
anySkyLight,
|
||||
biomes
|
||||
);
|
||||
}
|
||||
|
||||
buffer.writeShort(chunkCount);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
buffer.writeBoolean(anySkyLight);
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress bulk chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
uncompressed.release();
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunkCount; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.api.type.chunk;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
|
||||
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight.LIGHT_LENGTH;
|
||||
@@ -49,33 +51,41 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
|
||||
|
||||
final int size = calcSize(bitmask, addBitmask, hasSkyLight, biomes);
|
||||
final byte[] data = new byte[size];
|
||||
final ByteBuf uncompressed = buffer.alloc().buffer(size);
|
||||
|
||||
serialize(chunk, data, 0, addBitmask, hasSkyLight, biomes);
|
||||
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.writeBoolean(chunk.isFullChunk());
|
||||
buffer.writeShort(bitmask);
|
||||
buffer.writeShort(addBitmask);
|
||||
|
||||
final Deflater deflater = new Deflater();
|
||||
byte[] compressedData;
|
||||
int compressedSize;
|
||||
try {
|
||||
deflater.setInput(data, 0, data.length);
|
||||
deflater.finish();
|
||||
compressedData = new byte[data.length];
|
||||
compressedSize = deflater.deflate(compressedData);
|
||||
} finally {
|
||||
deflater.end();
|
||||
}
|
||||
serialize(chunk, uncompressed, addBitmask, hasSkyLight, biomes);
|
||||
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writeBytes(compressedData, 0, compressedSize);
|
||||
buffer.writeInt(chunk.getX());
|
||||
buffer.writeInt(chunk.getZ());
|
||||
buffer.writeBoolean(chunk.isFullChunk());
|
||||
buffer.writeShort(bitmask);
|
||||
buffer.writeShort(addBitmask);
|
||||
|
||||
// Reserve 4 bytes for the compressed size
|
||||
final int sizeIndex = buffer.writerIndex();
|
||||
buffer.writeInt(0); // Placeholder for compressed size
|
||||
|
||||
// Write compressed data directly to output buffer
|
||||
final int compressedStart = buffer.writerIndex();
|
||||
try {
|
||||
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
throw new RuntimeException("Failed to compress chunk data", e);
|
||||
}
|
||||
final int compressedSize = buffer.writerIndex() - compressedStart;
|
||||
|
||||
// Go back and write the compressed size
|
||||
final int endIndex = buffer.writerIndex();
|
||||
buffer.writerIndex(sizeIndex);
|
||||
buffer.writeInt(compressedSize);
|
||||
buffer.writerIndex(endIndex);
|
||||
} finally {
|
||||
uncompressed.release();
|
||||
}
|
||||
}
|
||||
|
||||
public static int serialize(Chunk chunk, byte[] output, int offset, int addBitmask, boolean writeSkyLight, boolean biomes) {
|
||||
public static void serialize(Chunk chunk, ByteBuf output, int addBitmask, boolean writeSkyLight, boolean biomes) {
|
||||
final ChunkSection[] sections = chunk.getSections();
|
||||
final int bitmask = chunk.getBitmask();
|
||||
|
||||
@@ -85,7 +95,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
final DataPalette palette = section.palette(PaletteType.BLOCKS);
|
||||
for (int j = 0; j < SIZE; j++) {
|
||||
final int block = palette.idAt(j);
|
||||
output[offset++] = (byte) ((block >> 4) & 0xFF);
|
||||
output.writeByte((block >> 4) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +107,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
for (int j = 0; j < ChunkSection.SIZE; j += 2) {
|
||||
final int meta1 = palette.idAt(j) & 0xF;
|
||||
final int meta2 = palette.idAt(j + 1) & 0xF;
|
||||
output[offset++] = (byte) (meta1 | (meta2 << 4));
|
||||
output.writeByte(meta1 | (meta2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +115,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
final byte[] blockLight = sections[i].getLight().getBlockLight();
|
||||
System.arraycopy(blockLight, 0, output, offset, LIGHT_LENGTH);
|
||||
offset += LIGHT_LENGTH;
|
||||
output.writeBytes(blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +124,11 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
if ((bitmask & (1 << i)) != 0) {
|
||||
if (sections[i].getLight().hasSkyLight()) {
|
||||
final byte[] skyLight = sections[i].getLight().getSkyLight();
|
||||
System.arraycopy(skyLight, 0, output, offset, LIGHT_LENGTH);
|
||||
output.writeBytes(skyLight);
|
||||
} else {
|
||||
// Write empty skylight data
|
||||
output.writeZero(LIGHT_LENGTH);
|
||||
}
|
||||
offset += LIGHT_LENGTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +141,7 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
for (int j = 0; j < SIZE; j += 2) {
|
||||
final int add1 = (palette.idAt(j) >> 12) & 0xF;
|
||||
final int add2 = (palette.idAt(j + 1) >> 12) & 0xF;
|
||||
output[offset++] = (byte) (add1 | (add2 << 4));
|
||||
output.writeByte(add1 | (add2 << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,11 +150,9 @@ public class ChunkType1_7_6 extends Type<Chunk> {
|
||||
if (biomes && chunk.getBiomeData() != null) {
|
||||
final int[] biomeData = chunk.getBiomeData();
|
||||
for (int biome : biomeData) {
|
||||
output[offset++] = (byte) biome;
|
||||
output.writeByte(biome);
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
|
||||
|
||||
-18
@@ -20,7 +20,6 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.CompressionStatusTracker;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.platform.providers.Provider;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
||||
public abstract class CompressionHandlerProvider implements Provider {
|
||||
|
||||
@@ -28,23 +27,6 @@ public abstract class CompressionHandlerProvider implements Provider {
|
||||
|
||||
public abstract void onTransformPacket(UserConnection user);
|
||||
|
||||
/**
|
||||
* Creates the compression encoder
|
||||
*
|
||||
* @param compressor A nullable object that may present a native backend for compression (if available)
|
||||
* @param threshold The compression threshold
|
||||
* @return The encoder
|
||||
*/
|
||||
public abstract ChannelHandler getEncoder(Object compressor, int threshold);
|
||||
|
||||
/**
|
||||
* Creates the compression decoder
|
||||
*
|
||||
* @param compressor A nullable object that may present a native backend for compression (if available)
|
||||
* @param threshold The compression threshold
|
||||
* @return The decoder
|
||||
*/
|
||||
public abstract ChannelHandler getDecoder(Object compressor, int threshold);
|
||||
|
||||
public boolean isRemoveCompression(UserConnection user) {
|
||||
return user.get(CompressionStatusTracker.class).removeCompression;
|
||||
|
||||
+3
-19
@@ -17,18 +17,15 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private final Inflater inflater = new Inflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -58,25 +55,12 @@ public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
|
||||
}
|
||||
|
||||
inflate(ctx, in, outLength, out);
|
||||
}
|
||||
|
||||
protected void inflate(final ChannelHandlerContext ctx, final ByteBuf source, final int outLength, final List<Object> out) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(outLength, outLength);
|
||||
ByteBuf output = ctx.alloc().buffer(outLength);
|
||||
try {
|
||||
this.inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
output.writerIndex(output.writerIndex() + this.inflater.inflate(output.array(), output.arrayOffset(), outLength));
|
||||
ThreadLocalCompressionProvider.inflate(in, output, outLength);
|
||||
out.add(output.retain());
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
this.inflater.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-30
@@ -17,16 +17,13 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
|
||||
|
||||
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
private final Deflater deflater = new Deflater();
|
||||
|
||||
private int threshold;
|
||||
|
||||
@@ -48,32 +45,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
Types.VAR_INT.writePrimitive(out, frameLength);
|
||||
|
||||
deflate(in, out);
|
||||
}
|
||||
|
||||
protected void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
ByteBuf temp = source;
|
||||
if (!source.hasArray()) {
|
||||
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
|
||||
} else {
|
||||
source.retain();
|
||||
}
|
||||
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
try {
|
||||
this.deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
|
||||
deflater.finish();
|
||||
|
||||
while (!deflater.finished()) {
|
||||
output.ensureWritable(4096);
|
||||
output.writerIndex(output.writerIndex() + this.deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
|
||||
}
|
||||
destination.writeBytes(output);
|
||||
} finally {
|
||||
output.release();
|
||||
temp.release();
|
||||
this.deflater.reset();
|
||||
}
|
||||
ThreadLocalCompressionProvider.deflate(in, out);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-40
@@ -21,28 +21,12 @@ import com.viaversion.viarewind.ViaRewind;
|
||||
import com.viaversion.viarewind.api.minecraft.netty.EmptyChannelHandler;
|
||||
import com.viaversion.viarewind.api.minecraft.netty.ForwardMessageToByteEncoder;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity.VelocityCompressionDecoder;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity.VelocityCompressionEncoder;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
public class TrackingCompressionHandlerProvider extends CompressionHandlerProvider {
|
||||
|
||||
// Use Velocity's compression backend if ViaRewind is loaded on a Velocity server or a custom server software
|
||||
// is shipping them in the classpath.
|
||||
private static boolean velocityNatives;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
|
||||
velocityNatives = true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
velocityNatives = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompressionThreshold(UserConnection user, int threshold) {
|
||||
final ChannelPipeline pipeline = user.getChannel().pipeline();
|
||||
@@ -51,17 +35,12 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
return;
|
||||
}
|
||||
|
||||
Object compressor = null;
|
||||
if (velocityNatives) {
|
||||
compressor = com.velocitypowered.natives.util.Natives.compress.get().create(-1);
|
||||
}
|
||||
|
||||
final String compressHandlerName = ViaRewind.getPlatform().compressHandlerName();
|
||||
final CompressionEncoder encoder = (CompressionEncoder) pipeline.get(compressHandlerName);
|
||||
if (encoder != null) {
|
||||
encoder.setThreshold(threshold);
|
||||
} else {
|
||||
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, getEncoder(compressor, threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, new CompressionEncoder(threshold));
|
||||
}
|
||||
|
||||
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
|
||||
@@ -69,7 +48,7 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
if (decoder != null) {
|
||||
decoder.setThreshold(threshold);
|
||||
} else {
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, getDecoder(compressor, threshold));
|
||||
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, new CompressionDecoder(threshold));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,22 +75,5 @@ public class TrackingCompressionHandlerProvider extends CompressionHandlerProvid
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler getEncoder(final Object compressor, int threshold) {
|
||||
if (velocityNatives) {
|
||||
return new VelocityCompressionEncoder(threshold, (com.velocitypowered.natives.compression.VelocityCompressor) compressor);
|
||||
} else {
|
||||
return new CompressionEncoder(threshold);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler getDecoder(final Object compressor, int threshold) {
|
||||
if (velocityNatives) {
|
||||
return new VelocityCompressionDecoder(threshold, (com.velocitypowered.natives.compression.VelocityCompressor) compressor);
|
||||
} else {
|
||||
return new CompressionDecoder(threshold);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity;
|
||||
|
||||
import com.velocitypowered.natives.compression.VelocityCompressor;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.CompressionDecoder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
public final class VelocityCompressionDecoder extends CompressionDecoder {
|
||||
|
||||
private final VelocityCompressor compressor;
|
||||
|
||||
public VelocityCompressionDecoder(final int threshold, final VelocityCompressor compressor) {
|
||||
super(threshold);
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inflate(final ChannelHandlerContext ctx, final ByteBuf source, final int outLength, final List<Object> out) throws DataFormatException {
|
||||
ByteBuf output = ctx.alloc().buffer(outLength);
|
||||
try {
|
||||
this.compressor.inflate(source, output, outLength);
|
||||
out.add(output.retain());
|
||||
} finally {
|
||||
output.release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
|
||||
* Copyright (C) 2018-2026 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.velocity;
|
||||
|
||||
import com.velocitypowered.natives.compression.VelocityCompressor;
|
||||
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.CompressionEncoder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
public final class VelocityCompressionEncoder extends CompressionEncoder {
|
||||
|
||||
private final VelocityCompressor compressor;
|
||||
|
||||
public VelocityCompressionEncoder(final int threshold, final VelocityCompressor compressor) {
|
||||
super(threshold);
|
||||
this.compressor = compressor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
|
||||
this.compressor.deflate(source, destination);
|
||||
}
|
||||
|
||||
}
|
||||
+65
-13
@@ -48,6 +48,7 @@ import com.viaversion.viaversion.api.rewriter.RewriterBase;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.libs.gson.JsonElement;
|
||||
import com.viaversion.viaversion.libs.gson.JsonObject;
|
||||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
|
||||
import com.viaversion.viaversion.util.ComponentUtil;
|
||||
@@ -511,7 +512,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 +536,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 +545,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 +617,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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -770,6 +789,8 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
});
|
||||
|
||||
// 1.7.10 custom payload constructor (String, ByteBuf) uses byteBuf.array(), so it sends the whole backing array capacity, not only the written bytes
|
||||
// After reading the proper packet data we discard the remaining bytes
|
||||
protocol.registerServerbound(ServerboundPackets1_7_2_5.CUSTOM_PAYLOAD, new PacketHandlers() {
|
||||
@Override
|
||||
public void register() {
|
||||
@@ -780,7 +801,27 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
switch (channel) {
|
||||
case "MC|TrSel": {
|
||||
wrapper.passthrough(Types.INT);
|
||||
wrapper.read(Types.REMAINING_BYTES); // unused data ???
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|AdvCdm": {
|
||||
final byte commandBlockType = wrapper.passthrough(Types.BYTE);
|
||||
if (commandBlockType == 0) { // Block
|
||||
wrapper.passthrough(Types.INT); // X
|
||||
wrapper.passthrough(Types.INT); // Y
|
||||
wrapper.passthrough(Types.INT); // Z
|
||||
} else if (commandBlockType == 1) { // Minecart
|
||||
wrapper.passthrough(Types.INT); // Entity id
|
||||
}
|
||||
wrapper.passthrough(Types.STRING); // Command
|
||||
wrapper.write(Types.BOOLEAN, true); // Track output, added in 1.8, default to true
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|Beacon": {
|
||||
wrapper.passthrough(Types.INT); // Primary effect
|
||||
wrapper.passthrough(Types.INT); // Secondary effect
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
break;
|
||||
}
|
||||
case "MC|ItemName": {
|
||||
@@ -797,17 +838,28 @@ public class PlayerPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10>
|
||||
}
|
||||
case "MC|BEdit":
|
||||
case "MC|BSign": {
|
||||
final boolean signBook = channel.equals("MC|BSign");
|
||||
final Item book = wrapper.read(RewindTypes.COMPRESSED_NBT_ITEM);
|
||||
CompoundTag tag = book.tag();
|
||||
if (tag != null && tag.contains("pages")) {
|
||||
ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = ComponentUtil.legacyToJsonString(value);
|
||||
page.setValue(value);
|
||||
wrapper.read(Types.REMAINING_BYTES); // Discard remaining bytes
|
||||
|
||||
if (book == null) {
|
||||
wrapper.write(Types.ITEM1_8, null);
|
||||
break;
|
||||
}
|
||||
|
||||
final CompoundTag tag = book.tag();
|
||||
if (signBook && tag != null && tag.contains("pages")) {
|
||||
final ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
|
||||
if (pages != null) {
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
final StringTag page = pages.get(i);
|
||||
String value = page.getValue();
|
||||
value = new JsonPrimitive(value).toString();
|
||||
page.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
protocol.getItemRewriter().handleItemToServer(wrapper.user(), book);
|
||||
wrapper.write(Types.ITEM1_8, book);
|
||||
break;
|
||||
}
|
||||
|
||||
+8
-9
@@ -222,17 +222,16 @@ public class EntityTracker1_8 extends EntityTrackerBase {
|
||||
|
||||
public void checkNametagVisbility(final String username) {
|
||||
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
|
||||
final GameProfileStorage.GameProfile profile = profileStorage.get(username, false);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
final List<GameProfileStorage.GameProfile> profiles = profileStorage.get(username, false);
|
||||
|
||||
final int entityId = getPlayerEntityId(profile.uuid);
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
for (GameProfileStorage.GameProfile profile : profiles) {
|
||||
final int entityId = getPlayerEntityId(profile.uuid);
|
||||
if (entityId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNametagVisibility(entityId);
|
||||
checkNametagVisibility(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPlayerNametagHidden(final int entityId) {
|
||||
|
||||
+13
-13
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
@@ -34,6 +38,7 @@ import java.util.UUID;
|
||||
|
||||
public class GameProfileStorage extends StoredObject {
|
||||
private final Map<UUID, GameProfile> properties = new HashMap<>();
|
||||
private final ListMultimap<String, GameProfile> nameToProfile = ArrayListMultimap.create();
|
||||
|
||||
public GameProfileStorage(UserConnection user) {
|
||||
super(user);
|
||||
@@ -42,6 +47,7 @@ public class GameProfileStorage extends StoredObject {
|
||||
public GameProfile put(UUID uuid, String name, String displayName, int ping, int gamemode) {
|
||||
GameProfile gameProfile = new GameProfile(uuid, name, displayName, ping, gamemode);
|
||||
properties.put(uuid, gameProfile);
|
||||
nameToProfile.put(name, gameProfile);
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
@@ -49,19 +55,9 @@ public class GameProfileStorage extends StoredObject {
|
||||
return properties.get(uuid);
|
||||
}
|
||||
|
||||
public GameProfile get(String name, boolean ignoreCase) {
|
||||
public List<GameProfile> get(String name, boolean ignoreCase) {
|
||||
if (ignoreCase) name = name.toLowerCase();
|
||||
|
||||
for (GameProfile profile : properties.values()) {
|
||||
if (profile.name == null) continue;
|
||||
|
||||
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
|
||||
|
||||
if (n.equals(name)) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return nameToProfile.get(name);
|
||||
}
|
||||
|
||||
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
|
||||
@@ -81,7 +77,11 @@ public class GameProfileStorage extends StoredObject {
|
||||
}
|
||||
|
||||
public GameProfile remove(UUID uuid) {
|
||||
return properties.remove(uuid);
|
||||
GameProfile removedProfile = properties.remove(uuid);
|
||||
if (removedProfile != null) {
|
||||
nameToProfile.get(removedProfile.name).remove(removedProfile);
|
||||
}
|
||||
return removedProfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+34
-28
@@ -140,37 +140,43 @@ public class ScoreboardTracker extends StoredObject {
|
||||
public String sendTeamForScore(String score) {
|
||||
if (score.length() <= 16) return score;
|
||||
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
|
||||
int l = 16;
|
||||
int i = Math.min(16, score.length() - 16);
|
||||
String name = score.substring(i, i + l);
|
||||
while (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
i--;
|
||||
while (score.length() - l - i > 16) {
|
||||
l--;
|
||||
if (l < 1) return score;
|
||||
i = Math.min(16, score.length() - l);
|
||||
|
||||
// Find the longest window score[i, i+l) that:
|
||||
// - is not already claimed as a team name
|
||||
// - keeps prefix (score[0,i)) at most 16 chars i <= 16
|
||||
// - keeps suffix (score[i+l, end)) at most 16 chars score.length()-l-i <= 16
|
||||
for (int l = 16; l >= 1; l--) {
|
||||
final int iMax = Math.min(16, score.length() - l);
|
||||
final int iMin = Math.max(0, score.length() - l - 16);
|
||||
for (int i = iMax; i >= iMin; i--) {
|
||||
final String name = score.substring(i, i + l);
|
||||
if (scoreTeamNames.contains(name) || teams.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String prefix = score.substring(0, i);
|
||||
final String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
final ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
}
|
||||
name = score.substring(i, i + l);
|
||||
}
|
||||
String prefix = score.substring(0, i);
|
||||
String suffix = i + l >= score.length() ? "" : score.substring(i + l);
|
||||
|
||||
ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
|
||||
scoreTeams.put(score, scoreTeam);
|
||||
scoreTeamNames.add(name);
|
||||
|
||||
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.STRING, "ViaRewind");
|
||||
teamPacket.write(Types.STRING, prefix);
|
||||
teamPacket.write(Types.STRING, suffix);
|
||||
teamPacket.write(Types.BYTE, (byte) 0);
|
||||
teamPacket.write(Types.SHORT, (short) 1);
|
||||
teamPacket.write(Types.STRING, name);
|
||||
teamPacket.send(Protocol1_8To1_7_6_10.class);
|
||||
|
||||
return name;
|
||||
return score;
|
||||
}
|
||||
|
||||
public String removeTeamForScore(String score) {
|
||||
|
||||
+3
@@ -35,6 +35,9 @@ public class WorldBorderUpdateTask implements Runnable {
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final WorldBorderEmulator worldBorderEmulatorTracker = connection.get(WorldBorderEmulator.class);
|
||||
if (worldBorderEmulatorTracker == null) {
|
||||
continue;
|
||||
}
|
||||
if (!worldBorderEmulatorTracker.isInit()) continue;
|
||||
|
||||
final PlayerSessionStorage playerSession = connection.get(PlayerSessionStorage.class);
|
||||
|
||||
+31
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.rewriter;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteTag;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
@@ -194,6 +195,7 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
@Override
|
||||
public Item handleItemToClient(UserConnection connection, Item item) {
|
||||
if (item == null) return null;
|
||||
final int originalId = item.identifier();
|
||||
super.handleItemToClient(connection, item);
|
||||
|
||||
CompoundTag tag = item.tag();
|
||||
@@ -280,6 +282,32 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
});
|
||||
}
|
||||
|
||||
// Colors the fake leather armor for an elytra gray
|
||||
if (originalId == 443) {
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
CompoundTag display = tag.getCompoundTag("display");
|
||||
if (display == null) {
|
||||
tag.put("display", display = new CompoundTag());
|
||||
}
|
||||
display.put("color", new IntTag(0x737373)); // Gray
|
||||
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
|
||||
}
|
||||
|
||||
// Makes the fake banner for a shield brown if it has no banner patterns
|
||||
if (originalId == 442) {
|
||||
final CompoundTag blockEntityTag = tag == null ? null : tag.getCompoundTag("BlockEntityTag");
|
||||
final ListTag<CompoundTag> patterns = blockEntityTag == null ? null : blockEntityTag.getListTag("Patterns", CompoundTag.class);
|
||||
if (patterns == null || patterns.isEmpty()) {
|
||||
item.setData((short) 3); // Brown
|
||||
if (tag == null) {
|
||||
item.setTag(tag = new CompoundTag());
|
||||
}
|
||||
tag.put(nbtTagName() + "|noData", new ByteTag(true));
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -322,6 +350,9 @@ public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundP
|
||||
if (tag == null) {
|
||||
return item;
|
||||
}
|
||||
if (tag.remove(nbtTagName() + "|noData") != null) {
|
||||
item.setData((short) 0);
|
||||
}
|
||||
final Tag noDisplayTag = tag.remove(nbtTagName() + "|noDisplay");
|
||||
if (noDisplayTag != null) {
|
||||
tag.remove("display");
|
||||
|
||||
+5
-5
@@ -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
-1
@@ -26,7 +26,11 @@ public class CooldownIndicatorTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
connection.get(CooldownStorage.class).tick(connection);
|
||||
final CooldownStorage cooldown = connection.get(CooldownStorage.class);
|
||||
if (cooldown == null) {
|
||||
continue;
|
||||
}
|
||||
cooldown.tick(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -36,6 +36,9 @@ public class LevitationUpdateTask implements Runnable {
|
||||
}
|
||||
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
|
||||
final LevitationStorage levitation = connection.get(LevitationStorage.class);
|
||||
if (levitation == null) {
|
||||
continue;
|
||||
}
|
||||
if (!levitation.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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.2
|
||||
project_description=ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.
|
||||
|
||||
publishing_dev_id=florianreuth
|
||||
@@ -15,6 +15,6 @@ publishing_dev_name=Florian Reuth
|
||||
publishing_dev_mail=git@florianreuth.de
|
||||
|
||||
# Modrinth/Hangar Publishing
|
||||
minecraft_versions=26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8
|
||||
minecraft_version_range=1.8-26.1.2
|
||||
minecraft_versions=26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9, 1.8.8
|
||||
minecraft_version_range=1.8-26.2
|
||||
velocity_version=3.4-3.5
|
||||
Vendored
BIN
Binary file not shown.
+4
-2
@@ -1,8 +1,10 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
distributionSha256Sum=bafc141b619ad6350fd975fc903156dd5c151998cc8b058e8c1044ab5f7b031f
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -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
@@ -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%
|
||||
Reference in new issue
Block a user