hollow-cube/1.19.3

Signed-off-by: mworzala <mattheworzala@gmail.com>

fix sounds v2

(cherry picked from commit e6d4a2cc919d6ab5aa9402cc871a70535069d803)

fix command packet

(cherry picked from commit f3efd64f7bd1d0473e0899d202268f77bab35abf)

fix sound effect packets

(cherry picked from commit 8530b37b354129a2149aafe2820183b28766be00)

remove named sound effect & fix sound effect, entity sound effect packet is still wrong

(cherry picked from commit 612f6065a12c465c07816c8551b1c44f17c21159)

update NamedSoundEffectPacket to 1.19.3

(cherry picked from commit 8c78d9beac96f94770f6fd0e9216452c3421bcfd)

update datagen, add read method to player info update (though it seems kinda broken)

(cherry picked from commit 6464a72dabc5edaf9b09ef1b8100815965bbad74)

Add ChatSession

Signed-off-by: TheMode <themode@outlook.fr>
(cherry picked from commit 0488915fda)

Unnecessary line change

Signed-off-by: TheMode <themode@outlook.fr>
(cherry picked from commit 915836f490)

Make tests compile

Signed-off-by: TheMode <themode@outlook.fr>
(cherry picked from commit 220217fcc1)

Fix info update

Signed-off-by: TheMode <themode@outlook.fr>

(cherry picked from commit 1a606285c0)

Fix unsigned message

Signed-off-by: TheMode <themode@outlook.fr>
(cherry picked from commit 7ba55fdfef)

1.19.3 support

(cherry picked from commit f09fdd862b)
This commit is contained in:
themode 2022-10-20 03:53:01 +02:00 committed by mworzala
parent 79ce9570ea
commit fe9a4291bf
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
39 changed files with 736 additions and 629 deletions

View File

@ -17,6 +17,8 @@ application {
dependencies {
implementation(rootProject)
implementation(libs.jNoise)
implementation("ch.qos.logback:logback-core:1.4.5")
implementation("ch.qos.logback:logback-classic:1.4.5")
}
tasks.withType<ShadowJar> {

View File

@ -1,5 +1,6 @@
package net.minestom.demo;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
@ -8,6 +9,7 @@ import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.demo.commands.*;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
import net.minestom.server.event.player.PlayerChatEvent;
import net.minestom.server.event.server.ServerListPingEvent;
import net.minestom.server.extras.lan.OpenToLAN;
import net.minestom.server.extras.lan.OpenToLANConfig;
@ -15,6 +17,7 @@ import net.minestom.server.extras.optifine.OptifineSupport;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule;
import net.minestom.server.ping.ResponseData;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.utils.identity.NamedAndIdentified;
import net.minestom.server.utils.time.TimeUnit;
@ -96,6 +99,27 @@ public class Main {
//responseData.setPlayersHidden(true);
});
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, e -> {
var playPos = e.getPlayer().getPosition().add(5.0, 0.0, 0.0);
switch (e.getMessage()) {
case "a" -> {
System.out.println("with position");
e.getPlayer().playSound(Sound.sound(SoundEvent.AMBIENT_CRIMSON_FOREST_MOOD, Sound.Source.MASTER, 1f, 1f), playPos.x(), playPos.y(), playPos.z());
}
case "b" -> {
System.out.println("without anything");
e.getPlayer().playSound(Sound.sound(SoundEvent.AMBIENT_CRIMSON_FOREST_MOOD, Sound.Source.MASTER, 1f, 1f));
}
case "c" -> {
System.out.println("with self emitter");
e.getPlayer().playSound(Sound.sound(SoundEvent.AMBIENT_CRIMSON_FOREST_MOOD, Sound.Source.MASTER, 1f, 1f), Sound.Emitter.self());
}
}
});
PlayerInit.init();
OptifineSupport.enable();

View File

