1.19.3 support

This commit is contained in:
themode 2022-10-20 03:53:01 +02:00 committed by TheMode
parent 7361bf0825
commit f09fdd862b
30 changed files with 419 additions and 538 deletions

View File

@ -7,7 +7,7 @@ adventure = "4.11.0"
kotlin = "1.6.20"
hydrazine = "1.7.2"
dependencyGetter = "v1.0.1"
minestomData = "1c1921cd41"
minestomData = "76da04ca1d"
hephaistos = "2.5.3"
jetbrainsAnnotations = "23.0.0"

View File

@ -45,8 +45,8 @@ public final class MinecraftServer {
public static final ComponentLogger LOGGER = ComponentLogger.logger(MinecraftServer.class);
public static final String VERSION_NAME = "1.19.2";
public static final int PROTOCOL_VERSION = 760;
public static final String VERSION_NAME = "1.19.3";
public static final int PROTOCOL_VERSION = 761;
// Threads
public static final String THREAD_NAME_BENCHMARK = "Ms-Benchmark";

View File

@ -14,7 +14,8 @@ public class ArgumentPotionEffect extends ArgumentRegistry<PotionEffect> {
@Override
public String parser() {
return "minecraft:mob_effect";
// TODO: what replace `minecraft:mob_effect`?
return "minecraft:uuid";
}
@Override

View File

@ -0,0 +1,22 @@
package net.minestom.server.crypto;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.COMPONENT;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record ChatBound(int chatType, Component name, @Nullable Component targetName) implements NetworkBuffer.Writer {
public ChatBound(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), reader.read(COMPONENT), reader.readOptional(COMPONENT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, chatType);
writer.write(COMPONENT, name);
writer.writeOptional(COMPONENT, targetName);
}
}

View File

@ -0,0 +1,28 @@
package net.minestom.server.crypto;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import java.util.BitSet;
import static net.minestom.server.network.NetworkBuffer.LONG_ARRAY;
public record FilterMask(@NotNull Type type, @NotNull BitSet mask) implements NetworkBuffer.Writer {
public FilterMask(@NotNull NetworkBuffer reader) {
this(reader.readEnum(Type.class), BitSet.valueOf(reader.read(LONG_ARRAY)));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeEnum(Type.class, type);
if (type == Type.PARTIALLY_FILTERED) {
writer.write(LONG_ARRAY, mask.toLongArray());
}
}
public enum Type {
PASS_THROUGH,
FULLY_FILTERED,
PARTIALLY_FILTERED
}
}

View File

@ -2,45 +2,47 @@ package net.minestom.server.crypto;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.BitSet;
import java.util.List;
import java.util.UUID;
public record LastSeenMessages(@NotNull List<@NotNull Entry> entries) implements NetworkBuffer.Writer {
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record LastSeenMessages(@NotNull List<@NotNull MessageSignature> entries) implements NetworkBuffer.Writer {
public LastSeenMessages {
entries = List.copyOf(entries);
}
public LastSeenMessages(@NotNull NetworkBuffer reader) {
this(reader.readCollection(Entry::new));
this(reader.readCollection(MessageSignature::new));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
}
public record Entry(UUID from, MessageSignature lastSignature) implements NetworkBuffer.Writer {
public Entry(@NotNull NetworkBuffer reader) {
this(reader.read(NetworkBuffer.UUID), new MessageSignature(reader));
public record Packed(@NotNull List<MessageSignature.@NotNull Packed> entries) implements NetworkBuffer.Writer {
public static final Packed EMPTY = new Packed(List.of());
public Packed(@NotNull NetworkBuffer reader) {
this(reader.readCollection(MessageSignature.Packed::new));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.UUID, from);
writer.write(lastSignature);
writer.writeCollection(entries);
}
}
public record Update(LastSeenMessages lastSeen, @Nullable Entry lastReceived) implements NetworkBuffer.Writer {
public record Update(int offset, @NotNull BitSet acknowledged) implements NetworkBuffer.Writer {
public Update(@NotNull NetworkBuffer reader) {
this(new LastSeenMessages(reader), reader.readOptional(Entry::new));
this(reader.read(VAR_INT), null);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(lastSeen);
writer.writeOptional(lastReceived);
writer.write(VAR_INT, offset);
//writer.writeFixedBitSet(acknowledged);
}
}
}

View File

@ -2,16 +2,45 @@ package net.minestom.server.crypto;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
import static net.minestom.server.network.NetworkBuffer.RAW_BYTES;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record MessageSignature(byte @NotNull [] signature) implements NetworkBuffer.Writer {
public MessageSignature {
if (signature.length != 256) {
throw new IllegalArgumentException("Signature must be 256 bytes long");
}
}
public MessageSignature(@NotNull NetworkBuffer reader) {
this(reader.read(BYTE_ARRAY));
this(reader.readBytes(256));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(BYTE_ARRAY, signature);
writer.write(RAW_BYTES, signature);
}
public record Packed(int id, @UnknownNullability MessageSignature fullSignature) implements NetworkBuffer.Writer {
public Packed(@NotNull NetworkBuffer reader) {
this(read(reader));
}
private Packed(@NotNull Packed packed) {
this(packed.id, packed.fullSignature);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, id + 1);
if (id == 0) writer.write(fullSignature);
}
private static Packed read(NetworkBuffer reader) {
final int id = reader.read(VAR_INT) - 1;
return new Packed(id, id == -1 ? new MessageSignature(reader) : null);
}
}
}

View File

@ -0,0 +1,31 @@
package net.minestom.server.crypto;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import java.time.Instant;
public final class SignedMessageBody {
public record Packed(@NotNull String content, @NotNull Instant timeStamp, long salt,
LastSeenMessages.@NotNull Packed lastSeen) implements NetworkBuffer.Writer {
public Packed {
if (content.length() > 256) {
throw new IllegalArgumentException("Message content too long");
}
}
public Packed(@NotNull NetworkBuffer reader) {
this(reader.read(NetworkBuffer.STRING), Instant.ofEpochMilli(reader.read(NetworkBuffer.LONG)),
reader.read(NetworkBuffer.LONG), new LastSeenMessages.Packed(reader));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.STRING, content);
writer.write(NetworkBuffer.LONG, timeStamp.toEpochMilli());
writer.write(NetworkBuffer.LONG, salt);
writer.write(lastSeen);
}
}
}

View File

@ -28,6 +28,10 @@ public final class Metadata {
return new MetadataImpl.EntryImpl<>(TYPE_VARINT, value, NetworkBuffer.VAR_INT);
}
public static Entry<Long> Long(long value) {
return new MetadataImpl.EntryImpl<>(TYPE_LONG, value, NetworkBuffer.LONG);
}
public static Entry<Float> Float(float value) {
return new MetadataImpl.EntryImpl<>(TYPE_FLOAT, value, NetworkBuffer.FLOAT);
}
@ -97,23 +101,24 @@ public final class Metadata {
public static final byte TYPE_BYTE = 0;
public static final byte TYPE_VARINT = 1;
public static final byte TYPE_FLOAT = 2;
public static final byte TYPE_STRING = 3;
public static final byte TYPE_CHAT = 4;
public static final byte TYPE_OPTCHAT = 5;
public static final byte TYPE_SLOT = 6;
public static final byte TYPE_BOOLEAN = 7;
public static final byte TYPE_ROTATION = 8;
public static final byte TYPE_POSITION = 9;
public static final byte TYPE_OPTPOSITION = 10;
public static final byte TYPE_DIRECTION = 11;
public static final byte TYPE_OPTUUID = 12;
public static final byte TYPE_OPTBLOCKID = 13;
public static final byte TYPE_NBT = 14;
public static final byte TYPE_PARTICLE = 15;
public static final byte TYPE_VILLAGERDATA = 16;
public static final byte TYPE_OPTVARINT = 17;
public static final byte TYPE_POSE = 18;
public static final byte TYPE_LONG = 2;
public static final byte TYPE_FLOAT = 3;
public static final byte TYPE_STRING = 4;
public static final byte TYPE_CHAT = 5;
public static final byte TYPE_OPTCHAT = 6;
public static final byte TYPE_SLOT = 7;
public static final byte TYPE_BOOLEAN = 8;
public static final byte TYPE_ROTATION = 9;
public static final byte TYPE_POSITION = 10;
public static final byte TYPE_OPTPOSITION = 11;
public static final byte TYPE_DIRECTION = 12;
public static final byte TYPE_OPTUUID = 13;
public static final byte TYPE_OPTBLOCKID = 14;
public static final byte TYPE_NBT = 15;
public static final byte TYPE_PARTICLE = 16;
public static final byte TYPE_VILLAGERDATA = 17;
public static final byte TYPE_OPTVARINT = 18;
public static final byte TYPE_POSE = 19;
private static final VarHandle NOTIFIED_CHANGES;

View File

@ -932,8 +932,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
*/
public void setDisplayName(@Nullable Component displayName) {
this.displayName = displayName;
PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME,
new PlayerInfoPacket.UpdateDisplayName(getUuid(), displayName)));
// TODO
//PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME,
// new PlayerInfoPacket.UpdateDisplayName(getUuid(), displayName)));
}
/**
@ -961,8 +962,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket(getEntityId());
final PlayerInfoPacket removePlayerPacket = getRemovePlayerToList();
final PlayerInfoPacket addPlayerPacket = getAddPlayerToList();
final PlayerInfoRemovePacket removePlayerPacket = getRemovePlayerToList();
final PlayerInfoUpdatePacket addPlayerPacket = getAddPlayerToList();
RespawnPacket respawnPacket = new RespawnPacket(getDimensionType().toString(), "minestom:world",
0, gameMode, gameMode, false, levelFlat, true);
@ -1288,8 +1289,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
// Condition to prevent sending the packets before spawning the player
if (isActive()) {
sendPacket(new ChangeGameStatePacket(ChangeGameStatePacket.Reason.CHANGE_GAMEMODE, gameMode.id()));
PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_GAMEMODE,
new PlayerInfoPacket.UpdateGameMode(getUuid(), gameMode)));
// TODO
//PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_GAMEMODE,
// new PlayerInfoPacket.UpdateGameMode(getUuid(), gameMode)));
}
// The client updates their abilities based on the GameMode as follows
@ -1789,8 +1791,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
*/
public void refreshLatency(int latency) {
this.latency = latency;
PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_LATENCY,
new PlayerInfoPacket.UpdateLatency(getUuid(), latency)));
// TODO
//PacketUtils.broadcastPacket(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_LATENCY,
// new PlayerInfoPacket.UpdateLatency(getUuid(), latency)));
}
public void refreshOnGround(boolean onGround) {
@ -1908,24 +1911,25 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
/**
* Gets the packet to add the player from the tab-list.
*
* @return a {@link PlayerInfoPacket} to add the player
* @return a {@link PlayerInfoUpdatePacket} to add the player
*/
protected @NotNull PlayerInfoPacket getAddPlayerToList() {
protected @NotNull PlayerInfoUpdatePacket getAddPlayerToList() {
final PlayerSkin skin = this.skin;
List<PlayerInfoPacket.AddPlayer.Property> prop = skin != null ?
List.of(new PlayerInfoPacket.AddPlayer.Property("textures", skin.textures(), skin.signature())) :
List<PlayerInfoUpdatePacket.Property> prop = skin != null ?
List.of(new PlayerInfoUpdatePacket.Property("textures", skin.textures(), skin.signature())) :
List.of();
return new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER,
new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), prop, getGameMode(), getLatency(), displayName, null));
return new PlayerInfoUpdatePacket(EnumSet.of(PlayerInfoUpdatePacket.Action.ADD_PLAYER, PlayerInfoUpdatePacket.Action.UPDATE_LISTED),
List.of(new PlayerInfoUpdatePacket.Entry(getUuid(), getUsername(), prop,
true, getLatency(), getGameMode(), displayName)));
}
/**
* Gets the packet to remove the player from the tab-list.
*
* @return a {@link PlayerInfoPacket} to remove the player
* @return a {@link PlayerInfoRemovePacket} to remove the player
*/
protected @NotNull PlayerInfoPacket getRemovePlayerToList() {
return new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER, new PlayerInfoPacket.RemovePlayer(getUuid()));
protected @NotNull PlayerInfoRemovePacket getRemovePlayerToList() {
return new PlayerInfoRemovePacket(List.of(getUuid()));
}
/**

View File

@ -32,7 +32,8 @@ public class ChatMessageListener {
}
public static void chatMessageListener(ClientChatMessagePacket packet, Player player) {
final String message = packet.message();
System.out.println("packet "+packet);
final String message = "test";
if (!Messenger.canReceiveMessage(player)) {
Messenger.sendRejectionMessage(player);
return;

View File

@ -15,9 +15,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@ -202,6 +200,25 @@ public final class NetworkBuffer {
return enumClass.getEnumConstants()[read(VAR_INT)];
}
public <E extends Enum<E>> void writeEnumSet(EnumSet<E> enumSet, Class<E> enumType) {
final E[] values = enumType.getEnumConstants();
BitSet bitSet = new BitSet(values.length);
for (int i = 0; i < values.length; ++i) {
bitSet.set(i, enumSet.contains(values[i]));
}
writeFixedBitSet(bitSet, values.length);
}
private void writeFixedBitSet(BitSet set, int length) {
final int setLength = set.length();
if (setLength > length) {
throw new IllegalArgumentException("BitSet is larger than expected size (" + setLength + ">" + length + ")");
} else {
final byte[] array = set.toByteArray();
write(RAW_BYTES, array);
}
}
public byte[] readBytes(int length) {
byte[] bytes = new byte[length];
nioBuffer.get(readIndex, bytes, 0, length);

View File

@ -58,33 +58,33 @@ public sealed class ClientPacketsHandler permits ClientPacketsHandler.Status, Cl
register(0x03, ClientChatAckPacket::new);
register(0x04, ClientCommandChatPacket::new);
register(0x05, ClientChatMessagePacket::new);
register(0x06, ClientChatPreviewPacket::new);
register(0x07, ClientStatusPacket::new);
register(0x08, ClientSettingsPacket::new);
register(0x09, ClientTabCompletePacket::new);
register(0x0A, ClientClickWindowButtonPacket::new);
register(0x0B, ClientClickWindowPacket::new);
register(0x0C, ClientCloseWindowPacket::new);
register(0x0D, ClientPluginMessagePacket::new);
register(0x0E, ClientEditBookPacket::new);
register(0x0F, ClientQueryEntityNbtPacket::new);
register(0x10, ClientInteractEntityPacket::new);
register(0x11, ClientGenerateStructurePacket::new);
register(0x12, ClientKeepAlivePacket::new);
register(0x06, ClientStatusPacket::new);
register(0x07, ClientSettingsPacket::new);
register(0x08, ClientTabCompletePacket::new);
register(0x09, ClientClickWindowButtonPacket::new);
register(0x0A, ClientClickWindowPacket::new);
register(0x0B, ClientCloseWindowPacket::new);
register(0x0C, ClientPluginMessagePacket::new);
register(0x0D, ClientEditBookPacket::new);
register(0x0E, ClientQueryEntityNbtPacket::new);
register(0x0F, ClientInteractEntityPacket::new);
register(0x10, ClientGenerateStructurePacket::new);
register(0x11, ClientKeepAlivePacket::new);
// 0x12 packet not used server-side
register(0x14, ClientPlayerPositionPacket::new);
register(0x15, ClientPlayerPositionAndRotationPacket::new);
register(0x16, ClientPlayerRotationPacket::new);
register(0x17, ClientPlayerPacket::new);
register(0x18, ClientVehicleMovePacket::new);
register(0x19, ClientSteerBoatPacket::new);
register(0x1A, ClientPickItemPacket::new);
register(0x1B, ClientCraftRecipeRequest::new);
register(0x1C, ClientPlayerAbilitiesPacket::new);
register(0x1D, ClientPlayerDiggingPacket::new);
register(0x1E, ClientEntityActionPacket::new);
register(0x1F, ClientSteerVehiclePacket::new);
register(0x20, ClientPongPacket::new);
register(0x13, ClientPlayerPositionPacket::new);
register(0x14, ClientPlayerPositionAndRotationPacket::new);
register(0x15, ClientPlayerRotationPacket::new);
register(0x16, ClientPlayerPacket::new);
register(0x17, ClientVehicleMovePacket::new);
register(0x18, ClientSteerBoatPacket::new);
register(0x19, ClientPickItemPacket::new);
register(0x1A, ClientCraftRecipeRequest::new);
register(0x1B, ClientPlayerAbilitiesPacket::new);
register(0x1C, ClientPlayerDiggingPacket::new);
register(0x1D, ClientEntityActionPacket::new);
register(0x1E, ClientSteerVehiclePacket::new);
register(0x1F, ClientPongPacket::new);
register(0x20, ClientChatSessionUpdatePacket::new);
register(0x21, ClientSetRecipeBookStatePacket::new);
register(0x22, ClientSetDisplayedRecipePacket::new);
register(0x23, ClientNameItemPacket::new);

View File

@ -3,18 +3,13 @@ package net.minestom.server.network.packet.client.login;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import net.minestom.server.MinecraftServer;
import net.minestom.server.crypto.SaltSignaturePair;
import net.minestom.server.crypto.SignatureValidator;
import net.minestom.server.extras.MojangAuth;
import net.minestom.server.extras.mojangAuth.MojangCrypt;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPreplayPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.utils.Either;
import net.minestom.server.utils.InterfaceUtils;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.utils.crypto.KeyUtils;
import org.jetbrains.annotations.NotNull;
import javax.crypto.SecretKey;
@ -28,14 +23,14 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.UUID;
import static net.minestom.server.network.NetworkBuffer.*;
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
public record EncryptionResponsePacket(byte[] sharedSecret,
Either<byte[], SaltSignaturePair> nonceOrSignature) implements ClientPreplayPacket {
byte[] encryptedVerifyToken) implements ClientPreplayPacket {
private static final Gson GSON = new Gson();
public EncryptionResponsePacket(@NotNull NetworkBuffer reader) {
this(reader.read(BYTE_ARRAY), reader.readEither(networkBuffer -> networkBuffer.read(BYTE_ARRAY), SaltSignaturePair::new));
this(reader.read(BYTE_ARRAY), reader.read(BYTE_ARRAY));
}
@Override
@ -50,15 +45,8 @@ public record EncryptionResponsePacket(byte[] sharedSecret,
}
final boolean hasPublicKey = connection.playerPublicKey() != null;
final boolean verificationFailed = nonceOrSignature.map(
nonce -> hasPublicKey || !Arrays.equals(socketConnection.getNonce(),
MojangCrypt.decryptUsingKey(MojangAuth.getKeyPair().getPrivate(), nonce)),
signature -> !hasPublicKey || !SignatureValidator
.from(connection.playerPublicKey().publicKey(), KeyUtils.SignatureAlgorithm.SHA256withRSA)
.validate(binaryWriter -> {
binaryWriter.write(RAW_BYTES, socketConnection.getNonce());
binaryWriter.write(LONG, signature.salt());
}, signature.signature()));
final boolean verificationFailed = hasPublicKey || !Arrays.equals(socketConnection.getNonce(),
MojangCrypt.decryptUsingKey(MojangAuth.getKeyPair().getPrivate(), encryptedVerifyToken));
if (verificationFailed) {
MinecraftServer.LOGGER.error("Encryption failed for {}", loginUsername);
@ -111,8 +99,7 @@ public record EncryptionResponsePacket(byte[] sharedSecret,
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(BYTE_ARRAY, sharedSecret);
writer.writeEither(nonceOrSignature, (networkBuffer, bytes) -> networkBuffer.write(BYTE_ARRAY, bytes),
InterfaceUtils.flipBiConsumer(SaltSignaturePair::write));
writer.write(BYTE_ARRAY, encryptedVerifyToken);
}
private SecretKey getSecretKey() {

View File

@ -2,9 +2,6 @@ package net.minestom.server.network.packet.client.login;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.crypto.PlayerPublicKey;
import net.minestom.server.crypto.SignatureValidator;
import net.minestom.server.extras.MojangAuth;
import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.extras.velocity.VelocityProxy;
@ -19,45 +16,22 @@ import net.minestom.server.network.player.PlayerSocketConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import static net.minestom.server.network.NetworkBuffer.*;
import static net.minestom.server.network.NetworkBuffer.STRING;
import static net.minestom.server.network.NetworkBuffer.UUID;
public record LoginStartPacket(@NotNull String username,
@Nullable PlayerPublicKey publicKey,
@Nullable UUID profileId) implements ClientPreplayPacket {
private static final Component ALREADY_CONNECTED = Component.text("You are already on this server", NamedTextColor.RED);
public LoginStartPacket(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.readOptional(PlayerPublicKey::new), reader.readOptional(UUID));
this(reader.read(STRING), reader.readOptional(UUID));
}
@Override
public void process(@NotNull PlayerConnection connection) {
// TODO use uuid
// TODO configurable check & messages
if (publicKey != null) {
if (!SignatureValidator.YGGDRASIL.validate(binaryWriter -> {
if (profileId != null) {
binaryWriter.write(LONG, profileId.getMostSignificantBits());
binaryWriter.write(LONG, profileId.getLeastSignificantBits());
} else {
MinecraftServer.LOGGER.warn("Profile ID was null for player {}, signature will not match!", username);
}
binaryWriter.write(LONG, publicKey.expiresAt().toEpochMilli());
binaryWriter.write(RAW_BYTES, publicKey.publicKey().getEncoded());
}, publicKey.signature())) {
connection.sendPacket(new LoginDisconnectPacket(Component.text("Invalid Profile Public Key!")));
connection.disconnect();
}
if (publicKey.expiresAt().isBefore(Instant.now())) {
connection.sendPacket(new LoginDisconnectPacket(Component.text("Expired Profile Public Key!")));
connection.disconnect();
}
connection.setPlayerPublicKey(publicKey);
}
final boolean isSocketConnection = connection instanceof PlayerSocketConnection;
// Proxy support (only for socket clients) and cache the login username
if (isSocketConnection) {

View File

@ -1,17 +1,18 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.crypto.LastSeenMessages;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
public record ClientChatAckPacket(@NotNull LastSeenMessages.Update update) implements ClientPacket {
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record ClientChatAckPacket(int offset) implements ClientPacket {
public ClientChatAckPacket(@NotNull NetworkBuffer reader) {
this(new LastSeenMessages.Update(reader));
this(reader.read(VAR_INT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(update);
writer.write(VAR_INT, offset);
}
}

View File

@ -1,37 +1,42 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.crypto.LastSeenMessages;
import net.kyori.adventure.text.Component;
import net.minestom.server.crypto.ChatBound;
import net.minestom.server.crypto.FilterMask;
import net.minestom.server.crypto.MessageSignature;
import net.minestom.server.crypto.SignedMessageBody;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.*;
import java.util.UUID;
public record ClientChatMessagePacket(@NotNull String message,
long timestamp, long salt, @NotNull MessageSignature signature,
boolean signedPreview,
@NotNull LastSeenMessages.Update lastSeenMessages) implements ClientPacket {
public ClientChatMessagePacket {
if (message.length() > 256) {
throw new IllegalArgumentException("Message cannot be more than 256 characters long.");
}
}
import static net.minestom.server.network.NetworkBuffer.COMPONENT;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record ClientChatMessagePacket(@NotNull UUID sender, int index,
@Nullable MessageSignature signature,
SignedMessageBody.@NotNull Packed body,
@Nullable Component unsignedContent, FilterMask filterMask,
@NotNull ChatBound chatBound) implements ClientPacket {
public ClientChatMessagePacket(@NotNull NetworkBuffer reader) {
this(reader.read(STRING),
reader.read(LONG), reader.read(LONG), new MessageSignature(reader),
reader.read(BOOLEAN),
new LastSeenMessages.Update(reader));
this(reader.read(NetworkBuffer.UUID), reader.read(VAR_INT),
reader.readOptional(MessageSignature::new),
new SignedMessageBody.Packed(reader),
reader.readOptional(COMPONENT), new FilterMask(reader),
new ChatBound(reader));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, message);
writer.write(LONG, timestamp);
writer.write(LONG, salt);
writer.write(signature);
writer.write(BOOLEAN, signedPreview);
writer.write(lastSeenMessages);
writer.write(NetworkBuffer.UUID, sender);
writer.write(VAR_INT, index);
writer.writeOptional(signature);
writer.write(body);
writer.writeOptional(COMPONENT, unsignedContent);
writer.write(filterMask);
writer.write(chatBound);
}
}

View File

@ -1,26 +0,0 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.INT;
import static net.minestom.server.network.NetworkBuffer.STRING;
public record ClientChatPreviewPacket(int queryId, @NotNull String query) implements ClientPacket {
public ClientChatPreviewPacket {
if (query.length() > 256) {
throw new IllegalArgumentException("Query length cannot be greater than 256");
}
}
public ClientChatPreviewPacket(@NotNull NetworkBuffer reader) {
this(reader.read(INT), reader.read(STRING));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(INT, queryId);
writer.write(STRING, query);
}
}

View File

@ -0,0 +1,22 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.crypto.PlayerPublicKey;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public record ClientChatSessionUpdatePacket(@NotNull UUID sessionId,
@Nullable PlayerPublicKey publicKey) implements ClientPacket {
public ClientChatSessionUpdatePacket(@NotNull NetworkBuffer reader) {
this(reader.read(NetworkBuffer.UUID), new PlayerPublicKey(reader));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.UUID, sessionId);
writer.writeOptional(publicKey);
}
}

View File

@ -6,11 +6,11 @@ import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.*;
import static net.minestom.server.network.NetworkBuffer.LONG;
import static net.minestom.server.network.NetworkBuffer.STRING;
public record ClientCommandChatPacket(@NotNull String message, long timestamp,
long salt, @NotNull ArgumentSignatures signatures,
boolean signedPreview,
LastSeenMessages.@NotNull Update lastSeenMessages) implements ClientPacket {
public ClientCommandChatPacket {
if (message.length() > 256) {
@ -20,7 +20,8 @@ public record ClientCommandChatPacket(@NotNull String message, long timestamp,
public ClientCommandChatPacket(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.read(LONG),
reader.read(LONG), new ArgumentSignatures(reader), reader.read(BOOLEAN), new LastSeenMessages.Update(reader));
reader.read(LONG), new ArgumentSignatures(reader),
new LastSeenMessages.Update(reader));
}
@Override
@ -29,7 +30,6 @@ public record ClientCommandChatPacket(@NotNull String message, long timestamp,
writer.write(LONG, timestamp);
writer.write(LONG, salt);
writer.write(signatures);
writer.write(BOOLEAN, signedPreview);
writer.write(lastSeenMessages);
}
}

View File

@ -23,7 +23,6 @@ public final class ServerPacketIdentifier {
public static final int BLOCK_CHANGE = nextPlayId();
public static final int BOSS_BAR = nextPlayId();
public static final int SERVER_DIFFICULTY = nextPlayId();
public static final int CHAT_PREVIEW = nextPlayId();
public static final int CLEAR_TITLES = nextPlayId();
public static final int TAB_COMPLETE = nextPlayId();
public static final int DECLARE_COMMANDS = nextPlayId();
@ -34,9 +33,9 @@ public final class ServerPacketIdentifier {
public static final int SET_COOLDOWN = nextPlayId();
public static final int CUSTOM_CHAT_COMPLETIONS = nextPlayId();
public static final int PLUGIN_MESSAGE = nextPlayId();
public static final int NAMED_SOUND_EFFECT = nextPlayId();
public static final int DELETE_CHAT_MESSAGE = nextPlayId();
public static final int DISCONNECT = nextPlayId();
public static final int DISGUISED_CHAT = nextPlayId();
public static final int ENTITY_STATUS = nextPlayId();
public static final int EXPLOSION = nextPlayId();
public static final int UNLOAD_CHUNK = nextPlayId();
@ -61,12 +60,12 @@ public final class ServerPacketIdentifier {
public static final int PING = nextPlayId();
public static final int CRAFT_RECIPE_RESPONSE = nextPlayId();
public static final int PLAYER_ABILITIES = nextPlayId();
public static final int PLAYER_CHAT_HEADER = nextPlayId();
public static final int PLAYER_CHAT = nextPlayId();
public static final int END_COMBAT_EVENT = nextPlayId();
public static final int ENTER_COMBAT_EVENT = nextPlayId();
public static final int DEATH_COMBAT_EVENT = nextPlayId();
public static final int PLAYER_INFO = nextPlayId();
public static final int PLAYER_INFO_REMOVE = nextPlayId();
public static final int PLAYER_INFO_UPDATE = nextPlayId();
public static final int FACE_PLAYER = nextPlayId();
public static final int PLAYER_POSITION_AND_LOOK = nextPlayId();
public static final int UNLOCK_RECIPES = nextPlayId();
@ -89,7 +88,6 @@ public final class ServerPacketIdentifier {
public static final int UPDATE_VIEW_POSITION = nextPlayId();
public static final int UPDATE_VIEW_DISTANCE = nextPlayId(); // Not used by the dedicated server
public static final int SPAWN_POSITION = nextPlayId();
public static final int SET_DISPLAY_CHAT_PREVIEW = nextPlayId();
public static final int DISPLAY_SCOREBOARD = nextPlayId();
public static final int ENTITY_METADATA = nextPlayId();
public static final int ATTACH_ENTITY = nextPlayId();
@ -116,6 +114,7 @@ public final class ServerPacketIdentifier {
public static final int ENTITY_TELEPORT = nextPlayId();
public static final int ADVANCEMENTS = nextPlayId();
public static final int ENTITY_PROPERTIES = nextPlayId();
public static final int UPDATE_ENABLED_FEATURES = nextPlayId();
public static final int ENTITY_EFFECT = nextPlayId();
public static final int DECLARE_RECIPES = nextPlayId();
public static final int TAGS = nextPlayId();

View File

@ -1,42 +0,0 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.NetworkBuffer.*;
public record ChatPreviewPacket(int queryId, @Nullable Component preview) implements ComponentHoldingServerPacket {
public ChatPreviewPacket(@NotNull NetworkBuffer reader) {
this(reader.read(INT), reader.readOptional(COMPONENT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(INT, queryId);
writer.writeOptional(COMPONENT, preview);
}
@Override
public int getId() {
return ServerPacketIdentifier.CHAT_PREVIEW;
}
@Override
public @NotNull Collection<Component> components() {
return List.of(preview);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new ChatPreviewPacket(queryId, operator.apply(preview));
}
}

View File

@ -7,19 +7,19 @@ import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.*;
public record ExplosionPacket(float x, float y, float z, float radius, byte @NotNull [] records,
public record ExplosionPacket(double x, double y, double z, float radius, byte @NotNull [] records,
float playerMotionX, float playerMotionY, float playerMotionZ) implements ServerPacket {
public ExplosionPacket(@NotNull NetworkBuffer reader) {
this(reader.read(FLOAT), reader.read(FLOAT), reader.read(FLOAT),
this(reader.read(DOUBLE), reader.read(DOUBLE), reader.read(DOUBLE),
reader.read(FLOAT), reader.readBytes(reader.read(VAR_INT) * 3),
reader.read(FLOAT), reader.read(FLOAT), reader.read(FLOAT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(FLOAT, x);
writer.write(FLOAT, y);
writer.write(FLOAT, z);
writer.write(DOUBLE, x);
writer.write(DOUBLE, y);
writer.write(DOUBLE, z);
writer.write(FLOAT, radius);
writer.write(VAR_INT, records.length / 3); // each record is 3 bytes long
writer.write(RAW_BYTES, records);

View File

@ -31,6 +31,6 @@ public record NamedSoundEffectPacket(String soundName, Source source, int x, int
@Override
public int getId() {
return ServerPacketIdentifier.NAMED_SOUND_EFFECT;
return 0;
}
}

View File

@ -1,29 +0,0 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.crypto.MessageSignature;
import net.minestom.server.crypto.SignedMessageHeader;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
public record PlayerChatHeaderPacket(@NotNull SignedMessageHeader messageHeader, @NotNull MessageSignature signature,
byte[] bodyDigest) implements ServerPacket {
public PlayerChatHeaderPacket(@NotNull NetworkBuffer reader) {
this(new SignedMessageHeader(reader), new MessageSignature(reader), reader.read(BYTE_ARRAY));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(messageHeader);
writer.write(signature);
writer.write(BYTE_ARRAY, bodyDigest);
}
@Override
public int getId() {
return ServerPacketIdentifier.PLAYER_CHAT_HEADER;
}
}

View File

@ -1,242 +0,0 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.ComponentHolder;
import net.minestom.server.crypto.PlayerPublicKey;
import net.minestom.server.entity.GameMode;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.NetworkBuffer.*;
public record PlayerInfoPacket(@NotNull Action action,
@NotNull List<Entry> entries) implements ComponentHoldingServerPacket {
public PlayerInfoPacket {
entries = List.copyOf(entries);
for (Entry entry : entries) {
if (!entry.getClass().equals(action.getClazz()))
throw new IllegalArgumentException("Invalid entry class for action " + action);
}
}
public PlayerInfoPacket(@NotNull Action action, @NotNull Entry entry) {
this(action, List.of(entry));
}
public PlayerInfoPacket(@NotNull NetworkBuffer reader) {
this(read(reader));
}
private PlayerInfoPacket(PlayerInfoPacket packet) {
this(packet.action, packet.entries);
}
private static PlayerInfoPacket read(@NotNull NetworkBuffer reader) {
var action = Action.values()[reader.read(VAR_INT)];
final int playerInfoCount = reader.read(VAR_INT);
List<Entry> entries = new ArrayList<>(playerInfoCount);
for (int i = 0; i < playerInfoCount; i++) {
final UUID uuid = reader.read(UUID);
entries.add(switch (action) {
case ADD_PLAYER -> new AddPlayer(uuid, reader);
case UPDATE_GAMEMODE -> new UpdateGameMode(uuid, reader);
case UPDATE_LATENCY -> new UpdateLatency(uuid, reader);
case UPDATE_DISPLAY_NAME -> new UpdateDisplayName(uuid, reader);
case REMOVE_PLAYER -> new RemovePlayer(uuid);
});
}
return new PlayerInfoPacket(action, entries);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, action.ordinal());
writer.writeCollection(entries, (w, entry) -> {
w.write(UUID, entry.uuid());
entry.write(w);
});
}
@Override
public int getId() {
return ServerPacketIdentifier.PLAYER_INFO;
}
@Override
public @NotNull Collection<Component> components() {
switch (this.action) {
case ADD_PLAYER, UPDATE_DISPLAY_NAME -> {
List<Component> components = new ArrayList<>();
for (Entry entry : entries) {
if (entry instanceof ComponentHolder) {
components.addAll(((ComponentHolder<? extends Entry>) entry).components());
}
}
return components;
}
default -> {
return List.of();
}
}
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return switch (action) {
case ADD_PLAYER, UPDATE_DISPLAY_NAME -> {
List<Entry> entries = new ArrayList<>(this.entries.size());
for (Entry entry : this.entries) {
if (entry instanceof ComponentHolder) {
entries.add(((ComponentHolder<? extends Entry>) entry).copyWithOperator(operator));
} else {
entries.add(entry);
}
}
yield new PlayerInfoPacket(action, entries);
}
default -> this;
};
}
public enum Action {
ADD_PLAYER(AddPlayer.class),
UPDATE_GAMEMODE(UpdateGameMode.class),
UPDATE_LATENCY(UpdateLatency.class),
UPDATE_DISPLAY_NAME(UpdateDisplayName.class),
REMOVE_PLAYER(RemovePlayer.class);
private final Class<? extends Entry> clazz;
Action(Class<? extends Entry> clazz) {
this.clazz = clazz;
}
@NotNull
public Class<? extends Entry> getClazz() {
return clazz;
}
}
public sealed interface Entry extends NetworkBuffer.Writer
permits AddPlayer, UpdateGameMode, UpdateLatency, UpdateDisplayName, RemovePlayer {
UUID uuid();
}
public record AddPlayer(UUID uuid, String name, List<Property> properties, GameMode gameMode, int ping,
@Nullable Component displayName,
@Nullable PlayerPublicKey playerPublicKey) implements Entry, ComponentHolder<AddPlayer> {
public AddPlayer {
properties = List.copyOf(properties);
}
public AddPlayer(UUID uuid, NetworkBuffer reader) {
this(uuid, reader.read(STRING),
reader.readCollection(Property::new),
GameMode.values()[reader.read(VAR_INT)], reader.read(VAR_INT),
reader.read(BOOLEAN) ? reader.read(COMPONENT) : null,
reader.read(BOOLEAN) ? new PlayerPublicKey(reader) : null);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, name);
writer.writeCollection(properties);
writer.write(VAR_INT, (int) gameMode.id());
writer.write(VAR_INT, ping);
writer.writeOptional(COMPONENT, displayName);
writer.writeOptional(playerPublicKey);
}
@Override
public @NotNull Collection<Component> components() {
return displayName != null ? List.of(displayName) : List.of();
}
@Override
public @NotNull AddPlayer copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return displayName != null ?
new AddPlayer(uuid, name, properties, gameMode, ping, operator.apply(displayName), playerPublicKey) : this;
}
public record Property(@NotNull String name, @NotNull String value,
@Nullable String signature) implements NetworkBuffer.Writer {
public Property(String name, String value) {
this(name, value, null);
}
public Property(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.read(STRING),
reader.read(BOOLEAN) ? reader.read(STRING) : null);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, name);
writer.write(STRING, value);
writer.write(BOOLEAN, signature != null);
if (signature != null) writer.write(STRING, signature);
}
}
}
public record UpdateGameMode(UUID uuid, GameMode gameMode) implements Entry {
public UpdateGameMode(UUID uuid, NetworkBuffer reader) {
this(uuid, GameMode.fromId(reader.read(VAR_INT).byteValue()));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, (int) gameMode.id());
}
}
public record UpdateLatency(UUID uuid, int ping) implements Entry {
public UpdateLatency(UUID uuid, NetworkBuffer reader) {
this(uuid, reader.read(VAR_INT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, ping);
}
}
public record UpdateDisplayName(@NotNull UUID uuid,
@Nullable Component displayName) implements Entry, ComponentHolder<UpdateDisplayName> {
public UpdateDisplayName(UUID uuid, NetworkBuffer reader) {
this(uuid, reader.read(BOOLEAN) ? reader.read(COMPONENT) : null);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(BOOLEAN, displayName != null);
if (displayName != null) writer.write(COMPONENT, displayName);
}
@Override
public @NotNull Collection<Component> components() {
return displayName != null ? List.of(displayName) : List.of();
}
@Override
public @NotNull UpdateDisplayName copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return displayName != null ? new UpdateDisplayName(uuid, operator.apply(displayName)) : this;
}
}
public record RemovePlayer(@NotNull UUID uuid) implements Entry {
@Override
public void write(@NotNull NetworkBuffer writer) {
}
}
}

View File

@ -0,0 +1,29 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.UUID;
public record PlayerInfoRemovePacket(@NotNull List<@NotNull UUID> uuids) implements ServerPacket {
public PlayerInfoRemovePacket {
uuids = List.copyOf(uuids);
}
public PlayerInfoRemovePacket(@NotNull NetworkBuffer reader) {
this(reader.readCollection(NetworkBuffer.UUID));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeCollection(NetworkBuffer.UUID, uuids);
}
@Override
public int getId() {
return ServerPacketIdentifier.PLAYER_INFO_REMOVE;
}
}

View File

@ -0,0 +1,88 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.GameMode;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import static net.minestom.server.network.NetworkBuffer.*;
public record PlayerInfoUpdatePacket(@NotNull EnumSet<@NotNull Action> actions,
@NotNull List<@NotNull Entry> entries) implements ServerPacket {
public PlayerInfoUpdatePacket {
actions = EnumSet.copyOf(actions);
entries = List.copyOf(entries);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeEnumSet(actions, Action.class);
writer.writeCollection(entries, (buffer, entry) -> {
buffer.write(NetworkBuffer.UUID, entry.uuid);
for (Action action : actions) {
action.writer.write(buffer, entry);
}
});
}
@Override
public int getId() {
return ServerPacketIdentifier.PLAYER_INFO_UPDATE;
}
public record Entry(UUID uuid, String username, List<Property> properties,
boolean listed, int latency, GameMode gameMode,
@Nullable Component displayName) {
public Entry {
properties = List.copyOf(properties);
}
}
public record Property(@NotNull String name, @NotNull String value,
@Nullable String signature) implements NetworkBuffer.Writer {
public Property(String name, String value) {
this(name, value, null);
}
public Property(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), reader.read(STRING),
reader.readOptional(STRING));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, name);
writer.write(STRING, value);
writer.writeOptional(STRING, signature);
}
}
public enum Action {
ADD_PLAYER((writer, entry) -> {
writer.write(STRING, entry.username);
writer.writeCollection(entry.properties);
}),
INITIALIZE_CHAT(null),
UPDATE_GAME_MODE((writer, entry) -> writer.write(VAR_INT, entry.gameMode.ordinal())),
UPDATE_LISTED((writer, entry) -> writer.write(BOOLEAN, entry.listed)),
UPDATE_LATENCY((writer, entry) -> writer.write(VAR_INT, entry.latency)),
UPDATE_DISPLAY_NAME((writer, entry) -> writer.writeOptional(COMPONENT, entry.displayName));
final Writer writer;
Action(Writer writer) {
this.writer = writer;
}
interface Writer {
void write(NetworkBuffer writer, Entry entry);
}
}
}

View File

@ -10,17 +10,16 @@ import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.*;
public record ServerDataPacket(@Nullable Component motd, @Nullable String iconBase64,
boolean previewsChat, boolean enforcesSecureChat) implements ServerPacket {
boolean enforcesSecureChat) implements ServerPacket {
public ServerDataPacket(@NotNull NetworkBuffer reader) {
this(reader.readOptional(COMPONENT), reader.readOptional(STRING),
reader.read(BOOLEAN), reader.read(BOOLEAN));
reader.read(BOOLEAN));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.writeOptional(COMPONENT, this.motd);
writer.writeOptional(STRING, this.iconBase64);
writer.write(BOOLEAN, previewsChat);
writer.write(BOOLEAN, enforcesSecureChat);
}

View File

@ -1,24 +0,0 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
public record SetChatPreviewPacket(boolean enable) implements ServerPacket {
public SetChatPreviewPacket(@NotNull NetworkBuffer reader) {
this(reader.read(BOOLEAN));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(BOOLEAN, enable);
}
@Override
public int getId() {
return ServerPacketIdentifier.SET_DISPLAY_CHAT_PREVIEW;
}
}