Compare commits

..
1 Commits
Author SHA1 Message Date
rjnasers 740686ac61 Fixing silly read write issue 2026-09-02 16:51:03 +02:00
15 changed files with 84 additions and 427 deletions

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@ name: Build and test
on:
pull_request:
branches: [ master, main, release/** ]
branches: [ master, main ]
push:
branches: [ master, main, release/** ]
branches: [ master, main ]
jobs:
build-and-test:
-2
View File
@@ -28,5 +28,3 @@ whitelist.json
src/main/resources/mixins.*.json
*.bat
.DS_Store
*.sh
-5
View File
@@ -180,11 +180,6 @@ modrinthRelations =
# Leave this empty if you don't want to publish on CurseForge.
curseForgeProjectId = 746279
# Comma-separated supported CurseForge environments: client, server (case-insensitive).
# Use client or server for mods that only support one environment. Defaults to client,server.
# These tags describe where the mod can run, not whether installation is required on both sides.
curseForgeEnvironments = client,server
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
# Syntax: type1:name1;type2:name2;...
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
+10 -10
View File
@@ -1,12 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9064f22734b42ac03e85df04b632e167/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/4d0277c49991306ac78048580fe34bcf/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a9fa3b809e484a1e1ac4308a5dda661a/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/aa99913fa6d767d899ffc96082da01ca/redirect
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVersion=25
Binary file not shown.
+1 -3
View File
@@ -1,9 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+3 -3
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# gradlew start up script for POSIX generated by Gradle.
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh gradlew
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/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
+23 -12
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem gradlew startup script for Windows
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
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
"%COMSPEC%" /c exit 1
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,18 +65,29 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto fail
:execute
@rem Setup the command line
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
@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" %*
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.30'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
}
@@ -89,22 +89,6 @@ public class ReflectionManager {
}
}
/**
* Set a field of the object {@code instance} of type {@code class1} to {@code value}.
*
* @deprecated According to {@link Class#getDeclaredFields()}, the array of fields has no defined order, therefore
* selecting a field based on its index will result in JVM-specific behaviour. Use
* {@link #setField(Class, Object, String, Object)} instead.
*
* @param class1 The class which defines the field in question.
* @param instance The object being altered - must be an instance of {@code class1}
* @param fieldindex The index of the field being set.
* @param value The value to set the field to.
* @throws IllegalArgumentException If {@code instance} is not an instance of {@code class1}, or if {@code value}
* doesn't match the type of the field.
* @throws IllegalAccessException If unable to write to the field.
*/
@Deprecated
public static void setField(Class<?> class1, Object instance, int fieldindex, Object value)
throws IllegalArgumentException, IllegalAccessException {
Field field = class1.getDeclaredFields()[fieldindex];
@@ -195,21 +179,6 @@ public class ReflectionManager {
return null;
}
/**
* Get a field of the object {@code instance} of type {@code class1}.
*
* @deprecated According to {@link Class#getDeclaredFields()}, the array of fields has no defined order, therefore
* selecting a field based on its index will result in JVM-specific behaviour. Use
* {@link #getField(Class, Class, Object, String)} instead.
*
* @param class1 The class which defines the field in question.
* @param fieldType The type of the field being read.
* @param instance The object being read from - must be an instance of {@code class1}
* @param fieldIndex The index of the field being read.
* @throws IllegalArgumentException If {@code instance} is not an instance of {@code class1}.
* @throws IllegalAccessException If unable to read from the field.
*/
@Deprecated
public static <T> T getField(Class<?> class1, Class<T> fieldType, Object instance, int fieldIndex)
throws IllegalArgumentException, IllegalAccessException {
Field field = class1.getDeclaredFields()[fieldIndex];
@@ -2,7 +2,6 @@ package codechicken.lib.packet;
import java.util.EnumMap;
import java.util.List;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
@@ -51,8 +50,6 @@ import io.netty.util.AttributeKey;
public final class PacketCustom implements MCDataInput, MCDataOutput {
private static final int MAX_DECOMPRESSED_SIZE = 2 * 1024 * 1024;
public static interface ICustomPacketHandler {
}
@@ -193,7 +190,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf = payload;
type = byteBuf.readUnsignedByte();
if (type > 0x80) decompressPayload();
if (type > 0x80) decompress();
type &= 0x7F;
}
@@ -207,61 +204,44 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
byteBuf.writeByte(type);
}
private void decompressPayload() {
if (byteBuf.readableBytes() < 4) throw new IllegalArgumentException("Missing compressed packet length");
int declaredLength = byteBuf.readInt();
if (declaredLength < 0 || declaredLength > MAX_DECOMPRESSED_SIZE)
throw new IllegalArgumentException("Invalid decompressed packet length: " + declaredLength);
/**
* Decompresses the remaining ByteBuf (after type has been read) using Snappy
*/
private void decompress() {
Inflater inflater = new Inflater();
ByteBuf decodedPayload = null;
try {
byte[] compressedInput = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(compressedInput);
inflater.setInput(compressedInput);
decodedPayload = Unpooled.buffer(Math.min(declaredLength, 8192), declaredLength);
byte[] inflatedChunk = new byte[Math.min(declaredLength + 1, 8192)];
while (!inflater.finished()) {
int inflatedBytes = inflater.inflate(inflatedChunk);
if (inflatedBytes > declaredLength - decodedPayload.writerIndex())
throw new IllegalArgumentException("Decompressed packet exceeds declared length");
if (inflatedBytes == 0 && !inflater.finished())
throw new IllegalArgumentException("Incomplete or dictionary-dependent compressed packet");
decodedPayload.writeBytes(inflatedChunk, 0, inflatedBytes);
}
if (decodedPayload.writerIndex() != declaredLength || inflater.getRemaining() != 0)
throw new IllegalArgumentException(
"Compressed packet does not match declared length or has trailing data");
byteBuf = decodedPayload;
decodedPayload = null;
} catch (DataFormatException e) {
throw new IllegalArgumentException("Invalid compressed packet", e);
int len = byteBuf.readInt();
ByteBuf out = Unpooled.buffer(len);
inflater.setInput(byteBuf.array(), byteBuf.readerIndex(), byteBuf.readableBytes());
inflater.inflate(out.array());
out.writerIndex(len);
byteBuf = out;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (decodedPayload != null) decodedPayload.release();
inflater.end();
}
}
private void compressPayload() {
if ((byteBuf.getUnsignedByte(0) & 0x80) != 0) return;
int payloadLength = byteBuf.writerIndex() - 1;
if (payloadLength > MAX_DECOMPRESSED_SIZE)
throw new IllegalArgumentException("Packet payload exceeds 2 MiB decompressed limit: " + payloadLength);
/**
* Compresses the payload ByteBuf after the type byte
*/
private void do_compress() {
Deflater deflater = new Deflater();
try {
byteBuf.readerIndex(1);
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), payloadLength);
int len = byteBuf.readableBytes();
deflater.setInput(byteBuf.array(), byteBuf.readerIndex(), len);
deflater.finish();
ByteBuf compressedPayload = Unpooled.buffer(payloadLength + 5);
int compressedLength = deflater.deflate(compressedPayload.array(), 5, payloadLength);
if (compressedLength >= payloadLength - 5 || !deflater.finished()) return;
ByteBuf out = Unpooled.buffer(len + 5);
int clen = deflater.deflate(out.array(), 5, len);
if (clen >= len - 5 || !deflater.finished()) // not worth compressing, gets larger
return;
compressedPayload.setByte(0, type | 0x80);
compressedPayload.setInt(1, payloadLength);
compressedPayload.writerIndex(compressedLength + 5);
byteBuf = compressedPayload;
out.setByte(0, type | 0x80);
out.setInt(1, len);
out.writerIndex(clen + 5);
byteBuf = out;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
@@ -494,7 +474,7 @@ public final class PacketCustom implements MCDataInput, MCDataOutput {
public FMLProxyPacket toPacket() {
if (incoming()) throw new IllegalStateException("Tried to write an incoming packet");
if (byteBuf.readableBytes() > 32000 || (type & 0x80) != 0) compressPayload();
if (byteBuf.readableBytes() > 32000 || (type & 0x80) != 0) do_compress();
// FML packet impl returns the whole of the backing array, copy used portion of array to another ByteBuf
return new FMLProxyPacket(byteBuf.copy(), channel);
@@ -1,7 +1,5 @@
package codechicken.lib.render;
import java.util.Arrays;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
@@ -13,9 +11,6 @@ import codechicken.lib.vec.Vector3;
public class EntityDigIconFX extends EntityFX {
/** Tint that leaves the particle colour untouched, for materials that don't colour their texture. */
public static final int NO_TINT = 0xFFFFFF;
public EntityDigIconFX(World world, double x, double y, double z, double dx, double dy, double dz, IIcon icon) {
super(world, x, y, z, dx, dy, dz);
particleIcon = icon;
@@ -24,18 +19,6 @@ public class EntityDigIconFX extends EntityFX {
particleScale /= 2.0F;
}
/**
* Multiplies the particle colour by an 0xRRGGBB tint, the equivalent of vanilla
* EntityDiggingFX.applyColourMultiplier. Materials that draw a greyscale texture and colour it at render time
* (leaves, grass tops) need this, or their particles come out flat grey.
*/
public EntityDigIconFX applyColourMultiplier(int colour) {
particleRed *= (colour >> 16 & 255) / 255F;
particleGreen *= (colour >> 8 & 255) / 255F;
particleBlue *= (colour & 255) / 255F;
return this;
}
@Override
public int getFXLayer() {
return 1;
@@ -107,24 +90,8 @@ public class EntityDigIconFX extends EntityFX {
f9);
}
/**
* @deprecated the particle is never tinted, so a material that colours a greyscale texture at render time (leaves,
* grass tops) breaks into grey particles. Use
* {@link #addBlockHitEffects(World, Cuboid6, int, IIcon, int, EffectRenderer)}, passing
* {@link #NO_TINT} for materials whose texture is already coloured.
*/
@Deprecated
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon,
EffectRenderer effectRenderer) {
addBlockHitEffects(world, bounds, side, icon, NO_TINT, effectRenderer);
}
/**
* Spawns a hit particle on side of bounds, tinted by colour (0xRRGGBB, {@link #NO_TINT} for none). The caller
* resolves the tint for the side it hit, so per-face materials like grass can tint only the faces that need it.
*/
public static void addBlockHitEffects(World world, Cuboid6 bounds, int side, IIcon icon, int colour,
EffectRenderer effectRenderer) {
float border = 0.1F;
Vector3 diff = bounds.max.copy().subtract(bounds.min).add(-2 * border);
diff.x *= world.rand.nextDouble();
@@ -139,34 +106,13 @@ public class EntityDigIconFX extends EntityFX {
if (side == 4) diff.x = bounds.min.x - border;
if (side == 5) diff.x = bounds.max.x + border;
EntityDigIconFX fx = new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon);
effectRenderer.addEffect(fx.applyColourMultiplier(colour).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
effectRenderer.addEffect(
new EntityDigIconFX(world, pos.x, pos.y, pos.z, 0, 0, 0, icon).multiplyVelocity(0.2F)
.multipleParticleScaleBy(0.6F));
}
/**
* @deprecated the particles are never tinted, so a material that colours a greyscale texture at render time
* (leaves, grass tops) breaks into grey particles. Use
* {@link #addBlockDestroyEffects(World, Cuboid6, IIcon[], int[], EffectRenderer)}, passing
* {@link #NO_TINT} for the faces whose texture is already coloured.
*/
@Deprecated
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons,
EffectRenderer effectRenderer) {
int[] colours = new int[icons.length];
Arrays.fill(colours, NO_TINT);
addBlockDestroyEffects(world, bounds, icons, colours, effectRenderer);
}
/**
* Spawns break particles filling bounds, each drawn from a random face. colours holds the 0xRRGGBB tint for the
* matching entry of icons ({@link #NO_TINT} for none) and must be the same length, so a particle is tinted by the
* face it was taken from.
*/
public static void addBlockDestroyEffects(World world, Cuboid6 bounds, IIcon[] icons, int[] colours,
EffectRenderer effectRenderer) {
if (icons.length != colours.length)
throw new IllegalArgumentException("icons and colours must have the same length");
Vector3 diff = bounds.max.copy().subtract(bounds.min);
Vector3 center = bounds.min.copy().add(bounds.max).multiply(0.5);
Vector3 density = diff.copy().multiply(4);
@@ -175,20 +121,19 @@ public class EntityDigIconFX extends EntityFX {
density.z = Math.ceil(density.z);
for (int i = 0; i < density.x; ++i) for (int j = 0; j < density.y; ++j) for (int k = 0; k < density.z; ++k) {
double px = bounds.min.x + (i + 0.5) * diff.x / density.x;
double py = bounds.min.y + (j + 0.5) * diff.y / density.y;
double pz = bounds.min.z + (k + 0.5) * diff.z / density.z;
int iconSide = world.rand.nextInt(icons.length);
EntityDigIconFX fx = new EntityDigIconFX(
world,
px,
py,
pz,
px - center.x,
py - center.y,
pz - center.z,
icons[iconSide]);
effectRenderer.addEffect(fx.applyColourMultiplier(colours[iconSide]));
double x = bounds.min.x + (i + 0.5) * diff.x / density.x;
double y = bounds.min.y + (j + 0.5) * diff.y / density.y;
double z = bounds.min.z + (k + 0.5) * diff.z / density.z;
effectRenderer.addEffect(
new EntityDigIconFX(
world,
x,
y,
z,
x - center.x,
y - center.y,
z - center.z,
icons[world.rand.nextInt(icons.length)]));
}
}
}
@@ -1 +0,0 @@
codechickencore.update=Доступна версія %s для %s
@@ -1,218 +0,0 @@
package codechicken.lib.packet;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.zip.Deflater;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class PacketCustomTest {
private static final int MAX_DECOMPRESSED_SIZE = 2 * 1024 * 1024;
@Test
public void rejectsLargeDeclarationWithoutCompressedData() {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(8 * 1024 * 1024));
}
@ParameterizedTest
@ValueSource(ints = { -1, Integer.MIN_VALUE, MAX_DECOMPRESSED_SIZE + 1, Integer.MAX_VALUE })
public void rejectsInvalidDeclaredLengths(int length) {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(length));
}
@Test
public void rejectsMissingCompressedDataWithinLimit() {
assertRejected(Unpooled.buffer(5).writeByte(0x81).writeInt(MAX_DECOMPRESSED_SIZE));
}
@Test
public void rejectsCompleteStreamAboveDecodeLimit() {
assertRejected(compressed(new byte[MAX_DECOMPRESSED_SIZE + 1]));
}
@Test
public void rejectsTruncatedLengthHeader() {
assertRejected(Unpooled.buffer(4).writeByte(0x81).writeMedium(10));
}
@Test
public void rejectsMismatchedInflatedLengths() {
byte[] data = new byte[16384];
assertRejected(compressed(data).setInt(1, data.length - 1));
assertRejected(compressed(data).setInt(1, data.length + 1));
assertRejected(compressed(data).setInt(1, 0));
}
@Test
public void rejectsTruncatedStreamEvenWhenAllOutputWasProduced() {
ByteBuf payload = compressed(new byte[16384]);
payload.writerIndex(payload.writerIndex() - 1);
assertRejected(payload);
}
@Test
public void rejectsCorruptStream() {
ByteBuf payload = compressed(new byte[16384]);
payload.setByte(payload.writerIndex() - 1, payload.getByte(payload.writerIndex() - 1) ^ 1);
assertRejected(payload);
}
@Test
public void rejectsDictionaryDependentStream() {
assertRejected(compressed(new byte[16384], new byte[] { 1, 2, 3 }));
}
@Test
public void rejectsTrailingCompressedData() {
assertRejected(compressed(new byte[16384]).writeByte(0));
}
@ParameterizedTest
@ValueSource(ints = { 0, 1, 8192, 8193, MAX_DECOMPRESSED_SIZE })
public void decodesCompleteStreams(int length) {
byte[] data = new byte[length];
Arrays.fill(data, (byte) 42);
assertDecoded(compressed(data), data);
}
@Test
public void decodesSlicedAndDirectBuffers() {
byte[] data = new byte[16384];
Arrays.fill(data, (byte) 42);
ByteBuf payload = compressed(data);
ByteBuf prefixed = Unpooled.buffer().writeLong(-1).writeBytes(payload, 0, payload.readableBytes());
ByteBuf direct = Unpooled.directBuffer().writeBytes(payload);
payload.release();
try {
assertDecoded(prefixed.slice(8, prefixed.readableBytes() - 8).retain(), data);
assertDecoded(direct, data);
} finally {
prefixed.release();
}
}
@Test
public void preservesUncompressedPackets() {
ByteBuf payload = Unpooled.buffer().writeByte(7).writeInt(42);
try {
PacketCustom packet = new PacketCustom(payload);
assertEquals(7, packet.getType());
assertEquals(42, packet.readInt());
assertEquals(0, packet.getByteBuf().readableBytes());
} finally {
payload.release();
}
}
@Test
public void roundTripsOutgoingCompression() {
for (int length : new int[] { 16, 64, 40000, MAX_DECOMPRESSED_SIZE }) {
byte[] data = new byte[length];
Arrays.fill(data, (byte) 42);
PacketCustom outgoing = new PacketCustom("PacketTest", 7);
FMLProxyPacket wire = null;
try {
outgoing.writeByteArray(data);
if (length <= 64) outgoing.compress();
wire = outgoing.toPacket();
assertEquals(length == 16 ? 7 : 0x87, wire.payload().getUnsignedByte(0));
assertDecoded(wire.payload().retain(), data);
} finally {
if (wire != null) wire.payload().release();
outgoing.getByteBuf().release();
}
}
}
@ParameterizedTest
@ValueSource(booleans = { false, true })
public void repeatedSerializationPreservesCompressedPayload(boolean forceCompression) {
byte[] data = new byte[40000];
Arrays.fill(data, (byte) 42);
PacketCustom outgoing = new PacketCustom("PacketTest", 7).writeByteArray(data);
if (forceCompression) outgoing.compress();
try {
for (int serialization = 0; serialization < 3; serialization++) {
FMLProxyPacket wire = outgoing.toPacket();
try {
assertEquals(0x87, wire.payload().getUnsignedByte(0));
assertDecoded(wire.payload().retain(), data);
} finally {
wire.payload().release();
}
}
} finally {
outgoing.getByteBuf().release();
}
}
@Test
public void rejectsOutgoingPayloadAboveDecodeLimit() {
PacketCustom outgoing = new PacketCustom("PacketTest", 7);
try {
outgoing.getByteBuf().writeZero(MAX_DECOMPRESSED_SIZE + 1);
assertThrows(IllegalArgumentException.class, outgoing::toPacket);
} finally {
outgoing.getByteBuf().release();
}
}
private static ByteBuf compressed(byte[] data) {
return compressed(data, null);
}
private static ByteBuf compressed(byte[] data, byte[] dictionary) {
Deflater deflater = new Deflater();
try {
if (dictionary != null) deflater.setDictionary(dictionary);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
byte[] scratch = new byte[8192];
while (!deflater.finished()) {
int count = deflater.deflate(scratch);
compressed.write(scratch, 0, count);
}
return Unpooled.buffer().writeByte(0x87).writeInt(data.length).writeBytes(compressed.toByteArray());
} finally {
deflater.end();
}
}
private static void assertDecoded(ByteBuf payload, byte[] expected) {
PacketCustom packet = null;
try {
packet = new PacketCustom(payload);
assertEquals(7, packet.getType());
assertEquals(expected.length, packet.getByteBuf().readableBytes());
byte[] actual = new byte[expected.length];
packet.getByteBuf().readBytes(actual);
assertArrayEquals(expected, actual);
} finally {
if (packet != null && packet.getByteBuf() != payload) packet.getByteBuf().release();
payload.release();
}
}
private static void assertRejected(ByteBuf payload) {
try {
assertThrows(IllegalArgumentException.class, () -> {
PacketCustom packet = new PacketCustom(payload);
if (packet.getByteBuf() != payload) packet.getByteBuf().release();
});
} finally {
payload.release();
}
}
}
@@ -1,20 +0,0 @@
package codechicken.lib.render;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import net.minecraft.util.IIcon;
import org.junit.jupiter.api.Test;
public class EntityDigIconFXTest {
@Test
public void rejectsMismatchedIconAndColourCountsBeforeSpawning() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> EntityDigIconFX.addBlockDestroyEffects(null, null, new IIcon[1], new int[0], null));
assertEquals("icons and colours must have the same length", exception.getMessage());
}
}