Compare commits

..
30 Commits
Author SHA1 Message Date
retrooper ccfb6b422c PacketEvents 2.9.5 Update 2025-08-26 01:22:29 +02:00
booky10 2c8143ddb0 Add WrappedBlockState utility constructor to WrapperPlayServerBlockChange
Fixes #1325
2025-08-25 23:02:05 +02:00
booky10 f9892c598c Don't try to read direct wolf variant below 1.21
Fixes #1327
2025-08-25 22:59:49 +02:00
booky10andMesterMan03 3eb79fd6d2 Don't encode SingleOptionInputControl entries as strings for now
Packetevents doesn't support mixed nbt lists properly, so adding a string tag to a compound list will cause errors

Fixes #1334

Co-authored-by: MesterMan03 <MesterMan03+github@proton.me>
2025-08-25 22:52:41 +02:00
booky10 8ddceada42 Only error when reading chunk data if we managed to read more than allowed 2025-08-25 22:41:57 +02:00
booky10 a1c355d4c9 Cleanup NBT tag reading/writing 2025-08-25 21:20:40 +02:00
booky10 5c23459e1e Fix Direction Y/Z axis being swapped 2025-08-25 21:04:22 +02:00
booky10 af3dbc928b Mark SpigotChannelInjector encoder/decoder getters as nullable 2025-08-25 18:13:24 +02:00
booky10 bd1bc7f06a Check if the network channel is open instead of Player#isOnline if possible on kick
Player#isOnline() returns whether the player still exists in the internal bukkit player list, not whether the network channel is still active
2025-08-25 18:11:18 +02:00
retrooper 7f16b992eb Merge remote-tracking branch 'origin/2.0' into 2.0 2025-08-19 08:43:23 +02:00
retrooper 460884cbd8 patch: remove paper OfflinePlayer#isConnected reference; use Player#isOnline instead 2025-08-19 08:43:00 +02:00
retrooper 24bf010d98 Merge pull request #1308 from Moulberry/fix/nbt-compound-getListOrThrow-NPE 2025-07-29 21:44:59 +02:00
retrooper e74adedf88 Merge pull request #1309 from Moulberry/fix/spigotchannelinjector-isplayerset-npe 2025-07-29 20:58:11 +02:00
Moulberry a34d03f422 Fix NPE in SpigotChannelInjector#isPlayerSet 2025-07-30 01:04:15 +08:00
Moulberry c0a9be3c7d Fix NBTCompound.java getListOrThrow always returning null 2025-07-30 01:01:28 +08:00
retrooper 8f352d9115 Merge pull request #1306 from rafi67000/gradleUpdate
ViaNBT, SpongeGradle, MockBukkit and shadow update
2025-07-22 23:55:39 +02:00
rafi67000 6ca306405c Update sponge plugin 2025-07-22 21:47:44 +02:00
rafi67000 d4190b2fde Update shadow plugin 2025-07-22 21:47:44 +02:00
rafi67000 30ed0bdcf3 Update ViaNBT 2025-07-22 21:47:44 +02:00
rafi67000 c3e2c3ee9e Update MockBukkit 2025-07-22 21:47:44 +02:00
retrooper 155f8520d4 increment packetevents version to 2.9.5-SNAPSHOT 2025-07-22 21:29:33 +02:00
retrooper 55b548d3ca packetevents release 2.9.4 2025-07-22 21:10:14 +02:00
retrooper 8eff09ff6e feat: SpigotConversionUtil#getEntityMetadata is now part of the API 2025-07-22 20:00:50 +02:00
retrooper 59596653a2 patch: correct WrapperPlayServerWorldBorder implementation for versions lower than 1.12 2025-07-20 20:54:10 +02:00
retrooper 4df76391cb feat: add support for extracting metadata from a Bukkit entity in SpigotReflectionUtil 2025-07-20 20:37:24 +02:00
retrooper 822f1d26b8 patch: re-add support for outdated server software with no accessible version 2025-07-19 22:04:32 +02:00
retrooper 55a42ab0e2 increment packetevents version to 2.9.4-SNAPSHOT 2025-07-17 16:47:11 +02:00
retrooper cdf3521154 packetevents 2.9.3 release 2025-07-17 16:36:51 +02:00
retrooper 820b530240 add 1.21.8 server version constant 2025-07-17 16:11:48 +02:00
retrooper 1823f61748 increment version to 2.9.3-snapshot 2025-07-17 15:54:24 +02:00
29 changed files with 554 additions and 319 deletions

No files matched your search