@ -1,10 +1,14 @@
package net.minestom.demo.commands;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.sound.SoundEvent;
public class TestCommand extends Command {
@ -15,7 +19,12 @@ public class TestCommand extends Command {
var block = ArgumentType.BlockState("block");
block.setCallback((sender, exception) -> exception.printStackTrace());
setDefaultExecutor((sender, context) -> {
sender.playSound(Sound.sound(Key.key("item.trumpet.doot"), Sound.Source.PLAYER, 1, 1));
AdventurePacketConvertor.createSoundPacket(Sound.sound(Key.key(SoundEvent.BLOCK_ANVIL_HIT.name()), Sound.Source.HOSTILE, 1, 1), sender.asPlayer());
});
addSyntax((sender, context) -> System.out.println("executed"), block);
}
private void usage(CommandSender sender, CommandContext context) {

View File

@ -7,7 +7,7 @@ adventure = "4.12.0"
kotlin = "1.7.22"
hydrazine = "1.7.2"
dependencyGetter = "v1.0.1"
minestomData = "1c1921cd41"
minestomData = "53e0da5be1"
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

@ -11,6 +11,7 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.Title;
import net.kyori.adventure.title.TitlePart;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.*;
@ -111,11 +112,11 @@ public class AdventurePacketConvertor {
public static @NotNull ServerPacket createSoundPacket(@NotNull Sound sound, double x, double y, double z) {
final SoundEvent minestomSound = SoundEvent.fromNamespaceId(sound.name().asString());
if (minestomSound == null) {
return new NamedSoundEffectPacket(sound.name().asString(), sound.source(),
(int) x, (int) y, (int) z, sound.volume(), sound.pitch(), 0);
return new SoundEffectPacket(sound.name().asString(), null, sound.source(),
new Vec(x, y, z), sound.volume(), sound.pitch(), 0);
} else {
return new SoundEffectPacket(minestomSound.id(), sound.source(),
(int) x, (int) y, (int) z, sound.volume(), sound.pitch(), 0);
return new SoundEffectPacket(minestomSound, null, sound.source(),
new Vec(x, y, z), sound.volume(), sound.pitch(), 0);
}
}
@ -135,11 +136,9 @@ public class AdventurePacketConvertor {
final SoundEvent minestomSound = SoundEvent.fromNamespaceId(sound.name().asString());
if (minestomSound != null) {
return new EntitySoundEffectPacket(minestomSound.id(), sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), 0);
return new EntitySoundEffectPacket(minestomSound, null, sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), 0);
} else {
final Pos pos = entity.getPosition();
return new NamedSoundEffectPacket(sound.name().asString(), sound.source(),
(int) pos.x(), (int) pos.y(), (int) pos.z(), sound.volume(), sound.pitch(), 0);
return new EntitySoundEffectPacket(sound.name().asString(), null, sound.source(), entity.getEntityId(), sound.volume(), sound.pitch(), 0);
}
}

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,18 @@
package net.minestom.server.crypto;
import net.minestom.server.network.NetworkBuffer;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public record ChatSession(@NotNull UUID sessionId, @NotNull PlayerPublicKey publicKey) implements NetworkBuffer.Writer {
public ChatSession(@NotNull NetworkBuffer reader) {
this(reader.read(NetworkBuffer.UUID), new PlayerPublicKey(reader));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(NetworkBuffer.UUID, sessionId);
writer.write(publicKey);
}
}

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), reader.readFixedBitSet(20));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(lastSeen);
writer.writeOptional(lastReceived);
writer.write(VAR_INT, offset);
writer.writeFixedBitSet(acknowledged, 20);
}
}
}

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

@ -374,8 +374,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
// TODO: separate living entity categories
soundCategory = Source.HOSTILE;
}
sendPacketToViewersAndSelf(new SoundEffectPacket(sound, soundCategory,
getPosition(), 1.0f, 1.0f));
sendPacketToViewersAndSelf(new SoundEffectPacket(sound, null, soundCategory,
getPosition(), 1.0f, 1.0f, 0));
}
});

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

@ -452,7 +452,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
refreshHealth();
sendPacket(new RespawnPacket(getDimensionType().toString(), getDimensionType().getName().asString(),
0, gameMode, gameMode, false, levelFlat, true, deathLocation));
0, gameMode, gameMode, false, levelFlat, true, deathLocation));
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(this);
EventDispatcher.call(respawnEvent);
@ -944,8 +944,7 @@ 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)));
PacketUtils.broadcastPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, infoEntry()));
}
/**
@ -973,8 +972,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(), getDimensionType().getName().asString(),
0, gameMode, gameMode, false, levelFlat, true, deathLocation);
@ -1308,8 +1307,7 @@ 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)));
PacketUtils.broadcastPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, infoEntry()));
}
// The client updates their abilities based on the GameMode as follows
@ -1809,8 +1807,7 @@ 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)));
PacketUtils.broadcastPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY, infoEntry()));
}
public void refreshOnGround(boolean onGround) {
@ -1928,24 +1925,29 @@ 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() {
final PlayerSkin skin = this.skin;
List<PlayerInfoPacket.AddPlayer.Property> prop = skin != null ?
List.of(new PlayerInfoPacket.AddPlayer.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));
protected @NotNull PlayerInfoUpdatePacket getAddPlayerToList() {
return new PlayerInfoUpdatePacket(EnumSet.of(PlayerInfoUpdatePacket.Action.ADD_PLAYER, PlayerInfoUpdatePacket.Action.UPDATE_LISTED),
List.of(infoEntry()));
}
/**
* 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(getUuid());
}
private PlayerInfoUpdatePacket.Entry infoEntry() {
final PlayerSkin skin = this.skin;
List<PlayerInfoUpdatePacket.Property> prop = skin != null ?
List.of(new PlayerInfoUpdatePacket.Property("textures", skin.textures(), skin.signature())) :
List.of();
return new PlayerInfoUpdatePacket.Entry(getUuid(), getUsername(), prop,
true, getLatency(), getGameMode(), displayName, null);
}
/**

View File

@ -16,9 +16,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;
@ -204,6 +202,43 @@ 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);
}
public <E extends Enum<E>> @NotNull EnumSet<E> readEnumSet(Class<E> enumType) {
final E[] values = enumType.getEnumConstants();
BitSet bitSet = readFixedBitSet(values.length);
EnumSet<E> enumSet = EnumSet.noneOf(enumType);
for (int i = 0; i < values.length; ++i) {
if (bitSet.get(i)) {
enumSet.add(values[i]);
}
}
return enumSet;
}
public 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);
}
}
@NotNull
public BitSet readFixedBitSet(int length) {
final byte[] array = readBytes((length + 7) / 8);
return BitSet.valueOf(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,28 +1,23 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.crypto.LastSeenMessages;
import net.minestom.server.crypto.MessageSignature;
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.Arrays;
import java.util.BitSet;
import static net.minestom.server.network.NetworkBuffer.*;
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.");
}
}
public record ClientChatMessagePacket(String message, long timestamp,
long salt, byte @Nullable [] signature,
int ackOffset, BitSet ackList) 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(STRING), reader.read(LONG),
reader.read(LONG), reader.readOptional(r -> r.readBytes(256)),
reader.read(VAR_INT), BitSet.valueOf(reader.readBytes(3)));
}
@Override
@ -30,8 +25,8 @@ public record ClientChatMessagePacket(@NotNull String message,
writer.write(STRING, message);
writer.write(LONG, timestamp);
writer.write(LONG, salt);
writer.write(signature);
writer.write(BOOLEAN, signedPreview);
writer.write(lastSeenMessages);
writer.writeOptional(BYTE_ARRAY, signature);
writer.write(VAR_INT, ackOffset);
writer.write(RAW_BYTES, Arrays.copyOf(ackList.toByteArray(), 3));
}
}

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,17 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.crypto.ChatSession;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPacket;
import org.jetbrains.annotations.NotNull;
public record ClientChatSessionUpdatePacket(@NotNull ChatSession chatSession) implements ClientPacket {
public ClientChatSessionUpdatePacket(@NotNull NetworkBuffer reader) {
this(new ChatSession(reader));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(chatSession);
}
}

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

@ -2,23 +2,86 @@ package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.sound.Sound;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.coordinate.Point;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.*;
public record EntitySoundEffectPacket(int soundId, Sound.Source source, int entityId,
float volume, float pitch, long seed) implements ServerPacket {
public record EntitySoundEffectPacket(
// only one of soundEvent and soundName may be present
@Nullable SoundEvent soundEvent,
@Nullable String soundName,
@Nullable Float range, // Only allowed with soundName
@NotNull Sound.Source source,
int entityId,
float volume,
float pitch,
long seed
) implements ServerPacket {
public EntitySoundEffectPacket {
Check.argCondition(soundEvent == null && soundName == null, "soundEvent and soundName cannot both be null");
Check.argCondition(soundEvent != null && soundName != null, "soundEvent and soundName cannot both be present");
}
public EntitySoundEffectPacket(@NotNull SoundEvent soundEvent, @Nullable Float range, @NotNull Sound.Source source,
int entityId, float volume, float pitch, long seed) {
this(soundEvent, null, range, source, entityId, volume, pitch, seed);
}
public EntitySoundEffectPacket(@NotNull String soundName, @Nullable Float range, @NotNull Sound.Source source,
int entityId, float volume, float pitch, long seed) {
this(null, soundName, range, source, entityId, volume, pitch, seed);
}
public EntitySoundEffectPacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), Sound.Source.values()[reader.read(VAR_INT)], reader.read(VAR_INT),
reader.read(FLOAT), reader.read(FLOAT), reader.read(LONG));
this(fromReader(reader));
}
private EntitySoundEffectPacket(@NotNull EntitySoundEffectPacket packet) {
this(packet.soundEvent, packet.soundName, packet.range, packet.source, packet.entityId, packet.volume, packet.pitch, packet.seed);
}
private static @NotNull EntitySoundEffectPacket fromReader(@NotNull NetworkBuffer reader) {
int soundId = reader.read(VAR_INT);
SoundEvent soundEvent;
String soundName;
Float range = null;
if (soundId == 0) {
soundEvent = null;
soundName = reader.read(STRING);
range = reader.readOptional(FLOAT);
} else {
soundEvent = SoundEvent.fromId(soundId - 1);
soundName = null;
}
return new EntitySoundEffectPacket(
soundEvent,
soundName,
range,
reader.readEnum(Sound.Source.class),
reader.read(VAR_INT),
reader.read(FLOAT),
reader.read(FLOAT),
reader.read(LONG)
);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, soundId);
if (soundEvent != null) {
writer.write(VAR_INT, soundEvent.id() + 1);
} else {
writer.write(VAR_INT, 0);
writer.write(STRING, soundName);
writer.writeOptional(FLOAT, null);
}
writer.write(VAR_INT, AdventurePacketConvertor.getSoundSourceValue(source));
writer.write(VAR_INT, entityId);
writer.write(FLOAT, volume);

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

@ -1,36 +0,0 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.sound.Sound.Source;
import net.minestom.server.adventure.AdventurePacketConvertor;
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.*;
public record NamedSoundEffectPacket(String soundName, Source source, int x, int y, int z,
float volume, float pitch, long seed) implements ServerPacket {
public NamedSoundEffectPacket(@NotNull NetworkBuffer reader) {
this(reader.read(STRING), Source.values()[reader.read(VAR_INT)],
reader.read(INT) / 8, reader.read(INT) / 8, reader.read(INT) / 8,
reader.read(FLOAT), reader.read(FLOAT), reader.read(LONG));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(STRING, soundName);
writer.write(VAR_INT, AdventurePacketConvertor.getSoundSourceValue(source));
writer.write(INT, x * 8);
writer.write(INT, y * 8);
writer.write(INT, z * 8);
writer.write(FLOAT, volume);
writer.write(FLOAT, pitch);
writer.write(LONG, seed);
}
@Override
public int getId() {
return ServerPacketIdentifier.NAMED_SOUND_EFFECT;
}
}

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,7 +1,8 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.crypto.MessageSignature;
import net.minestom.server.crypto.FilterMask;
import net.minestom.server.crypto.SignedMessageBody;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ComponentHoldingServerPacket;
import net.minestom.server.network.packet.server.ServerPacket;
@ -9,6 +10,7 @@ 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;
@ -19,26 +21,30 @@ import static net.minestom.server.network.NetworkBuffer.*;
/**
* Represents an outgoing chat message packet.
*/
public record PlayerChatMessagePacket(@NotNull Component signedContent, @Nullable Component unsignedContent,
int type, @NotNull UUID uuid,
@NotNull Component displayName, @Nullable Component teamDisplayName,
@NotNull MessageSignature signature) implements ComponentHoldingServerPacket {
public record PlayerChatMessagePacket(UUID sender, int index, byte @Nullable [] signature,
SignedMessageBody.@NotNull Packed messageBody,
@Nullable Component unsignedContent, FilterMask filterMask,
int msgTypeId, Component msgTypeName,
@Nullable Component msgTypeTarget) implements ComponentHoldingServerPacket {
public PlayerChatMessagePacket(@NotNull NetworkBuffer reader) {
this(reader.read(COMPONENT), reader.readOptional(COMPONENT),
reader.read(VAR_INT), reader.read(NetworkBuffer.UUID),
reader.read(COMPONENT), reader.readOptional(COMPONENT),
new MessageSignature(reader));
this(reader.read(UUID), reader.read(VAR_INT), reader.readOptional(r -> r.readBytes(256)),
new SignedMessageBody.Packed(reader),
reader.readOptional(COMPONENT), new FilterMask(reader),
reader.read(VAR_INT), reader.read(COMPONENT),
reader.readOptional(COMPONENT));
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(COMPONENT, signedContent);
writer.write(UUID, sender);
writer.write(VAR_INT, index);
writer.writeOptional(RAW_BYTES, signature);
writer.write(messageBody);
writer.writeOptional(COMPONENT, unsignedContent);
writer.write(VAR_INT, type);
writer.write(UUID, uuid);
writer.write(COMPONENT, displayName);
writer.writeOptional(COMPONENT, teamDisplayName);
writer.write(signature);
writer.write(filterMask);
writer.write(VAR_INT, msgTypeId);
writer.write(COMPONENT, msgTypeName);
writer.writeOptional(COMPONENT, msgTypeTarget);
}
@Override
@ -48,12 +54,19 @@ public record PlayerChatMessagePacket(@NotNull Component signedContent, @Nullabl
@Override
public @NotNull Collection<Component> components() {
return List.of(signedContent);
final ArrayList<Component> list = new ArrayList<>();
list.add(msgTypeName);
if (unsignedContent != null) list.add(unsignedContent);
if (msgTypeTarget != null) list.add(msgTypeTarget);
return List.copyOf(list);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new PlayerChatMessagePacket(signedContent, unsignedContent, type,
uuid, displayName, teamDisplayName, signature);
return new PlayerChatMessagePacket(sender, index, signature,
messageBody,
operator.apply(unsignedContent), filterMask,
msgTypeId, operator.apply(msgTypeName),
operator.apply(msgTypeTarget));
}
}

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,33 @@
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(@NotNull UUID uuid) {
this(List.of(uuid));
}
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,154 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.crypto.ChatSession;
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.Objects;
import java.util.UUID;
import static net.minestom.server.network.NetworkBuffer.*;
public final class PlayerInfoUpdatePacket implements ServerPacket {
private final @NotNull EnumSet<@NotNull Action> actions;
private final @NotNull List<@NotNull Entry> entries;
public PlayerInfoUpdatePacket(@NotNull EnumSet<@NotNull Action> actions, @NotNull List<@NotNull Entry> entries) {
this.actions = EnumSet.copyOf(actions);
this.entries = List.copyOf(entries);
}
public PlayerInfoUpdatePacket(@NotNull Action action, @NotNull Entry entry) {
this.actions = EnumSet.of(action);
this.entries = List.of(entry);
}
public PlayerInfoUpdatePacket(@NotNull NetworkBuffer reader) {
this.actions = reader.readEnumSet(Action.class);
this.entries = reader.readCollection(buffer -> {
UUID uuid = buffer.read(NetworkBuffer.UUID);
String username = "";
List<Property> properties = List.of();
boolean listed = false;
int latency = 0;
GameMode gameMode = GameMode.SURVIVAL;
Component displayName = null;
ChatSession chatSession = null;
for (Action action : actions) {
switch (action) {
case ADD_PLAYER -> {
username = reader.read(STRING);
properties = reader.readCollection(Property::new);
}
case INITIALIZE_CHAT -> chatSession = new ChatSession(reader);
case UPDATE_GAME_MODE -> gameMode = reader.readEnum(GameMode.class);
case UPDATE_LISTED -> listed = reader.read(BOOLEAN);
case UPDATE_LATENCY -> latency = reader.read(VAR_INT);
case UPDATE_DISPLAY_NAME -> displayName = reader.read(COMPONENT);
}
}
return new Entry(uuid, username, properties, listed, latency, gameMode, displayName, chatSession);
});
}
@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 @NotNull EnumSet<Action> actions() {
return actions;
}
public @NotNull List<Entry> entries() {
return entries;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlayerInfoUpdatePacket that = (PlayerInfoUpdatePacket) o;
return actions.equals(that.actions) && entries.equals(that.entries);
}
@Override
public int hashCode() {
return Objects.hash(actions, entries);
}
@Override
public String toString() {
return "PlayerInfoUpdatePacket{" +
"actions=" + actions +
", entries=" + entries +
'}';
}
public record Entry(UUID uuid, String username, List<Property> properties,
boolean listed, int latency, GameMode gameMode,
@Nullable Component displayName, @Nullable ChatSession chatSession) {
public Entry {
properties = List.copyOf(properties);
}
}
public record Property(@NotNull String name, @NotNull String value,
@Nullable String signature) implements NetworkBuffer.Writer {
public Property(@NotNull String name, @NotNull 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((writer, entry) -> writer.writeOptional(entry.chatSession)),
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;
}
}

View File

@ -8,27 +8,78 @@ import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.sound.SoundEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.minestom.server.network.NetworkBuffer.*;
public record SoundEffectPacket(int soundId, @NotNull Source source,
int x, int y, int z,
float volume, float pitch, long seed) implements ServerPacket {
public SoundEffectPacket(@NotNull NetworkBuffer reader) {
this(reader.read(VAR_INT), reader.readEnum(Source.class),
reader.read(INT) * 8, reader.read(INT) * 8, reader.read(INT) * 8,
reader.read(FLOAT), reader.read(FLOAT), reader.read(LONG));
public record SoundEffectPacket(
// only one of soundEvent and soundName may be present
@Nullable SoundEvent soundEvent,
@Nullable String soundName,
@Nullable Float range, // Only allowed with soundName
@NotNull Source source,
int x,
int y,
int z,
float volume,
float pitch,
long seed
) implements ServerPacket {
private static @NotNull SoundEffectPacket fromReader(@NotNull NetworkBuffer reader) {
int soundId = reader.read(VAR_INT);
SoundEvent soundEvent;
String soundName;
Float range = null;
if (soundId == 0) {
soundEvent = null;
soundName = reader.read(STRING);
range = reader.readOptional(FLOAT);
} else {
soundEvent = SoundEvent.fromId(soundId - 1);
soundName = null;
}
return new SoundEffectPacket(
soundEvent,
soundName,
range,
reader.readEnum(Source.class),
reader.read(INT) * 8,
reader.read(INT) * 8,
reader.read(INT) * 8,
reader.read(FLOAT),
reader.read(FLOAT),
reader.read(LONG)
);
}
public SoundEffectPacket(@NotNull SoundEvent sound, @NotNull Source source,
@NotNull Point position, float volume, float pitch) {
this(sound.id(), source, (int) position.x(), (int) position.y(), (int) position.z(),
volume, pitch, 0);
public SoundEffectPacket(@NotNull SoundEvent soundEvent, @Nullable Float range, @NotNull Source source,
@NotNull Point position, float volume, float pitch, long seed) {
this(soundEvent, null, range, source, position.blockX(), position.blockY(), position.blockZ(), volume, pitch, seed);
}
public SoundEffectPacket(@NotNull String soundName, @Nullable Float range, @NotNull Source source,
@NotNull Point position, float volume, float pitch, long seed) {
this(null, soundName, range, source, position.blockX(), position.blockY(), position.blockZ(), volume, pitch, seed);
}
public SoundEffectPacket(@NotNull NetworkBuffer reader) {
this(fromReader(reader));
}
private SoundEffectPacket(@NotNull SoundEffectPacket packet) {
this(packet.soundEvent, packet.soundName, packet.range, packet.source,
packet.x, packet.y, packet.z, packet.volume, packet.pitch, packet.seed);
}
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, soundId);
if (soundEvent != null) {
writer.write(VAR_INT, soundEvent.id() + 1);
} else {
writer.write(VAR_INT, 0);
writer.write(STRING, soundName);
writer.writeOptional(FLOAT, range);
}
writer.write(VAR_INT, AdventurePacketConvertor.getSoundSourceValue(source));
writer.write(INT, x * 8);
writer.write(INT, y * 8);
@ -42,4 +93,5 @@ public record SoundEffectPacket(int soundId, @NotNull Source source,
public int getId() {
return ServerPacketIdentifier.SOUND_EFFECT;
}
}

View File

@ -4,9 +4,12 @@ import com.google.gson.JsonObject;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.crypto.ChatSession;
import net.minestom.server.crypto.PlayerPublicKey;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Metadata;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.packet.client.ClientPacket;
@ -19,11 +22,18 @@ import net.minestom.server.network.packet.server.login.SetCompressionPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.packet.server.play.DeclareRecipesPacket.Ingredient;
import net.minestom.server.network.packet.server.status.PongPacket;
import net.minestom.server.utils.crypto.KeyUtils;
import org.apache.commons.net.util.Base64;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -110,18 +120,20 @@ public class PacketWriteReadTest {
SERVER_PACKETS.add(new EntityPropertiesPacket(5, List.of()));
SERVER_PACKETS.add(new EntityRotationPacket(5, 45f, 45f, false));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME,
new PlayerInfoPacket.UpdateDisplayName(UUID.randomUUID(), COMPONENT)));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME,
new PlayerInfoPacket.UpdateDisplayName(UUID.randomUUID(), (Component) null)));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_GAMEMODE,
new PlayerInfoPacket.UpdateGameMode(UUID.randomUUID(), GameMode.CREATIVE)));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_LATENCY,
new PlayerInfoPacket.UpdateLatency(UUID.randomUUID(), 5)));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER,
new PlayerInfoPacket.AddPlayer(UUID.randomUUID(), "TheMode911",
List.of(new PlayerInfoPacket.AddPlayer.Property("name", "value")), GameMode.CREATIVE, 5, COMPONENT, null)));
SERVER_PACKETS.add(new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER, new PlayerInfoPacket.RemovePlayer(UUID.randomUUID())));
final PlayerSkin skin = new PlayerSkin("hh", "hh");
List<PlayerInfoUpdatePacket.Property> prop = List.of(new PlayerInfoUpdatePacket.Property("textures", skin.textures(), skin.signature()));
SERVER_PACKETS.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.ADD_PLAYER,
new PlayerInfoUpdatePacket.Entry(UUID.randomUUID(), "TheMode911", prop, false, 0, GameMode.SURVIVAL, null, null)));
// SERVER_PACKETS.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
// new PlayerInfoUpdatePacket.Entry(UUID.randomUUID(), "", List.of(), false, 0, GameMode.SURVIVAL, Component.text("NotTheMode911"), null)));
SERVER_PACKETS.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE,
new PlayerInfoUpdatePacket.Entry(UUID.randomUUID(), "", List.of(), false, 0, GameMode.CREATIVE, null, null)));
SERVER_PACKETS.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY,
new PlayerInfoUpdatePacket.Entry(UUID.randomUUID(), "", List.of(), false, 20, GameMode.SURVIVAL, null, null)));
SERVER_PACKETS.add(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LISTED,
new PlayerInfoUpdatePacket.Entry(UUID.randomUUID(), "", List.of(), true, 0, GameMode.SURVIVAL, null, null)));
SERVER_PACKETS.add(new PlayerInfoRemovePacket(UUID.randomUUID()));
//SERVER_PACKETS.add(new MultiBlockChangePacket(5,5,5,true, new long[]{0,5,543534,1321}));
}