+85
View File
@@ -1,3 +1,88 @@
!!!!! v2.9.5
# PacketEvents 2.9.5 is out now! 🎉
This update includes critical bug fixes & minor additions to the API.
# Brief Announcement 📢⚠️
### We have our very own website: https://packetevents.com
### Documentation: https://docs.packetevents.com
### JavaDocs: https://javadocs.packetevents.com
## What's Changed (Summary)
* Injector bug fix (for Spigot)
* WolfVariant issue fixed
* Chunk Data error fixed
* NBT tag issues fixed
* Direction Y/Z axis being swapped fixed
* Added WrappedBlockState to constructor of WrapperPlayServerBlockChange
**View Full Changelog**: https://github.com/retrooper/packetevents/compare/v2.9.4...v2.9.5
## If you want to support PacketEvents, consider sponsoring us on [GitHub Sponsors](https://github.com/sponsors/retrooper)
## Maven/Gradle Dependency
### **Check out**: https://docs.packetevents.com/getting-started
!!!!! v2.9.4
# PacketEvents 2.9.4 is out now! 🎉
This update includes minor bug fixes & minor additions to the API.
# Brief Announcement 📢⚠️
### We have our very own website: https://packetevents.com
### Documentation: https://docs.packetevents.com
### JavaDocs: https://javadocs.packetevents.com
## What's Changed (Summary)
* Added SpigotConversionUtil#getEntityMetadata to retrieve metadata from a Bukkit entity.
* Fixed outdated 1.8 Spigot forks from being unable to load on PacketEvents.
* Fixed WrapperPlayServerWorldBorder for versions lower than 1.12.
**View Full Changelog**: https://github.com/retrooper/packetevents/compare/v2.9.3...v2.9.4
## If you want to support PacketEvents, consider sponsoring us on [GitHub Sponsors](https://github.com/sponsors/retrooper)
## Maven/Gradle Dependency
### **Check out**: https://docs.packetevents.com/getting-started
!!!!! v2.9.3
# PacketEvents 2.9.3 is out now! 🎉
This is an update that adds support for Minecraft 1.21.8 server builds.
# Brief Announcement 📢⚠️
### We have our very own website: https://packetevents.com
### Documentation: https://docs.packetevents.com
### JavaDocs: https://javadocs.packetevents.com
## What's Changed (Summary)
* Added support for 1.21.8 Minecraft server builds.
**View Full Changelog**: https://github.com/retrooper/packetevents/compare/v2.9.2...v2.9.3
## If you want to support PacketEvents, consider sponsoring us on [GitHub Sponsors](https://github.com/sponsors/retrooper)
## Maven/Gradle Dependency
### **Check out**: https://docs.packetevents.com/getting-started
!!!!! v2.9.2
# PacketEvents 2.9.2 is out now! 🎉
@@ -51,7 +51,7 @@ public enum ServerVersion {
//1.20 and 1.20.1 have the same protocol version. 1.20.3 and 1.20.4 have the same protocol version. 1.20.5 and 1.20.6 have the same protocol version
V_1_20(763), V_1_20_1(763), V_1_20_2(764), V_1_20_3(765), V_1_20_4(765), V_1_20_5(766), V_1_20_6(766),
//1.21 and 1.21.1 have the same protocol version. 1.21.2 and 1.21.3 have the same protocol version
V_1_21(767), V_1_21_1(767), V_1_21_2(768), V_1_21_3(768), V_1_21_4(769), V_1_21_5(770), V_1_21_6(771), V_1_21_7(772),
V_1_21(767), V_1_21_1(767), V_1_21_2(768), V_1_21_3(768), V_1_21_4(769), V_1_21_5(770), V_1_21_6(771), V_1_21_7(772), V_1_21_8(772),
//TODO UPDATE Add server version constant
ERROR(-1, true);
@@ -119,9 +119,6 @@ public class SingleOptionInputControl implements InputControl {
}
public static NBT encode(PacketWrapper<?> wrapper, Entry entry) {
if (entry.display == null && !entry.initial) {
return new NBTString(entry.id);
}
NBTCompound compound = new NBTCompound();
compound.setTag("id", new NBTString(entry.id));
if (entry.display != null) {
@@ -50,14 +50,16 @@ public interface WolfVariant extends MappedEntity, CopyableEntity<WolfVariant>,
MappedEntitySet<Biome> getBiomes();
static WolfVariant read(PacketWrapper<?> wrapper) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)
|| wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21)) {
return wrapper.readMappedEntity(WolfVariants.getRegistry());
}
return wrapper.readMappedEntityOrDirect(WolfVariants.getRegistry(), WolfVariant::readDirect);
}
static void write(PacketWrapper<?> wrapper, WolfVariant variant) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21_5)
|| wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_21)) {
wrapper.writeMappedEntity(variant);
} else {
wrapper.writeMappedEntityOrDirect(variant, WolfVariant::writeDirect);
@@ -284,7 +284,7 @@ public class NBTCompound extends NBT {
if (list == null) {
throw new IllegalStateException(MessageFormat.format("NBT {0} does not exist", key));
}
return null;
return list;
}
public <T> void set(String key, T value, NbtEncoder<T> encoder, PacketWrapper<?> wrapper) {
@@ -18,11 +18,15 @@
package com.github.retrooper.packetevents.protocol.nbt;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
@ApiStatus.NonExtendable
public interface NBTLimiter {
int DEFAULT_MAX_SIZE = 2097152;
int DEFAULT_MAX_SIZE = Integer.getInteger("packetevents.nbt.default-max-size", 1 << 20); // 2MiB
int DEFAULT_MAX_DEPTH = Integer.getInteger("packetevents.nbt.default-max-depth", 512);
static NBTLimiter noop() {
return new NBTLimiter() {
@@ -35,27 +39,64 @@ public interface NBTLimiter {
public void checkReadability(int length) {
// no-op
}
@Override
public void enterDepth() {
// no-op
}
@Override
public void exitDepth() {
// no-op
}
};
}
static NBTLimiter forBuffer(final @NotNull Object byteBuf) {
static NBTLimiter forBuffer(Object byteBuf) {
return forBuffer(byteBuf, DEFAULT_MAX_SIZE);
}
static NBTLimiter forBuffer(final @NotNull Object byteBuf, final int max) {
static NBTLimiter forBuffer(Object byteBuf, int maxBytes) {
return forBuffer(byteBuf, maxBytes, DEFAULT_MAX_DEPTH);
}
static NBTLimiter forBuffer(Object byteBuf, int maxBytes, int maxDepth) {
return new NBTLimiter() {
private int bytes;
private int depth;
@Override
public void increment(int amount) {
bytes += amount;
if (bytes > max) throw new IllegalArgumentException("NBT size limit reached (" + bytes + "/" + max + ")");
if (amount < 0) {
throw new IllegalArgumentException("Can't increment NBT limiter by negative amount: " + amount);
} else if (this.bytes + amount > maxBytes) {
throw new IllegalArgumentException("NBT size limit reached (" + this.bytes + "/" + maxBytes + ")");
}
this.bytes += amount;
}
@Override
public void checkReadability(int length) {
if (length > ByteBufHelper.readableBytes(byteBuf)) throw new IllegalArgumentException("Length is too large: " + length + ", readable: " + ByteBufHelper.readableBytes(byteBuf));
int readableBytes = ByteBufHelper.readableBytes(byteBuf);
if (length > readableBytes) {
throw new IllegalArgumentException("Can't read more than possible: " + length + " > " + readableBytes);
}
}
@Override
public void enterDepth() {
if (this.depth >= maxDepth) {
throw new IllegalArgumentException("NBT depth limit reached (" + this.depth + "/" + maxDepth + ")");
}
this.depth++;
}
@Override
public void exitDepth() {
if (this.depth <= 0) {
throw new IllegalArgumentException("Can't exit top-level depth");
}
this.depth--;
}
};
}
@@ -64,4 +105,7 @@ public interface NBTLimiter {
void checkReadability(int length);
void enterDepth();
void exitDepth();
}
@@ -18,72 +18,94 @@
package com.github.retrooper.packetevents.protocol.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.*;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTByteArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTDouble;
import com.github.retrooper.packetevents.protocol.nbt.NBTEnd;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTIntArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTLong;
import com.github.retrooper.packetevents.protocol.nbt.NBTLongArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTShort;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import org.jetbrains.annotations.ApiStatus;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;
import java.util.Map.Entry;
public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
@ApiStatus.Internal
public static final int OBJECT_HEADER_BYTES = 8;
@ApiStatus.Internal
public static final int ARRAY_HEADER_BYTES = 12;
@ApiStatus.Internal
public static final int OBJECT_REF_BYTES = 4;
@ApiStatus.Internal
public static final int STRING_SIZE_BYTES = 28;
public static final DefaultNBTSerializer INSTANCE = new DefaultNBTSerializer();
@SuppressWarnings("unchecked")
public DefaultNBTSerializer() {
super(
(limiter, dataInput) -> {
limiter.increment(1);
return dataInput.readByte();
},
(limiter, dataInput) -> dataInput.readByte(),
DataOutput::writeByte,
(limiter, dataInput) -> {
String name = dataInput.readUTF();
limiter.increment(name.length() * 2 + 28);
return name;
// only used for skipping strings
dataInput.skipBytes(dataInput.readUnsignedShort());
return "";
},
DataOutput::writeUTF
);
registerType(NBTType.END, 0, (limiter, stream) -> {
limiter.increment(8);
limiter.increment(OBJECT_HEADER_BYTES);
return NBTEnd.INSTANCE;
}, (stream, tag) -> {
});
registerType(NBTType.BYTE, 1, (limiter, stream) -> {
limiter.increment(9);
limiter.increment(OBJECT_HEADER_BYTES + Byte.BYTES);
return new NBTByte(stream.readByte());
}, (stream, tag) -> stream.writeByte(tag.getAsByte()));
registerType(NBTType.SHORT, 2, (limiter, stream) -> {
limiter.increment(10);
limiter.increment(OBJECT_HEADER_BYTES + Short.SIZE);
return new NBTShort(stream.readShort());
}, (stream, tag) -> stream.writeShort(tag.getAsShort()));
registerType(NBTType.INT, 3, (limiter, stream) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Integer.BYTES);
return new NBTInt(stream.readInt());
}, (stream, tag) -> stream.writeInt(tag.getAsInt()));
registerType(NBTType.LONG, 4, (limiter, stream) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Long.BYTES);
return new NBTLong(stream.readLong());
}, (stream, tag) -> stream.writeLong(tag.getAsLong()));
registerType(NBTType.FLOAT, 5, (limiter, stream) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Float.BYTES);
return new NBTFloat(stream.readFloat());
}, (stream, tag) -> stream.writeFloat(tag.getAsFloat()));
registerType(NBTType.DOUBLE, 6, (limiter, stream) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Double.BYTES);
return new NBTDouble(stream.readDouble());
}, (stream, tag) -> stream.writeDouble(tag.getAsDouble()));
registerType(
NBTType.BYTE_ARRAY, 7,
(limiter, stream) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = stream.readInt();
if (length >= 1 << 24)
if (length >= 1 << 24) {
throw new IllegalArgumentException("Byte array length is too large: " + length);
limiter.checkReadability(length);
limiter.increment(length);
}
limiter.increment(Byte.BYTES * length);
limiter.checkReadability(Byte.BYTES * length);
byte[] array = new byte[length];
stream.readFully(array);
@@ -96,28 +118,32 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
}
);
registerType(NBTType.STRING, 8, (limiter, stream) -> {
limiter.increment(36);
limiter.increment(OBJECT_HEADER_BYTES + STRING_SIZE_BYTES);
String string = stream.readUTF();
limiter.increment(string.length() * 2);
limiter.increment(string.length() * Character.BYTES);
return new NBTString(string);
}, (stream, tag) -> stream.writeUTF(tag.getValue()));
registerType(
NBTType.LIST, 9,
(limiter, stream) -> {
limiter.increment(37);
NBTType<? extends NBT> valueType = readTagType(limiter, stream);
int size = stream.readInt();
if ((valueType == NBTType.END) && (size > 0)) {
throw new IllegalStateException("Missing nbt list values tag type");
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // list tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES); // arraylist
NBTType<? extends NBT> valueType = readTagType(limiter, stream);
int size = stream.readInt();
if (valueType == NBTType.END && size > 0) {
throw new IllegalStateException("Missing nbt list values tag type");
}
limiter.increment(OBJECT_REF_BYTES * size);
NBTList<NBT> list = new NBTList<>((NBTType<NBT>) valueType, size);
for (int i = 0; i < size; i++) {
list.addTag(readTag(limiter, stream, valueType));
}
return list;
} finally {
limiter.exitDepth();
}
limiter.increment(4 * size);
NBTList<NBT> list = new NBTList<>((NBTType<NBT>) valueType, size);
for (int i = 0; i < size; i++) {
list.addTag(readTag(limiter, stream, valueType));
}
return list;
},
(stream, tag) -> {
writeTagType(stream, tag.getTagsType());
@@ -130,17 +156,24 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
registerType(
NBTType.COMPOUND, 10,
(limiter, stream) -> {
limiter.increment(48);
NBTCompound compound = new NBTCompound();
NBTType<?> valueType;
while ((valueType = readTagType(limiter, stream)) != NBTType.END) {
String name = readTagName(limiter, stream);
NBT nbt = readTag(limiter, stream, valueType);
if(!compound.getTags().containsKey(name)) limiter.increment(36);
compound.setTag(name, nbt);
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // compound tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES + Integer.BYTES + Integer.BYTES + Float.BYTES); // hashmap
NBTCompound compound = new NBTCompound();
NBTType<?> valueType;
while ((valueType = readTagType(limiter, stream)) != NBTType.END) {
String name = readString(limiter, stream);
NBT tag = readTag(limiter, stream, valueType);
if (!compound.getTags().containsKey(name)) {
limiter.increment(12 + OBJECT_HEADER_BYTES + Integer.BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES);
}
compound.setTag(name, tag);
}
return compound;
} finally {
limiter.exitDepth();
}
return compound;
},
(stream, tag) -> {
for (Entry<String, NBT> entry : tag.getTags().entrySet()) {
@@ -155,14 +188,13 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
registerType(
NBTType.INT_ARRAY, 11,
(limiter, stream) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = stream.readInt();
if (length >= 1 << 24)
if (length >= 1 << 24) {
throw new IllegalArgumentException("Int array length is too large: " + length);
limiter.checkReadability(length * 4);
limiter.increment(length * 4);
}
limiter.increment(length * Integer.BYTES);
limiter.checkReadability(length * Integer.BYTES);
int[] array = new int[length];
for (int i = 0; i < array.length; i++) {
@@ -181,14 +213,13 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
registerType(
NBTType.LONG_ARRAY, 12,
(limiter, stream) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = stream.readInt();
if (length >= 1 << 24)
if (length >= 1 << 24) {
throw new IllegalArgumentException("Long array length is too large: " + length);
limiter.checkReadability(length * 8);
limiter.increment(length * 8);
}
limiter.increment(length * Long.BYTES);
limiter.checkReadability(length * Long.BYTES);
long[] array = new long[length];
for (int i = 0; i < array.length; i++) {
@@ -206,4 +237,10 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
);
}
@ApiStatus.Internal
public static String readString(NBTLimiter limiter, DataInput input) throws IOException {
String string = input.readUTF();
limiter.increment(STRING_SIZE_BYTES + Character.BYTES * string.length());
return string;
}
}
@@ -21,12 +21,14 @@ package com.github.retrooper.packetevents.protocol.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import org.jetbrains.annotations.ApiStatus;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
@ApiStatus.NonExtendable
public class NBTSerializer<IN, OUT> implements NBTReader<NBT, IN>, NBTWriter<NBT, OUT> {
protected final IdReader<IN> idReader;
@@ -37,6 +39,7 @@ public class NBTSerializer<IN, OUT> implements NBTReader<NBT, IN>, NBTWriter<NBT
protected final Map<NBTType<? extends NBT>, Integer> typeToId = new HashMap<>();
protected final Map<NBTType<? extends NBT>, TagReader<IN, ? extends NBT>> typeReaders = new HashMap<>();
protected final Map<NBTType<? extends NBT>, TagWriter<OUT, ? extends NBT>> typeWriters = new HashMap<>();
public NBTSerializer(
IdReader<IN> idReader, IdWriter<OUT> idWriter,
NameReader<IN> nameReader, NameWriter<OUT> nameWriter
@@ -98,6 +101,7 @@ public class NBTSerializer<IN, OUT> implements NBTReader<NBT, IN>, NBTWriter<NBT
return type;
}
@ApiStatus.Internal
String readTagName(NBTLimiter limiter, IN from) throws IOException {
return nameReader.readName(limiter, from);
}
@@ -17,11 +17,30 @@
*/
package com.github.retrooper.packetevents.protocol.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.*;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.util.*;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import static com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer.ARRAY_HEADER_BYTES;
import static com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer.OBJECT_HEADER_BYTES;
import static com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer.OBJECT_REF_BYTES;
import static com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer.STRING_SIZE_BYTES;
public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream> {
@@ -42,9 +61,9 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
NBT nbt;
if (type == NBTType.COMPOUND) {
nbt = new Compound(from, limiter, () -> {});
nbt = new Compound(from, limiter, () -> { /**/ });
} else if (type == NBTType.LIST) {
nbt = new List(from, limiter, () -> {});
nbt = new List(from, limiter, () -> { /**/ });
} else {
nbt = DefaultNBTSerializer.INSTANCE.readTag(limiter, from, type);
}
@@ -120,7 +139,7 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
try {
hasReadType = false;
String name = DefaultNBTSerializer.INSTANCE.readTagName(limiter, stream);
String name = DefaultNBTSerializer.readString(limiter, stream);
if (nextType == NBTType.COMPOUND) {
lastRead = new Compound(stream, limiter, this::runCompleted);
@@ -209,7 +228,7 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
// we have read a tag type, so we can start reading the tag
do {
String name = DefaultNBTSerializer.INSTANCE.readTagName(limiter, stream);
String name = DefaultNBTSerializer.readString(limiter, stream);
NBT nbt = DefaultNBTSerializer.INSTANCE.readTag(limiter, stream, nextType);
limiter.increment(36);
compound.setTag(name, nbt);
@@ -230,7 +249,7 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
((Skippable) lastRead).skip();
}
if (!hasNext()) return new byte[] { 10, 0 }; // empty compound
if (!hasNext()) return new byte[]{10, 0}; // empty compound
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bytes)) {
out.write(10); // compound type
@@ -417,7 +436,7 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
((Skippable) lastRead).skip();
}
if (!hasNext()) return new byte[] { 9 };
if (!hasNext()) return new byte[]{9};
byte[] array = null;
for (int i = 0; i < remaining; i++) {
@@ -470,229 +489,224 @@ public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream
static {
TAG_SKIPS.put(NBTType.BYTE, (limiter, in) -> {
limiter.increment(9);
limiter.increment(OBJECT_HEADER_BYTES + Byte.BYTES);
in.skipBytes(Byte.BYTES);
});
TAG_SKIPS.put(NBTType.SHORT, (limiter, in) -> {
limiter.increment(10);
limiter.increment(OBJECT_HEADER_BYTES + Short.BYTES);
in.skipBytes(Short.BYTES);
});
TAG_SKIPS.put(NBTType.INT, (limiter, in) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Integer.BYTES);
in.skipBytes(Integer.BYTES);
});
TAG_SKIPS.put(NBTType.LONG, (limiter, in) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Long.BYTES);
in.skipBytes(Long.BYTES);
});
TAG_SKIPS.put(NBTType.FLOAT, (limiter, in) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Float.BYTES);
in.skipBytes(Float.BYTES);
});
TAG_SKIPS.put(NBTType.DOUBLE, (limiter, in) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Double.BYTES);
in.skipBytes(Double.BYTES);
});
TAG_SKIPS.put(NBTType.BYTE_ARRAY, (limiter, in) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = in.readInt();
limiter.checkReadability(length);
limiter.increment(length);
limiter.increment(length * Byte.BYTES);
limiter.checkReadability(length * Byte.BYTES);
in.skipBytes(length);
});
TAG_SKIPS.put(NBTType.STRING, (limiter, in) -> {
limiter.increment(36);
limiter.increment(OBJECT_HEADER_BYTES + STRING_SIZE_BYTES);
int length = in.readUnsignedShort();
limiter.increment(length * 2);
limiter.increment(length * Character.BYTES);
in.skipBytes(length);
});
TAG_SKIPS.put(NBTType.LIST, (limiter, in) -> {
limiter.increment(37);
NBTType<?> listType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in);
int length = in.readInt();
limiter.increment(length * 4);
for (int i = 0; i < length; i++) {
TAG_SKIPS.get(listType).skip(limiter, in);
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // list tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES); // arraylist
NBTType<?> listType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in);
int length = in.readInt();
limiter.increment(length * OBJECT_REF_BYTES);
for (int i = 0; i < length; i++) {
TAG_SKIPS.get(listType).skip(limiter, in);
}
} finally {
limiter.exitDepth();
}
});
TAG_SKIPS.put(NBTType.COMPOUND, (limiter, in) -> {
limiter.increment(48);
NBTType<?> valueType;
while ((valueType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in)) != NBTType.END) {
int utfLen = in.readUnsignedShort();
in.skipBytes(utfLen);
limiter.increment(36);
TAG_SKIPS.get(valueType).skip(limiter, in);
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // compound tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES + Integer.BYTES + Integer.BYTES + Float.BYTES); // hashmap
Set<String> names = new HashSet<>();
NBTType<?> valueType;
while ((valueType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in)) != NBTType.END) {
String name = DefaultNBTSerializer.readString(limiter, in);
if (names.add(name)) {
limiter.increment(12 + OBJECT_HEADER_BYTES + Integer.BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES);
}
TAG_SKIPS.get(valueType).skip(limiter, in);
}
} finally {
limiter.exitDepth();
}
});
TAG_SKIPS.put(NBTType.INT_ARRAY, (limiter, in) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = in.readInt();
limiter.checkReadability(length * 4);
limiter.increment(length * 4);
limiter.increment(length * Integer.BYTES);
limiter.checkReadability(length * Integer.BYTES);
in.skipBytes(length * Integer.BYTES);
});
TAG_SKIPS.put(NBTType.LONG_ARRAY, (limiter, in) -> {
limiter.increment(24);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int length = in.readInt();
limiter.checkReadability(length * 8);
limiter.increment(length * 8);
limiter.increment(length * Long.BYTES);
limiter.checkReadability(length * Long.BYTES);
in.skipBytes(length * Long.BYTES);
});
TAG_BINARY_READERS.put(NBTType.BYTE, (limiter, in) -> {
limiter.increment(9);
return new byte[] { in.readByte() };
limiter.increment(OBJECT_HEADER_BYTES + Byte.BYTES);
return new byte[]{in.readByte()};
});
TAG_BINARY_READERS.put(NBTType.SHORT, (limiter, in) -> {
limiter.increment(10);
return new byte[] { in.readByte(), in.readByte() };
limiter.increment(OBJECT_HEADER_BYTES + Short.BYTES);
return new byte[]{in.readByte(), in.readByte()};
});
TAG_BINARY_READERS.put(NBTType.INT, (limiter, in) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Integer.BYTES);
byte[] bytes = new byte[Integer.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.LONG, (limiter, in) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Long.BYTES);
byte[] bytes = new byte[Long.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.FLOAT, (limiter, in) -> {
limiter.increment(12);
limiter.increment(OBJECT_HEADER_BYTES + Float.BYTES);
byte[] bytes = new byte[Float.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.DOUBLE, (limiter, in) -> {
limiter.increment(16);
limiter.increment(OBJECT_HEADER_BYTES + Double.BYTES);
byte[] bytes = new byte[Double.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.BYTE_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
if (len >= 1 << 24)
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int len = in.readInt();
if (len >= 1 << 24) {
throw new IllegalArgumentException("Byte array length is too large: " + len);
limiter.checkReadability(len);
limiter.increment(len);
}
limiter.increment(len * Byte.BYTES);
limiter.checkReadability(len * Byte.BYTES);
byte[] array = new byte[Integer.BYTES + len];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
intToBytes(array, len, 0);
in.readFully(array, Integer.BYTES, len);
return array;
});
TAG_BINARY_READERS.put(NBTType.STRING, (limiter, in) -> {
limiter.increment(36);
byte[] length = new byte[Short.BYTES];
in.readFully(length);
// read len
short signedVal = (short) ((length[0] & 0xFF) << 8 | (length[1] & 0xFF));
int len = signedVal >= 0 ? signedVal : 0x10000 + signedVal;
limiter.increment(OBJECT_HEADER_BYTES + STRING_SIZE_BYTES);
String string = in.readUTF();
limiter.increment(string.length() * Character.BYTES);
limiter.increment(len * 2);
byte[] array = new byte[Short.BYTES + len];
System.arraycopy(length, 0, array, 0, Short.BYTES);
in.readFully(array, Short.BYTES, len);
return array;
});
TAG_BINARY_READERS.put(NBTType.LIST, (limiter, in) -> {
limiter.increment(37);
byte type = in.readByte();
NBTType<? extends NBT> nbtType = DefaultNBTSerializer.INSTANCE.idToType.get((int) type);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
limiter.increment(len * 4);
byte[] array = null;
for (int i = 0; i < len; i++) {
byte[] element = TAG_BINARY_READERS.get(nbtType).read(limiter, in);
if (array == null) {
array = new byte[1 + Integer.BYTES + len * element.length]; // all the remaining child tags will be of same size
array[0] = type;
System.arraycopy(length, 0, array, 1, Integer.BYTES);
}
System.arraycopy(element, 0, array, 1 + Integer.BYTES + i * element.length, element.length);
}
return array;
});
TAG_BINARY_READERS.put(NBTType.COMPOUND, (limiter, in) -> {
limiter.increment(48);
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bytes)) {
byte type;
while ((type = in.readByte()) != 0) {
out.writeByte(type);
// name
byte[] name = TAG_BINARY_READERS.get(NBTType.STRING).read(NBTLimiter.noop(), in);
limiter.increment(name.length * 2 + 28);
out.write(name);
// nbt
byte[] nbt = TAG_BINARY_READERS.get(DefaultNBTSerializer.INSTANCE.idToType.get((int) type)).read(limiter, in);
limiter.increment(36);
out.write(nbt);
}
out.write(0); // end
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes)) {
out.writeUTF(string);
return bytes.toByteArray();
}
});
TAG_BINARY_READERS.put(NBTType.INT_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
TAG_BINARY_READERS.put(NBTType.LIST, (limiter, in) -> {
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // list tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES); // arraylist
byte type = in.readByte();
NBTType<? extends NBT> nbtType = DefaultNBTSerializer.INSTANCE.idToType.get((int) type);
limiter.checkReadability(len * 4);
limiter.increment(len * 4);
int len = in.readInt();
limiter.increment(len * OBJECT_REF_BYTES);
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes)) {
out.write(type);
out.writeInt(len);
for (int i = 0; i < len; i++) {
out.write(TAG_BINARY_READERS.get(nbtType).read(limiter, in));
}
return bytes.toByteArray();
}
} finally {
limiter.exitDepth();
}
});
TAG_BINARY_READERS.put(NBTType.COMPOUND, (limiter, in) -> {
limiter.enterDepth();
try {
limiter.increment(OBJECT_HEADER_BYTES + OBJECT_REF_BYTES // compound tag
+ OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES + Integer.BYTES + Integer.BYTES + Float.BYTES); // hashmap
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes)) {
Set<String> names = new HashSet<>();
byte type;
while ((type = in.readByte()) != 0) {
out.write(type);
String name = DefaultNBTSerializer.readString(limiter, in);
out.writeUTF(name);
out.write(TAG_BINARY_READERS.get(DefaultNBTSerializer.INSTANCE.idToType.get((int) type)).read(limiter, in));
if (names.add(name)) {
limiter.increment(12 + OBJECT_HEADER_BYTES + Integer.BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES + OBJECT_REF_BYTES);
}
}
out.write(type);
return bytes.toByteArray();
}
} finally {
limiter.exitDepth();
}
});
TAG_BINARY_READERS.put(NBTType.INT_ARRAY, (limiter, in) -> {
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int len = in.readInt();
limiter.increment(len * Integer.BYTES);
limiter.checkReadability(len * Integer.BYTES);
byte[] array = new byte[Integer.BYTES + len * Integer.BYTES];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
intToBytes(array, len, 0);
in.readFully(array, Integer.BYTES, len * Integer.BYTES);
return array;
});
TAG_BINARY_READERS.put(NBTType.LONG_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
limiter.checkReadability(len * 8);
limiter.increment(len * 8);
limiter.increment(OBJECT_HEADER_BYTES + ARRAY_HEADER_BYTES + Integer.BYTES);
int len = in.readInt();
limiter.increment(len * Long.BYTES);
limiter.checkReadability(len * Long.BYTES);
byte[] array = new byte[Integer.BYTES + len * Long.BYTES];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
intToBytes(array, len, 0);
in.readFully(array, Integer.BYTES, len * Long.BYTES);
return array;
});
}
private static int bytesToInt(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) |
((bytes[1] & 0xFF) << 16) |
((bytes[2] & 0xFF) << 8 ) |
((bytes[3] & 0xFF));
public static void intToBytes(byte[] array, int val, int offset) {
array[offset] = (byte) ((val >>> 24) & 0xFF);
array[offset + 1] = (byte) ((val >>> 16) & 0xFF);
array[offset + 2] = (byte) ((val >>> 8) & 0xFF);
array[offset + 3] = (byte) (val & 0xFF);
}
}
@@ -19,16 +19,16 @@
package com.github.retrooper.packetevents.protocol.world;
import com.github.retrooper.packetevents.protocol.world.states.enums.Axis;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.util.Vector3i;
// From MCProtocolLib
public enum Direction {
DOWN(-1, Axis.Z, new Vector3i(0 , -1, 0)),
UP(-1, Axis.Z, new Vector3i(0, 1, 0)),
NORTH(0, Axis.Y, new Vector3i(0 , 0, -1)),
SOUTH(1, Axis.Y, new Vector3i(0 ,0 ,1)),
WEST(2, Axis.X, new Vector3i(-1, 0 ,0)),
DOWN(-1, Axis.Y, new Vector3i(0, -1, 0)),
UP(-1, Axis.Y, new Vector3i(0, 1, 0)),
NORTH(0, Axis.Z, new Vector3i(0, 0, -1)),
SOUTH(1, Axis.Z, new Vector3i(0, 0, 1)),
WEST(2, Axis.X, new Vector3i(-1, 0, 0)),
EAST(3, Axis.X, new Vector3i(1, 0, 0));
private final int horizontalIndex;
@@ -63,4 +63,4 @@ public enum Direction {
public Axis getAxis() {
return this.axis;
}
}
}
@@ -33,6 +33,10 @@ public class WrapperPlayServerBlockChange extends PacketWrapper<WrapperPlayServe
super(event);
}
public WrapperPlayServerBlockChange(Vector3i blockPosition, WrappedBlockState state) {
this(blockPosition, state.getGlobalId());
}
public WrapperPlayServerBlockChange(Vector3i blockPosition, int blockID) {
super(PacketType.Play.Server.BLOCK_CHANGE);
this.blockPosition = blockPosition;
@@ -194,8 +194,13 @@ public class WrapperPlayServerChunkData extends PacketWrapper<WrapperPlayServerC
// verify the full chunk has been read
int readerIndex = ByteBufHelper.readerIndex(this.buffer);
if (expectedReaderIndex != readerIndex) {
throw new RuntimeException("Error while decoding chunk at " + chunkX + " " + chunkZ
+ "; expected reader index " + expectedReaderIndex + ", got " + readerIndex);
if (expectedReaderIndex < readerIndex) {
// we advanced too far, error
throw new RuntimeException("Error while decoding chunk at " + chunkX + " " + chunkZ
+ "; expected reader index " + expectedReaderIndex + ", got " + readerIndex);
}
// we didn't read the whole buffer, skip the rest
ByteBufHelper.readerIndex(this.buffer, expectedReaderIndex);
}
} finally {
// change buffer back if it has been switched
@@ -93,9 +93,7 @@ public class WrapperPlayServerWorldBorder extends PacketWrapper<WrapperPlayServe
@Override
public void read() {
int actionId = readVarInt();
action = serverVersion.isNewerThanOrEquals(ServerVersion.V_1_12) ? WorldBorderAction.fromId(actionId)
: WorldBorderAction.fromLegacyId(actionId);
action = readEnum(WorldBorderAction.class);
if (this.action == WorldBorderAction.SET_SIZE) {
this.radius = readDouble();
} else if (this.action == WorldBorderAction.LERP_SIZE) {
@@ -123,8 +121,7 @@ public class WrapperPlayServerWorldBorder extends PacketWrapper<WrapperPlayServe
@Override
public void write() {
int actionId = serverVersion.isNewerThanOrEquals(ServerVersion.V_1_12) ? action.getId() : action.getLegacyId();
writeVarInt(actionId);
writeEnum(action);
if (this.action == WorldBorderAction.SET_SIZE) {
writeDouble(this.radius);
} else if (this.action == WorldBorderAction.LERP_SIZE) {
@@ -245,38 +242,20 @@ public class WrapperPlayServerWorldBorder extends PacketWrapper<WrapperPlayServe
}
public enum WorldBorderAction {
SET_SIZE(1),
LERP_SIZE(2),
SET_CENTER(3),
INITIALIZE(6),
SET_WARNING_TIME(5),
SET_WARNING_BLOCKS(4);
SET_SIZE,
LERP_SIZE,
SET_CENTER,
INITIALIZE,
SET_WARNING_TIME,
SET_WARNING_BLOCKS;
private final int legacyId;
WorldBorderAction(int legacyId) {
this.legacyId = legacyId;
}
public int getId() {
return ordinal();
}
public int getLegacyId() {
return legacyId;
}
public static WorldBorderAction fromId(int id) {
return WorldBorderAction.values()[id];
}
public static WorldBorderAction fromLegacyId(int legacyId) {
for (WorldBorderAction action : WorldBorderAction.values()) {
if (action.legacyId == legacyId) {
return action;
}
}
return null;
}
}
}
@@ -1,11 +1,11 @@
package com.github.retrooper.packetevents.test.base;
import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.MockPlugin;
import be.seeseemelk.mockbukkit.ServerMock;
import com.github.retrooper.packetevents.PacketEvents;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.mockbukkit.mockbukkit.MockBukkit;
import org.mockbukkit.mockbukkit.ServerMock;
import org.mockbukkit.mockbukkit.plugin.PluginMock;
import org.slf4j.Logger;
public abstract class BaseDummyAPITest {
@@ -13,7 +13,7 @@ public abstract class BaseDummyAPITest {
public static final Logger LOGGER = TestPacketEventsBuilder.LOGGER;
private ServerMock server;
private MockPlugin plugin;
private PluginMock plugin;
@BeforeEach
public void setup() {
+2 -2
View File
@@ -1,7 +1,7 @@
[versions]
shadow = "8.3.3"
shadow = "8.3.8"
jetbrains-annotations = "23.0.0"
via-nbt = "4.1.0"
via-nbt = "5.1.2"
fast-util = "8.5.6"
gson = "2.8.9"
java-diff-utils = "4.12"
@@ -17,9 +17,9 @@
*/
package com.github.retrooper.compression
import com.github.steveice10.opennbt.tag.builtin.Tag
import com.github.steveice10.opennbt.tag.io.NBTIO
import com.github.steveice10.opennbt.tag.io.TagWriter
import com.viaversion.nbt.tag.Tag
import com.viaversion.nbt.io.NBTIO
import com.viaversion.nbt.io.TagWriter
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonElement
@@ -23,12 +23,12 @@ import com.github.difflib.patch.ChangeDelta
import com.github.difflib.patch.DeleteDelta
import com.github.difflib.patch.InsertDelta
import com.github.retrooper.compression.asStringList
import com.github.steveice10.opennbt.tag.builtin.ByteTag
import com.github.steveice10.opennbt.tag.builtin.CompoundTag
import com.github.steveice10.opennbt.tag.builtin.IntTag
import com.github.steveice10.opennbt.tag.builtin.ListTag
import com.github.steveice10.opennbt.tag.builtin.StringTag
import com.github.steveice10.opennbt.tag.builtin.Tag
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
import com.google.gson.JsonElement
object JsonArrayCompressionStrategy : JsonCompressionStrategy() {
@@ -18,10 +18,10 @@
package com.github.retrooper.compression.strategy.json
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag
import com.github.steveice10.opennbt.tag.builtin.ByteTag
import com.github.steveice10.opennbt.tag.builtin.CompoundTag
import com.github.steveice10.opennbt.tag.builtin.Tag
import com.viaversion.nbt.tag.ByteArrayTag
import com.viaversion.nbt.tag.ByteTag
import com.viaversion.nbt.tag.CompoundTag
import com.viaversion.nbt.tag.Tag
import com.google.gson.JsonElement
import java.util.*
@@ -20,9 +20,9 @@ package com.github.retrooper.compression.strategy.json
import com.github.retrooper.compression.CompressionUtil
import com.github.retrooper.compression.EntryVersion
import com.github.retrooper.compression.strategy.CompressionStrategy
import com.github.steveice10.opennbt.tag.builtin.ByteTag
import com.github.steveice10.opennbt.tag.builtin.CompoundTag
import com.github.steveice10.opennbt.tag.builtin.Tag
import com.viaversion.nbt.tag.ByteTag
import com.viaversion.nbt.tag.CompoundTag
import com.viaversion.nbt.tag.Tag
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import java.nio.file.Path
@@ -23,7 +23,7 @@ import com.github.difflib.patch.ChangeDelta
import com.github.difflib.patch.DeleteDelta
import com.github.difflib.patch.InsertDelta
import com.github.retrooper.compression.asPrimitiveMap
import com.github.steveice10.opennbt.tag.builtin.*
import com.viaversion.nbt.tag.*
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
@@ -17,7 +17,7 @@
*/
package com.github.retrooper.compression.strategy.json
import com.github.steveice10.opennbt.tag.builtin.*
import com.viaversion.nbt.tag.*
import com.google.gson.JsonElement
import com.google.gson.internal.LazilyParsedNumber
+1 -1
View File
@@ -1,7 +1,7 @@
org.gradle.jvmargs=-Xmx2000m
# TODO UPDATE
fullVersion=2.9.2
fullVersion=2.9.5
snapshot=false
publishing.display_name=PacketEvents
+1 -1
View File
@@ -11,7 +11,7 @@ bungeecord = "1.21-R0.1-SNAPSHOT"
velocity = "3.4.0-SNAPSHOT"
run-paper = "2.3.1"
fabric-loom = "1.11.4"
spongeGradle = "2.2.0"
spongeGradle = "2.3.0"
classgraph = "4.8.179"
[libraries]
@@ -20,6 +20,7 @@ package io.github.retrooper.packetevents.bukkit;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.util.FakeChannelUtil;
import io.github.retrooper.packetevents.injector.SpigotChannelInjector;
@@ -103,7 +104,7 @@ public class InternalBukkitListener implements Listener {
// delay by a tick
FoliaScheduler.getEntityScheduler().runDelayed(player, this.plugin, __ -> {
// only kick if the player is actually still connected
if (player.isConnected()) {
if (channel != null ? ChannelHelper.isOpen(channel) : player.isOnline()) {
player.kickPlayer(KICK_MESSAGE);
}
}, null, 0);
@@ -144,16 +144,23 @@ public class SpigotPacketEventsBuilder {
if (!initialized) {
Plugin plugin = (Plugin) PacketEvents.getAPI().getPlugin();
String bukkitVersion = Bukkit.getBukkitVersion();
// Our PEVersion class can parse this version and detect if it is a newer version than what is currently supported
PEVersion bukkitServerVersion = PEVersion.fromString(bukkitVersion.substring(0, bukkitVersion.indexOf("-")));
PEVersion latestSupportedVersion = PEVersion.fromString(ServerVersion.getLatest().getReleaseName());
if (bukkitServerVersion.isNewerThan(latestSupportedVersion)) {
//We do not support this version yet, so let us warn the user
plugin.getLogger().warning("Your build of PacketEvents does not support the Minecraft version "
+ bukkitServerVersion + "! The latest Minecraft version supported by your build of PacketEvents is " + latestSupportedVersion
+ ". Please test the development builds, as they may already have support for your Minecraft version (hint: select the build that contains 'spigot'): https://ci.codemc.io/job/retrooper/job/packetevents/ If you're in need of any help, join our Discord server: https://discord.gg/DVHxPPxHZc");
Bukkit.getPluginManager().disablePlugin(plugin);
return;
if (bukkitVersion.contains("Unknown")) {
ServerVersion fallbackVersion = ServerVersion.V_1_8_8;
String failureToDetectVersionMsg = "Your server software is preventing us from checking the Minecraft Server version. This is what we found: " + bukkitVersion + ". We will assume the Server version is " + fallbackVersion.name() + "...\n If you need assistance, join our Discord server: https://discord.gg/DVHxPPxHZc";
plugin.getLogger().warning(failureToDetectVersionMsg);
} else {
// Our PEVersion class can parse this version and detect if it is a newer version than what is currently supported
PEVersion bukkitServerVersion = PEVersion.fromString(bukkitVersion.substring(0, bukkitVersion.indexOf("-")));
PEVersion latestSupportedVersion = PEVersion.fromString(ServerVersion.getLatest().getReleaseName());
if (bukkitServerVersion.isNewerThan(latestSupportedVersion)) {
//We do not support this version yet, so let us warn the user
plugin.getLogger().warning("Your build of PacketEvents does not support the Minecraft version "
+ bukkitServerVersion + "! The latest Minecraft version supported by your build of PacketEvents is " + latestSupportedVersion
+ ". Please test the development builds, as they may already have support for your Minecraft version (hint: select the build that contains 'spigot'): https://ci.codemc.io/job/retrooper/job/packetevents/ If you're in need of any help, join our Discord server: https://discord.gg/DVHxPPxHZc");
Bukkit.getPluginManager().disablePlugin(plugin);
return;
}
}
if (settings.shouldCheckForUpdates()) {
getUpdateChecker().handleUpdateCheck();
@@ -35,6 +35,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
import org.bukkit.entity.Player;
import org.jspecify.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
@@ -58,10 +59,10 @@ public class SpigotChannelInjector implements ChannelInjector {
public boolean isPlayerSet(Object channel) {
if (channel == null) return false;
PacketEventsEncoder encoder = getEncoder((Channel) channel);
if (encoder.player != null) return true;
if (encoder != null && encoder.player != null) return true;
PacketEventsDecoder decoder = getDecoder((Channel) channel);
return decoder.player != null;
return decoder != null && decoder.player != null;
}
@Override
@@ -227,11 +228,17 @@ public class SpigotChannelInjector implements ChannelInjector {
}
}
private PacketEventsEncoder getEncoder(Channel channel) {
/**
* @return may return null if the channel is closed already or not initialized yet
*/
private @Nullable PacketEventsEncoder getEncoder(Channel channel) {
return (PacketEventsEncoder) channel.pipeline().get(PacketEvents.ENCODER_NAME);
}
private PacketEventsDecoder getDecoder(Channel channel) {
/**
* @return may return null if the channel is closed already or not initialized yet
*/
private @Nullable PacketEventsDecoder getDecoder(Channel channel) {
return (PacketEventsDecoder) channel.pipeline().get(PacketEvents.DECODER_NAME);
}
@@ -20,6 +20,7 @@ package io.github.retrooper.packetevents.util;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
@@ -41,10 +42,13 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.SimpleTypesBuilderData;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Pose;
import org.bukkit.inventory.MainHand;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class SpigotConversionUtil {
public static Location fromBukkitLocation(org.bukkit.Location location) {
return new Location(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
@@ -233,4 +237,16 @@ public class SpigotConversionUtil {
public static MainHand toBukkitHand(HumanoidArm arm) {
return MainHand.values()[arm.ordinal()];
}
/**
* Retrieves the metadata of a given Bukkit {@link org.bukkit.entity.Entity},
* returned as a list of {@link com.github.retrooper.packetevents.protocol.entity.data.EntityData}
* compatible with PacketEvents.
*
* @param entity the Bukkit entity to extract metadata from
* @return a list of the entity's metadata values
*/
public static List<EntityData<?>> getEntityMetadata(Entity entity) {
return SpigotReflectionUtil.getEntityMetadata(entity);
}
}
@@ -22,46 +22,33 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.particle.type.ParticleType;
import com.github.retrooper.packetevents.protocol.particle.type.ParticleTypes;
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.reflection.NestedClassUtil;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import com.github.retrooper.packetevents.util.reflection.ReflectionObject;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.google.common.collect.BiMap;
import com.google.common.collect.MapMaker;
import io.netty.buffer.PooledByteBufAllocator;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Registry;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@@ -103,7 +90,8 @@ public final class SpigotReflectionUtil {
DYNAMIC_OPS_NBT_CLASS, NMS_NBT_COMPOUND_CLASS, NMS_NBT_BASE_CLASS, NBT_COMPRESSION_STREAM_TOOLS_CLASS,
STREAM_CODEC, STREAM_DECODER, STREAM_ENCODER, REGISTRY_FRIENDLY_BYTE_BUF, REGISTRY_ACCESS, REGISTRY_ACCESS_FROZEN,
RESOURCE_KEY, REGISTRY, WRITABLE_REGISTRY, NBT_ACCOUNTER, CHUNK_PROVIDER_SERVER_CLASS, ICHUNKPROVIDER_CLASS, CHUNK_STATUS_CLASS,
BLOCK_POSITION_CLASS, PLAYER_CHUNK_MAP_CLASS, PLAYER_CHUNK_CLASS, CHUNK_CLASS, IBLOCKACCESS_CLASS, ICHUNKACCESS_CLASS, REMOTE_CHAT_SESSION_CLASS;
BLOCK_POSITION_CLASS, PLAYER_CHUNK_MAP_CLASS, PLAYER_CHUNK_CLASS, CHUNK_CLASS, IBLOCKACCESS_CLASS, ICHUNKACCESS_CLASS, REMOTE_CHAT_SESSION_CLASS,
DATA_WATCHER_CLASS, CLIENTBOUND_SET_ENTITY_DATA_PACKET_CLASS, DATA_WATCHER_ITEM_CLASS, DATA_WATCHER_VALUE_CLASS;
//Netty classes
public static Class<?> CHANNEL_CLASS, BYTE_BUF_CLASS, BYTE_TO_MESSAGE_DECODER, MESSAGE_TO_BYTE_ENCODER;
@@ -111,7 +99,7 @@ public final class SpigotReflectionUtil {
//Fields
public static Field ENTITY_PLAYER_PING_FIELD, ENTITY_BOUNDING_BOX_FIELD, BYTE_BUF_IN_PACKET_DATA_SERIALIZER, DIMENSION_CODEC_FIELD,
DYNAMIC_OPS_NBT_INSTANCE_FIELD, CHUNK_PROVIDER_SERVER_FIELD, CRAFT_PARTICLE_PARTICLES_FIELD, NMS_MK_KEY_FIELD, LEGACY_NMS_PARTICLE_KEY_FIELD, LEGACY_NMS_KEY_TO_NMS_PARTICLE,
REMOTE_CHAT_SESSION_FIELD, REGISTRY_KEY_LOCATION_FIELD;
REMOTE_CHAT_SESSION_FIELD, REGISTRY_KEY_LOCATION_FIELD, DATA_WATCHER_FIELD;
//Methods
public static Method IS_DEBUGGING, GET_CRAFT_PLAYER_HANDLE_METHOD, GET_CRAFT_ENTITY_HANDLE_METHOD, GET_CRAFT_WORLD_HANDLE_METHOD,
@@ -125,7 +113,8 @@ public final class SpigotReflectionUtil {
READ_NBT_FROM_STREAM_METHOD, WRITE_NBT_TO_STREAM_METHOD, STREAM_DECODER_DECODE, STREAM_ENCODER_ENCODE,
CREATE_REGISTRY_RESOURCE_KEY, GET_REGISTRY_OR_THROW, GET_DIMENSION_TYPES, GET_REGISTRY_ID,
NBT_ACCOUNTER_UNLIMITED_HEAP, CHUNK_CACHE_GET_IBLOCKACCESS, CHUNK_CACHE_GET_ICHUNKACCESS,
IBLOCKACCESS_GET_BLOCK_DATA, CHUNK_GET_BLOCK_DATA, PLAYER_CHUNK_MAP_GET_PLAYER_CHUNK, PLAYER_CHUNK_GET_CHUNK;
IBLOCKACCESS_GET_BLOCK_DATA, CHUNK_GET_BLOCK_DATA, PLAYER_CHUNK_MAP_GET_PLAYER_CHUNK, PLAYER_CHUNK_GET_CHUNK,
LEGACY_DATA_WATCHER_WRITE_METHOD, CLIENTBOUND_SET_ENTITY_DATA_PACKET_WRITE_DATA_WATCHER_METHOD, GET_DATA_VALUE_FROM_DATA_ITEM_METHOD;
//Constructors
private static Constructor<?> NMS_ITEM_STACK_CONSTRUCTOR, NMS_PACKET_DATA_SERIALIZER_CONSTRUCTOR,
@@ -275,6 +264,10 @@ public final class SpigotReflectionUtil {
if (CHUNK_CLASS != null) {
PLAYER_CHUNK_GET_CHUNK = Reflection.getMethod(SpigotReflectionUtil.PLAYER_CHUNK_CLASS, SpigotReflectionUtil.CHUNK_CLASS, 0);
}
LEGACY_DATA_WATCHER_WRITE_METHOD = Reflection.getMethod(DATA_WATCHER_CLASS, void.class, 0, NMS_PACKET_DATA_SERIALIZER_CLASS);
CLIENTBOUND_SET_ENTITY_DATA_PACKET_WRITE_DATA_WATCHER_METHOD = Reflection.getMethod(CLIENTBOUND_SET_ENTITY_DATA_PACKET_CLASS, 0, List.class, REGISTRY_FRIENDLY_BYTE_BUF);
GET_DATA_VALUE_FROM_DATA_ITEM_METHOD = Reflection.getMethod(DATA_WATCHER_ITEM_CLASS, DATA_WATCHER_VALUE_CLASS, 0);
}
private static void initFields() {
@@ -302,6 +295,7 @@ public final class SpigotReflectionUtil {
REMOTE_CHAT_SESSION_FIELD = Reflection.getField(ENTITY_PLAYER_CLASS, REMOTE_CHAT_SESSION_CLASS, 0);
REGISTRY_KEY_LOCATION_FIELD = Reflection.getField(RESOURCE_KEY, NMS_MINECRAFT_KEY_CLASS, 1);
DATA_WATCHER_FIELD = Reflection.getField(NMS_ENTITY_CLASS, DATA_WATCHER_CLASS, 0, true);
}
private static void initClasses() {
@@ -402,6 +396,14 @@ public final class SpigotReflectionUtil {
WRITABLE_REGISTRY = getServerClass(IS_OBFUSCATED ? "core.IRegistryWritable" : "core.WritableRegistry", "IRegistryWritable");
REMOTE_CHAT_SESSION_CLASS = Reflection.getClassByNameWithoutException("net.minecraft.network.chat.RemoteChatSession");
DATA_WATCHER_CLASS = SpigotReflectionUtil.getServerClass("network.syncher.DataWatcher", "DataWatcher");
if (DATA_WATCHER_CLASS == null) {
DATA_WATCHER_CLASS = SpigotReflectionUtil.getServerClass("network.syncher.SynchedEntityData", "DataWatcher");
}
CLIENTBOUND_SET_ENTITY_DATA_PACKET_CLASS = SpigotReflectionUtil.getServerClass("network.protocol.game.ClientboundSetEntityDataPacket", "PacketPlayOutEntityMetadata");
DATA_WATCHER_ITEM_CLASS = NestedClassUtil.getNestedClass(DATA_WATCHER_CLASS, 0);
DATA_WATCHER_VALUE_CLASS = NestedClassUtil.getNestedClass(DATA_WATCHER_CLASS, 1);
}
private static void initObjects() {
@@ -1207,4 +1209,35 @@ public final class SpigotReflectionUtil {
return null;
}
}
public static List<EntityData<?>> getEntityMetadata(@NotNull Entity entity) {
Object byteBuf = PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().buffer();
try {
Object handle = SpigotReflectionUtil.getNMSEntity(entity);
Object dataWatcher = DATA_WATCHER_FIELD.get(handle);
Object packetDataSerializer = createPacketDataSerializer(byteBuf);
if (LEGACY_DATA_WATCHER_WRITE_METHOD != null) {
// Legacy Minecraft versions have the DataWatcher class with a method to write to the PacketDataSerializer
LEGACY_DATA_WATCHER_WRITE_METHOD.invoke(dataWatcher, packetDataSerializer);
}
else {
// Modern Minecraft versions require the use of the pack method in the ClientboundSetEntityDataPacket class.
Object[] dataItems = new ReflectionObject(dataWatcher).readObjectArray(0, DATA_WATCHER_ITEM_CLASS);
List<Object> dataValues = new ArrayList<>(dataItems.length);
for (Object dataItem : dataItems) {
dataValues.add(GET_DATA_VALUE_FROM_DATA_ITEM_METHOD.invoke(dataItem));
}
CLIENTBOUND_SET_ENTITY_DATA_PACKET_WRITE_DATA_WATCHER_METHOD.invoke(null, dataValues, packetDataSerializer);
}
PacketWrapper<?> packetWrapper = PacketWrapper.createUniversalPacketWrapper(byteBuf);
return packetWrapper.readEntityMetadata();
} catch (Exception e) {
throw new RuntimeException(e);
}
finally {
ByteBufHelper.release(byteBuf);
}
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
[versions]
mockbukkit = "3.131.0"
mockbukkit = "4.72.2"
slf4j = "2.0.16"
junit = "5.11.2"
[libraries]
mockbukkit = { group = "com.github.seeseemelk", name = "MockBukkit-v1.21", version.ref = "mockbukkit" }
mockbukkit = { group = "org.mockbukkit.mockbukkit", name = "mockbukkit-v1.21", version.ref = "mockbukkit" }
slf4j = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }