mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-10 17:31:28 +01:00
Network improvements (#2324)
This commit is contained in:
parent
561470c82a
commit
3f079d252e
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.utils;
|
package net.minestom.server.utils;
|
||||||
|
|
||||||
import net.minestom.server.utils.binary.BinaryBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.packet.PacketVanilla;
|
||||||
import org.openjdk.jcstress.annotations.*;
|
import org.openjdk.jcstress.annotations.*;
|
||||||
import org.openjdk.jcstress.infra.results.L_Result;
|
import org.openjdk.jcstress.infra.results.L_Result;
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ import static org.openjdk.jcstress.annotations.Expect.ACCEPTABLE;
|
|||||||
@Outcome(id = "2", expect = ACCEPTABLE)
|
@Outcome(id = "2", expect = ACCEPTABLE)
|
||||||
@State
|
@State
|
||||||
public class ObjectPoolTest {
|
public class ObjectPoolTest {
|
||||||
private final ObjectPool<BinaryBuffer> pool = ObjectPool.BUFFER_POOL;
|
private final ObjectPool<NetworkBuffer> pool = PacketVanilla.PACKET_POOL;
|
||||||
|
|
||||||
@Actor
|
@Actor
|
||||||
public void actor1() {
|
public void actor1() {
|
||||||
|
@ -22,6 +22,7 @@ import net.minestom.server.message.ChatType;
|
|||||||
import net.minestom.server.monitoring.BenchmarkManager;
|
import net.minestom.server.monitoring.BenchmarkManager;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.network.packet.PacketParser;
|
import net.minestom.server.network.packet.PacketParser;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import net.minestom.server.network.packet.server.common.PluginMessagePacket;
|
import net.minestom.server.network.packet.server.common.PluginMessagePacket;
|
||||||
import net.minestom.server.network.packet.server.play.ServerDifficultyPacket;
|
import net.minestom.server.network.packet.server.play.ServerDifficultyPacket;
|
||||||
import net.minestom.server.network.socket.Server;
|
import net.minestom.server.network.socket.Server;
|
||||||
@ -30,7 +31,7 @@ import net.minestom.server.registry.DynamicRegistry;
|
|||||||
import net.minestom.server.scoreboard.TeamManager;
|
import net.minestom.server.scoreboard.TeamManager;
|
||||||
import net.minestom.server.thread.TickSchedulerThread;
|
import net.minestom.server.thread.TickSchedulerThread;
|
||||||
import net.minestom.server.timer.SchedulerManager;
|
import net.minestom.server.timer.SchedulerManager;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import net.minestom.server.world.Difficulty;
|
import net.minestom.server.world.Difficulty;
|
||||||
@ -40,7 +41,6 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
@ -80,13 +80,8 @@ public final class MinecraftServer implements MinecraftConstants {
|
|||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public static ServerProcess updateProcess() {
|
public static ServerProcess updateProcess() {
|
||||||
ServerProcess process;
|
ServerProcess process = new ServerProcessImpl();
|
||||||
try {
|
|
||||||
process = new ServerProcessImpl();
|
|
||||||
serverProcess = process;
|
serverProcess = process;
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +103,7 @@ public final class MinecraftServer implements MinecraftConstants {
|
|||||||
*/
|
*/
|
||||||
public static void setBrandName(@NotNull String brandName) {
|
public static void setBrandName(@NotNull String brandName) {
|
||||||
MinecraftServer.brandName = brandName;
|
MinecraftServer.brandName = brandName;
|
||||||
PacketUtils.broadcastPlayPacket(PluginMessagePacket.brandPacket(brandName));
|
PacketSendingUtils.broadcastPlayPacket(PluginMessagePacket.brandPacket(brandName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +123,7 @@ public final class MinecraftServer implements MinecraftConstants {
|
|||||||
*/
|
*/
|
||||||
public static void setDifficulty(@NotNull Difficulty difficulty) {
|
public static void setDifficulty(@NotNull Difficulty difficulty) {
|
||||||
MinecraftServer.difficulty = difficulty;
|
MinecraftServer.difficulty = difficulty;
|
||||||
PacketUtils.broadcastPlayPacket(new ServerDifficultyPacket(difficulty, true));
|
PacketSendingUtils.broadcastPlayPacket(new ServerDifficultyPacket(difficulty, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @UnknownNullability ServerProcess process() {
|
public static @UnknownNullability ServerProcess process() {
|
||||||
@ -188,7 +183,7 @@ public final class MinecraftServer implements MinecraftConstants {
|
|||||||
return serverProcess.bossBar();
|
return serverProcess.bossBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull PacketParser.Client getPacketParser() {
|
public static @NotNull PacketParser<ClientPacket> getPacketParser() {
|
||||||
return serverProcess.packetParser();
|
return serverProcess.packetParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,7 @@ public final class ServerFlag {
|
|||||||
public static final int CHUNK_VIEW_DISTANCE = intProperty("minestom.chunk-view-distance", 8);
|
public static final int CHUNK_VIEW_DISTANCE = intProperty("minestom.chunk-view-distance", 8);
|
||||||
public static final int ENTITY_VIEW_DISTANCE = intProperty("minestom.entity-view-distance", 5);
|
public static final int ENTITY_VIEW_DISTANCE = intProperty("minestom.entity-view-distance", 5);
|
||||||
public static final int ENTITY_SYNCHRONIZATION_TICKS = intProperty("minestom.entity-synchronization-ticks", 20);
|
public static final int ENTITY_SYNCHRONIZATION_TICKS = intProperty("minestom.entity-synchronization-ticks", 20);
|
||||||
public static final int WORKER_COUNT = intProperty("minestom.workers", Runtime.getRuntime().availableProcessors());
|
|
||||||
public static final int DISPATCHER_THREADS = intProperty("minestom.dispatcher-threads", 1);
|
public static final int DISPATCHER_THREADS = intProperty("minestom.dispatcher-threads", 1);
|
||||||
public static final int MAX_PACKET_SIZE = intProperty("minestom.max-packet-size", 2_097_151); // 3 bytes var-int
|
|
||||||
public static final int SOCKET_SEND_BUFFER_SIZE = intProperty("minestom.send-buffer-size", 262_143);
|
|
||||||
public static final int SOCKET_RECEIVE_BUFFER_SIZE = intProperty("minestom.receive-buffer-size", 32_767);
|
|
||||||
public static final boolean SOCKET_NO_DELAY = booleanProperty("minestom.tcp-no-delay", true);
|
|
||||||
public static final int POOLED_BUFFER_SIZE = intProperty("minestom.pooled-buffer-size", 262_143);
|
|
||||||
public static final int SEND_LIGHT_AFTER_BLOCK_PLACEMENT_DELAY = intProperty("minestom.send-light-after-block-placement-delay", 100);
|
public static final int SEND_LIGHT_AFTER_BLOCK_PLACEMENT_DELAY = intProperty("minestom.send-light-after-block-placement-delay", 100);
|
||||||
public static final long LOGIN_PLUGIN_MESSAGE_TIMEOUT = longProperty("minestom.login-plugin-message-timeout", 5_000);
|
public static final long LOGIN_PLUGIN_MESSAGE_TIMEOUT = longProperty("minestom.login-plugin-message-timeout", 5_000);
|
||||||
|
|
||||||
@ -34,6 +28,15 @@ public final class ServerFlag {
|
|||||||
public static final long KEEP_ALIVE_DELAY = longProperty("minestom.keep-alive-delay", 10_000);
|
public static final long KEEP_ALIVE_DELAY = longProperty("minestom.keep-alive-delay", 10_000);
|
||||||
public static final long KEEP_ALIVE_KICK = longProperty("minestom.keep-alive-kick", 15_000);
|
public static final long KEEP_ALIVE_KICK = longProperty("minestom.keep-alive-kick", 15_000);
|
||||||
|
|
||||||
|
// Network buffers
|
||||||
|
public static final int MAX_PACKET_SIZE = intProperty("minestom.max-packet-size", 2_097_151); // 3 bytes var-int
|
||||||
|
public static final int MAX_PACKET_SIZE_PRE_AUTH = intProperty("minestom.max-packet-size-pre-auth", 8_192);
|
||||||
|
public static final int SOCKET_SEND_BUFFER_SIZE = intProperty("minestom.send-buffer-size", 262_143);
|
||||||
|
public static final int SOCKET_RECEIVE_BUFFER_SIZE = intProperty("minestom.receive-buffer-size", 32_767);
|
||||||
|
public static final boolean SOCKET_NO_DELAY = booleanProperty("minestom.tcp-no-delay", true);
|
||||||
|
public static final int SOCKET_TIMEOUT = intProperty("minestom.socket-timeout", 15_000);
|
||||||
|
public static final int POOLED_BUFFER_SIZE = intProperty("minestom.pooled-buffer-size", 16_383);
|
||||||
|
|
||||||
// Chunk update
|
// Chunk update
|
||||||
public static final float MIN_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.min-per-tick", 0.01f);
|
public static final float MIN_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.min-per-tick", 0.01f);
|
||||||
public static final float MAX_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.max-per-tick", 64.0f);
|
public static final float MAX_CHUNKS_PER_TICK = floatProperty("minestom.chunk-queue.max-per-tick", 64.0f);
|
||||||
|
@ -14,6 +14,7 @@ import net.minestom.server.listener.manager.PacketListenerManager;
|
|||||||
import net.minestom.server.monitoring.BenchmarkManager;
|
import net.minestom.server.monitoring.BenchmarkManager;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.network.packet.PacketParser;
|
import net.minestom.server.network.packet.PacketParser;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import net.minestom.server.network.socket.Server;
|
import net.minestom.server.network.socket.Server;
|
||||||
import net.minestom.server.recipe.RecipeManager;
|
import net.minestom.server.recipe.RecipeManager;
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
@ -103,7 +104,7 @@ public interface ServerProcess extends Registries, Snapshotable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Can be used if you want to convert a buffer to a client packet object.
|
* Can be used if you want to convert a buffer to a client packet object.
|
||||||
*/
|
*/
|
||||||
@NotNull PacketParser.Client packetParser();
|
@NotNull PacketParser<ClientPacket> packetParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposed socket server.
|
* Exposed socket server.
|
||||||
|
@ -28,6 +28,8 @@ import net.minestom.server.monitoring.BenchmarkManager;
|
|||||||
import net.minestom.server.monitoring.TickMonitor;
|
import net.minestom.server.monitoring.TickMonitor;
|
||||||
import net.minestom.server.network.ConnectionManager;
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.network.packet.PacketParser;
|
import net.minestom.server.network.packet.PacketParser;
|
||||||
|
import net.minestom.server.network.packet.PacketVanilla;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import net.minestom.server.network.socket.Server;
|
import net.minestom.server.network.socket.Server;
|
||||||
import net.minestom.server.recipe.RecipeManager;
|
import net.minestom.server.recipe.RecipeManager;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
@ -37,7 +39,7 @@ import net.minestom.server.thread.Acquirable;
|
|||||||
import net.minestom.server.thread.ThreadDispatcher;
|
import net.minestom.server.thread.ThreadDispatcher;
|
||||||
import net.minestom.server.thread.ThreadProvider;
|
import net.minestom.server.thread.ThreadProvider;
|
||||||
import net.minestom.server.timer.SchedulerManager;
|
import net.minestom.server.timer.SchedulerManager;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketViewableUtils;
|
||||||
import net.minestom.server.utils.collection.MappedCollection;
|
import net.minestom.server.utils.collection.MappedCollection;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import net.minestom.server.world.DimensionType;
|
import net.minestom.server.world.DimensionType;
|
||||||
@ -77,7 +79,7 @@ final class ServerProcessImpl implements ServerProcess {
|
|||||||
|
|
||||||
private final ConnectionManager connection;
|
private final ConnectionManager connection;
|
||||||
private final PacketListenerManager packetListener;
|
private final PacketListenerManager packetListener;
|
||||||
private final PacketParser.Client packetParser;
|
private final PacketParser<ClientPacket> packetParser;
|
||||||
private final InstanceManager instance;
|
private final InstanceManager instance;
|
||||||
private final BlockManager block;
|
private final BlockManager block;
|
||||||
private final CommandManager command;
|
private final CommandManager command;
|
||||||
@ -98,7 +100,7 @@ final class ServerProcessImpl implements ServerProcess {
|
|||||||
private final AtomicBoolean started = new AtomicBoolean();
|
private final AtomicBoolean started = new AtomicBoolean();
|
||||||
private final AtomicBoolean stopped = new AtomicBoolean();
|
private final AtomicBoolean stopped = new AtomicBoolean();
|
||||||
|
|
||||||
public ServerProcessImpl() throws IOException {
|
public ServerProcessImpl() {
|
||||||
this.exception = new ExceptionManager();
|
this.exception = new ExceptionManager();
|
||||||
|
|
||||||
// The order of initialization here is relevant, we must load the enchantment util registries before the vanilla data is loaded.
|
// The order of initialization here is relevant, we must load the enchantment util registries before the vanilla data is loaded.
|
||||||
@ -122,7 +124,7 @@ final class ServerProcessImpl implements ServerProcess {
|
|||||||
|
|
||||||
this.connection = new ConnectionManager();
|
this.connection = new ConnectionManager();
|
||||||
this.packetListener = new PacketListenerManager();
|
this.packetListener = new PacketListenerManager();
|
||||||
this.packetParser = new PacketParser.Client();
|
this.packetParser = PacketVanilla.CLIENT_PACKET_PARSER;
|
||||||
this.instance = new InstanceManager(this);
|
this.instance = new InstanceManager(this);
|
||||||
this.block = new BlockManager();
|
this.block = new BlockManager();
|
||||||
this.command = new CommandManager();
|
this.command = new CommandManager();
|
||||||
@ -287,7 +289,7 @@ final class ServerProcessImpl implements ServerProcess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull PacketParser.Client packetParser() {
|
public @NotNull PacketParser<ClientPacket> packetParser() {
|
||||||
return packetParser;
|
return packetParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,10 +381,7 @@ final class ServerProcessImpl implements ServerProcess {
|
|||||||
scheduler().processTickEnd();
|
scheduler().processTickEnd();
|
||||||
|
|
||||||
// Flush all waiting packets
|
// Flush all waiting packets
|
||||||
PacketUtils.flush();
|
PacketViewableUtils.flush();
|
||||||
|
|
||||||
// Server connection tick
|
|
||||||
server().tick();
|
|
||||||
|
|
||||||
// Monitoring
|
// Monitoring
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
|||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.network.packet.server.SendablePacket;
|
import net.minestom.server.network.packet.server.SendablePacket;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -60,7 +60,7 @@ public interface Viewable {
|
|||||||
*/
|
*/
|
||||||
default void sendPacketToViewers(@NotNull SendablePacket packet) {
|
default void sendPacketToViewers(@NotNull SendablePacket packet) {
|
||||||
if (packet instanceof ServerPacket serverPacket) {
|
if (packet instanceof ServerPacket serverPacket) {
|
||||||
PacketUtils.sendGroupedPacket(getViewers(), serverPacket);
|
PacketSendingUtils.sendGroupedPacket(getViewers(), serverPacket);
|
||||||
} else {
|
} else {
|
||||||
getViewers().forEach(player -> player.sendPacket(packet));
|
getViewers().forEach(player -> player.sendPacket(packet));
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import net.minestom.server.network.packet.server.ServerPacket;
|
|||||||
import net.minestom.server.network.packet.server.play.ActionBarPacket;
|
import net.minestom.server.network.packet.server.play.ActionBarPacket;
|
||||||
import net.minestom.server.network.packet.server.play.ClearTitlesPacket;
|
import net.minestom.server.network.packet.server.play.ClearTitlesPacket;
|
||||||
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
|
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -54,7 +54,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
|
|||||||
* @param packet the packet to broadcast
|
* @param packet the packet to broadcast
|
||||||
*/
|
*/
|
||||||
default void sendGroupedPacket(@NotNull ServerPacket packet) {
|
default void sendGroupedPacket(@NotNull ServerPacket packet) {
|
||||||
PacketUtils.sendGroupedPacket(getPlayers(), packet);
|
PacketSendingUtils.sendGroupedPacket(getPlayers(), packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -3,7 +3,7 @@ package net.minestom.server.adventure.bossbar;
|
|||||||
import net.kyori.adventure.audience.Audience;
|
import net.kyori.adventure.audience.Audience;
|
||||||
import net.kyori.adventure.bossbar.BossBar;
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -29,33 +29,32 @@ class BossBarListener implements BossBar.Listener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
|
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
|
||||||
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createTitleUpdate(newName)));
|
this.doIfRegistered(bar, holder -> PacketSendingUtils.sendGroupedPacket(holder.players, holder.createTitleUpdate(newName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) {
|
public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) {
|
||||||
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createPercentUpdate(newProgress)));
|
this.doIfRegistered(bar, holder -> PacketSendingUtils.sendGroupedPacket(holder.players, holder.createPercentUpdate(newProgress)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bossBarColorChanged(@NotNull BossBar bar, @NotNull BossBar.Color oldColor, @NotNull BossBar.Color newColor) {
|
public void bossBarColorChanged(@NotNull BossBar bar, @NotNull BossBar.Color oldColor, @NotNull BossBar.Color newColor) {
|
||||||
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createColorUpdate(newColor)));
|
this.doIfRegistered(bar, holder -> PacketSendingUtils.sendGroupedPacket(holder.players, holder.createColorUpdate(newColor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) {
|
public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) {
|
||||||
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createOverlayUpdate(newOverlay)));
|
this.doIfRegistered(bar, holder -> PacketSendingUtils.sendGroupedPacket(holder.players, holder.createOverlayUpdate(newOverlay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set<BossBar.Flag> flagsAdded, @NotNull Set<BossBar.Flag> flagsRemoved) {
|
public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set<BossBar.Flag> flagsAdded, @NotNull Set<BossBar.Flag> flagsRemoved) {
|
||||||
this.doIfRegistered(bar, holder -> PacketUtils.sendGroupedPacket(holder.players, holder.createFlagsUpdate()));
|
this.doIfRegistered(bar, holder -> PacketSendingUtils.sendGroupedPacket(holder.players, holder.createFlagsUpdate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doIfRegistered(@NotNull BossBar bar, @NotNull Consumer<BossBarHolder> consumer) {
|
private void doIfRegistered(@NotNull BossBar bar, @NotNull Consumer<BossBarHolder> consumer) {
|
||||||
BossBarHolder holder = this.manager.bars.get(bar);
|
BossBarHolder holder = this.manager.bars.get(bar);
|
||||||
|
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
consumer.accept(holder);
|
consumer.accept(holder);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import net.kyori.adventure.audience.Audience;
|
|||||||
import net.kyori.adventure.bossbar.BossBar;
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -75,7 +75,7 @@ public class BossBarManager {
|
|||||||
BossBarHolder holder = this.getOrCreateHandler(bar);
|
BossBarHolder holder = this.getOrCreateHandler(bar);
|
||||||
Collection<Player> addedPlayers = players.stream().filter(holder::addViewer).toList();
|
Collection<Player> addedPlayers = players.stream().filter(holder::addViewer).toList();
|
||||||
if (!addedPlayers.isEmpty()) {
|
if (!addedPlayers.isEmpty()) {
|
||||||
PacketUtils.sendGroupedPacket(addedPlayers, holder.createAddPacket());
|
PacketSendingUtils.sendGroupedPacket(addedPlayers, holder.createAddPacket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ public class BossBarManager {
|
|||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
Collection<Player> removedPlayers = players.stream().filter(holder::removeViewer).toList();
|
Collection<Player> removedPlayers = players.stream().filter(holder::removeViewer).toList();
|
||||||
if (!removedPlayers.isEmpty()) {
|
if (!removedPlayers.isEmpty()) {
|
||||||
PacketUtils.sendGroupedPacket(removedPlayers, holder.createRemovePacket());
|
PacketSendingUtils.sendGroupedPacket(removedPlayers, holder.createRemovePacket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ public class BossBarManager {
|
|||||||
public void destroyBossBar(@NotNull BossBar bossBar) {
|
public void destroyBossBar(@NotNull BossBar bossBar) {
|
||||||
BossBarHolder holder = this.bars.remove(bossBar);
|
BossBarHolder holder = this.bars.remove(bossBar);
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
PacketUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
|
PacketSendingUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
|
||||||
for (Player player : holder.players) {
|
for (Player player : holder.players) {
|
||||||
this.removePlayer(player, holder);
|
this.removePlayer(player, holder);
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,7 @@ import java.util.Objects;
|
|||||||
public final class AlphaColor extends Color {
|
public final class AlphaColor extends Color {
|
||||||
private static final int BIT_MASK = 0xff;
|
private static final int BIT_MASK = 0xff;
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<AlphaColor> NETWORK_TYPE = new NetworkBuffer.Type<AlphaColor>() {
|
public static final NetworkBuffer.Type<AlphaColor> NETWORK_TYPE = NetworkBuffer.INT.transform(AlphaColor::new, AlphaColor::asARGB);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer buffer, AlphaColor value) {
|
|
||||||
buffer.write(NetworkBuffer.INT, value.asARGB());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlphaColor read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new AlphaColor(buffer.read(NetworkBuffer.INT));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private final int alpha;
|
private final int alpha;
|
||||||
|
|
||||||
public AlphaColor(int alpha, int red, int green, int blue) {
|
public AlphaColor(int alpha, int red, int green, int blue) {
|
||||||
|
@ -17,17 +17,10 @@ import java.util.Objects;
|
|||||||
public class Color implements RGBLike {
|
public class Color implements RGBLike {
|
||||||
private static final int BIT_MASK = 0xff;
|
private static final int BIT_MASK = 0xff;
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<RGBLike> NETWORK_TYPE = new NetworkBuffer.Type<RGBLike>() {
|
public static final NetworkBuffer.Type<RGBLike> NETWORK_TYPE = NetworkBuffer.INT.transform(
|
||||||
@Override
|
Color::new,
|
||||||
public void write(@NotNull NetworkBuffer buffer, RGBLike value) {
|
color -> Color.fromRGBLike(color).asRGB()
|
||||||
buffer.write(NetworkBuffer.INT, Color.fromRGBLike(value).asRGB());
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RGBLike read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Color(buffer.read(NetworkBuffer.INT));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<RGBLike> NBT_TYPE = BinaryTagSerializer.INT
|
public static final BinaryTagSerializer<RGBLike> NBT_TYPE = BinaryTagSerializer.INT
|
||||||
.map(Color::new, color -> Color.fromRGBLike(color).asRGB());
|
.map(Color::new, color -> Color.fromRGBLike(color).asRGB());
|
||||||
private final int red;
|
private final int red;
|
||||||
|
@ -37,9 +37,7 @@ public class ArgumentString extends Argument<String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer -> {
|
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 1); // Quotable phrase
|
||||||
buffer.write(NetworkBuffer.VAR_INT, 1); // Quotable phrase
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,9 +32,7 @@ public class ArgumentStringArray extends Argument<String[]> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer -> {
|
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 2); // Greedy phrase
|
||||||
buffer.write(NetworkBuffer.VAR_INT, 2); // Greedy phrase
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,9 +75,7 @@ public class ArgumentWord extends Argument<String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer -> {
|
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 0); // Single word
|
||||||
buffer.write(NetworkBuffer.VAR_INT, 0); // Single word
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +39,6 @@ public class ArgumentResource extends Argument<String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer -> buffer.write(NetworkBuffer.STRING, identifier));
|
return NetworkBuffer.makeArray(NetworkBuffer.STRING, identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,6 @@ public class ArgumentResourceOrTag extends Argument<String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer ->
|
return NetworkBuffer.makeArray(NetworkBuffer.STRING, identifier);
|
||||||
buffer.write(NetworkBuffer.STRING, this.identifier)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ public class ArgumentTime extends Argument<Duration> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte @Nullable [] nodeProperties() {
|
public byte @Nullable [] nodeProperties() {
|
||||||
return NetworkBuffer.makeArray(buffer -> buffer.write(NetworkBuffer.INT, min));
|
return NetworkBuffer.makeArray(NetworkBuffer.INT, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,37 +1,30 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.STRING;
|
import static net.minestom.server.network.NetworkBuffer.STRING;
|
||||||
|
|
||||||
public record ArgumentSignatures(@NotNull List<@NotNull Entry> entries) implements NetworkBuffer.Writer {
|
public record ArgumentSignatures(@NotNull List<@NotNull Entry> entries) {
|
||||||
public static final int MAX_ENTRIES = 8;
|
public static final int MAX_ENTRIES = 8;
|
||||||
|
|
||||||
public ArgumentSignatures {
|
public ArgumentSignatures {
|
||||||
entries = List.copyOf(entries);
|
entries = List.copyOf(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgumentSignatures(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<ArgumentSignatures> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
this(reader.readCollection(Entry::new, MAX_ENTRIES));
|
Entry.SERIALIZER.list(MAX_ENTRIES), ArgumentSignatures::entries,
|
||||||
}
|
ArgumentSignatures::new
|
||||||
|
);
|
||||||
|
|
||||||
@Override
|
public record Entry(@NotNull String name, @NotNull MessageSignature signature) {
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public static final NetworkBuffer.Type<Entry> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
writer.writeCollection(entries);
|
STRING, Entry::name,
|
||||||
}
|
MessageSignature.SERIALIZER, Entry::signature,
|
||||||
|
Entry::new
|
||||||
public record Entry(@NotNull String name, @NotNull MessageSignature signature) implements NetworkBuffer.Writer {
|
);
|
||||||
public Entry(@NotNull NetworkBuffer reader) {
|
|
||||||
this(reader.read(STRING), new MessageSignature(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(STRING, name);
|
|
||||||
writer.write(signature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public record ChatSession(@NotNull UUID sessionId, @NotNull PlayerPublicKey publicKey) implements NetworkBuffer.Writer {
|
import static net.minestom.server.network.NetworkBuffer.UUID;
|
||||||
public ChatSession(@NotNull NetworkBuffer reader) {
|
|
||||||
this(reader.read(NetworkBuffer.UUID), new PlayerPublicKey(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public record ChatSession(@NotNull UUID sessionId, @NotNull PlayerPublicKey publicKey) {
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public static final NetworkBuffer.Type<ChatSession> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
writer.write(NetworkBuffer.UUID, sessionId);
|
UUID, ChatSession::sessionId,
|
||||||
writer.write(publicKey);
|
PlayerPublicKey.SERIALIZER, ChatSession::publicKey,
|
||||||
}
|
ChatSession::new
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,25 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.LONG_ARRAY;
|
import static net.minestom.server.network.NetworkBuffer.BITSET;
|
||||||
|
|
||||||
public record FilterMask(@NotNull Type type, @NotNull BitSet mask) implements NetworkBuffer.Writer {
|
public record FilterMask(@NotNull Type type, @NotNull BitSet mask) {
|
||||||
public FilterMask(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<FilterMask> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||||
this(reader.readEnum(Type.class), BitSet.valueOf(reader.read(LONG_ARRAY)));
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, FilterMask value) {
|
||||||
|
buffer.write(NetworkBuffer.Enum(Type.class), value.type);
|
||||||
|
if (value.type == Type.PARTIALLY_FILTERED) {
|
||||||
|
buffer.write(BITSET, value.mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public FilterMask read(@NotNull NetworkBuffer buffer) {
|
||||||
writer.writeEnum(Type.class, type);
|
Type type = buffer.read(NetworkBuffer.Enum(Type.class));
|
||||||
if (type == Type.PARTIALLY_FILTERED) {
|
BitSet mask = type == Type.PARTIALLY_FILTERED ? buffer.read(BITSET) : new BitSet();
|
||||||
writer.write(LONG_ARRAY, mask.toLongArray());
|
return new FilterMask(type, mask);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
PASS_THROUGH,
|
PASS_THROUGH,
|
||||||
|
@ -1,50 +1,41 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.FixedBitSet;
|
||||||
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||||
|
|
||||||
public record LastSeenMessages(@NotNull List<@NotNull MessageSignature> entries) implements NetworkBuffer.Writer {
|
public record LastSeenMessages(@NotNull List<@NotNull MessageSignature> entries) {
|
||||||
public static final int MAX_ENTRIES = 20;
|
public static final int MAX_ENTRIES = 20;
|
||||||
|
|
||||||
public LastSeenMessages {
|
public LastSeenMessages {
|
||||||
entries = List.copyOf(entries);
|
entries = List.copyOf(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<LastSeenMessages> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
this(reader.readCollection(MessageSignature::new, MAX_ENTRIES));
|
MessageSignature.SERIALIZER.list(MAX_ENTRIES), LastSeenMessages::entries,
|
||||||
}
|
LastSeenMessages::new
|
||||||
|
);
|
||||||
|
|
||||||
@Override
|
public record Packed(@NotNull List<MessageSignature.@NotNull Packed> entries) {
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public record Packed(@NotNull List<MessageSignature.@NotNull Packed> entries) implements NetworkBuffer.Writer {
|
|
||||||
public static final Packed EMPTY = new Packed(List.of());
|
public static final Packed EMPTY = new Packed(List.of());
|
||||||
|
|
||||||
public Packed(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<Packed> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
this(reader.readCollection(MessageSignature.Packed::new, MAX_ENTRIES));
|
MessageSignature.Packed.SERIALIZER.list(MAX_ENTRIES), Packed::entries,
|
||||||
|
Packed::new
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public record Update(int offset, @NotNull BitSet acknowledged) {
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public static final NetworkBuffer.Type<Update> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
writer.writeCollection(entries);
|
VAR_INT, Update::offset,
|
||||||
}
|
FixedBitSet(20), Update::acknowledged,
|
||||||
}
|
Update::new
|
||||||
|
);
|
||||||
public record Update(int offset, @NotNull BitSet acknowledged) implements NetworkBuffer.Writer {
|
|
||||||
public Update(@NotNull NetworkBuffer reader) {
|
|
||||||
this(reader.read(VAR_INT), reader.readFixedBitSet(20));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(VAR_INT, offset);
|
|
||||||
writer.writeFixedBitSet(acknowledged, 20);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.RAW_BYTES;
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||||
|
|
||||||
public record MessageSignature(byte @NotNull [] signature) implements NetworkBuffer.Writer {
|
public record MessageSignature(byte @NotNull [] signature) {
|
||||||
|
|
||||||
static final int SIGNATURE_BYTE_LENGTH = 256;
|
static final int SIGNATURE_BYTE_LENGTH = 256;
|
||||||
|
|
||||||
public MessageSignature {
|
public MessageSignature {
|
||||||
@ -17,33 +16,28 @@ public record MessageSignature(byte @NotNull [] signature) implements NetworkBuf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageSignature(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<MessageSignature> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
this(reader.readBytes(SIGNATURE_BYTE_LENGTH));
|
NetworkBuffer.RAW_BYTES, MessageSignature::signature,
|
||||||
}
|
MessageSignature::new
|
||||||
|
);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(RAW_BYTES, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
public record Packed(int id, @UnknownNullability MessageSignature fullSignature) implements NetworkBuffer.Writer {
|
|
||||||
public Packed(@NotNull NetworkBuffer reader) {
|
|
||||||
this(read(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public record Packed(int id, @UnknownNullability MessageSignature fullSignature) {
|
||||||
private Packed(@NotNull Packed packed) {
|
private Packed(@NotNull Packed packed) {
|
||||||
this(packed.id, packed.fullSignature);
|
this(packed.id, packed.fullSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final NetworkBuffer.Type<Packed> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public void write(@NotNull NetworkBuffer buffer, Packed value) {
|
||||||
writer.write(VAR_INT, id + 1);
|
buffer.write(VAR_INT, value.id + 1);
|
||||||
if (id == 0) writer.write(fullSignature);
|
if (value.id == 0) buffer.write(MessageSignature.SERIALIZER, value.fullSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Packed read(NetworkBuffer reader) {
|
@Override
|
||||||
final int id = reader.read(VAR_INT) - 1;
|
public Packed read(@NotNull NetworkBuffer buffer) {
|
||||||
return new Packed(id, id == -1 ? new MessageSignature(reader) : null);
|
final int id = buffer.read(VAR_INT) - 1;
|
||||||
|
return new Packed(id, id == -1 ? buffer.read(MessageSignature.SERIALIZER) : null);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,21 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import net.minestom.server.utils.crypto.KeyUtils;
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
|
import static net.minestom.server.network.NetworkBuffer.*;
|
||||||
import static net.minestom.server.network.NetworkBuffer.LONG;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player's public key used to sign chat messages
|
* Player's public key used to sign chat messages
|
||||||
*/
|
*/
|
||||||
public record PlayerPublicKey(Instant expiresAt, PublicKey publicKey,
|
public record PlayerPublicKey(Instant expiresAt, PublicKey publicKey, byte[] signature) {
|
||||||
byte[] signature) implements NetworkBuffer.Writer {
|
public static final NetworkBuffer.Type<PlayerPublicKey> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
public PlayerPublicKey(@NotNull NetworkBuffer reader) {
|
INSTANT_MS, PlayerPublicKey::expiresAt,
|
||||||
this(Instant.ofEpochMilli(reader.read(LONG)),
|
PUBLIC_KEY, PlayerPublicKey::publicKey,
|
||||||
KeyUtils.publicRSAKeyFrom(reader.read(BYTE_ARRAY)), reader.read(BYTE_ARRAY));
|
BYTE_ARRAY, PlayerPublicKey::signature,
|
||||||
}
|
PlayerPublicKey::new
|
||||||
|
);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(LONG, expiresAt().toEpochMilli());
|
|
||||||
writer.write(BYTE_ARRAY, publicKey.getEncoded());
|
|
||||||
writer.write(BYTE_ARRAY, signature());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj instanceof PlayerPublicKey ppk) {
|
|
||||||
return expiresAt.equals(ppk.expiresAt) && publicKey.equals(ppk.publicKey) && Arrays.equals(signature, ppk.signature);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,12 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
|
public record SaltSignaturePair(long salt, byte[] signature) {
|
||||||
import static net.minestom.server.network.NetworkBuffer.LONG;
|
public static final NetworkBuffer.Type<SaltSignaturePair> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
|
NetworkBuffer.LONG, SaltSignaturePair::salt,
|
||||||
public record SaltSignaturePair(long salt, byte[] signature) implements NetworkBuffer.Writer {
|
NetworkBuffer.BYTE_ARRAY, SaltSignaturePair::signature,
|
||||||
public SaltSignaturePair(@NotNull NetworkBuffer reader) {
|
SaltSignaturePair::new
|
||||||
this(reader.read(LONG), reader.read(BYTE_ARRAY));
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(LONG, salt);
|
|
||||||
writer.write(BYTE_ARRAY, signature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -8,24 +9,19 @@ import java.time.Instant;
|
|||||||
public final class SignedMessageBody {
|
public final class SignedMessageBody {
|
||||||
|
|
||||||
public record Packed(@NotNull String content, @NotNull Instant timeStamp, long salt,
|
public record Packed(@NotNull String content, @NotNull Instant timeStamp, long salt,
|
||||||
LastSeenMessages.@NotNull Packed lastSeen) implements NetworkBuffer.Writer {
|
LastSeenMessages.@NotNull Packed lastSeen) {
|
||||||
public Packed {
|
public Packed {
|
||||||
if (content.length() > MessageSignature.SIGNATURE_BYTE_LENGTH) {
|
if (content.length() > MessageSignature.SIGNATURE_BYTE_LENGTH) {
|
||||||
throw new IllegalArgumentException("Message content too long");
|
throw new IllegalArgumentException("Message content too long");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Packed(@NotNull NetworkBuffer reader) {
|
public static final NetworkBuffer.Type<Packed> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
this(reader.read(NetworkBuffer.STRING), Instant.ofEpochMilli(reader.read(NetworkBuffer.LONG)),
|
NetworkBuffer.STRING, Packed::content,
|
||||||
reader.read(NetworkBuffer.LONG), new LastSeenMessages.Packed(reader));
|
NetworkBuffer.INSTANT_MS, Packed::timeStamp,
|
||||||
}
|
NetworkBuffer.LONG, Packed::salt,
|
||||||
|
LastSeenMessages.Packed.SERIALIZER, Packed::lastSeen,
|
||||||
@Override
|
Packed::new
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
package net.minestom.server.crypto;
|
package net.minestom.server.crypto;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public record SignedMessageHeader(@Nullable MessageSignature previousSignature,
|
public record SignedMessageHeader(@Nullable MessageSignature previousSignature, @NotNull UUID sender) {
|
||||||
@NotNull UUID sender) implements NetworkBuffer.Writer {
|
public static final NetworkBuffer.Type<SignedMessageHeader> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
public SignedMessageHeader(@NotNull NetworkBuffer reader) {
|
MessageSignature.SERIALIZER.optional(), SignedMessageHeader::previousSignature,
|
||||||
this(reader.readOptional(MessageSignature::new), reader.read(NetworkBuffer.UUID));
|
NetworkBuffer.UUID, SignedMessageHeader::sender,
|
||||||
}
|
SignedMessageHeader::new
|
||||||
|
);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.writeOptional(previousSignature);
|
|
||||||
writer.write(NetworkBuffer.UUID, sender);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ import net.minestom.server.timer.Schedulable;
|
|||||||
import net.minestom.server.timer.Scheduler;
|
import net.minestom.server.timer.Scheduler;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
import net.minestom.server.utils.ArrayUtils;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketViewableUtils;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.block.BlockIterator;
|
import net.minestom.server.utils.block.BlockIterator;
|
||||||
import net.minestom.server.utils.chunk.ChunkCache;
|
import net.minestom.server.utils.chunk.ChunkCache;
|
||||||
@ -1244,22 +1244,21 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
|||||||
final Chunk chunk = getChunk();
|
final Chunk chunk = getChunk();
|
||||||
assert chunk != null;
|
assert chunk != null;
|
||||||
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
|
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
|
||||||
PacketUtils.prepareViewablePacket(chunk, new EntityTeleportPacket(getEntityId(), position, isOnGround()), this);
|
PacketViewableUtils.prepareViewablePacket(chunk, new EntityTeleportPacket(getEntityId(), position, isOnGround()), this);
|
||||||
nextSynchronizationTick = synchronizationTicks + 1;
|
nextSynchronizationTick = synchronizationTicks + 1;
|
||||||
} else if (positionChange && viewChange) {
|
} else if (positionChange && viewChange) {
|
||||||
// PacketUtils.prepareViewablePacket(chunk, new EntityVelocityPacket(getEntityId(), new Vec(distanceX, distanceY, distanceZ).div(20 * 8000)));
|
PacketViewableUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||||
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
|
||||||
lastSyncedPosition, isOnGround()), this);
|
lastSyncedPosition, isOnGround()), this);
|
||||||
// Fix head rotation
|
// Fix head rotation
|
||||||
PacketUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
PacketViewableUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||||
} else if (positionChange) {
|
} else if (positionChange) {
|
||||||
// This is a confusing fix for a confusing issue. If rotation is only sent when the entity actually changes, then spawning an entity
|
// This is a confusing fix for a confusing issue. If rotation is only sent when the entity actually changes, then spawning an entity
|
||||||
// on the ground causes the entity not to update its rotation correctly. It works fine if the entity is spawned in the air. Very weird.
|
// on the ground causes the entity not to update its rotation correctly. It works fine if the entity is spawned in the air. Very weird.
|
||||||
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
PacketViewableUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||||
lastSyncedPosition, onGround), this);
|
lastSyncedPosition, onGround), this);
|
||||||
} else if (viewChange) {
|
} else if (viewChange) {
|
||||||
PacketUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
PacketViewableUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||||
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
PacketViewableUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||||
lastSyncedPosition, isOnGround()), this);
|
lastSyncedPosition, isOnGround()), this);
|
||||||
}
|
}
|
||||||
this.lastSyncedPosition = position;
|
this.lastSyncedPosition = position;
|
||||||
@ -1535,9 +1534,9 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
|||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
protected void synchronizePosition() {
|
protected void synchronizePosition() {
|
||||||
final Pos posCache = this.position;
|
final Pos posCache = this.position;
|
||||||
PacketUtils.prepareViewablePacket(currentChunk, new EntityTeleportPacket(getEntityId(), posCache, isOnGround()), this);
|
PacketViewableUtils.prepareViewablePacket(currentChunk, new EntityTeleportPacket(getEntityId(), posCache, isOnGround()), this);
|
||||||
if (posCache.yaw() != lastSyncedPosition.yaw()) {
|
if (posCache.yaw() != lastSyncedPosition.yaw()) {
|
||||||
PacketUtils.prepareViewablePacket(currentChunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
PacketViewableUtils.prepareViewablePacket(currentChunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||||
}
|
}
|
||||||
nextSynchronizationTick = ticks + synchronizationTicks;
|
nextSynchronizationTick = ticks + synchronizationTicks;
|
||||||
this.lastSyncedPosition = posCache;
|
this.lastSyncedPosition = posCache;
|
||||||
|
@ -32,15 +32,18 @@ public enum GameMode {
|
|||||||
return canTakeDamage;
|
return canTakeDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<GameMode> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<GameMode> NETWORK_TYPE = BYTE.transform(GameMode::fromId, gameMode -> gameMode.id);
|
||||||
|
|
||||||
|
public static final NetworkBuffer.Type<GameMode> OPT_NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, GameMode value) {
|
public void write(@NotNull NetworkBuffer buffer, GameMode value) {
|
||||||
buffer.write(BYTE, value.id());
|
buffer.write(BYTE, value != null ? value.id() : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GameMode read(@NotNull NetworkBuffer buffer) {
|
public GameMode read(@NotNull NetworkBuffer buffer) {
|
||||||
return GameMode.fromId(buffer.read(BYTE));
|
final byte id = buffer.read(BYTE);
|
||||||
|
return id != -1 ? GameMode.fromId(id) : null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import net.minestom.server.particle.Particle;
|
|||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.Direction;
|
import net.minestom.server.utils.Direction;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
@ -213,15 +212,13 @@ public final class Metadata {
|
|||||||
return (byte) NEXT_ID.getAndIncrement();
|
return (byte) NEXT_ID.getAndIncrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed interface Entry<T> extends NetworkBuffer.Writer
|
public sealed interface Entry<T> permits MetadataImpl.EntryImpl {
|
||||||
permits MetadataImpl.EntryImpl {
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
NetworkBuffer.Type<Entry<?>> SERIALIZER = (NetworkBuffer.Type) MetadataImpl.EntryImpl.SERIALIZER;
|
||||||
|
|
||||||
int type();
|
int type();
|
||||||
|
|
||||||
@UnknownNullability T value();
|
@UnknownNullability
|
||||||
|
T value();
|
||||||
@ApiStatus.Internal
|
|
||||||
static @NotNull Entry<?> read(int type, @NotNull NetworkBuffer reader) {
|
|
||||||
return MetadataImpl.EntryImpl.read(type, reader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,22 +61,23 @@ final class MetadataImpl {
|
|||||||
EMPTY_VALUES.trim();
|
EMPTY_VALUES.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
record EntryImpl<T>(int type, @UnknownNullability T value,
|
record EntryImpl<T>(int type, @UnknownNullability T value,
|
||||||
@NotNull NetworkBuffer.Type<T> serializer) implements Metadata.Entry<T> {
|
@NotNull NetworkBuffer.Type<T> serializer) implements Metadata.Entry<T> {
|
||||||
static Entry<?> read(int type, @NotNull NetworkBuffer reader) {
|
static final NetworkBuffer.Type<EntryImpl<?>> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||||
final EntryImpl<?> value = (EntryImpl<?>) EMPTY_VALUES.get(type);
|
@Override
|
||||||
if (value == null) throw new UnsupportedOperationException("Unknown value type: " + type);
|
public void write(@NotNull NetworkBuffer buffer, EntryImpl value) {
|
||||||
return value.withValue(reader);
|
buffer.write(VAR_INT, value.type);
|
||||||
|
buffer.write(value.serializer, value.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public EntryImpl read(@NotNull NetworkBuffer buffer) {
|
||||||
writer.write(VAR_INT, type);
|
final int type = buffer.read(VAR_INT);
|
||||||
writer.write(serializer, value);
|
final EntryImpl<?> value = (EntryImpl<?>) EMPTY_VALUES.get(type);
|
||||||
}
|
if (value == null) throw new UnsupportedOperationException("Unknown value type: " + type);
|
||||||
|
return new EntryImpl(type, value.serializer.read(buffer), value.serializer);
|
||||||
private EntryImpl<T> withValue(@NotNull NetworkBuffer reader) {
|
|
||||||
return new EntryImpl<>(type, reader.read(serializer), serializer);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ import net.minestom.server.statistic.PlayerStatistic;
|
|||||||
import net.minestom.server.thread.Acquirable;
|
import net.minestom.server.thread.Acquirable;
|
||||||
import net.minestom.server.timer.Scheduler;
|
import net.minestom.server.timer.Scheduler;
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.chunk.ChunkUpdateLimitChecker;
|
import net.minestom.server.utils.chunk.ChunkUpdateLimitChecker;
|
||||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||||
@ -328,7 +328,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
EventDispatcher.call(skinInitEvent);
|
EventDispatcher.call(skinInitEvent);
|
||||||
this.skin = skinInitEvent.getSkin();
|
this.skin = skinInitEvent.getSkin();
|
||||||
// FIXME: when using Geyser, this line remove the skin of the client
|
// FIXME: when using Geyser, this line remove the skin of the client
|
||||||
PacketUtils.broadcastPlayPacket(getAddPlayerToList());
|
PacketSendingUtils.broadcastPlayPacket(getAddPlayerToList());
|
||||||
|
|
||||||
var connectionManager = MinecraftServer.getConnectionManager();
|
var connectionManager = MinecraftServer.getConnectionManager();
|
||||||
for (var player : connectionManager.getOnlinePlayers()) {
|
for (var player : connectionManager.getOnlinePlayers()) {
|
||||||
@ -604,7 +604,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
// Clear all viewable chunks
|
// Clear all viewable chunks
|
||||||
ChunkUtils.forChunksInRange(chunkX, chunkZ, settings.getEffectiveViewDistance(), chunkRemover);
|
ChunkUtils.forChunksInRange(chunkX, chunkZ, settings.getEffectiveViewDistance(), chunkRemover);
|
||||||
// Remove from the tab-list
|
// Remove from the tab-list
|
||||||
PacketUtils.broadcastPlayPacket(getRemovePlayerToList());
|
PacketSendingUtils.broadcastPlayPacket(getRemovePlayerToList());
|
||||||
|
|
||||||
// Prevent the player from being stuck in loading screen, or just unable to interact with the server
|
// Prevent the player from being stuck in loading screen, or just unable to interact with the server
|
||||||
// This should be considered as a bug, since the player will ultimately time out anyway.
|
// This should be considered as a bug, since the player will ultimately time out anyway.
|
||||||
@ -1010,7 +1010,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
String title = PlainTextComponentSerializer.plainText().serialize(book.title());
|
String title = PlainTextComponentSerializer.plainText().serialize(book.title());
|
||||||
String author = PlainTextComponentSerializer.plainText().serialize(book.author());
|
String author = PlainTextComponentSerializer.plainText().serialize(book.author());
|
||||||
final ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK)
|
final ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK)
|
||||||
.set(ItemComponent.WRITTEN_BOOK_CONTENT, new WrittenBookContent(book.pages(), title, author, 0, false))
|
.set(ItemComponent.WRITTEN_BOOK_CONTENT, new WrittenBookContent(title, author, 0, book.pages(), false))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Set book in offhand
|
// Set book in offhand
|
||||||
@ -1176,7 +1176,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
*/
|
*/
|
||||||
public void setDisplayName(@Nullable Component displayName) {
|
public void setDisplayName(@Nullable Component displayName) {
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
PacketUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, infoEntry()));
|
PacketSendingUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, infoEntry()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1218,11 +1218,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Remove player
|
// Remove player
|
||||||
PacketUtils.broadcastPlayPacket(removePlayerPacket);
|
PacketSendingUtils.broadcastPlayPacket(removePlayerPacket);
|
||||||
sendPacketToViewers(destroyEntitiesPacket);
|
sendPacketToViewers(destroyEntitiesPacket);
|
||||||
|
|
||||||
// Show player again
|
// Show player again
|
||||||
PacketUtils.broadcastPlayPacket(addPlayerPacket);
|
PacketSendingUtils.broadcastPlayPacket(addPlayerPacket);
|
||||||
getViewers().forEach(player -> showPlayer(player.getPlayerConnection()));
|
getViewers().forEach(player -> showPlayer(player.getPlayerConnection()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1606,7 +1606,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
// Condition to prevent sending the packets before spawning the player
|
// Condition to prevent sending the packets before spawning the player
|
||||||
if (isActive()) {
|
if (isActive()) {
|
||||||
sendPacket(new ChangeGameStatePacket(ChangeGameStatePacket.Reason.CHANGE_GAMEMODE, gameMode.id()));
|
sendPacket(new ChangeGameStatePacket(ChangeGameStatePacket.Reason.CHANGE_GAMEMODE, gameMode.id()));
|
||||||
PacketUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, infoEntry()));
|
PacketSendingUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, infoEntry()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The client updates their abilities based on the GameMode as follows
|
// The client updates their abilities based on the GameMode as follows
|
||||||
@ -2138,7 +2138,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
public void refreshLatency(int latency) {
|
public void refreshLatency(int latency) {
|
||||||
this.latency = latency;
|
this.latency = latency;
|
||||||
if (getPlayerConnection().getConnectionState() == ConnectionState.PLAY) {
|
if (getPlayerConnection().getConnectionState() == ConnectionState.PLAY) {
|
||||||
PacketUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY, infoEntry()));
|
PacketSendingUtils.broadcastPlayPacket(new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.UPDATE_LATENCY, infoEntry()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2361,6 +2361,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a {@link Notification} to the player.
|
* Send a {@link Notification} to the player.
|
||||||
|
*
|
||||||
* @param notification the {@link Notification} to send
|
* @param notification the {@link Notification} to send
|
||||||
*/
|
*/
|
||||||
public void sendNotification(@NotNull Notification notification) {
|
public void sendNotification(@NotNull Notification notification) {
|
||||||
|
@ -12,7 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public sealed interface Attribute extends StaticProtocolObject, Attributes permits AttributeImpl {
|
public sealed interface Attribute extends StaticProtocolObject, Attributes permits AttributeImpl {
|
||||||
@NotNull NetworkBuffer.Type<Attribute> NETWORK_TYPE = NetworkBuffer.VAR_INT.map(AttributeImpl::getId, Attribute::id);
|
@NotNull NetworkBuffer.Type<Attribute> NETWORK_TYPE = NetworkBuffer.VAR_INT.transform(AttributeImpl::getId, Attribute::id);
|
||||||
@NotNull BinaryTagSerializer<Attribute> NBT_TYPE = BinaryTagSerializer.STRING.map(AttributeImpl::get, Attribute::name);
|
@NotNull BinaryTagSerializer<Attribute> NBT_TYPE = BinaryTagSerializer.STRING.map(AttributeImpl::get, Attribute::name);
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
|
@ -22,13 +22,13 @@ public final class AttributeInstance {
|
|||||||
public void write(@NotNull NetworkBuffer buffer, AttributeInstance value) {
|
public void write(@NotNull NetworkBuffer buffer, AttributeInstance value) {
|
||||||
buffer.write(Attribute.NETWORK_TYPE, value.attribute());
|
buffer.write(Attribute.NETWORK_TYPE, value.attribute());
|
||||||
buffer.write(NetworkBuffer.DOUBLE, value.getBaseValue());
|
buffer.write(NetworkBuffer.DOUBLE, value.getBaseValue());
|
||||||
buffer.writeCollection(AttributeModifier.NETWORK_TYPE, value.modifiers());
|
buffer.write(AttributeModifier.NETWORK_TYPE.list(Short.MAX_VALUE), List.copyOf(value.modifiers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttributeInstance read(@NotNull NetworkBuffer buffer) {
|
public AttributeInstance read(@NotNull NetworkBuffer buffer) {
|
||||||
return new AttributeInstance(buffer.read(Attribute.NETWORK_TYPE), buffer.read(NetworkBuffer.DOUBLE),
|
return new AttributeInstance(buffer.read(Attribute.NETWORK_TYPE), buffer.read(NetworkBuffer.DOUBLE),
|
||||||
buffer.readCollection(AttributeModifier.NETWORK_TYPE, Short.MAX_VALUE), null);
|
buffer.read(AttributeModifier.NETWORK_TYPE.list(Short.MAX_VALUE)), null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package net.minestom.server.event.player;
|
|||||||
|
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.trait.PlayerEvent;
|
import net.minestom.server.event.trait.PlayerEvent;
|
||||||
|
import net.minestom.server.network.plugin.LoginPlugin;
|
||||||
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
|
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
|
||||||
import net.minestom.server.network.plugin.LoginPluginResponse;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -64,7 +64,7 @@ public class AsyncPlayerPreLoginEvent implements PlayerEvent {
|
|||||||
*
|
*
|
||||||
* @return a CompletableFuture for the response. The thread on which it completes is asynchronous.
|
* @return a CompletableFuture for the response. The thread on which it completes is asynchronous.
|
||||||
*/
|
*/
|
||||||
public @NotNull CompletableFuture<LoginPluginResponse> sendPluginRequest(String channel, byte[] requestPayload) {
|
public @NotNull CompletableFuture<LoginPlugin.Response> sendPluginRequest(String channel, byte[] requestPayload) {
|
||||||
return pluginMessageProcessor.request(channel, requestPayload);
|
return pluginMessageProcessor.request(channel, requestPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ import net.minestom.server.MinecraftServer;
|
|||||||
import net.minestom.server.event.EventDispatcher;
|
import net.minestom.server.event.EventDispatcher;
|
||||||
import net.minestom.server.extras.query.event.BasicQueryEvent;
|
import net.minestom.server.extras.query.event.BasicQueryEvent;
|
||||||
import net.minestom.server.extras.query.event.FullQueryEvent;
|
import net.minestom.server.extras.query.event.FullQueryEvent;
|
||||||
|
import net.minestom.server.extras.query.response.BasicQueryResponse;
|
||||||
|
import net.minestom.server.extras.query.response.FullQueryResponse;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import net.minestom.server.timer.Task;
|
import net.minestom.server.timer.Task;
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
@ -183,24 +185,24 @@ public class Query {
|
|||||||
if (remaining == 0) { // basic
|
if (remaining == 0) { // basic
|
||||||
BasicQueryEvent event = new BasicQueryEvent(sender, sessionID);
|
BasicQueryEvent event = new BasicQueryEvent(sender, sessionID);
|
||||||
EventDispatcher.callCancellable(event, () ->
|
EventDispatcher.callCancellable(event, () ->
|
||||||
sendResponse(event.getQueryResponse(), sessionID, sender));
|
sendResponse(BasicQueryResponse.SERIALIZER, event.getQueryResponse(), sessionID, sender));
|
||||||
} else if (remaining == 5) { // full
|
} else if (remaining == 5) { // full
|
||||||
FullQueryEvent event = new FullQueryEvent(sender, sessionID);
|
FullQueryEvent event = new FullQueryEvent(sender, sessionID);
|
||||||
EventDispatcher.callCancellable(event, () ->
|
EventDispatcher.callCancellable(event, () ->
|
||||||
sendResponse(event.getQueryResponse(), sessionID, sender));
|
sendResponse(FullQueryResponse.SERIALIZER, event.getQueryResponse(), sessionID, sender));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendResponse(@NotNull NetworkBuffer.Writer queryResponse, int sessionID, @NotNull SocketAddress sender) {
|
private static <T> void sendResponse(NetworkBuffer.Type<T> type, @NotNull T queryResponse, int sessionID, @NotNull SocketAddress sender) {
|
||||||
final byte[] responseData = NetworkBuffer.makeArray(buffer -> {
|
final byte[] responseData = NetworkBuffer.makeArray(buffer -> {
|
||||||
// header
|
// header
|
||||||
buffer.write(NetworkBuffer.BYTE, (byte) 0);
|
buffer.write(NetworkBuffer.BYTE, (byte) 0);
|
||||||
buffer.write(NetworkBuffer.INT, sessionID);
|
buffer.write(NetworkBuffer.INT, sessionID);
|
||||||
// payload
|
// payload
|
||||||
buffer.write(queryResponse);
|
buffer.write(type, queryResponse);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
socket.send(new DatagramPacket(responseData, responseData.length, sender));
|
socket.send(new DatagramPacket(responseData, responseData.length, sender));
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.minestom.server.extras.query.event;
|
package net.minestom.server.extras.query.event;
|
||||||
|
|
||||||
import net.minestom.server.event.trait.CancellableEvent;
|
import net.minestom.server.event.trait.CancellableEvent;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@ -12,7 +11,7 @@ import java.util.Objects;
|
|||||||
*
|
*
|
||||||
* @param <T> the type of the response
|
* @param <T> the type of the response
|
||||||
*/
|
*/
|
||||||
public abstract class QueryEvent<T extends NetworkBuffer.Writer> implements CancellableEvent {
|
public abstract class QueryEvent<T> implements CancellableEvent {
|
||||||
private final SocketAddress sender;
|
private final SocketAddress sender;
|
||||||
private final int sessionID;
|
private final int sessionID;
|
||||||
|
|
||||||
|
@ -2,145 +2,43 @@ package net.minestom.server.extras.query.response;
|
|||||||
|
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.SHORT;
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.STRING_TERMINATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic query response containing a fixed set of responses.
|
* A basic query response containing a fixed set of responses.
|
||||||
*/
|
*/
|
||||||
public class BasicQueryResponse implements NetworkBuffer.Writer {
|
public record BasicQueryResponse(String motd, String gameType,
|
||||||
private String motd, gametype, map, numPlayers, maxPlayers;
|
String map,
|
||||||
|
String numPlayers, String maxPlayers,
|
||||||
|
short port, String address) {
|
||||||
/**
|
/**
|
||||||
* Creates a new basic query response with pre-filled default values.
|
* Creates a new basic query response with pre-filled default values.
|
||||||
*/
|
*/
|
||||||
public BasicQueryResponse() {
|
public BasicQueryResponse() {
|
||||||
this.motd = "A Minestom Server";
|
this(
|
||||||
this.gametype = "SMP";
|
"A Minestom Server",
|
||||||
this.map = "world";
|
"SMP",
|
||||||
this.numPlayers = String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayerCount());
|
"world",
|
||||||
this.maxPlayers = String.valueOf(Integer.parseInt(this.numPlayers) + 1);
|
String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayerCount()),
|
||||||
|
"9999",
|
||||||
|
(short) MinecraftServer.getServer().getPort(),
|
||||||
|
Objects.requireNonNullElse(MinecraftServer.getServer().getAddress(), "")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static final NetworkBuffer.Type<BasicQueryResponse> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
* Gets the MoTD.
|
STRING_TERMINATED, BasicQueryResponse::motd,
|
||||||
*
|
STRING_TERMINATED, BasicQueryResponse::gameType,
|
||||||
* @return the motd
|
STRING_TERMINATED, BasicQueryResponse::map,
|
||||||
*/
|
STRING_TERMINATED, BasicQueryResponse::numPlayers,
|
||||||
public @NotNull String getMotd() {
|
STRING_TERMINATED, BasicQueryResponse::maxPlayers,
|
||||||
return this.motd;
|
SHORT, BasicQueryResponse::port, // TODO little endian?
|
||||||
}
|
STRING_TERMINATED, BasicQueryResponse::address,
|
||||||
|
BasicQueryResponse::new
|
||||||
/**
|
);
|
||||||
* Sets the MoTD.
|
|
||||||
*
|
|
||||||
* @param motd the motd
|
|
||||||
*/
|
|
||||||
public void setMotd(@NotNull String motd) {
|
|
||||||
this.motd = Objects.requireNonNull(motd, "motd");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the gametype.
|
|
||||||
*
|
|
||||||
* @return the gametype
|
|
||||||
*/
|
|
||||||
public @NotNull String getGametype() {
|
|
||||||
return this.gametype;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the gametype.
|
|
||||||
*
|
|
||||||
* @param gametype the gametype
|
|
||||||
*/
|
|
||||||
public void setGametype(@NotNull String gametype) {
|
|
||||||
this.gametype = Objects.requireNonNull(gametype, "gametype");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the map.
|
|
||||||
*
|
|
||||||
* @return the map
|
|
||||||
*/
|
|
||||||
public @NotNull String getMap() {
|
|
||||||
return this.map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the map.
|
|
||||||
*
|
|
||||||
* @param map the map
|
|
||||||
*/
|
|
||||||
public void setMap(@NotNull String map) {
|
|
||||||
this.map = Objects.requireNonNull(map, "map");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of players.
|
|
||||||
*
|
|
||||||
* @return the number of players
|
|
||||||
*/
|
|
||||||
public @NotNull String getNumPlayers() {
|
|
||||||
return this.numPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the number of players.
|
|
||||||
*
|
|
||||||
* @param numPlayers the number of players
|
|
||||||
*/
|
|
||||||
public void setNumPlayers(@NotNull String numPlayers) {
|
|
||||||
this.numPlayers = Objects.requireNonNull(numPlayers, "numPlayers");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the number of players.
|
|
||||||
* This method is just an overload for {@link #setNumPlayers(String)}.
|
|
||||||
*
|
|
||||||
* @param numPlayers the number of players
|
|
||||||
*/
|
|
||||||
public void setNumPlayers(int numPlayers) {
|
|
||||||
this.setNumPlayers(String.valueOf(numPlayers));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the max number of players.
|
|
||||||
*
|
|
||||||
* @return the max number of players
|
|
||||||
*/
|
|
||||||
public @NotNull String getMaxPlayers() {
|
|
||||||
return this.maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the max number of players.
|
|
||||||
*
|
|
||||||
* @param maxPlayers the max number of players
|
|
||||||
*/
|
|
||||||
public void setMaxPlayers(@NotNull String maxPlayers) {
|
|
||||||
this.maxPlayers = Objects.requireNonNull(maxPlayers, "maxPlayers");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the max number of players.
|
|
||||||
* This method is just an overload for {@link #setMaxPlayers(String)}
|
|
||||||
*
|
|
||||||
* @param maxPlayers the max number of players
|
|
||||||
*/
|
|
||||||
public void setMaxPlayers(int maxPlayers) {
|
|
||||||
this.setMaxPlayers(String.valueOf(maxPlayers));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, this.motd);
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, this.gametype);
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, this.map);
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, this.numPlayers);
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, this.maxPlayers);
|
|
||||||
writer.write(NetworkBuffer.SHORT, (short) MinecraftServer.getServer().getPort()); // TODO little endian?
|
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, Objects.requireNonNullElse(MinecraftServer.getServer().getAddress(), ""));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import java.util.*;
|
|||||||
/**
|
/**
|
||||||
* A full query response containing a dynamic set of responses.
|
* A full query response containing a dynamic set of responses.
|
||||||
*/
|
*/
|
||||||
public class FullQueryResponse implements NetworkBuffer.Writer {
|
public class FullQueryResponse {
|
||||||
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
|
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
|
||||||
private static final byte[] PADDING_10 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
private static final byte[] PADDING_10 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
PADDING_11 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
PADDING_11 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
@ -123,20 +123,27 @@ public class FullQueryResponse implements NetworkBuffer.Writer {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final NetworkBuffer.Type<FullQueryResponse> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public void write(@NotNull NetworkBuffer buffer, FullQueryResponse value) {
|
||||||
writer.write(NetworkBuffer.RAW_BYTES, PADDING_11);
|
buffer.write(NetworkBuffer.RAW_BYTES, PADDING_11);
|
||||||
// key-values
|
// key-values
|
||||||
for (var entry : this.kv.entrySet()) {
|
for (var entry : value.kv.entrySet()) {
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, entry.getKey());
|
buffer.write(NetworkBuffer.STRING_TERMINATED, entry.getKey());
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, entry.getValue());
|
buffer.write(NetworkBuffer.STRING_TERMINATED, entry.getValue());
|
||||||
}
|
}
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, "");
|
buffer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||||
writer.write(NetworkBuffer.RAW_BYTES, PADDING_10);
|
buffer.write(NetworkBuffer.RAW_BYTES, PADDING_10);
|
||||||
// players
|
// players
|
||||||
for (String player : this.players) {
|
for (String player : value.players) {
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, player);
|
buffer.write(NetworkBuffer.STRING_TERMINATED, player);
|
||||||
}
|
}
|
||||||
writer.write(NetworkBuffer.STRING_TERMINATED, "");
|
buffer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FullQueryResponse read(@NotNull NetworkBuffer buffer) {
|
||||||
|
throw new UnsupportedOperationException("FullQueryResponse is write-only");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public final class VelocityProxy {
|
|||||||
for (int i = 0; i < signature.length; i++) {
|
for (int i = 0; i < signature.length; i++) {
|
||||||
signature[i] = buffer.read(BYTE);
|
signature[i] = buffer.read(BYTE);
|
||||||
}
|
}
|
||||||
final int index = buffer.readIndex();
|
final long index = buffer.readIndex();
|
||||||
final byte[] data = buffer.read(RAW_BYTES);
|
final byte[] data = buffer.read(RAW_BYTES);
|
||||||
buffer.readIndex(index);
|
buffer.readIndex(index);
|
||||||
try {
|
try {
|
||||||
@ -66,7 +66,7 @@ public final class VelocityProxy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
e.printStackTrace();
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
final int version = buffer.read(VAR_INT);
|
final int version = buffer.read(VAR_INT);
|
||||||
return version == SUPPORTED_FORWARDING_VERSION;
|
return version == SUPPORTED_FORWARDING_VERSION;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.minestom.server.gamedata.tags;
|
package net.minestom.server.gamedata.tags;
|
||||||
|
|
||||||
import net.minestom.server.registry.Registry;
|
import net.minestom.server.network.packet.server.common.TagsPacket;
|
||||||
import net.minestom.server.utils.NamespaceID;
|
import net.minestom.server.utils.NamespaceID;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ public final class TagManager {
|
|||||||
// Load required tags from files
|
// Load required tags from files
|
||||||
for (var type : Tag.BasicType.values()) {
|
for (var type : Tag.BasicType.values()) {
|
||||||
if (type.getResource() == null || type.getFunction() == null) continue;
|
if (type.getResource() == null || type.getFunction() == null) continue;
|
||||||
final var json = Registry.load(type.getResource());
|
final var json = net.minestom.server.registry.Registry.load(type.getResource());
|
||||||
final var tagIdentifierMap = tagMap.computeIfAbsent(type, s -> new CopyOnWriteArrayList<>());
|
final var tagIdentifierMap = tagMap.computeIfAbsent(type, s -> new CopyOnWriteArrayList<>());
|
||||||
json.keySet().forEach(tagName -> {
|
json.keySet().forEach(tagName -> {
|
||||||
final var tag = new Tag(NamespaceID.from(tagName), getValues(json, tagName));
|
final var tag = new Tag(NamespaceID.from(tagName), getValues(json, tagName));
|
||||||
@ -40,6 +40,22 @@ public final class TagManager {
|
|||||||
return Collections.unmodifiableMap(tagMap);
|
return Collections.unmodifiableMap(tagMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TagsPacket packet() {
|
||||||
|
List<TagsPacket.Registry> registries = new ArrayList<>();
|
||||||
|
for (Map.Entry<Tag.BasicType, List<Tag>> entry : tagMap.entrySet()) {
|
||||||
|
final Tag.BasicType type = entry.getKey();
|
||||||
|
final String registry = type.getIdentifier();
|
||||||
|
List<TagsPacket.Tag> tags = new ArrayList<>();
|
||||||
|
for (Tag tag : entry.getValue()) {
|
||||||
|
final String identifier = tag.getName().asString();
|
||||||
|
final int[] values = tag.getValues().stream().mapToInt(value -> type.getFunction().apply(value.asString())).toArray();
|
||||||
|
tags.add(new TagsPacket.Tag(identifier, values));
|
||||||
|
}
|
||||||
|
registries.add(new TagsPacket.Registry(registry, tags));
|
||||||
|
}
|
||||||
|
return new TagsPacket(registries);
|
||||||
|
}
|
||||||
|
|
||||||
private Set<NamespaceID> getValues(Map<String, Map<String, Object>> main, String value) {
|
private Set<NamespaceID> getValues(Map<String, Map<String, Object>> main, String value) {
|
||||||
Map<String, Object> tagObject = main.get(value);
|
Map<String, Object> tagObject = main.get(value);
|
||||||
final List<String> tagValues = (List<String>) tagObject.get("values");
|
final List<String> tagValues = (List<String>) tagObject.get("values");
|
||||||
|
@ -12,6 +12,7 @@ import net.minestom.server.instance.block.BlockHandler;
|
|||||||
import net.minestom.server.instance.heightmap.Heightmap;
|
import net.minestom.server.instance.heightmap.Heightmap;
|
||||||
import net.minestom.server.instance.heightmap.MotionBlockingHeightmap;
|
import net.minestom.server.instance.heightmap.MotionBlockingHeightmap;
|
||||||
import net.minestom.server.instance.heightmap.WorldSurfaceHeightmap;
|
import net.minestom.server.instance.heightmap.WorldSurfaceHeightmap;
|
||||||
|
import net.minestom.server.instance.palette.Palette;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import net.minestom.server.network.packet.server.CachedPacket;
|
import net.minestom.server.network.packet.server.CachedPacket;
|
||||||
import net.minestom.server.network.packet.server.SendablePacket;
|
import net.minestom.server.network.packet.server.SendablePacket;
|
||||||
@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.SHORT;
|
||||||
import static net.minestom.server.utils.chunk.ChunkUtils.toSectionRelativeCoordinate;
|
import static net.minestom.server.utils.chunk.ChunkUtils.toSectionRelativeCoordinate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,7 +262,11 @@ public class DynamicChunk extends Chunk {
|
|||||||
heightmapsNBT = getHeightmapNBT();
|
heightmapsNBT = getHeightmapNBT();
|
||||||
|
|
||||||
data = NetworkBuffer.makeArray(networkBuffer -> {
|
data = NetworkBuffer.makeArray(networkBuffer -> {
|
||||||
for (Section section : sections) networkBuffer.write(section);
|
for (Section section : sections) {
|
||||||
|
networkBuffer.write(SHORT, (short) section.blockPalette().count());
|
||||||
|
networkBuffer.write(Palette.BLOCK_SERIALIZER, section.blockPalette());
|
||||||
|
networkBuffer.write(Palette.BIOME_SERIALIZER, section.biomePalette());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package net.minestom.server.instance;
|
|||||||
import net.minestom.server.coordinate.Point;
|
import net.minestom.server.coordinate.Point;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.network.packet.server.play.ExplosionPacket;
|
import net.minestom.server.network.packet.server.play.ExplosionPacket;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -73,7 +73,7 @@ public abstract class Explosion {
|
|||||||
ExplosionPacket packet = new ExplosionPacket(centerX, centerY, centerZ, strength,
|
ExplosionPacket packet = new ExplosionPacket(centerX, centerY, centerZ, strength,
|
||||||
records, 0, 0, 0);
|
records, 0, 0, 0);
|
||||||
postExplosion(instance, blocks, packet);
|
postExplosion(instance, blocks, packet);
|
||||||
PacketUtils.sendGroupedPacket(instance.getPlayers(), packet);
|
PacketSendingUtils.sendGroupedPacket(instance.getPlayers(), packet);
|
||||||
|
|
||||||
postSend(instance, blocks);
|
postSend(instance, blocks);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ import net.minestom.server.timer.Schedulable;
|
|||||||
import net.minestom.server.timer.Scheduler;
|
import net.minestom.server.timer.Scheduler;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
import net.minestom.server.utils.ArrayUtils;
|
||||||
import net.minestom.server.utils.NamespaceID;
|
import net.minestom.server.utils.NamespaceID;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import net.minestom.server.utils.chunk.ChunkCache;
|
import net.minestom.server.utils.chunk.ChunkCache;
|
||||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||||
@ -472,7 +472,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
|
|||||||
*/
|
*/
|
||||||
public void setTime(long time) {
|
public void setTime(long time) {
|
||||||
this.time = time;
|
this.time = time;
|
||||||
PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
PacketSendingUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -774,7 +774,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
|
|||||||
this.time += timeRate;
|
this.time += timeRate;
|
||||||
// time needs to be sent to players
|
// time needs to be sent to players
|
||||||
if (timeSynchronizationTicks > 0 && this.worldAge % timeSynchronizationTicks == 0) {
|
if (timeSynchronizationTicks > 0 && this.worldAge % timeSynchronizationTicks == 0) {
|
||||||
PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
PacketSendingUtils.sendGroupedPacket(getPlayers(), createTimePacket());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import net.minestom.server.network.packet.server.play.EffectPacket;
|
|||||||
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
|
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.NamespaceID;
|
import net.minestom.server.utils.NamespaceID;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.block.BlockUtils;
|
import net.minestom.server.utils.block.BlockUtils;
|
||||||
import net.minestom.server.utils.chunk.ChunkCache;
|
import net.minestom.server.utils.chunk.ChunkCache;
|
||||||
@ -240,7 +240,7 @@ public class InstanceContainer extends Instance {
|
|||||||
UNSAFE_setBlock(chunk, x, y, z, resultBlock, null,
|
UNSAFE_setBlock(chunk, x, y, z, resultBlock, null,
|
||||||
new BlockHandler.PlayerDestroy(block, this, blockPosition, player), doBlockUpdates, 0);
|
new BlockHandler.PlayerDestroy(block, this, blockPosition, player), doBlockUpdates, 0);
|
||||||
// Send the block break effect packet
|
// Send the block break effect packet
|
||||||
PacketUtils.sendGroupedPacket(chunk.getViewers(),
|
PacketSendingUtils.sendGroupedPacket(chunk.getViewers(),
|
||||||
new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false),
|
new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false),
|
||||||
// Prevent the block breaker to play the particles and sound two times
|
// Prevent the block breaker to play the particles and sound two times
|
||||||
(viewer) -> !viewer.equals(player));
|
(viewer) -> !viewer.equals(player));
|
||||||
|
@ -2,16 +2,14 @@ package net.minestom.server.instance;
|
|||||||
|
|
||||||
import net.minestom.server.instance.light.Light;
|
import net.minestom.server.instance.light.Light;
|
||||||
import net.minestom.server.instance.palette.Palette;
|
import net.minestom.server.instance.palette.Palette;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static net.minestom.server.instance.light.LightCompute.CONTENT_FULLY_LIT;
|
import static net.minestom.server.instance.light.LightCompute.CONTENT_FULLY_LIT;
|
||||||
import static net.minestom.server.instance.light.LightCompute.EMPTY_CONTENT;
|
import static net.minestom.server.instance.light.LightCompute.EMPTY_CONTENT;
|
||||||
import static net.minestom.server.network.NetworkBuffer.SHORT;
|
|
||||||
|
|
||||||
public final class Section implements NetworkBuffer.Writer {
|
public final class Section {
|
||||||
private final Palette blockPalette;
|
private final Palette blockPalette;
|
||||||
private final Palette biomePalette;
|
private final Palette biomePalette;
|
||||||
private final Light skyLight;
|
private final Light skyLight;
|
||||||
@ -56,13 +54,6 @@ public final class Section implements NetworkBuffer.Writer {
|
|||||||
return new Section(this.blockPalette.clone(), this.biomePalette.clone(), skyLight, blockLight);
|
return new Section(this.blockPalette.clone(), this.biomePalette.clone(), skyLight, blockLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(SHORT, (short) blockPalette.count());
|
|
||||||
writer.write(blockPalette);
|
|
||||||
writer.write(biomePalette);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSkyLight(byte[] copyArray) {
|
public void setSkyLight(byte[] copyArray) {
|
||||||
if (copyArray == null || copyArray.length == 0) this.skyLight.set(EMPTY_CONTENT);
|
if (copyArray == null || copyArray.length == 0) this.skyLight.set(EMPTY_CONTENT);
|
||||||
else if (Arrays.equals(copyArray, EMPTY_CONTENT)) this.skyLight.set(EMPTY_CONTENT);
|
else if (Arrays.equals(copyArray, EMPTY_CONTENT)) this.skyLight.set(EMPTY_CONTENT);
|
||||||
|
@ -9,8 +9,8 @@ import net.minestom.server.instance.Instance;
|
|||||||
import net.minestom.server.instance.Section;
|
import net.minestom.server.instance.Section;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.instance.block.BlockHandler;
|
import net.minestom.server.instance.block.BlockHandler;
|
||||||
|
import net.minestom.server.instance.palette.Palettes;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import net.minestom.server.utils.NamespaceID;
|
import net.minestom.server.utils.NamespaceID;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
@ -196,7 +196,7 @@ public class AnvilLoader implements IChunkLoader {
|
|||||||
|
|
||||||
int bitsPerEntry = packedIndices.length * 64 / biomeIndices.length;
|
int bitsPerEntry = packedIndices.length * 64 / biomeIndices.length;
|
||||||
if (bitsPerEntry > 3) bitsPerEntry = MathUtils.bitsToRepresent(convertedBiomePalette.length);
|
if (bitsPerEntry > 3) bitsPerEntry = MathUtils.bitsToRepresent(convertedBiomePalette.length);
|
||||||
ArrayUtils.unpack(biomeIndices, packedIndices, bitsPerEntry);
|
Palettes.unpack(biomeIndices, packedIndices, bitsPerEntry);
|
||||||
|
|
||||||
section.biomePalette().setAll((x, y, z) -> {
|
section.biomePalette().setAll((x, y, z) -> {
|
||||||
final int index = x + z * 4 + y * 16;
|
final int index = x + z * 4 + y * 16;
|
||||||
@ -216,7 +216,7 @@ public class AnvilLoader implements IChunkLoader {
|
|||||||
final long[] packedStates = blockStatesTag.getLongArray("data");
|
final long[] packedStates = blockStatesTag.getLongArray("data");
|
||||||
Check.stateCondition(packedStates.length == 0, "Missing packed states data");
|
Check.stateCondition(packedStates.length == 0, "Missing packed states data");
|
||||||
int[] blockStateIndices = new int[Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE];
|
int[] blockStateIndices = new int[Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SECTION_SIZE];
|
||||||
ArrayUtils.unpack(blockStateIndices, packedStates, packedStates.length * 64 / blockStateIndices.length);
|
Palettes.unpack(blockStateIndices, packedStates, packedStates.length * 64 / blockStateIndices.length);
|
||||||
|
|
||||||
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
||||||
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
|
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
|
||||||
@ -465,7 +465,7 @@ public class AnvilLoader implements IChunkLoader {
|
|||||||
if (blockPaletteEntries.size() > 1) {
|
if (blockPaletteEntries.size() > 1) {
|
||||||
// If there is only one entry we do not need to write the packed indices
|
// If there is only one entry we do not need to write the packed indices
|
||||||
var bitsPerEntry = (int) Math.max(4, Math.ceil(Math.log(blockPaletteEntries.size()) / Math.log(2)));
|
var bitsPerEntry = (int) Math.max(4, Math.ceil(Math.log(blockPaletteEntries.size()) / Math.log(2)));
|
||||||
blockStates.putLongArray("data", ArrayUtils.pack(blockIndices, bitsPerEntry));
|
blockStates.putLongArray("data", Palettes.pack(blockIndices, bitsPerEntry));
|
||||||
}
|
}
|
||||||
sectionData.put("block_states", blockStates.build());
|
sectionData.put("block_states", blockStates.build());
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ public class AnvilLoader implements IChunkLoader {
|
|||||||
if (biomePalette.size() > 1) {
|
if (biomePalette.size() > 1) {
|
||||||
// If there is only one entry we do not need to write the packed indices
|
// If there is only one entry we do not need to write the packed indices
|
||||||
var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(biomePalette.size()) / Math.log(2)));
|
var bitsPerEntry = (int) Math.max(1, Math.ceil(Math.log(biomePalette.size()) / Math.log(2)));
|
||||||
biomes.putLongArray("data", ArrayUtils.pack(biomeIndices, bitsPerEntry));
|
biomes.putLongArray("data", Palettes.pack(biomeIndices, bitsPerEntry));
|
||||||
}
|
}
|
||||||
sectionData.put("biomes", biomes.build());
|
sectionData.put("biomes", biomes.build());
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import java.util.function.BiPredicate;
|
|||||||
public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks permits BlockImpl {
|
public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks permits BlockImpl {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
NetworkBuffer.Type<Block> NETWORK_TYPE = NetworkBuffer.VAR_INT.map(Block::fromStateId, Block::stateId);
|
NetworkBuffer.Type<Block> NETWORK_TYPE = NetworkBuffer.VAR_INT.transform(Block::fromStateId, Block::stateId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new block with the the property {@code property} sets to {@code value}.
|
* Creates a new block with the the property {@code property} sets to {@code value}.
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.instance.block.BlockHandler;
|
import net.minestom.server.instance.block.BlockHandler;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.block.BlockUtils;
|
import net.minestom.server.utils.block.BlockUtils;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -14,6 +15,8 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.NBT_COMPOUND;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A predicate to filter blocks based on their name, properties, and/or nbt.</p>
|
* <p>A predicate to filter blocks based on their name, properties, and/or nbt.</p>
|
||||||
*
|
*
|
||||||
@ -44,23 +47,13 @@ public record BlockPredicate(
|
|||||||
*/
|
*/
|
||||||
public static final BlockPredicate NONE = new BlockPredicate(null, new PropertiesPredicate(Map.of("no_such_property", new PropertiesPredicate.ValuePredicate.Exact("never"))), null);
|
public static final BlockPredicate NONE = new BlockPredicate(null, new PropertiesPredicate(Map.of("no_such_property", new PropertiesPredicate.ValuePredicate.Exact("never"))), null);
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<BlockPredicate> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<BlockPredicate> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
BlockTypeFilter.NETWORK_TYPE.optional(), BlockPredicate::blocks,
|
||||||
public void write(@NotNull NetworkBuffer buffer, BlockPredicate value) {
|
PropertiesPredicate.NETWORK_TYPE.optional(), BlockPredicate::state,
|
||||||
buffer.writeOptional(BlockTypeFilter.NETWORK_TYPE, value.blocks);
|
NBT_COMPOUND.optional(), BlockPredicate::nbt,
|
||||||
buffer.writeOptional(PropertiesPredicate.NETWORK_TYPE, value.state);
|
BlockPredicate::new
|
||||||
buffer.writeOptional(NetworkBuffer.NBT, value.nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPredicate read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new BlockPredicate(
|
|
||||||
buffer.readOptional(BlockTypeFilter.NETWORK_TYPE),
|
|
||||||
buffer.readOptional(PropertiesPredicate.NETWORK_TYPE),
|
|
||||||
(CompoundBinaryTag) buffer.readOptional(NetworkBuffer.NBT)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<BlockPredicate> NBT_TYPE = new BinaryTagSerializer<>() {
|
public static final BinaryTagSerializer<BlockPredicate> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull BinaryTag write(@NotNull BlockPredicate value) {
|
public @NotNull BinaryTag write(@NotNull BlockPredicate value) {
|
||||||
@ -116,5 +109,4 @@ public record BlockPredicate(
|
|||||||
return false;
|
return false;
|
||||||
return nbt == null || Objects.equals(nbt, BlockUtils.extractClientNbt(block));
|
return nbt == null || Objects.equals(nbt, BlockUtils.extractClientNbt(block));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -13,28 +14,14 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.STRING;
|
||||||
|
|
||||||
public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> properties) implements Predicate<Block> {
|
public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> properties) implements Predicate<Block> {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<PropertiesPredicate> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<PropertiesPredicate> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
NetworkBuffer.STRING.mapValue(ValuePredicate.NETWORK_TYPE), PropertiesPredicate::properties,
|
||||||
public void write(@NotNull NetworkBuffer buffer, PropertiesPredicate value) {
|
PropertiesPredicate::new
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.properties.size());
|
);
|
||||||
for (Map.Entry<String, ValuePredicate> entry : value.properties.entrySet()) {
|
|
||||||
buffer.write(NetworkBuffer.STRING, entry.getKey());
|
|
||||||
buffer.write(ValuePredicate.NETWORK_TYPE, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PropertiesPredicate read(@NotNull NetworkBuffer buffer) {
|
|
||||||
int size = buffer.read(NetworkBuffer.VAR_INT);
|
|
||||||
Map<String, ValuePredicate> properties = new HashMap<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
properties.put(buffer.read(NetworkBuffer.STRING), buffer.read(ValuePredicate.NETWORK_TYPE));
|
|
||||||
}
|
|
||||||
return new PropertiesPredicate(properties);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<PropertiesPredicate> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<PropertiesPredicate> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> {
|
tag -> {
|
||||||
Map<String, ValuePredicate> properties = new HashMap<>();
|
Map<String, ValuePredicate> properties = new HashMap<>();
|
||||||
@ -74,7 +61,7 @@ public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> propertie
|
|||||||
|
|
||||||
record Exact(@Nullable String value) implements ValuePredicate {
|
record Exact(@Nullable String value) implements ValuePredicate {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Exact> NETWORK_TYPE = NetworkBuffer.STRING.map(Exact::new, Exact::value);
|
public static final NetworkBuffer.Type<Exact> NETWORK_TYPE = NetworkBuffer.STRING.transform(Exact::new, Exact::value);
|
||||||
public static final BinaryTagSerializer<Exact> NBT_TYPE = BinaryTagSerializer.STRING.map(Exact::new, Exact::value);
|
public static final BinaryTagSerializer<Exact> NBT_TYPE = BinaryTagSerializer.STRING.map(Exact::new, Exact::value);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,19 +81,12 @@ public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> propertie
|
|||||||
* @param max The max value to match, exclusive
|
* @param max The max value to match, exclusive
|
||||||
*/
|
*/
|
||||||
record Range(@Nullable String min, @Nullable String max) implements ValuePredicate {
|
record Range(@Nullable String min, @Nullable String max) implements ValuePredicate {
|
||||||
|
public static final NetworkBuffer.Type<Range> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
|
STRING.optional(), Range::min,
|
||||||
|
STRING.optional(), Range::max,
|
||||||
|
Range::new
|
||||||
|
);
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Range> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer buffer, Range value) {
|
|
||||||
buffer.writeOptional(NetworkBuffer.STRING, value.min);
|
|
||||||
buffer.writeOptional(NetworkBuffer.STRING, value.max);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Range read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Range(buffer.readOptional(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.STRING));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Range> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Range> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Range(
|
tag -> new Range(
|
||||||
tag.get("min") instanceof StringBinaryTag string ? string.value() : null,
|
tag.get("min") instanceof StringBinaryTag string ? string.value() : null,
|
||||||
|
@ -2,7 +2,6 @@ package net.minestom.server.instance.palette;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ final class AdaptivePalette implements Palette, Cloneable {
|
|||||||
this.dimension = dimension;
|
this.dimension = dimension;
|
||||||
this.maxBitsPerEntry = maxBitsPerEntry;
|
this.maxBitsPerEntry = maxBitsPerEntry;
|
||||||
this.defaultBitsPerEntry = bitsPerEntry;
|
this.defaultBitsPerEntry = bitsPerEntry;
|
||||||
this.palette = new FilledPalette(dimension, 0);
|
this.palette = new PaletteSingle(dimension, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,12 +50,12 @@ final class AdaptivePalette implements Palette, Cloneable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fill(int value) {
|
public void fill(int value) {
|
||||||
this.palette = new FilledPalette(dimension, value);
|
this.palette = new PaletteSingle(dimension, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAll(@NotNull EntrySupplier supplier) {
|
public void setAll(@NotNull EntrySupplier supplier) {
|
||||||
SpecializedPalette newPalette = new FlexiblePalette(this);
|
SpecializedPalette newPalette = new PaletteIndirect(this);
|
||||||
newPalette.setAll(supplier);
|
newPalette.setAll(supplier);
|
||||||
this.palette = newPalette;
|
this.palette = newPalette;
|
||||||
}
|
}
|
||||||
@ -105,31 +104,24 @@ final class AdaptivePalette implements Palette, Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
final SpecializedPalette optimized = optimizedPalette();
|
|
||||||
this.palette = optimized;
|
|
||||||
optimized.write(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpecializedPalette optimizedPalette() {
|
SpecializedPalette optimizedPalette() {
|
||||||
var currentPalette = this.palette;
|
var currentPalette = this.palette;
|
||||||
if (currentPalette instanceof FlexiblePalette flexiblePalette) {
|
if (currentPalette instanceof PaletteIndirect paletteIndirect) {
|
||||||
final int count = flexiblePalette.count();
|
final int count = paletteIndirect.count();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return new FilledPalette(dimension, 0);
|
return new PaletteSingle(dimension, 0);
|
||||||
} else {
|
} else {
|
||||||
// Find all entries and compress the palette
|
// Find all entries and compress the palette
|
||||||
IntSet entries = new IntOpenHashSet(flexiblePalette.paletteToValueList.size());
|
IntSet entries = new IntOpenHashSet(paletteIndirect.paletteToValueList.size());
|
||||||
flexiblePalette.getAll((x, y, z, value) -> entries.add(value));
|
paletteIndirect.getAll((x, y, z, value) -> entries.add(value));
|
||||||
final int currentBitsPerEntry = flexiblePalette.bitsPerEntry();
|
final int currentBitsPerEntry = paletteIndirect.bitsPerEntry();
|
||||||
final int bitsPerEntry;
|
final int bitsPerEntry;
|
||||||
if (entries.size() == 1) {
|
if (entries.size() == 1) {
|
||||||
return new FilledPalette(dimension, entries.iterator().nextInt());
|
return new PaletteSingle(dimension, entries.iterator().nextInt());
|
||||||
} else if (currentBitsPerEntry > defaultBitsPerEntry &&
|
} else if (currentBitsPerEntry > defaultBitsPerEntry &&
|
||||||
(bitsPerEntry = MathUtils.bitsToRepresent(entries.size() - 1)) < currentBitsPerEntry) {
|
(bitsPerEntry = MathUtils.bitsToRepresent(entries.size() - 1)) < currentBitsPerEntry) {
|
||||||
flexiblePalette.resize((byte) bitsPerEntry);
|
paletteIndirect.resize((byte) bitsPerEntry);
|
||||||
return flexiblePalette;
|
return paletteIndirect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,9 +130,9 @@ final class AdaptivePalette implements Palette, Cloneable {
|
|||||||
|
|
||||||
Palette flexiblePalette() {
|
Palette flexiblePalette() {
|
||||||
SpecializedPalette currentPalette = this.palette;
|
SpecializedPalette currentPalette = this.palette;
|
||||||
if (currentPalette instanceof FilledPalette filledPalette) {
|
if (currentPalette instanceof PaletteSingle paletteSingle) {
|
||||||
currentPalette = new FlexiblePalette(this);
|
currentPalette = new PaletteIndirect(this);
|
||||||
currentPalette.fill(filledPalette.value());
|
currentPalette.fill(paletteSingle.value());
|
||||||
this.palette = currentPalette;
|
this.palette = currentPalette;
|
||||||
}
|
}
|
||||||
return currentPalette;
|
return currentPalette;
|
||||||
|
@ -5,12 +5,14 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.function.IntUnaryOperator;
|
import java.util.function.IntUnaryOperator;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a palette used to store blocks and biomes.
|
* Represents a palette used to store blocks and biomes.
|
||||||
* <p>
|
* <p>
|
||||||
* 0 is the default value.
|
* 0 is the default value.
|
||||||
*/
|
*/
|
||||||
public interface Palette extends NetworkBuffer.Writer {
|
public interface Palette {
|
||||||
static Palette blocks() {
|
static Palette blocks() {
|
||||||
return newPalette(16, 8, 4);
|
return newPalette(16, 8, 4);
|
||||||
}
|
}
|
||||||
@ -77,4 +79,58 @@ public interface Palette extends NetworkBuffer.Writer {
|
|||||||
interface EntryFunction {
|
interface EntryFunction {
|
||||||
int apply(int x, int y, int z, int value);
|
int apply(int x, int y, int z, int value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkBuffer.Type<Palette> BLOCK_SERIALIZER = serializer(16, 4, 8);
|
||||||
|
NetworkBuffer.Type<Palette> BIOME_SERIALIZER = serializer(4, 1, 3);
|
||||||
|
|
||||||
|
static NetworkBuffer.Type<Palette> serializer(int dimension, int minIndirect, int maxIndirect) {
|
||||||
|
return new NetworkBuffer.Type<>() {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, Palette value) {
|
||||||
|
switch (value) {
|
||||||
|
case AdaptivePalette adaptive -> {
|
||||||
|
final SpecializedPalette optimized = adaptive.optimizedPalette();
|
||||||
|
adaptive.palette = optimized;
|
||||||
|
BLOCK_SERIALIZER.write(buffer, optimized);
|
||||||
|
}
|
||||||
|
case PaletteSingle single -> {
|
||||||
|
buffer.write(BYTE, (byte) 0);
|
||||||
|
buffer.write(VAR_INT, single.value());
|
||||||
|
buffer.write(VAR_INT, 0);
|
||||||
|
}
|
||||||
|
case PaletteIndirect indirect -> {
|
||||||
|
buffer.write(BYTE, (byte) value.bitsPerEntry());
|
||||||
|
if (indirect.bitsPerEntry() <= indirect.maxBitsPerEntry()) { // Palette index
|
||||||
|
buffer.write(VAR_INT.list(), indirect.paletteToValueList);
|
||||||
|
}
|
||||||
|
buffer.write(LONG_ARRAY, indirect.values);
|
||||||
|
}
|
||||||
|
default -> throw new UnsupportedOperationException("Unsupported palette type: " + value.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Palette read(@NotNull NetworkBuffer buffer) {
|
||||||
|
final byte bitsPerEntry = buffer.read(BYTE);
|
||||||
|
if (bitsPerEntry == 0) {
|
||||||
|
// Single valued 0-0
|
||||||
|
final int value = buffer.read(VAR_INT);
|
||||||
|
return new PaletteSingle((byte) dimension, value);
|
||||||
|
} else if (bitsPerEntry >= minIndirect && bitsPerEntry <= maxIndirect) {
|
||||||
|
// Indirect palette
|
||||||
|
final int[] palette = buffer.read(VAR_INT_ARRAY);
|
||||||
|
final long[] data = buffer.read(LONG_ARRAY);
|
||||||
|
return new PaletteIndirect(dimension, maxIndirect, bitsPerEntry,
|
||||||
|
Palettes.count(bitsPerEntry, data),
|
||||||
|
palette, data);
|
||||||
|
} else {
|
||||||
|
// Direct palette
|
||||||
|
final long[] data = buffer.read(LONG_ARRAY);
|
||||||
|
return new PaletteIndirect(dimension, maxIndirect, bitsPerEntry,
|
||||||
|
Palettes.count(bitsPerEntry, data),
|
||||||
|
new int[0], data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package net.minestom.server.instance.palette;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -11,52 +10,64 @@ import java.util.Arrays;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.IntUnaryOperator;
|
import java.util.function.IntUnaryOperator;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.*;
|
import static net.minestom.server.instance.palette.Palettes.arrayLength;
|
||||||
|
import static net.minestom.server.instance.palette.Palettes.read;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Palette able to take any value anywhere. May consume more memory than required.
|
* Palette able to take any value anywhere. May consume more memory than required.
|
||||||
*/
|
*/
|
||||||
final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
final class PaletteIndirect implements SpecializedPalette, Cloneable {
|
||||||
private static final ThreadLocal<int[]> WRITE_CACHE = ThreadLocal.withInitial(() -> new int[4096]);
|
private static final ThreadLocal<int[]> WRITE_CACHE = ThreadLocal.withInitial(() -> new int[4096]);
|
||||||
|
|
||||||
// Specific to this palette type
|
// Specific to this palette type
|
||||||
private final AdaptivePalette adaptivePalette;
|
private final int dimension;
|
||||||
|
private final int maxBitsPerEntry;
|
||||||
|
|
||||||
private byte bitsPerEntry;
|
private byte bitsPerEntry;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
||||||
private long[] values;
|
long[] values;
|
||||||
// palette index = value
|
// palette index = value
|
||||||
IntArrayList paletteToValueList;
|
IntArrayList paletteToValueList;
|
||||||
// value = palette index
|
// value = palette index
|
||||||
private Int2IntOpenHashMap valueToPaletteMap;
|
private Int2IntOpenHashMap valueToPaletteMap;
|
||||||
|
|
||||||
FlexiblePalette(AdaptivePalette adaptivePalette, byte bitsPerEntry) {
|
PaletteIndirect(int dimension, int maxBitsPerEntry, byte bitsPerEntry,
|
||||||
this.adaptivePalette = adaptivePalette;
|
int count, int[] palette, long[] values) {
|
||||||
|
this.dimension = dimension;
|
||||||
|
this.maxBitsPerEntry = maxBitsPerEntry;
|
||||||
this.bitsPerEntry = bitsPerEntry;
|
this.bitsPerEntry = bitsPerEntry;
|
||||||
|
|
||||||
this.paletteToValueList = new IntArrayList(1);
|
this.count = count;
|
||||||
this.paletteToValueList.add(0);
|
this.values = values;
|
||||||
this.valueToPaletteMap = new Int2IntOpenHashMap(1);
|
|
||||||
this.valueToPaletteMap.put(0, 0);
|
this.paletteToValueList = new IntArrayList(palette.length);
|
||||||
|
this.valueToPaletteMap = new Int2IntOpenHashMap(palette.length);
|
||||||
this.valueToPaletteMap.defaultReturnValue(-1);
|
this.valueToPaletteMap.defaultReturnValue(-1);
|
||||||
|
|
||||||
final int valuesPerLong = 64 / bitsPerEntry;
|
for (int i = 0; i < palette.length; i++) {
|
||||||
this.values = new long[(maxSize() + valuesPerLong - 1) / valuesPerLong];
|
this.paletteToValueList.add(palette[i]);
|
||||||
|
this.valueToPaletteMap.put(palette[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexiblePalette(AdaptivePalette adaptivePalette) {
|
this.values = new long[arrayLength(dimension(), bitsPerEntry)];
|
||||||
this(adaptivePalette, adaptivePalette.defaultBitsPerEntry);
|
}
|
||||||
|
|
||||||
|
PaletteIndirect(int dimension, int maxBitsPerEntry, byte bitsPerEntry) {
|
||||||
|
this(dimension, maxBitsPerEntry, bitsPerEntry,
|
||||||
|
0,
|
||||||
|
new int[]{0},
|
||||||
|
new long[arrayLength(dimension, bitsPerEntry)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PaletteIndirect(AdaptivePalette palette) {
|
||||||
|
this(palette.dimension, palette.maxBitsPerEntry, palette.defaultBitsPerEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int get(int x, int y, int z) {
|
public int get(int x, int y, int z) {
|
||||||
final int bitsPerEntry = this.bitsPerEntry;
|
final int value = read(dimension(), bitsPerEntry, values, x, y, z);
|
||||||
final int sectionIndex = getSectionIndex(dimension(), x, y, z);
|
|
||||||
final int valuesPerLong = 64 / bitsPerEntry;
|
|
||||||
final int index = sectionIndex / valuesPerLong;
|
|
||||||
final int bitIndex = (sectionIndex - index * valuesPerLong) * bitsPerEntry;
|
|
||||||
final int value = (int) (values[index] >> bitIndex) & ((1 << bitsPerEntry) - 1);
|
|
||||||
// Change to palette value and return
|
// Change to palette value and return
|
||||||
return hasPalette() ? paletteToValueList.getInt(value) : value;
|
return hasPalette() ? paletteToValueList.getInt(value) : value;
|
||||||
}
|
}
|
||||||
@ -74,20 +85,9 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
@Override
|
@Override
|
||||||
public void set(int x, int y, int z, int value) {
|
public void set(int x, int y, int z, int value) {
|
||||||
value = getPaletteIndex(value);
|
value = getPaletteIndex(value);
|
||||||
final int bitsPerEntry = this.bitsPerEntry;
|
final int oldValue = Palettes.write(dimension(), bitsPerEntry, values, x, y, z, value);
|
||||||
final long[] values = this.values;
|
|
||||||
// Change to palette value
|
|
||||||
final int valuesPerLong = 64 / bitsPerEntry;
|
|
||||||
final int sectionIndex = getSectionIndex(dimension(), x, y, z);
|
|
||||||
final int index = sectionIndex / valuesPerLong;
|
|
||||||
final int bitIndex = (sectionIndex - index * valuesPerLong) * bitsPerEntry;
|
|
||||||
|
|
||||||
final long block = values[index];
|
|
||||||
final long clear = (1L << bitsPerEntry) - 1L;
|
|
||||||
final long oldBlock = block >> bitIndex & clear;
|
|
||||||
values[index] = block & ~(clear << bitIndex) | ((long) value << bitIndex);
|
|
||||||
// Check if block count needs to be updated
|
// Check if block count needs to be updated
|
||||||
final boolean currentAir = oldBlock == 0;
|
final boolean currentAir = oldValue == 0;
|
||||||
if (currentAir != (value == 0)) this.count += currentAir ? 1 : -1;
|
if (currentAir != (value == 0)) this.count += currentAir ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,13 +99,7 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
value = getPaletteIndex(value);
|
value = getPaletteIndex(value);
|
||||||
final int bitsPerEntry = this.bitsPerEntry;
|
Palettes.fill(bitsPerEntry, values, value);
|
||||||
final int valuesPerLong = 64 / bitsPerEntry;
|
|
||||||
final long[] values = this.values;
|
|
||||||
long block = 0;
|
|
||||||
for (int i = 0; i < valuesPerLong; i++)
|
|
||||||
block |= (long) value << i * bitsPerEntry;
|
|
||||||
Arrays.fill(values, block);
|
|
||||||
this.count = maxSize();
|
this.count = maxSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,18 +179,18 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int maxBitsPerEntry() {
|
public int maxBitsPerEntry() {
|
||||||
return adaptivePalette.maxBitsPerEntry();
|
return maxBitsPerEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int dimension() {
|
public int dimension() {
|
||||||
return adaptivePalette.dimension();
|
return dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull SpecializedPalette clone() {
|
public @NotNull SpecializedPalette clone() {
|
||||||
try {
|
try {
|
||||||
FlexiblePalette palette = (FlexiblePalette) super.clone();
|
PaletteIndirect palette = (PaletteIndirect) super.clone();
|
||||||
palette.values = values != null ? values.clone() : null;
|
palette.values = values != null ? values.clone() : null;
|
||||||
palette.paletteToValueList = paletteToValueList.clone();
|
palette.paletteToValueList = paletteToValueList.clone();
|
||||||
palette.valueToPaletteMap = valueToPaletteMap.clone();
|
palette.valueToPaletteMap = valueToPaletteMap.clone();
|
||||||
@ -208,15 +202,6 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(BYTE, bitsPerEntry);
|
|
||||||
if (bitsPerEntry <= maxBitsPerEntry()) { // Palette index
|
|
||||||
writer.writeCollection(VAR_INT, paletteToValueList);
|
|
||||||
}
|
|
||||||
writer.write(LONG_ARRAY, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void retrieveAll(@NotNull EntryConsumer consumer, boolean consumeEmpty) {
|
private void retrieveAll(@NotNull EntryConsumer consumer, boolean consumeEmpty) {
|
||||||
if (!consumeEmpty && count == 0) return;
|
if (!consumeEmpty && count == 0) return;
|
||||||
final long[] values = this.values;
|
final long[] values = this.values;
|
||||||
@ -268,7 +253,7 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
|
|
||||||
void resize(byte newBitsPerEntry) {
|
void resize(byte newBitsPerEntry) {
|
||||||
newBitsPerEntry = newBitsPerEntry > maxBitsPerEntry() ? 15 : newBitsPerEntry;
|
newBitsPerEntry = newBitsPerEntry > maxBitsPerEntry() ? 15 : newBitsPerEntry;
|
||||||
FlexiblePalette palette = new FlexiblePalette(adaptivePalette, newBitsPerEntry);
|
PaletteIndirect palette = new PaletteIndirect(dimension, maxBitsPerEntry, newBitsPerEntry);
|
||||||
palette.paletteToValueList = paletteToValueList;
|
palette.paletteToValueList = paletteToValueList;
|
||||||
palette.valueToPaletteMap = valueToPaletteMap;
|
palette.valueToPaletteMap = valueToPaletteMap;
|
||||||
getAll(palette::set);
|
getAll(palette::set);
|
||||||
@ -297,14 +282,6 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
|||||||
return bitsPerEntry <= maxBitsPerEntry();
|
return bitsPerEntry <= maxBitsPerEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getSectionIndex(int dimension, int x, int y, int z) {
|
|
||||||
final int dimensionMask = dimension - 1;
|
|
||||||
final int dimensionBitCount = MathUtils.bitsToRepresent(dimensionMask);
|
|
||||||
return (y & dimensionMask) << (dimensionBitCount << 1) |
|
|
||||||
(z & dimensionMask) << dimensionBitCount |
|
|
||||||
(x & dimensionMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int maxPaletteSize(int bitsPerEntry) {
|
static int maxPaletteSize(int bitsPerEntry) {
|
||||||
return 1 << bitsPerEntry;
|
return 1 << bitsPerEntry;
|
||||||
}
|
}
|
@ -1,15 +1,11 @@
|
|||||||
package net.minestom.server.instance.palette;
|
package net.minestom.server.instance.palette;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.BYTE;
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Palette containing a single value. Useful for both empty and full palettes.
|
* Palette containing a single value. Useful for both empty and full palettes.
|
||||||
*/
|
*/
|
||||||
record FilledPalette(byte dim, int value) implements SpecializedPalette.Immutable {
|
record PaletteSingle(byte dim, int value) implements SpecializedPalette.Immutable {
|
||||||
@Override
|
@Override
|
||||||
public int get(int x, int y, int z) {
|
public int get(int x, int y, int z) {
|
||||||
return value;
|
return value;
|
||||||
@ -44,11 +40,4 @@ record FilledPalette(byte dim, int value) implements SpecializedPalette.Immutabl
|
|||||||
public @NotNull SpecializedPalette clone() {
|
public @NotNull SpecializedPalette clone() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
|
||||||
writer.write(BYTE, (byte) 0);
|
|
||||||
writer.write(VAR_INT, value);
|
|
||||||
writer.write(VAR_INT, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package net.minestom.server.instance.palette;
|
||||||
|
|
||||||
|
import net.minestom.server.utils.MathUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class Palettes {
|
||||||
|
private Palettes() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long[] pack(int[] ints, int bitsPerEntry) {
|
||||||
|
final int intsPerLong = (int) Math.floor(64d / bitsPerEntry);
|
||||||
|
long[] longs = new long[(int) Math.ceil(ints.length / (double) intsPerLong)];
|
||||||
|
|
||||||
|
final long mask = (1L << bitsPerEntry) - 1L;
|
||||||
|
for (int i = 0; i < longs.length; i++) {
|
||||||
|
for (int intIndex = 0; intIndex < intsPerLong; intIndex++) {
|
||||||
|
final int bitIndex = intIndex * bitsPerEntry;
|
||||||
|
final int intActualIndex = intIndex + i * intsPerLong;
|
||||||
|
if (intActualIndex < ints.length) {
|
||||||
|
longs[i] |= (ints[intActualIndex] & mask) << bitIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return longs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unpack(int[] out, long[] in, int bitsPerEntry) {
|
||||||
|
assert in.length != 0 : "unpack input array is zero";
|
||||||
|
|
||||||
|
final double intsPerLong = Math.floor(64d / bitsPerEntry);
|
||||||
|
final int intsPerLongCeil = (int) Math.ceil(intsPerLong);
|
||||||
|
|
||||||
|
long mask = (1L << bitsPerEntry) - 1L;
|
||||||
|
for (int i = 0; i < out.length; i++) {
|
||||||
|
final int longIndex = i / intsPerLongCeil;
|
||||||
|
final int subIndex = i % intsPerLongCeil;
|
||||||
|
|
||||||
|
out[i] = (int) ((in[longIndex] >>> (bitsPerEntry * subIndex)) & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int arrayLength(int dimension, int bitsPerEntry) {
|
||||||
|
final int elementCount = dimension * dimension * dimension;
|
||||||
|
final int valuesPerLong = 64 / bitsPerEntry;
|
||||||
|
return (elementCount + valuesPerLong - 1) / valuesPerLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int read(int dimension, int bitsPerEntry, long[] values,
|
||||||
|
int x, int y, int z) {
|
||||||
|
final int sectionIndex = sectionIndex(dimension, x, y, z);
|
||||||
|
final int valuesPerLong = 64 / bitsPerEntry;
|
||||||
|
final int index = sectionIndex / valuesPerLong;
|
||||||
|
final int bitIndex = (sectionIndex - index * valuesPerLong) * bitsPerEntry;
|
||||||
|
return (int) (values[index] >> bitIndex) & ((1 << bitsPerEntry) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int write(int dimension, int bitsPerEntry, long[] values,
|
||||||
|
int x, int y, int z, int value) {
|
||||||
|
final int valuesPerLong = 64 / bitsPerEntry;
|
||||||
|
final int sectionIndex = sectionIndex(dimension, x, y, z);
|
||||||
|
final int index = sectionIndex / valuesPerLong;
|
||||||
|
final int bitIndex = (sectionIndex - index * valuesPerLong) * bitsPerEntry;
|
||||||
|
|
||||||
|
final long block = values[index];
|
||||||
|
final long clear = (1L << bitsPerEntry) - 1L;
|
||||||
|
final long oldBlock = block >> bitIndex & clear;
|
||||||
|
values[index] = block & ~(clear << bitIndex) | ((long) value << bitIndex);
|
||||||
|
return (int) oldBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fill(int bitsPerEntry, long[] values, int value) {
|
||||||
|
final int valuesPerLong = 64 / bitsPerEntry;
|
||||||
|
long block = 0;
|
||||||
|
for (int i = 0; i < valuesPerLong; i++) block |= (long) value << i * bitsPerEntry;
|
||||||
|
Arrays.fill(values, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int count(int bitsPerEntry, long[] values) {
|
||||||
|
final int valuesPerLong = 64 / bitsPerEntry;
|
||||||
|
int count = 0;
|
||||||
|
for (long block : values) {
|
||||||
|
for (int i = 0; i < valuesPerLong; i++) {
|
||||||
|
count += (block >>> i * bitsPerEntry) & ((1 << bitsPerEntry) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int sectionIndex(int dimension, int x, int y, int z) {
|
||||||
|
final int dimensionMask = dimension - 1;
|
||||||
|
final int dimensionBitCount = MathUtils.bitsToRepresent(dimensionMask);
|
||||||
|
return (y & dimensionMask) << (dimensionBitCount << 1) |
|
||||||
|
(z & dimensionMask) << dimensionBitCount |
|
||||||
|
(x & dimensionMask);
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,7 @@ public sealed interface ItemStack extends TagReadable, DataComponent.Holder, Hov
|
|||||||
return ItemStackImpl.create(material, amount, components);
|
return ItemStackImpl.create(material, amount, components);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@NotNull NetworkBuffer.Type<ItemStack> STRICT_NETWORK_TYPE = NETWORK_TYPE.map(itemStack -> {
|
@NotNull NetworkBuffer.Type<ItemStack> STRICT_NETWORK_TYPE = NETWORK_TYPE.transform(itemStack -> {
|
||||||
Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty");
|
Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty");
|
||||||
return itemStack;
|
return itemStack;
|
||||||
}, itemStack -> {
|
}, itemStack -> {
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
public sealed interface Material extends StaticProtocolObject, Materials permits MaterialImpl {
|
public sealed interface Material extends StaticProtocolObject, Materials permits MaterialImpl {
|
||||||
|
|
||||||
NetworkBuffer.Type<Material> NETWORK_TYPE = NetworkBuffer.VAR_INT.map(MaterialImpl::getId, Material::id);
|
NetworkBuffer.Type<Material> NETWORK_TYPE = NetworkBuffer.VAR_INT.transform(MaterialImpl::getId, Material::id);
|
||||||
BinaryTagSerializer<Material> NBT_TYPE = BinaryTagSerializer.STRING.map(MaterialImpl::getSafe, Material::name);
|
BinaryTagSerializer<Material> NBT_TYPE = BinaryTagSerializer.STRING.map(MaterialImpl::getSafe, Material::name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,12 +21,12 @@ public record FilteredText<T>(@NotNull T text, @Nullable T filtered) {
|
|||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, FilteredText<T> value) {
|
public void write(@NotNull NetworkBuffer buffer, FilteredText<T> value) {
|
||||||
buffer.write(inner, value.text);
|
buffer.write(inner, value.text);
|
||||||
buffer.writeOptional(inner, value.filtered);
|
buffer.write(inner.optional(), value.filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FilteredText<T> read(@NotNull NetworkBuffer buffer) {
|
public FilteredText<T> read(@NotNull NetworkBuffer buffer) {
|
||||||
return new FilteredText<>(buffer.read(inner), buffer.readOptional(inner));
|
return new FilteredText<>(buffer.read(inner), buffer.read(inner.optional()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,20 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.minestom.server.item.armor.TrimMaterial;
|
import net.minestom.server.item.armor.TrimMaterial;
|
||||||
import net.minestom.server.item.armor.TrimPattern;
|
import net.minestom.server.item.armor.TrimPattern;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record ArmorTrim(@NotNull DynamicRegistry.Key<TrimMaterial> material, @NotNull DynamicRegistry.Key<TrimPattern> pattern, boolean showInTooltip) {
|
public record ArmorTrim(@NotNull DynamicRegistry.Key<TrimMaterial> material,
|
||||||
|
@NotNull DynamicRegistry.Key<TrimPattern> pattern, boolean showInTooltip) {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<ArmorTrim> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<ArmorTrim> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
TrimMaterial.NETWORK_TYPE, ArmorTrim::material,
|
||||||
public void write(@NotNull NetworkBuffer buffer, ArmorTrim value) {
|
TrimPattern.NETWORK_TYPE, ArmorTrim::pattern,
|
||||||
buffer.write(TrimMaterial.NETWORK_TYPE, value.material);
|
NetworkBuffer.BOOLEAN, ArmorTrim::showInTooltip,
|
||||||
buffer.write(TrimPattern.NETWORK_TYPE, value.pattern);
|
ArmorTrim::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArmorTrim read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new ArmorTrim(buffer.read(TrimMaterial.NETWORK_TYPE),
|
|
||||||
buffer.read(TrimPattern.NETWORK_TYPE),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<ArmorTrim> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<ArmorTrim> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> {
|
tag -> {
|
||||||
@ -43,5 +36,4 @@ public record ArmorTrim(@NotNull DynamicRegistry.Key<TrimMaterial> material, @No
|
|||||||
public @NotNull ArmorTrim withTooltip(boolean showInTooltip) {
|
public @NotNull ArmorTrim withTooltip(boolean showInTooltip) {
|
||||||
return new ArmorTrim(material, pattern, showInTooltip);
|
return new ArmorTrim(material, pattern, showInTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,28 +8,23 @@ import net.minestom.server.entity.EquipmentSlotGroup;
|
|||||||
import net.minestom.server.entity.attribute.Attribute;
|
import net.minestom.server.entity.attribute.Attribute;
|
||||||
import net.minestom.server.entity.attribute.AttributeModifier;
|
import net.minestom.server.entity.attribute.AttributeModifier;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
|
||||||
|
|
||||||
public record AttributeList(@NotNull List<Modifier> modifiers, boolean showInTooltip) {
|
public record AttributeList(@NotNull List<Modifier> modifiers, boolean showInTooltip) {
|
||||||
public static final AttributeList EMPTY = new AttributeList(List.of(), true);
|
public static final AttributeList EMPTY = new AttributeList(List.of(), true);
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<AttributeList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<AttributeList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
Modifier.NETWORK_TYPE.list(Short.MAX_VALUE), AttributeList::modifiers,
|
||||||
public void write(@NotNull NetworkBuffer buffer, AttributeList value) {
|
BOOLEAN, AttributeList::showInTooltip,
|
||||||
buffer.writeCollection(Modifier.NETWORK_TYPE, value.modifiers);
|
AttributeList::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttributeList read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new AttributeList(buffer.readCollection(Modifier.NETWORK_TYPE, Short.MAX_VALUE),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<AttributeList> NBT_TYPE = new BinaryTagSerializer<>() {
|
public static final BinaryTagSerializer<AttributeList> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -57,22 +52,14 @@ public record AttributeList(@NotNull List<Modifier> modifiers, boolean showInToo
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public record Modifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier, @NotNull EquipmentSlotGroup slot) {
|
public record Modifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier,
|
||||||
public static final NetworkBuffer.Type<Modifier> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
@NotNull EquipmentSlotGroup slot) {
|
||||||
@Override
|
public static final NetworkBuffer.Type<Modifier> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
public void write(@NotNull NetworkBuffer buffer, Modifier value) {
|
Attribute.NETWORK_TYPE, Modifier::attribute,
|
||||||
buffer.write(Attribute.NETWORK_TYPE, value.attribute);
|
AttributeModifier.NETWORK_TYPE, Modifier::modifier,
|
||||||
buffer.write(AttributeModifier.NETWORK_TYPE, value.modifier);
|
NetworkBuffer.Enum(EquipmentSlotGroup.class), Modifier::slot,
|
||||||
buffer.writeEnum(EquipmentSlotGroup.class, value.slot);
|
Modifier::new
|
||||||
}
|
);
|
||||||
|
|
||||||
@Override
|
|
||||||
public Modifier read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Modifier(buffer.read(Attribute.NETWORK_TYPE),
|
|
||||||
buffer.read(AttributeModifier.NETWORK_TYPE),
|
|
||||||
buffer.readEnum(EquipmentSlotGroup.class));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Modifier> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Modifier> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Modifier(
|
tag -> new Modifier(
|
||||||
Attribute.NBT_TYPE.read(tag.get("type")),
|
Attribute.NBT_TYPE.read(tag.get("type")),
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.minestom.server.color.DyeColor;
|
import net.minestom.server.color.DyeColor;
|
||||||
import net.minestom.server.instance.block.banner.BannerPattern;
|
import net.minestom.server.instance.block.banner.BannerPattern;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -15,22 +16,15 @@ import java.util.List;
|
|||||||
public record BannerPatterns(@NotNull List<Layer> layers) {
|
public record BannerPatterns(@NotNull List<Layer> layers) {
|
||||||
public static final int MAX_LAYERS = 1024;
|
public static final int MAX_LAYERS = 1024;
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<BannerPatterns> NETWORK_TYPE = Layer.NETWORK_TYPE.list(MAX_LAYERS).map(BannerPatterns::new, BannerPatterns::layers);
|
public static final NetworkBuffer.Type<BannerPatterns> NETWORK_TYPE = Layer.NETWORK_TYPE.list(MAX_LAYERS).transform(BannerPatterns::new, BannerPatterns::layers);
|
||||||
public static final BinaryTagSerializer<BannerPatterns> NBT_TYPE = Layer.NBT_TYPE.list().map(BannerPatterns::new, BannerPatterns::layers);
|
public static final BinaryTagSerializer<BannerPatterns> NBT_TYPE = Layer.NBT_TYPE.list().map(BannerPatterns::new, BannerPatterns::layers);
|
||||||
|
|
||||||
public record Layer(@NotNull DynamicRegistry.Key<BannerPattern> pattern, @NotNull DyeColor color) {
|
public record Layer(@NotNull DynamicRegistry.Key<BannerPattern> pattern, @NotNull DyeColor color) {
|
||||||
public static final NetworkBuffer.Type<Layer> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<Layer> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
BannerPattern.NETWORK_TYPE, Layer::pattern,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Layer value) {
|
DyeColor.NETWORK_TYPE, Layer::color,
|
||||||
buffer.write(BannerPattern.NETWORK_TYPE, value.pattern);
|
Layer::new
|
||||||
buffer.write(DyeColor.NETWORK_TYPE, value.color);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Layer read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Layer(buffer.read(BannerPattern.NETWORK_TYPE), buffer.read(DyeColor.NETWORK_TYPE));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Layer> NBT_TYPE = new BinaryTagSerializer<Layer>() {
|
public static final BinaryTagSerializer<Layer> NBT_TYPE = new BinaryTagSerializer<Layer>() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull BinaryTag write(@NotNull Context context, @NotNull Layer value) {
|
public @NotNull BinaryTag write(@NotNull Context context, @NotNull Layer value) {
|
||||||
|
@ -2,26 +2,18 @@ package net.minestom.server.item.component;
|
|||||||
|
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record Bee(@NotNull CustomData entityData, int ticksInHive, int minTicksInHive) {
|
public record Bee(@NotNull CustomData entityData, int ticksInHive, int minTicksInHive) {
|
||||||
|
|
||||||
public static @NotNull NetworkBuffer.Type<Bee> NETWORK_TYPE = new NetworkBuffer.Type<Bee>() {
|
public static final NetworkBuffer.Type<Bee> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
CustomData.NETWORK_TYPE, Bee::entityData,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Bee value) {
|
NetworkBuffer.VAR_INT, Bee::ticksInHive,
|
||||||
buffer.write(CustomData.NETWORK_TYPE, value.entityData);
|
NetworkBuffer.VAR_INT, Bee::minTicksInHive,
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.ticksInHive);
|
Bee::new
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.minTicksInHive);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Bee read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Bee(buffer.read(CustomData.NETWORK_TYPE),
|
|
||||||
buffer.read(NetworkBuffer.VAR_INT),
|
|
||||||
buffer.read(NetworkBuffer.VAR_INT));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static @NotNull BinaryTagSerializer<Bee> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static @NotNull BinaryTagSerializer<Bee> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Bee(CustomData.NBT_TYPE.read(tag.getCompound("entity_data")),
|
tag -> new Bee(CustomData.NBT_TYPE.read(tag.getCompound("entity_data")),
|
||||||
tag.getInt("ticks_in_hive"),
|
tag.getInt("ticks_in_hive"),
|
||||||
|
@ -6,24 +6,18 @@ import net.kyori.adventure.nbt.IntBinaryTag;
|
|||||||
import net.kyori.adventure.util.RGBLike;
|
import net.kyori.adventure.util.RGBLike;
|
||||||
import net.minestom.server.color.Color;
|
import net.minestom.server.color.Color;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record DyedItemColor(@NotNull RGBLike color, boolean showInTooltip) {
|
public record DyedItemColor(@NotNull RGBLike color, boolean showInTooltip) {
|
||||||
public static DyedItemColor LEATHER = new DyedItemColor(new Color(-6265536), true);
|
public static DyedItemColor LEATHER = new DyedItemColor(new Color(-6265536), true);
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<DyedItemColor> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<DyedItemColor> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
Color.NETWORK_TYPE, DyedItemColor::color,
|
||||||
public void write(@NotNull NetworkBuffer buffer, DyedItemColor value) {
|
NetworkBuffer.BOOLEAN, DyedItemColor::showInTooltip,
|
||||||
buffer.write(Color.NETWORK_TYPE, value.color);
|
DyedItemColor::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DyedItemColor read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new DyedItemColor(buffer.read(Color.NETWORK_TYPE), buffer.read(NetworkBuffer.BOOLEAN));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<DyedItemColor> NBT_TYPE = new BinaryTagSerializer<>() {
|
public static final BinaryTagSerializer<DyedItemColor> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -4,9 +4,9 @@ import net.kyori.adventure.nbt.BinaryTag;
|
|||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.item.enchant.Enchantment;
|
import net.minestom.server.item.enchant.Enchantment;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import net.minestom.server.utils.validate.Check;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -18,30 +18,11 @@ public record EnchantmentList(@NotNull Map<DynamicRegistry.Key<Enchantment>, Int
|
|||||||
boolean showInTooltip) {
|
boolean showInTooltip) {
|
||||||
public static final EnchantmentList EMPTY = new EnchantmentList(Map.of(), true);
|
public static final EnchantmentList EMPTY = new EnchantmentList(Map.of(), true);
|
||||||
|
|
||||||
public static NetworkBuffer.Type<EnchantmentList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<EnchantmentList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
Enchantment.NETWORK_TYPE.mapValue(NetworkBuffer.VAR_INT, Short.MAX_VALUE), EnchantmentList::enchantments,
|
||||||
public void write(@NotNull NetworkBuffer buffer, @NotNull EnchantmentList value) {
|
NetworkBuffer.BOOLEAN, EnchantmentList::showInTooltip,
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.enchantments.size());
|
EnchantmentList::new
|
||||||
for (Map.Entry<DynamicRegistry.Key<Enchantment>, Integer> entry : value.enchantments.entrySet()) {
|
);
|
||||||
buffer.write(Enchantment.NETWORK_TYPE, entry.getKey());
|
|
||||||
buffer.write(NetworkBuffer.VAR_INT, entry.getValue());
|
|
||||||
}
|
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull EnchantmentList read(@NotNull NetworkBuffer buffer) {
|
|
||||||
int size = buffer.read(NetworkBuffer.VAR_INT);
|
|
||||||
Check.argCondition(size < 0 || size > Short.MAX_VALUE, "Invalid enchantment list size: {0}", size);
|
|
||||||
Map<DynamicRegistry.Key<Enchantment>, Integer> enchantments = new HashMap<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
DynamicRegistry.Key<Enchantment> enchantment = buffer.read(Enchantment.NETWORK_TYPE);
|
|
||||||
enchantments.put(enchantment, buffer.read(NetworkBuffer.VAR_INT));
|
|
||||||
}
|
|
||||||
boolean showInTooltip = buffer.read(NetworkBuffer.BOOLEAN);
|
|
||||||
return new EnchantmentList(enchantments, showInTooltip);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static BinaryTagSerializer<EnchantmentList> NBT_TYPE = new BinaryTagSerializer<>() {
|
public static BinaryTagSerializer<EnchantmentList> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull BinaryTag write(@NotNull Context context, @NotNull EnchantmentList value) {
|
public @NotNull BinaryTag write(@NotNull Context context, @NotNull EnchantmentList value) {
|
||||||
|
@ -4,6 +4,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.util.RGBLike;
|
import net.kyori.adventure.util.RGBLike;
|
||||||
import net.minestom.server.color.Color;
|
import net.minestom.server.color.Color;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
|
||||||
|
|
||||||
public record FireworkExplosion(
|
public record FireworkExplosion(
|
||||||
@NotNull Shape shape,
|
@NotNull Shape shape,
|
||||||
@NotNull List<RGBLike> colors,
|
@NotNull List<RGBLike> colors,
|
||||||
@ -27,27 +30,14 @@ public record FireworkExplosion(
|
|||||||
BURST
|
BURST
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<FireworkExplosion> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<FireworkExplosion> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
NetworkBuffer.Enum(Shape.class), FireworkExplosion::shape,
|
||||||
public void write(@NotNull NetworkBuffer buffer, FireworkExplosion value) {
|
Color.NETWORK_TYPE.list(Short.MAX_VALUE), FireworkExplosion::colors,
|
||||||
buffer.writeEnum(Shape.class, value.shape);
|
Color.NETWORK_TYPE.list(Short.MAX_VALUE), FireworkExplosion::fadeColors,
|
||||||
buffer.writeCollection(Color.NETWORK_TYPE, value.colors);
|
BOOLEAN, FireworkExplosion::hasTrail,
|
||||||
buffer.writeCollection(Color.NETWORK_TYPE, value.fadeColors);
|
BOOLEAN, FireworkExplosion::hasTwinkle,
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.hasTrail);
|
FireworkExplosion::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.hasTwinkle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FireworkExplosion read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new FireworkExplosion(
|
|
||||||
buffer.readEnum(Shape.class),
|
|
||||||
buffer.readCollection(Color.NETWORK_TYPE, Short.MAX_VALUE),
|
|
||||||
buffer.readCollection(Color.NETWORK_TYPE, Short.MAX_VALUE),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<FireworkExplosion> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<FireworkExplosion> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> {
|
tag -> {
|
||||||
|
@ -2,6 +2,7 @@ package net.minestom.server.item.component;
|
|||||||
|
|
||||||
import net.kyori.adventure.nbt.*;
|
import net.kyori.adventure.nbt.*;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -11,19 +12,11 @@ import java.util.List;
|
|||||||
public record FireworkList(int flightDuration, @NotNull List<FireworkExplosion> explosions) {
|
public record FireworkList(int flightDuration, @NotNull List<FireworkExplosion> explosions) {
|
||||||
public static final FireworkList EMPTY = new FireworkList(0, List.of());
|
public static final FireworkList EMPTY = new FireworkList(0, List.of());
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<FireworkList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<FireworkList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
NetworkBuffer.VAR_INT, FireworkList::flightDuration,
|
||||||
public void write(@NotNull NetworkBuffer buffer, FireworkList value) {
|
FireworkExplosion.NETWORK_TYPE.list(256), FireworkList::explosions,
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.flightDuration);
|
FireworkList::new
|
||||||
buffer.writeCollection(FireworkExplosion.NETWORK_TYPE, value.explosions);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FireworkList read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new FireworkList(buffer.read(NetworkBuffer.VAR_INT),
|
|
||||||
buffer.readCollection(FireworkExplosion.NETWORK_TYPE, 256));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<FireworkList> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<FireworkList> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> {
|
tag -> {
|
||||||
|
@ -6,43 +6,33 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.minestom.server.ServerFlag;
|
import net.minestom.server.ServerFlag;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.potion.CustomPotionEffect;
|
import net.minestom.server.potion.CustomPotionEffect;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.*;
|
||||||
|
|
||||||
public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat, float eatSeconds,
|
public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat, float eatSeconds,
|
||||||
@NotNull ItemStack usingConvertsTo, @NotNull List<EffectChance> effects) {
|
@NotNull ItemStack usingConvertsTo, @NotNull List<EffectChance> effects) {
|
||||||
public static final float DEFAULT_EAT_SECONDS = 1.6f;
|
public static final float DEFAULT_EAT_SECONDS = 1.6f;
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Food> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<Food> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
VAR_INT, Food::nutrition,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Food value) {
|
FLOAT, Food::saturationModifier,
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.nutrition);
|
BOOLEAN, Food::canAlwaysEat,
|
||||||
buffer.write(NetworkBuffer.FLOAT, value.saturationModifier);
|
FLOAT, Food::eatSeconds,
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.canAlwaysEat);
|
ItemStack.NETWORK_TYPE, Food::usingConvertsTo,
|
||||||
buffer.write(NetworkBuffer.FLOAT, value.eatSeconds);
|
EffectChance.NETWORK_TYPE.list(Short.MAX_VALUE), Food::effects,
|
||||||
buffer.write(ItemStack.NETWORK_TYPE, value.usingConvertsTo);
|
Food::new
|
||||||
buffer.writeCollection(EffectChance.NETWORK_TYPE, value.effects);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Food read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Food(
|
|
||||||
buffer.read(NetworkBuffer.VAR_INT),
|
|
||||||
buffer.read(NetworkBuffer.FLOAT),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN),
|
|
||||||
buffer.read(NetworkBuffer.FLOAT),
|
|
||||||
buffer.read(ItemStack.NETWORK_TYPE),
|
|
||||||
buffer.readCollection(EffectChance.NETWORK_TYPE, Short.MAX_VALUE)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Food> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Food> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Food(
|
tag -> new Food(
|
||||||
tag.getInt("nutrition"),
|
tag.getInt("nutrition"),
|
||||||
tag.getFloat("saturation"),
|
tag.getFloat("saturation_modifier"),
|
||||||
tag.getBoolean("can_always_eat"),
|
tag.getBoolean("can_always_eat"),
|
||||||
tag.getFloat("eat_seconds", DEFAULT_EAT_SECONDS),
|
tag.getFloat("eat_seconds", DEFAULT_EAT_SECONDS),
|
||||||
tag.get("using_converts_to") instanceof BinaryTag usingConvertsTo
|
tag.get("using_converts_to") instanceof BinaryTag usingConvertsTo
|
||||||
@ -51,7 +41,7 @@ public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat
|
|||||||
value -> {
|
value -> {
|
||||||
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
|
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
|
||||||
.putInt("nutrition", value.nutrition)
|
.putInt("nutrition", value.nutrition)
|
||||||
.putFloat("saturation", value.saturationModifier)
|
.putFloat("saturation_odifier", value.saturationModifier)
|
||||||
.putBoolean("can_always_eat", value.canAlwaysEat)
|
.putBoolean("can_always_eat", value.canAlwaysEat)
|
||||||
.putFloat("eat_seconds", value.eatSeconds)
|
.putFloat("eat_seconds", value.eatSeconds)
|
||||||
.put("effects", EffectChance.NBT_LIST_TYPE.write(value.effects));
|
.put("effects", EffectChance.NBT_LIST_TYPE.write(value.effects));
|
||||||
@ -71,18 +61,12 @@ public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record EffectChance(@NotNull CustomPotionEffect effect, float probability) {
|
public record EffectChance(@NotNull CustomPotionEffect effect, float probability) {
|
||||||
public static final NetworkBuffer.Type<EffectChance> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<EffectChance> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
CustomPotionEffect.NETWORK_TYPE, EffectChance::effect,
|
||||||
public void write(@NotNull NetworkBuffer buffer, EffectChance value) {
|
FLOAT, EffectChance::probability,
|
||||||
CustomPotionEffect.NETWORK_TYPE.write(buffer, value.effect);
|
EffectChance::new
|
||||||
buffer.write(NetworkBuffer.FLOAT, value.probability);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EffectChance read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<EffectChance> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<EffectChance> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new EffectChance(
|
tag -> new EffectChance(
|
||||||
CustomPotionEffect.NBT_TYPE.read(tag.getCompound("effect")),
|
CustomPotionEffect.NBT_TYPE.read(tag.getCompound("effect")),
|
||||||
@ -94,5 +78,4 @@ public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat
|
|||||||
);
|
);
|
||||||
public static final BinaryTagSerializer<List<EffectChance>> NBT_LIST_TYPE = NBT_TYPE.list();
|
public static final BinaryTagSerializer<List<EffectChance>> NBT_LIST_TYPE = NBT_TYPE.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.IntArrayBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||||
import net.minestom.server.entity.PlayerSkin;
|
import net.minestom.server.entity.PlayerSkin;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -12,22 +13,19 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.STRING;
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.UUID;
|
||||||
|
|
||||||
public record HeadProfile(@Nullable String name, @Nullable UUID uuid, @NotNull List<Property> properties) {
|
public record HeadProfile(@Nullable String name, @Nullable UUID uuid, @NotNull List<Property> properties) {
|
||||||
public static final HeadProfile EMPTY = new HeadProfile(null, null, List.of());
|
public static final HeadProfile EMPTY = new HeadProfile(null, null, List.of());
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<HeadProfile> NETWORK_TYPE = new NetworkBuffer.Type<HeadProfile>() {
|
public static final NetworkBuffer.Type<HeadProfile> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
STRING.optional(), HeadProfile::name,
|
||||||
public void write(@NotNull NetworkBuffer buffer, HeadProfile value) {
|
UUID.optional(), HeadProfile::uuid,
|
||||||
buffer.writeOptional(NetworkBuffer.STRING, value.name);
|
Property.NETWORK_TYPE.list(Short.MAX_VALUE), HeadProfile::properties,
|
||||||
buffer.writeOptional(NetworkBuffer.UUID, value.uuid);
|
HeadProfile::new
|
||||||
buffer.writeCollection(Property.NETWORK_TYPE, value.properties);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeadProfile read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new HeadProfile(buffer.readOptional(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.UUID), buffer.readCollection(Property.NETWORK_TYPE, Short.MAX_VALUE));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<HeadProfile> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<HeadProfile> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new HeadProfile(
|
tag -> new HeadProfile(
|
||||||
tag.get("name") instanceof StringBinaryTag string ? string.value() : null,
|
tag.get("name") instanceof StringBinaryTag string ? string.value() : null,
|
||||||
@ -38,7 +36,8 @@ public record HeadProfile(@Nullable String name, @Nullable UUID uuid, @NotNull L
|
|||||||
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
|
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder();
|
||||||
if (profile.name != null) builder.putString("name", profile.name);
|
if (profile.name != null) builder.putString("name", profile.name);
|
||||||
if (profile.uuid != null) builder.put("uuid", BinaryTagSerializer.UUID.write(profile.uuid));
|
if (profile.uuid != null) builder.put("uuid", BinaryTagSerializer.UUID.write(profile.uuid));
|
||||||
if (!profile.properties.isEmpty()) builder.put("properties", Property.NBT_LIST_TYPE.write(profile.properties));
|
if (!profile.properties.isEmpty())
|
||||||
|
builder.put("properties", Property.NBT_LIST_TYPE.write(profile.properties));
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -57,19 +56,13 @@ public record HeadProfile(@Nullable String name, @Nullable UUID uuid, @NotNull L
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record Property(@NotNull String name, @NotNull String value, @Nullable String signature) {
|
public record Property(@NotNull String name, @NotNull String value, @Nullable String signature) {
|
||||||
public static final NetworkBuffer.Type<Property> NETWORK_TYPE = new NetworkBuffer.Type<Property>() {
|
public static final NetworkBuffer.Type<Property> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
STRING, Property::name,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Property value) {
|
STRING, Property::value,
|
||||||
buffer.write(NetworkBuffer.STRING, value.name);
|
STRING.optional(), Property::signature,
|
||||||
buffer.write(NetworkBuffer.STRING, value.value);
|
Property::new
|
||||||
buffer.writeOptional(NetworkBuffer.STRING, value.signature);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Property read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Property(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.STRING), buffer.readOptional(NetworkBuffer.STRING));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Property> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Property> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Property(tag.getString("name"), tag.getString("value"),
|
tag -> new Property(tag.getString("name"), tag.getString("value"),
|
||||||
tag.get("signature") instanceof StringBinaryTag signature ? signature.value() : null),
|
tag.get("signature") instanceof StringBinaryTag signature ? signature.value() : null),
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -14,26 +15,10 @@ import java.util.Map;
|
|||||||
public record ItemBlockState(@NotNull Map<String, String> properties) {
|
public record ItemBlockState(@NotNull Map<String, String> properties) {
|
||||||
public static final ItemBlockState EMPTY = new ItemBlockState(Map.of());
|
public static final ItemBlockState EMPTY = new ItemBlockState(Map.of());
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<ItemBlockState> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<ItemBlockState> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
NetworkBuffer.STRING.mapValue(NetworkBuffer.STRING), ItemBlockState::properties,
|
||||||
public void write(@NotNull NetworkBuffer buffer, ItemBlockState value) {
|
ItemBlockState::new
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.properties.size());
|
);
|
||||||
for (Map.Entry<String, String> entry : value.properties.entrySet()) {
|
|
||||||
buffer.write(NetworkBuffer.STRING, entry.getKey());
|
|
||||||
buffer.write(NetworkBuffer.STRING, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemBlockState read(@NotNull NetworkBuffer buffer) {
|
|
||||||
int size = buffer.read(NetworkBuffer.VAR_INT);
|
|
||||||
Map<String, String> properties = new HashMap<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
properties.put(buffer.read(NetworkBuffer.STRING), buffer.read(NetworkBuffer.STRING));
|
|
||||||
}
|
|
||||||
return new ItemBlockState(properties);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<ItemBlockState> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<ItemBlockState> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> {
|
tag -> {
|
||||||
|
@ -3,6 +3,7 @@ package net.minestom.server.item.component;
|
|||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.coordinate.Point;
|
import net.minestom.server.coordinate.Point;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.network.packet.server.play.data.WorldPos;
|
import net.minestom.server.network.packet.server.play.data.WorldPos;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -10,21 +11,11 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
public record LodestoneTracker(@Nullable WorldPos target, boolean tracked) {
|
public record LodestoneTracker(@Nullable WorldPos target, boolean tracked) {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<LodestoneTracker> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<LodestoneTracker> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
WorldPos.NETWORK_TYPE.optional(), LodestoneTracker::target,
|
||||||
public void write(@NotNull NetworkBuffer buffer, @NotNull LodestoneTracker value) {
|
NetworkBuffer.BOOLEAN, LodestoneTracker::tracked,
|
||||||
buffer.writeOptional(WorldPos.NETWORK_TYPE, value.target);
|
LodestoneTracker::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.tracked);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull LodestoneTracker read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new LodestoneTracker(
|
|
||||||
buffer.readOptional(WorldPos.NETWORK_TYPE),
|
|
||||||
buffer.read(NetworkBuffer.BOOLEAN)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<LodestoneTracker> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<LodestoneTracker> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new LodestoneTracker(
|
tag -> new LodestoneTracker(
|
||||||
|
@ -1,22 +1,10 @@
|
|||||||
package net.minestom.server.item.component;
|
package net.minestom.server.item.component;
|
||||||
|
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public enum MapPostProcessing {
|
public enum MapPostProcessing {
|
||||||
LOCK,
|
LOCK,
|
||||||
SCALE;
|
SCALE;
|
||||||
private static final MapPostProcessing[] VALUES = values();
|
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<MapPostProcessing> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<MapPostProcessing> NETWORK_TYPE = NetworkBuffer.Enum(MapPostProcessing.class);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer buffer, MapPostProcessing value) {
|
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.ordinal());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MapPostProcessing read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return VALUES[buffer.read(NetworkBuffer.VAR_INT)];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public record PotDecorations(
|
|||||||
public static final @NotNull Material DEFAULT_ITEM = Material.BRICK;
|
public static final @NotNull Material DEFAULT_ITEM = Material.BRICK;
|
||||||
public static final PotDecorations EMPTY = new PotDecorations(DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM);
|
public static final PotDecorations EMPTY = new PotDecorations(DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM, DEFAULT_ITEM);
|
||||||
|
|
||||||
public static NetworkBuffer.Type<PotDecorations> NETWORK_TYPE = Material.NETWORK_TYPE.list(4).map(PotDecorations::new, PotDecorations::asList);
|
public static final NetworkBuffer.Type<PotDecorations> NETWORK_TYPE = Material.NETWORK_TYPE.list(4).transform(PotDecorations::new, PotDecorations::asList);
|
||||||
public static BinaryTagSerializer<PotDecorations> NBT_TYPE = Material.NBT_TYPE.list().map(PotDecorations::new, PotDecorations::asList);
|
public static BinaryTagSerializer<PotDecorations> NBT_TYPE = Material.NBT_TYPE.list().map(PotDecorations::new, PotDecorations::asList);
|
||||||
|
|
||||||
public PotDecorations(@NotNull List<Material> list) {
|
public PotDecorations(@NotNull List<Material> list) {
|
||||||
|
@ -25,18 +25,18 @@ public record PotionContents(
|
|||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, PotionContents value) {
|
public void write(@NotNull NetworkBuffer buffer, PotionContents value) {
|
||||||
Integer typeId = value.potion == null ? null : value.potion.id();
|
Integer typeId = value.potion == null ? null : value.potion.id();
|
||||||
buffer.writeOptional(NetworkBuffer.VAR_INT, typeId);
|
buffer.write(NetworkBuffer.VAR_INT.optional(), typeId);
|
||||||
buffer.writeOptional(Color.NETWORK_TYPE, value.customColor);
|
buffer.write(Color.NETWORK_TYPE.optional(), value.customColor);
|
||||||
buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects);
|
buffer.write(CustomPotionEffect.NETWORK_TYPE.list(), value.customEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PotionContents read(@NotNull NetworkBuffer buffer) {
|
public PotionContents read(@NotNull NetworkBuffer buffer) {
|
||||||
Integer typeId = buffer.readOptional(NetworkBuffer.VAR_INT);
|
Integer typeId = buffer.read(NetworkBuffer.VAR_INT.optional());
|
||||||
return new PotionContents(
|
return new PotionContents(
|
||||||
typeId == null ? null : PotionType.fromId(typeId),
|
typeId == null ? null : PotionType.fromId(typeId),
|
||||||
buffer.readOptional(Color.NETWORK_TYPE),
|
buffer.read(Color.NETWORK_TYPE.optional()),
|
||||||
buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE)
|
buffer.read(CustomPotionEffect.NETWORK_TYPE.list(Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ package net.minestom.server.item.component;
|
|||||||
|
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.potion.PotionEffect;
|
import net.minestom.server.potion.PotionEffect;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -13,7 +14,7 @@ public record SuspiciousStewEffects(@NotNull List<Effect> effects) {
|
|||||||
public static final int DEFAULT_DURATION = 160;
|
public static final int DEFAULT_DURATION = 160;
|
||||||
public static final SuspiciousStewEffects EMPTY = new SuspiciousStewEffects(List.of());
|
public static final SuspiciousStewEffects EMPTY = new SuspiciousStewEffects(List.of());
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<SuspiciousStewEffects> NETWORK_TYPE = Effect.NETWORK_TYPE.list(Short.MAX_VALUE).map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
|
public static final NetworkBuffer.Type<SuspiciousStewEffects> NETWORK_TYPE = Effect.NETWORK_TYPE.list(Short.MAX_VALUE).transform(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
|
||||||
public static final BinaryTagSerializer<SuspiciousStewEffects> NBT_TYPE = Effect.NBT_TYPE.list().map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
|
public static final BinaryTagSerializer<SuspiciousStewEffects> NBT_TYPE = Effect.NBT_TYPE.list().map(SuspiciousStewEffects::new, SuspiciousStewEffects::effects);
|
||||||
|
|
||||||
public SuspiciousStewEffects {
|
public SuspiciousStewEffects {
|
||||||
@ -32,18 +33,11 @@ public record SuspiciousStewEffects(@NotNull List<Effect> effects) {
|
|||||||
|
|
||||||
public record Effect(@NotNull PotionEffect id, int durationTicks) {
|
public record Effect(@NotNull PotionEffect id, int durationTicks) {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Effect> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<Effect> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
PotionEffect.NETWORK_TYPE, Effect::id,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Effect value) {
|
NetworkBuffer.VAR_INT, Effect::durationTicks,
|
||||||
buffer.write(PotionEffect.NETWORK_TYPE, value.id);
|
Effect::new
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.durationTicks);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Effect read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Effect(buffer.read(PotionEffect.NETWORK_TYPE), buffer.read(NetworkBuffer.VAR_INT));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<Effect> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Effect> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Effect(PotionEffect.fromNamespaceId(tag.getString("id")),
|
tag -> new Effect(PotionEffect.fromNamespaceId(tag.getString("id")),
|
||||||
|
@ -6,6 +6,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.FloatBinaryTag;
|
import net.kyori.adventure.nbt.FloatBinaryTag;
|
||||||
import net.minestom.server.instance.block.predicate.BlockTypeFilter;
|
import net.minestom.server.instance.block.predicate.BlockTypeFilter;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -62,23 +63,12 @@ public record Tool(@NotNull List<Rule> rules, float defaultMiningSpeed, int dama
|
|||||||
|
|
||||||
public record Rule(@NotNull BlockTypeFilter blocks, @Nullable Float speed, @Nullable Boolean correctForDrops) {
|
public record Rule(@NotNull BlockTypeFilter blocks, @Nullable Float speed, @Nullable Boolean correctForDrops) {
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Rule> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<Rule> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
BlockTypeFilter.NETWORK_TYPE, Rule::blocks,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Rule value) {
|
NetworkBuffer.FLOAT, Rule::speed,
|
||||||
buffer.write(BlockTypeFilter.NETWORK_TYPE, value.blocks());
|
NetworkBuffer.BOOLEAN.optional(), Rule::correctForDrops,
|
||||||
buffer.writeOptional(NetworkBuffer.FLOAT, value.speed());
|
Rule::new
|
||||||
buffer.writeOptional(NetworkBuffer.BOOLEAN, value.correctForDrops());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Rule read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Rule(
|
|
||||||
buffer.read(BlockTypeFilter.NETWORK_TYPE),
|
|
||||||
buffer.readOptional(NetworkBuffer.FLOAT),
|
|
||||||
buffer.readOptional(NetworkBuffer.BOOLEAN)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final BinaryTagSerializer<Rule> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final BinaryTagSerializer<Rule> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
tag -> new Rule(
|
tag -> new Rule(
|
||||||
BlockTypeFilter.NBT_TYPE.read(Objects.requireNonNull(tag.get("blocks"))),
|
BlockTypeFilter.NBT_TYPE.read(Objects.requireNonNull(tag.get("blocks"))),
|
||||||
|
@ -2,23 +2,16 @@ package net.minestom.server.item.component;
|
|||||||
|
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public record Unbreakable(boolean showInTooltip) {
|
public record Unbreakable(boolean showInTooltip) {
|
||||||
public static final Unbreakable DEFAULT = new Unbreakable();
|
public static final Unbreakable DEFAULT = new Unbreakable();
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<Unbreakable> NETWORK_TYPE = new NetworkBuffer.Type<Unbreakable>() {
|
public static final NetworkBuffer.Type<Unbreakable> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
NetworkBuffer.BOOLEAN, Unbreakable::showInTooltip,
|
||||||
public void write(@NotNull NetworkBuffer buffer, Unbreakable value) {
|
Unbreakable::new
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip());
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Unbreakable read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new Unbreakable(buffer.read(NetworkBuffer.BOOLEAN));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public Unbreakable() {
|
public Unbreakable() {
|
||||||
this(true);
|
this(true);
|
||||||
@ -28,5 +21,4 @@ public record Unbreakable(boolean showInTooltip) {
|
|||||||
tag -> new Unbreakable(tag.getBoolean("showInTooltip", true)),
|
tag -> new Unbreakable(tag.getBoolean("showInTooltip", true)),
|
||||||
unbreakable -> CompoundBinaryTag.builder().putBoolean("showInTooltip", unbreakable.showInTooltip()).build()
|
unbreakable -> CompoundBinaryTag.builder().putBoolean("showInTooltip", unbreakable.showInTooltip()).build()
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.ListBinaryTag;
|
import net.kyori.adventure.nbt.ListBinaryTag;
|
||||||
import net.minestom.server.item.book.FilteredText;
|
import net.minestom.server.item.book.FilteredText;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -14,17 +15,10 @@ import java.util.List;
|
|||||||
public record WritableBookContent(@NotNull List<FilteredText<String>> pages) {
|
public record WritableBookContent(@NotNull List<FilteredText<String>> pages) {
|
||||||
public static final WritableBookContent EMPTY = new WritableBookContent(List.of());
|
public static final WritableBookContent EMPTY = new WritableBookContent(List.of());
|
||||||
|
|
||||||
public static final NetworkBuffer.Type<WritableBookContent> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<WritableBookContent> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
@Override
|
FilteredText.STRING_NETWORK_TYPE.list(100), WritableBookContent::pages,
|
||||||
public void write(@NotNull NetworkBuffer buffer, WritableBookContent value) {
|
WritableBookContent::new
|
||||||
buffer.writeCollection(FilteredText.STRING_NETWORK_TYPE, value.pages);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WritableBookContent read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new WritableBookContent(buffer.readCollection(FilteredText.STRING_NETWORK_TYPE, 100));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final BinaryTagSerializer<WritableBookContent> NBT_TYPE = new BinaryTagSerializer<>() {
|
public static final BinaryTagSerializer<WritableBookContent> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -6,34 +6,26 @@ import net.kyori.adventure.nbt.ListBinaryTag;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.item.book.FilteredText;
|
import net.minestom.server.item.book.FilteredText;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @NotNull FilteredText<String> title, @NotNull String author, int generation, boolean resolved) {
|
import static net.minestom.server.network.NetworkBuffer.*;
|
||||||
public static final WrittenBookContent EMPTY = new WrittenBookContent(List.of(), new FilteredText<>("", null), "", 0, true);
|
|
||||||
|
|
||||||
public static final @NotNull NetworkBuffer.Type<WrittenBookContent> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
public record WrittenBookContent(@NotNull FilteredText<String> title, @NotNull String author, int generation,
|
||||||
@Override
|
@NotNull List<FilteredText<Component>> pages, boolean resolved) {
|
||||||
public void write(@NotNull NetworkBuffer buffer, WrittenBookContent value) {
|
public static final WrittenBookContent EMPTY = new WrittenBookContent(new FilteredText<>("", null), "", 0, List.of(), true);
|
||||||
buffer.write(FilteredText.STRING_NETWORK_TYPE, value.title);
|
|
||||||
buffer.write(NetworkBuffer.STRING, value.author);
|
|
||||||
buffer.write(NetworkBuffer.VAR_INT, value.generation);
|
|
||||||
buffer.writeCollection(FilteredText.COMPONENT_NETWORK_TYPE, value.pages);
|
|
||||||
buffer.write(NetworkBuffer.BOOLEAN, value.resolved);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public static final NetworkBuffer.Type<WrittenBookContent> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||||
public WrittenBookContent read(@NotNull NetworkBuffer buffer) {
|
FilteredText.STRING_NETWORK_TYPE, WrittenBookContent::title,
|
||||||
FilteredText<String> title = buffer.read(FilteredText.STRING_NETWORK_TYPE);
|
STRING, WrittenBookContent::author,
|
||||||
String author = buffer.read(NetworkBuffer.STRING);
|
VAR_INT, WrittenBookContent::generation,
|
||||||
int generation = buffer.read(NetworkBuffer.VAR_INT);
|
FilteredText.COMPONENT_NETWORK_TYPE.list(100), WrittenBookContent::pages,
|
||||||
List<FilteredText<Component>> pages = buffer.readCollection(FilteredText.COMPONENT_NETWORK_TYPE, 100);
|
BOOLEAN, WrittenBookContent::resolved,
|
||||||
boolean resolved = buffer.read(NetworkBuffer.BOOLEAN);
|
WrittenBookContent::new
|
||||||
return new WrittenBookContent(pages, title, author, generation, resolved);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final @NotNull BinaryTagSerializer<WrittenBookContent> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
public static final @NotNull BinaryTagSerializer<WrittenBookContent> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||||
compound -> {
|
compound -> {
|
||||||
@ -45,7 +37,7 @@ public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @
|
|||||||
String author = compound.getString("author");
|
String author = compound.getString("author");
|
||||||
int generation = compound.getInt("generation");
|
int generation = compound.getInt("generation");
|
||||||
boolean resolved = compound.getBoolean("resolved");
|
boolean resolved = compound.getBoolean("resolved");
|
||||||
return new WrittenBookContent(pages, title, author, generation, resolved);
|
return new WrittenBookContent(title, author, generation, pages, resolved);
|
||||||
},
|
},
|
||||||
value -> {
|
value -> {
|
||||||
ListBinaryTag.Builder<BinaryTag> pagesTag = ListBinaryTag.builder();
|
ListBinaryTag.Builder<BinaryTag> pagesTag = ListBinaryTag.builder();
|
||||||
@ -66,11 +58,11 @@ public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @
|
|||||||
pages = List.copyOf(pages);
|
pages = List.copyOf(pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author) {
|
public WrittenBookContent(@NotNull String title, @NotNull String author, @NotNull List<Component> pages) {
|
||||||
this(pages, title, author, 0, true);
|
this(title, author, 0, pages, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author, int generation, boolean resolved) {
|
public WrittenBookContent(@NotNull String title, @NotNull String author, int generation, @NotNull List<Component> pages, boolean resolved) {
|
||||||
this(pages.stream().map(page -> new FilteredText<>(page, null)).toList(), new FilteredText<>(title, null), author, generation, resolved);
|
this(new FilteredText<>(title, null), author, generation, pages.stream().map(page -> new FilteredText<>(page, null)).toList(), resolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ public final class PacketListenerManager {
|
|||||||
|
|
||||||
// Listener can be null if none has been set before, call PacketConsumer anyway
|
// Listener can be null if none has been set before, call PacketConsumer anyway
|
||||||
if (packetListenerConsumer == null) {
|
if (packetListenerConsumer == null) {
|
||||||
LOGGER.warn("Packet " + clazz + " does not have any default listener! (The issue comes from Minestom)");
|
LOGGER.warn("Packet {}:{} does not have any default listener! (The issue likely comes from Minestom)", clazz, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ import net.minestom.server.network.packet.server.login.LoginDisconnectPacket;
|
|||||||
import net.minestom.server.network.player.GameProfile;
|
import net.minestom.server.network.player.GameProfile;
|
||||||
import net.minestom.server.network.player.PlayerConnection;
|
import net.minestom.server.network.player.PlayerConnection;
|
||||||
import net.minestom.server.network.player.PlayerSocketConnection;
|
import net.minestom.server.network.player.PlayerSocketConnection;
|
||||||
|
import net.minestom.server.network.plugin.LoginPlugin;
|
||||||
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
|
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
|
||||||
import net.minestom.server.network.plugin.LoginPluginResponse;
|
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -33,7 +33,6 @@ import java.net.*;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
@ -56,7 +55,7 @@ public final class LoginListener {
|
|||||||
socketConnection.UNSAFE_setLoginUsername(packet.username());
|
socketConnection.UNSAFE_setLoginUsername(packet.username());
|
||||||
// Velocity support
|
// Velocity support
|
||||||
if (VelocityProxy.isEnabled()) {
|
if (VelocityProxy.isEnabled()) {
|
||||||
connection.loginPluginMessageProcessor().request(VelocityProxy.PLAYER_INFO_CHANNEL, null)
|
connection.loginPluginMessageProcessor().request(VelocityProxy.PLAYER_INFO_CHANNEL, new byte[0])
|
||||||
.thenAccept(response -> handleVelocityProxyResponse(socketConnection, response));
|
.thenAccept(response -> handleVelocityProxyResponse(socketConnection, response));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -171,14 +170,13 @@ public final class LoginListener {
|
|||||||
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleVelocityProxyResponse(PlayerSocketConnection socketConnection, LoginPluginResponse response) {
|
private static void handleVelocityProxyResponse(PlayerSocketConnection socketConnection, LoginPlugin.Response response) {
|
||||||
byte[] data = response.getPayload();
|
final byte[] data = response.payload();
|
||||||
|
|
||||||
SocketAddress socketAddress = null;
|
SocketAddress socketAddress = null;
|
||||||
GameProfile gameProfile = null;
|
GameProfile gameProfile = null;
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
if (data != null && data.length > 0) {
|
if (data != null && data.length > 0) {
|
||||||
NetworkBuffer buffer = new NetworkBuffer(ByteBuffer.wrap(data));
|
NetworkBuffer buffer = NetworkBuffer.wrap(data, 0, data.length);
|
||||||
success = VelocityProxy.checkIntegrity(buffer);
|
success = VelocityProxy.checkIntegrity(buffer);
|
||||||
if (success) {
|
if (success) {
|
||||||
// Get the real connection address
|
// Get the real connection address
|
||||||
@ -191,7 +189,7 @@ public final class LoginListener {
|
|||||||
}
|
}
|
||||||
final int port = ((java.net.InetSocketAddress) socketConnection.getRemoteAddress()).getPort();
|
final int port = ((java.net.InetSocketAddress) socketConnection.getRemoteAddress()).getPort();
|
||||||
socketAddress = new InetSocketAddress(address, port);
|
socketAddress = new InetSocketAddress(address, port);
|
||||||
gameProfile = new GameProfile(buffer);
|
gameProfile = GameProfile.SERIALIZER.read(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component;
|
|||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.network.packet.server.play.SystemChatPacket;
|
import net.minestom.server.network.packet.server.play.SystemChatPacket;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketSendingUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ public final class Messenger {
|
|||||||
*/
|
*/
|
||||||
public static void sendMessage(@NotNull Collection<Player> players, @NotNull Component message,
|
public static void sendMessage(@NotNull Collection<Player> players, @NotNull Component message,
|
||||||
@NotNull ChatPosition position, @Nullable UUID uuid) {
|
@NotNull ChatPosition position, @Nullable UUID uuid) {
|
||||||
PacketUtils.sendGroupedPacket(players, new SystemChatPacket(message, false),
|
PacketSendingUtils.sendGroupedPacket(players, new SystemChatPacket(message, false),
|
||||||
player -> getChatMessageType(player).accepts(position));
|
player -> getChatMessageType(player).accepts(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,8 @@ public final class ConnectionManager {
|
|||||||
private CachedPacket getDefaultTags() {
|
private CachedPacket getDefaultTags() {
|
||||||
var defaultTags = this.defaultTags;
|
var defaultTags = this.defaultTags;
|
||||||
if (defaultTags == null) {
|
if (defaultTags == null) {
|
||||||
this.defaultTags = defaultTags = new CachedPacket(new TagsPacket(MinecraftServer.getTagManager().getTagMap()));
|
final TagsPacket packet = MinecraftServer.getTagManager().packet();
|
||||||
|
this.defaultTags = defaultTags = new CachedPacket(packet);
|
||||||
}
|
}
|
||||||
return defaultTags;
|
return defaultTags;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,28 @@
|
|||||||
package net.minestom.server.network;
|
package net.minestom.server.network;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current connection state of a {@link net.minestom.server.network.player.PlayerConnection}.
|
* Represents the connection state of a client.
|
||||||
*/
|
*/
|
||||||
public enum ConnectionState {
|
public enum ConnectionState {
|
||||||
HANDSHAKE, STATUS, LOGIN, CONFIGURATION, PLAY
|
/**
|
||||||
|
* Default state before any packet is received.
|
||||||
|
*/
|
||||||
|
HANDSHAKE,
|
||||||
|
/**
|
||||||
|
* Client declares `Status` intent during handshake.
|
||||||
|
*/
|
||||||
|
STATUS,
|
||||||
|
/**
|
||||||
|
* Client declares `Login` intent during handshake.
|
||||||
|
*/
|
||||||
|
LOGIN,
|
||||||
|
/**
|
||||||
|
* Client acknowledged login and is now configuring the game.
|
||||||
|
* Can also go back to configuration from play.
|
||||||
|
*/
|
||||||
|
CONFIGURATION,
|
||||||
|
/**
|
||||||
|
* Client (re-)finished configuration.
|
||||||
|
*/
|
||||||
|
PLAY
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package net.minestom.server.network;
|
package net.minestom.server.network;
|
||||||
|
|
||||||
import net.kyori.adventure.nbt.BinaryTag;
|
import net.kyori.adventure.nbt.BinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.MinecraftServer;
|
|
||||||
import net.minestom.server.coordinate.Point;
|
import net.minestom.server.coordinate.Point;
|
||||||
import net.minestom.server.coordinate.Pos;
|
import net.minestom.server.coordinate.Pos;
|
||||||
import net.minestom.server.entity.EntityPose;
|
import net.minestom.server.entity.EntityPose;
|
||||||
@ -11,349 +11,278 @@ import net.minestom.server.registry.ProtocolObject;
|
|||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
import net.minestom.server.utils.Direction;
|
import net.minestom.server.utils.Direction;
|
||||||
import net.minestom.server.utils.Unit;
|
import net.minestom.server.utils.Unit;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagReader;
|
import net.minestom.server.utils.crypto.KeyUtils;
|
||||||
import net.minestom.server.utils.nbt.BinaryTagWriter;
|
|
||||||
import net.minestom.server.utils.validate.Check;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import javax.crypto.Cipher;
|
||||||
import java.nio.ByteOrder;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
public final class NetworkBuffer {
|
public sealed interface NetworkBuffer permits NetworkBufferImpl {
|
||||||
public static final Type<Unit> UNIT = new NetworkBufferTypeImpl.UnitType();
|
Type<Unit> UNIT = new NetworkBufferTypeImpl.UnitType();
|
||||||
public static final Type<Boolean> BOOLEAN = new NetworkBufferTypeImpl.BooleanType();
|
Type<Boolean> BOOLEAN = new NetworkBufferTypeImpl.BooleanType();
|
||||||
public static final Type<Byte> BYTE = new NetworkBufferTypeImpl.ByteType();
|
Type<Byte> BYTE = new NetworkBufferTypeImpl.ByteType();
|
||||||
public static final Type<Short> SHORT = new NetworkBufferTypeImpl.ShortType();
|
Type<Short> SHORT = new NetworkBufferTypeImpl.ShortType();
|
||||||
public static final Type<Integer> UNSIGNED_SHORT = new NetworkBufferTypeImpl.UnsignedShortType();
|
Type<Integer> UNSIGNED_SHORT = new NetworkBufferTypeImpl.UnsignedShortType();
|
||||||
public static final Type<Integer> INT = new NetworkBufferTypeImpl.IntType();
|
Type<Integer> INT = new NetworkBufferTypeImpl.IntType();
|
||||||
public static final Type<Long> LONG = new NetworkBufferTypeImpl.LongType();
|
Type<Long> LONG = new NetworkBufferTypeImpl.LongType();
|
||||||
public static final Type<Float> FLOAT = new NetworkBufferTypeImpl.FloatType();
|
Type<Float> FLOAT = new NetworkBufferTypeImpl.FloatType();
|
||||||
public static final Type<Double> DOUBLE = new NetworkBufferTypeImpl.DoubleType();
|
Type<Double> DOUBLE = new NetworkBufferTypeImpl.DoubleType();
|
||||||
public static final Type<Integer> VAR_INT = new NetworkBufferTypeImpl.VarIntType();
|
Type<Integer> VAR_INT = new NetworkBufferTypeImpl.VarIntType();
|
||||||
public static final Type<Long> VAR_LONG = new NetworkBufferTypeImpl.VarLongType();
|
Type<Integer> VAR_INT_3 = new NetworkBufferTypeImpl.VarInt3Type();
|
||||||
public static final Type<byte[]> RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType();
|
Type<Long> VAR_LONG = new NetworkBufferTypeImpl.VarLongType();
|
||||||
public static final Type<String> STRING = new NetworkBufferTypeImpl.StringType();
|
Type<byte[]> RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType(-1);
|
||||||
public static final Type<String> STRING_TERMINATED = new NetworkBufferTypeImpl.StringTerminatedType();
|
Type<String> STRING = new NetworkBufferTypeImpl.StringType();
|
||||||
public static final Type<BinaryTag> NBT = new NetworkBufferTypeImpl.NbtType();
|
Type<String> STRING_TERMINATED = new NetworkBufferTypeImpl.StringTerminatedType();
|
||||||
public static final Type<Point> BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType();
|
Type<BinaryTag> NBT = new NetworkBufferTypeImpl.NbtType();
|
||||||
public static final Type<Component> COMPONENT = new ComponentNetworkBufferTypeImpl();
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public static final Type<Component> JSON_COMPONENT = new NetworkBufferTypeImpl.JsonComponentType();
|
Type<CompoundBinaryTag> NBT_COMPOUND = (Type) new NetworkBufferTypeImpl.NbtType();
|
||||||
public static final Type<UUID> UUID = new NetworkBufferTypeImpl.UUIDType();
|
Type<Point> BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType();
|
||||||
public static final Type<Pos> POS = new NetworkBufferTypeImpl.PosType();
|
Type<Component> COMPONENT = new NetworkBufferTypeImpl.ComponentType();
|
||||||
|
Type<Component> JSON_COMPONENT = new NetworkBufferTypeImpl.JsonComponentType();
|
||||||
|
Type<UUID> UUID = new NetworkBufferTypeImpl.UUIDType();
|
||||||
|
Type<Pos> POS = new NetworkBufferTypeImpl.PosType();
|
||||||
|
|
||||||
public static final Type<byte[]> BYTE_ARRAY = new NetworkBufferTypeImpl.ByteArrayType();
|
Type<byte[]> BYTE_ARRAY = new NetworkBufferTypeImpl.ByteArrayType();
|
||||||
public static final Type<long[]> LONG_ARRAY = new NetworkBufferTypeImpl.LongArrayType();
|
Type<long[]> LONG_ARRAY = new NetworkBufferTypeImpl.LongArrayType();
|
||||||
public static final Type<int[]> VAR_INT_ARRAY = new NetworkBufferTypeImpl.VarIntArrayType();
|
Type<int[]> VAR_INT_ARRAY = new NetworkBufferTypeImpl.VarIntArrayType();
|
||||||
public static final Type<long[]> VAR_LONG_ARRAY = new NetworkBufferTypeImpl.VarLongArrayType();
|
Type<long[]> VAR_LONG_ARRAY = new NetworkBufferTypeImpl.VarLongArrayType();
|
||||||
|
|
||||||
public static <T extends ProtocolObject> @NotNull Type<DynamicRegistry.Key<T>> RegistryKey(@NotNull Function<Registries, DynamicRegistry<T>> selector) {
|
Type<BitSet> BITSET = LONG_ARRAY.transform(BitSet::valueOf, BitSet::toLongArray);
|
||||||
|
Type<Instant> INSTANT_MS = LONG.transform(Instant::ofEpochMilli, Instant::toEpochMilli);
|
||||||
|
Type<PublicKey> PUBLIC_KEY = BYTE_ARRAY.transform(KeyUtils::publicRSAKeyFrom, PublicKey::getEncoded);
|
||||||
|
|
||||||
|
static <T extends ProtocolObject> @NotNull Type<DynamicRegistry.Key<T>> RegistryKey(@NotNull Function<Registries, DynamicRegistry<T>> selector) {
|
||||||
return new NetworkBufferTypeImpl.RegistryTypeType<>(selector);
|
return new NetworkBufferTypeImpl.RegistryTypeType<>(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// METADATA
|
// METADATA
|
||||||
public static final Type<int[]> VILLAGER_DATA = new NetworkBufferTypeImpl.VillagerDataType();
|
Type<int[]> VILLAGER_DATA = new NetworkBufferTypeImpl.VillagerDataType();
|
||||||
public static final Type<Point> VECTOR3 = new NetworkBufferTypeImpl.Vector3Type();
|
Type<Point> VECTOR3 = new NetworkBufferTypeImpl.Vector3Type();
|
||||||
public static final Type<Point> VECTOR3D = new NetworkBufferTypeImpl.Vector3DType();
|
Type<Point> VECTOR3D = new NetworkBufferTypeImpl.Vector3DType();
|
||||||
public static final Type<float[]> QUATERNION = new NetworkBufferTypeImpl.QuaternionType();
|
Type<Point> VECTOR3B = new NetworkBufferTypeImpl.Vector3BType();
|
||||||
|
Type<float[]> QUATERNION = new NetworkBufferTypeImpl.QuaternionType();
|
||||||
|
|
||||||
public static final Type<@Nullable Component> OPT_CHAT = Optional(COMPONENT);
|
Type<@Nullable Component> OPT_CHAT = COMPONENT.optional();
|
||||||
public static final Type<@Nullable Point> OPT_BLOCK_POSITION = Optional(BLOCK_POSITION);
|
Type<@Nullable Point> OPT_BLOCK_POSITION = BLOCK_POSITION.optional();
|
||||||
public static final Type<@Nullable UUID> OPT_UUID = Optional(UUID);
|
Type<@Nullable UUID> OPT_UUID = UUID.optional();
|
||||||
|
|
||||||
public static final Type<Direction> DIRECTION = new NetworkBufferTypeImpl.EnumType<>(Direction.class);
|
Type<Direction> DIRECTION = Enum(Direction.class);
|
||||||
public static final Type<EntityPose> POSE = new NetworkBufferTypeImpl.EnumType<>(EntityPose.class);
|
Type<EntityPose> POSE = Enum(EntityPose.class);
|
||||||
|
|
||||||
// Combinators
|
// Combinators
|
||||||
|
|
||||||
public static <T> @NotNull Type<@Nullable T> Optional(@NotNull Type<T> type) {
|
static <E extends Enum<E>> @NotNull Type<E> Enum(@NotNull Class<E> enumClass) {
|
||||||
return new NetworkBufferTypeImpl.OptionalType<>(type);
|
final E[] values = enumClass.getEnumConstants();
|
||||||
|
return VAR_INT.transform(integer -> values[integer], Enum::ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Enum<E>> @NotNull Type<E> Enum(@NotNull Class<E> enumClass) {
|
static <E extends Enum<E>> @NotNull Type<EnumSet<E>> EnumSet(@NotNull Class<E> enumClass) {
|
||||||
return new NetworkBufferTypeImpl.EnumType<>(enumClass);
|
return new NetworkBufferTypeImpl.EnumSetType<>(enumClass, enumClass.getEnumConstants());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> @NotNull Type<T> Lazy(@NotNull Supplier<NetworkBuffer.@NotNull Type<T>> supplier) {
|
static @NotNull Type<BitSet> FixedBitSet(int length) {
|
||||||
|
return new NetworkBufferTypeImpl.FixedBitSetType(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull Type<byte[]> FixedRawBytes(int length) {
|
||||||
|
return new NetworkBufferTypeImpl.RawBytesType(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> @NotNull Type<T> Lazy(@NotNull Supplier<@NotNull Type<T>> supplier) {
|
||||||
return new NetworkBufferTypeImpl.LazyType<>(supplier);
|
return new NetworkBufferTypeImpl.LazyType<>(supplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<T> void write(@NotNull Type<T> type, @UnknownNullability T value);
|
||||||
|
|
||||||
ByteBuffer nioBuffer;
|
<T> @UnknownNullability T read(@NotNull Type<T> type);
|
||||||
final boolean resizable;
|
|
||||||
int writeIndex;
|
|
||||||
int readIndex;
|
|
||||||
|
|
||||||
BinaryTagWriter nbtWriter;
|
<T> void writeAt(long index, @NotNull Type<T> type, @UnknownNullability T value);
|
||||||
BinaryTagReader nbtReader;
|
|
||||||
|
|
||||||
// In the future, this should be passed as a parameter.
|
<T> @UnknownNullability T readAt(long index, @NotNull Type<T> type);
|
||||||
final Registries registries = MinecraftServer.process();
|
|
||||||
|
|
||||||
public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) {
|
void copyTo(long srcOffset, byte @NotNull [] dest, long destOffset, long length);
|
||||||
this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN);
|
|
||||||
this.resizable = resizable;
|
|
||||||
|
|
||||||
this.writeIndex = buffer.position();
|
byte @NotNull [] extractBytes(@NotNull Consumer<@NotNull NetworkBuffer> extractor);
|
||||||
this.readIndex = buffer.position();
|
|
||||||
|
@NotNull NetworkBuffer clear();
|
||||||
|
|
||||||
|
long writeIndex();
|
||||||
|
|
||||||
|
long readIndex();
|
||||||
|
|
||||||
|
@NotNull NetworkBuffer writeIndex(long writeIndex);
|
||||||
|
|
||||||
|
@NotNull NetworkBuffer readIndex(long readIndex);
|
||||||
|
|
||||||
|
@NotNull NetworkBuffer index(long readIndex, long writeIndex);
|
||||||
|
|
||||||
|
long advanceWrite(long length);
|
||||||
|
|
||||||
|
long advanceRead(long length);
|
||||||
|
|
||||||
|
long readableBytes();
|
||||||
|
|
||||||
|
long writableBytes();
|
||||||
|
|
||||||
|
long capacity();
|
||||||
|
|
||||||
|
void readOnly();
|
||||||
|
|
||||||
|
boolean isReadOnly();
|
||||||
|
|
||||||
|
void resize(long newSize);
|
||||||
|
|
||||||
|
void ensureWritable(long length);
|
||||||
|
|
||||||
|
void compact();
|
||||||
|
|
||||||
|
NetworkBuffer slice(long index, long length, long readIndex, long writeIndex);
|
||||||
|
|
||||||
|
NetworkBuffer copy(long index, long length, long readIndex, long writeIndex);
|
||||||
|
|
||||||
|
default NetworkBuffer copy(long index, long length) {
|
||||||
|
return copy(index, length, readIndex(), writeIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkBuffer(@NotNull ByteBuffer buffer) {
|
int readChannel(ReadableByteChannel channel) throws IOException;
|
||||||
this(buffer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkBuffer(int initialCapacity) {
|
boolean writeChannel(SocketChannel channel) throws IOException;
|
||||||
this(ByteBuffer.allocateDirect(initialCapacity), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkBuffer() {
|
void cipher(Cipher cipher, long start, long length);
|
||||||
this(1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void write(@NotNull Type<T> type, @NotNull T value) {
|
long compress(long start, long length, NetworkBuffer output);
|
||||||
type.write(this, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void write(@NotNull Writer writer) {
|
long decompress(long start, long length, NetworkBuffer output) throws DataFormatException;
|
||||||
writer.write(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @NotNull T read(@NotNull Type<T> type) {
|
@Nullable Registries registries();
|
||||||
return type.read(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void writeOptional(@NotNull Type<T> type, @Nullable T value) {
|
interface Type<T> {
|
||||||
write(BOOLEAN, value != null);
|
|
||||||
if (value != null) write(type, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeOptional(@Nullable Writer writer) {
|
|
||||||
write(BOOLEAN, writer != null);
|
|
||||||
if (writer != null) write(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @Nullable T readOptional(@NotNull Type<T> type) {
|
|
||||||
return read(BOOLEAN) ? read(type) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @Nullable T readOptional(@NotNull Function<@NotNull NetworkBuffer, @NotNull T> function) {
|
|
||||||
return read(BOOLEAN) ? function.apply(this) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void writeCollection(@NotNull Type<T> type, @Nullable Collection<@NotNull T> values) {
|
|
||||||
if (values == null) {
|
|
||||||
write(BYTE, (byte) 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
write(VAR_INT, values.size());
|
|
||||||
for (T value : values) write(type, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
public final <T> void writeCollection(@NotNull Type<T> type, @NotNull T @Nullable ... values) {
|
|
||||||
writeCollection(type, values == null ? null : List.of(values));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Writer> void writeCollection(@Nullable Collection<@NotNull T> values) {
|
|
||||||
if (values == null) {
|
|
||||||
write(BYTE, (byte) 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
write(VAR_INT, values.size());
|
|
||||||
for (T value : values) write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void writeCollection(@Nullable Collection<@NotNull T> values,
|
|
||||||
@NotNull BiConsumer<@NotNull NetworkBuffer, @NotNull T> consumer) {
|
|
||||||
if (values == null) {
|
|
||||||
write(BYTE, (byte) 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
write(VAR_INT, values.size());
|
|
||||||
for (T value : values) consumer.accept(this, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @NotNull List<@NotNull T> readCollection(@NotNull Type<T> type, int maxSize) {
|
|
||||||
final int size = read(VAR_INT);
|
|
||||||
Check.argCondition(size > maxSize, "Collection size ({0}) is higher than the maximum allowed size ({1})", size, maxSize);
|
|
||||||
final List<T> values = new java.util.ArrayList<>(size);
|
|
||||||
for (int i = 0; i < size; i++) values.add(read(type));
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @NotNull List<@NotNull T> readCollection(@NotNull Function<@NotNull NetworkBuffer, @NotNull T> function, int maxSize) {
|
|
||||||
final int size = read(VAR_INT);
|
|
||||||
Check.argCondition(size > maxSize, "Collection size ({0}) is higher than the maximum allowed size ({1})", size, maxSize);
|
|
||||||
final List<T> values = new java.util.ArrayList<>(size);
|
|
||||||
for (int i = 0; i < size; i++) values.add(function.apply(this));
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K, V> @NotNull Map<K, V> writeMap(@NotNull NetworkBuffer.Type<K> keyType, @NotNull NetworkBuffer.Type<V> valueType, @NotNull Map<K, V> map) {
|
|
||||||
write(VAR_INT, map.size());
|
|
||||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
|
||||||
write(keyType, entry.getKey());
|
|
||||||
write(valueType, entry.getValue());
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K, V> @NotNull Map<K, V> readMap(@NotNull NetworkBuffer.Type<K> keyType, @NotNull NetworkBuffer.Type<V> valueType, int maxSize) {
|
|
||||||
final int size = read(VAR_INT);
|
|
||||||
Check.argCondition(size > maxSize, "Map size ({0}) is higher than the maximum allowed size ({1})", size, maxSize);
|
|
||||||
final Map<K, V> map = new HashMap<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
map.put(read(keyType), read(valueType));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <E extends Enum<?>> void writeEnum(@NotNull Class<E> enumClass, @NotNull E value) {
|
|
||||||
write(VAR_INT, value.ordinal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <E extends Enum<?>> @NotNull E readEnum(@NotNull Class<@NotNull E> enumClass) {
|
|
||||||
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);
|
|
||||||
readIndex += length;
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyTo(int srcOffset, byte @NotNull [] dest, int destOffset, int length) {
|
|
||||||
this.nioBuffer.get(srcOffset, dest, destOffset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte @NotNull [] extractBytes(@NotNull Consumer<@NotNull NetworkBuffer> extractor) {
|
|
||||||
final int startingPosition = readIndex();
|
|
||||||
extractor.accept(this);
|
|
||||||
final int endingPosition = readIndex();
|
|
||||||
byte[] output = new byte[endingPosition - startingPosition];
|
|
||||||
copyTo(startingPosition, output, 0, output.length);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
this.writeIndex = 0;
|
|
||||||
this.readIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int writeIndex() {
|
|
||||||
return writeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readIndex() {
|
|
||||||
return readIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeIndex(int writeIndex) {
|
|
||||||
this.writeIndex = writeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readIndex(int readIndex) {
|
|
||||||
this.readIndex = readIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int skipWrite(int length) {
|
|
||||||
final int oldWriteIndex = writeIndex;
|
|
||||||
writeIndex += length;
|
|
||||||
return oldWriteIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readableBytes() {
|
|
||||||
return writeIndex - readIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ensureSize(int length) {
|
|
||||||
if (!resizable) return;
|
|
||||||
if (nioBuffer.capacity() < writeIndex + length) {
|
|
||||||
final int newCapacity = Math.max(nioBuffer.capacity() * 2, writeIndex + length);
|
|
||||||
ByteBuffer newBuffer = ByteBuffer.allocateDirect(newCapacity);
|
|
||||||
nioBuffer.position(0);
|
|
||||||
newBuffer.put(nioBuffer);
|
|
||||||
nioBuffer = newBuffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface Type<T> {
|
|
||||||
void write(@NotNull NetworkBuffer buffer, T value);
|
void write(@NotNull NetworkBuffer buffer, T value);
|
||||||
|
|
||||||
T read(@NotNull NetworkBuffer buffer);
|
T read(@NotNull NetworkBuffer buffer);
|
||||||
|
|
||||||
default <S> @NotNull Type<S> map(@NotNull Function<T, S> to, @NotNull Function<S, T> from) {
|
default long sizeOf(@NotNull T value, @Nullable Registries registries) {
|
||||||
return new NetworkBufferTypeImpl.MappedType<>(this, to, from);
|
return NetworkBufferTypeImpl.sizeOf(this, value, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
default long sizeOf(@NotNull T value) {
|
||||||
|
return sizeOf(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
default <S> @NotNull Type<S> transform(@NotNull Function<T, S> to, @NotNull Function<S, T> from) {
|
||||||
|
return new NetworkBufferTypeImpl.TransformType<>(this, to, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> @NotNull Type<Map<T, V>> mapValue(@NotNull Type<V> valueType, int maxSize) {
|
||||||
|
return new NetworkBufferTypeImpl.MapType<>(this, valueType, maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> @NotNull Type<Map<T, V>> mapValue(@NotNull Type<V> valueType) {
|
||||||
|
return mapValue(valueType, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
default @NotNull Type<List<T>> list(int maxSize) {
|
default @NotNull Type<List<T>> list(int maxSize) {
|
||||||
return new NetworkBufferTypeImpl.ListType<>(this, maxSize);
|
return new NetworkBufferTypeImpl.ListType<>(this, maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default @NotNull Type<List<T>> list() {
|
||||||
|
return list(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
default @NotNull Type<T> optional() {
|
default @NotNull Type<T> optional() {
|
||||||
return new NetworkBufferTypeImpl.OptionalTypeImpl<>(this);
|
return new NetworkBufferTypeImpl.OptionalType<>(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static @NotNull Builder builder(long size) {
|
||||||
|
return new NetworkBufferImpl.Builder(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer staticBuffer(long size, Registries registries) {
|
||||||
|
return builder(size).registry(registries).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer staticBuffer(long size) {
|
||||||
|
return staticBuffer(size, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer resizableBuffer(long initialSize, Registries registries) {
|
||||||
|
return builder(initialSize)
|
||||||
|
.autoResize(AutoResize.DOUBLE)
|
||||||
|
.registry(registries)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer resizableBuffer(int initialSize) {
|
||||||
|
return resizableBuffer(initialSize, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer resizableBuffer(Registries registries) {
|
||||||
|
return resizableBuffer(256, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer resizableBuffer() {
|
||||||
|
return resizableBuffer(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer wrap(byte @NotNull [] bytes, int readIndex, int writeIndex, @Nullable Registries registries) {
|
||||||
|
return NetworkBufferImpl.wrap(bytes, readIndex, writeIndex, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull NetworkBuffer wrap(byte @NotNull [] bytes, int readIndex, int writeIndex) {
|
||||||
|
return wrap(bytes, readIndex, writeIndex, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface Builder permits NetworkBufferImpl.Builder {
|
||||||
|
@NotNull Builder autoResize(@Nullable AutoResize autoResize);
|
||||||
|
|
||||||
|
@NotNull Builder registry(@Nullable Registries registries);
|
||||||
|
|
||||||
|
@NotNull NetworkBuffer build();
|
||||||
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface Writer {
|
interface AutoResize {
|
||||||
void write(@NotNull NetworkBuffer writer);
|
AutoResize DOUBLE = (capacity, targetSize) -> Math.max(capacity * 2, targetSize);
|
||||||
|
|
||||||
|
long resize(long capacity, long targetSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing, @Nullable Registries registries) {
|
||||||
public interface Reader<T> {
|
NetworkBuffer buffer = resizableBuffer(256, registries);
|
||||||
@NotNull T read(@NotNull NetworkBuffer reader);
|
writing.accept(buffer);
|
||||||
|
return buffer.read(RAW_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) {
|
static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) {
|
||||||
NetworkBuffer writer = new NetworkBuffer();
|
return makeArray(writing, null);
|
||||||
writing.accept(writer);
|
}
|
||||||
byte[] bytes = new byte[writer.writeIndex];
|
|
||||||
writer.copyTo(0, bytes, 0, bytes.length);
|
static <T> byte[] makeArray(@NotNull Type<T> type, @NotNull T value, @Nullable Registries registries) {
|
||||||
return bytes;
|
return makeArray(buffer -> buffer.write(type, value), registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> byte[] makeArray(@NotNull Type<T> type, @NotNull T value) {
|
||||||
|
return makeArray(type, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy(NetworkBuffer srcBuffer, long srcOffset,
|
||||||
|
NetworkBuffer dstBuffer, long dstOffset, long length) {
|
||||||
|
NetworkBufferImpl.copy(srcBuffer, srcOffset, dstBuffer, dstOffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean equals(NetworkBuffer buffer1, NetworkBuffer buffer2) {
|
||||||
|
return NetworkBufferImpl.equals(buffer1, buffer2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
575
src/main/java/net/minestom/server/network/NetworkBufferImpl.java
Normal file
575
src/main/java/net/minestom/server/network/NetworkBufferImpl.java
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
package net.minestom.server.network;
|
||||||
|
|
||||||
|
import net.minestom.server.registry.Registries;
|
||||||
|
import net.minestom.server.utils.ObjectPool;
|
||||||
|
import net.minestom.server.utils.nbt.BinaryTagReader;
|
||||||
|
import net.minestom.server.utils.nbt.BinaryTagWriter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.ShortBufferException;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.Cleaner;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
import java.util.zip.Deflater;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBufferUnsafe.*;
|
||||||
|
|
||||||
|
final class NetworkBufferImpl implements NetworkBuffer {
|
||||||
|
private static final Cleaner CLEANER = Cleaner.create();
|
||||||
|
private static final long DUMMY_ADDRESS = -1;
|
||||||
|
|
||||||
|
private final NetworkBufferImpl parent; // Used for slices so we can control GC over the parent buffer
|
||||||
|
private final BufferCleaner state;
|
||||||
|
// Address may be -1 if the buffer is a dummy buffer
|
||||||
|
// Dummy buffers are used for size calculations and do not have memory allocated
|
||||||
|
private long address, capacity;
|
||||||
|
private long readIndex, writeIndex;
|
||||||
|
boolean readOnly;
|
||||||
|
|
||||||
|
BinaryTagWriter nbtWriter;
|
||||||
|
BinaryTagReader nbtReader;
|
||||||
|
|
||||||
|
final @Nullable AutoResize autoResize;
|
||||||
|
final @Nullable Registries registries;
|
||||||
|
|
||||||
|
NetworkBufferImpl(NetworkBufferImpl parent,
|
||||||
|
long address, long capacity,
|
||||||
|
long readIndex, long writeIndex,
|
||||||
|
@Nullable AutoResize autoResize,
|
||||||
|
@Nullable Registries registries) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.address = address;
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.readIndex = readIndex;
|
||||||
|
this.writeIndex = writeIndex;
|
||||||
|
this.autoResize = autoResize;
|
||||||
|
this.registries = registries;
|
||||||
|
|
||||||
|
this.state = new BufferCleaner(new AtomicLong(address));
|
||||||
|
if (this.parent == null && address != DUMMY_ADDRESS) CLEANER.register(this, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record BufferCleaner(AtomicLong address) implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
UNSAFE.freeMemory(address.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void write(@NotNull Type<T> type, @UnknownNullability T value) {
|
||||||
|
assertReadOnly();
|
||||||
|
type.write(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @UnknownNullability T read(@NotNull Type<T> type) {
|
||||||
|
assertDummy();
|
||||||
|
return type.read(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void writeAt(long index, @NotNull Type<T> type, @UnknownNullability T value) {
|
||||||
|
assertReadOnly();
|
||||||
|
final long oldWriteIndex = writeIndex;
|
||||||
|
writeIndex = index;
|
||||||
|
try {
|
||||||
|
write(type, value);
|
||||||
|
} finally {
|
||||||
|
writeIndex = oldWriteIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @UnknownNullability T readAt(long index, @NotNull Type<T> type) {
|
||||||
|
assertDummy();
|
||||||
|
final long oldReadIndex = readIndex;
|
||||||
|
readIndex = index;
|
||||||
|
try {
|
||||||
|
return read(type);
|
||||||
|
} finally {
|
||||||
|
readIndex = oldReadIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copyTo(long srcOffset, byte @NotNull [] dest, long destOffset, long length) {
|
||||||
|
assertDummy();
|
||||||
|
assertOverflow(srcOffset + length);
|
||||||
|
assertOverflow(destOffset + length);
|
||||||
|
if (length == 0) return;
|
||||||
|
if (dest.length < destOffset + length) {
|
||||||
|
throw new IndexOutOfBoundsException("Destination array is too small: " + dest.length + " < " + (destOffset + length));
|
||||||
|
}
|
||||||
|
UNSAFE.copyMemory(null, address + srcOffset, dest, BYTE_ARRAY_OFFSET + destOffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte @NotNull [] extractBytes(@NotNull Consumer<@NotNull NetworkBuffer> extractor) {
|
||||||
|
assertDummy();
|
||||||
|
final long startingPosition = readIndex();
|
||||||
|
extractor.accept(this);
|
||||||
|
final long endingPosition = readIndex();
|
||||||
|
final long length = endingPosition - startingPosition;
|
||||||
|
assertOverflow(length);
|
||||||
|
byte[] output = new byte[(int) length];
|
||||||
|
copyTo(startingPosition, output, 0, output.length);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull NetworkBuffer clear() {
|
||||||
|
return index(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long writeIndex() {
|
||||||
|
return writeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long readIndex() {
|
||||||
|
return readIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull NetworkBuffer writeIndex(long writeIndex) {
|
||||||
|
this.writeIndex = writeIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull NetworkBuffer readIndex(long readIndex) {
|
||||||
|
this.readIndex = readIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull NetworkBuffer index(long readIndex, long writeIndex) {
|
||||||
|
this.readIndex = readIndex;
|
||||||
|
this.writeIndex = writeIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long advanceWrite(long length) {
|
||||||
|
final long oldWriteIndex = writeIndex;
|
||||||
|
writeIndex += length;
|
||||||
|
return oldWriteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long advanceRead(long length) {
|
||||||
|
final long oldReadIndex = readIndex;
|
||||||
|
readIndex += length;
|
||||||
|
return oldReadIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long readableBytes() {
|
||||||
|
return writeIndex - readIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long writableBytes() {
|
||||||
|
return capacity() - writeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long capacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readOnly() {
|
||||||
|
this.readOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resize(long newSize) {
|
||||||
|
assertDummy();
|
||||||
|
assertReadOnly();
|
||||||
|
if (newSize < capacity) throw new IllegalArgumentException("New size is smaller than the current size");
|
||||||
|
if (newSize == capacity) throw new IllegalArgumentException("New size is the same as the current size");
|
||||||
|
final long newAddress = UNSAFE.reallocateMemory(address, newSize);
|
||||||
|
this.address = newAddress;
|
||||||
|
this.capacity = newSize;
|
||||||
|
this.state.address.set(newAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ensureWritable(long length) {
|
||||||
|
assertReadOnly();
|
||||||
|
if (writableBytes() >= length) return;
|
||||||
|
final long newCapacity = newCapacity(length, capacity());
|
||||||
|
resize(newCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long newCapacity(long length, long capacity) {
|
||||||
|
final long targetSize = writeIndex + length;
|
||||||
|
final AutoResize strategy = this.autoResize;
|
||||||
|
if (strategy == null)
|
||||||
|
throw new IndexOutOfBoundsException("Buffer is full and cannot be resized: " + capacity + " -> " + targetSize);
|
||||||
|
final long newCapacity = strategy.resize(capacity, targetSize);
|
||||||
|
if (newCapacity == capacity)
|
||||||
|
throw new IndexOutOfBoundsException("Buffer is full has been resized to the same capacity: " + capacity + " -> " + targetSize);
|
||||||
|
return newCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compact() {
|
||||||
|
assertDummy();
|
||||||
|
assertReadOnly();
|
||||||
|
ByteBuffer nioBuffer = bufferSlice((int) readIndex, (int) readableBytes());
|
||||||
|
nioBuffer.compact();
|
||||||
|
writeIndex -= readIndex;
|
||||||
|
readIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NetworkBuffer slice(long index, long length, long readIndex, long writeIndex) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, length, capacity);
|
||||||
|
NetworkBufferImpl slice = new NetworkBufferImpl(this,
|
||||||
|
address + index, length,
|
||||||
|
readIndex, writeIndex,
|
||||||
|
autoResize, registries);
|
||||||
|
slice.readOnly = readOnly;
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NetworkBuffer copy(long index, long length, long readIndex, long writeIndex) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, length, capacity);
|
||||||
|
final long newAddress = UNSAFE.allocateMemory(length);
|
||||||
|
if (newAddress == 0) {
|
||||||
|
throw new OutOfMemoryError("Failed to allocate memory");
|
||||||
|
}
|
||||||
|
UNSAFE.copyMemory(address + index, newAddress, length);
|
||||||
|
return new NetworkBufferImpl(null,
|
||||||
|
newAddress, length,
|
||||||
|
readIndex, writeIndex,
|
||||||
|
autoResize, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readChannel(ReadableByteChannel channel) throws IOException {
|
||||||
|
assertDummy();
|
||||||
|
assertReadOnly();
|
||||||
|
assertOverflow(writeIndex + writableBytes());
|
||||||
|
var buffer = bufferSlice((int) writeIndex, (int) writableBytes());
|
||||||
|
final int count = channel.read(buffer);
|
||||||
|
if (count == -1) throw new EOFException("Disconnected");
|
||||||
|
advanceWrite(count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeChannel(SocketChannel channel) throws IOException {
|
||||||
|
assertDummy();
|
||||||
|
final long readableBytes = readableBytes();
|
||||||
|
if (readableBytes == 0) return true; // Nothing to write
|
||||||
|
assertOverflow(readIndex + readableBytes);
|
||||||
|
var buffer = bufferSlice((int) readIndex, (int) readableBytes);
|
||||||
|
if (!buffer.hasRemaining())
|
||||||
|
return true; // Nothing to write
|
||||||
|
final int count = channel.write(buffer);
|
||||||
|
if (count == -1) throw new EOFException("Disconnected");
|
||||||
|
advanceRead(count);
|
||||||
|
return !buffer.hasRemaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cipher(Cipher cipher, long start, long length) {
|
||||||
|
assertDummy();
|
||||||
|
assertOverflow(start + length);
|
||||||
|
ByteBuffer input = bufferSlice((int) start, (int) length);
|
||||||
|
try {
|
||||||
|
cipher.update(input, input.duplicate());
|
||||||
|
} catch (ShortBufferException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ObjectPool<Deflater> DEFLATER_POOL = ObjectPool.pool(Deflater::new);
|
||||||
|
private static final ObjectPool<Inflater> INFLATER_POOL = ObjectPool.pool(Inflater::new);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long compress(long start, long length, NetworkBuffer output) {
|
||||||
|
assertDummy();
|
||||||
|
impl(output).assertReadOnly();
|
||||||
|
assertOverflow(start + length);
|
||||||
|
|
||||||
|
ByteBuffer input = bufferSlice((int) start, (int) length);
|
||||||
|
ByteBuffer outputBuffer = impl(output).bufferSlice((int) output.writeIndex(), (int) output.writableBytes());
|
||||||
|
|
||||||
|
Deflater deflater = DEFLATER_POOL.get();
|
||||||
|
try {
|
||||||
|
deflater.setInput(input);
|
||||||
|
deflater.finish();
|
||||||
|
final int bytes = deflater.deflate(outputBuffer);
|
||||||
|
deflater.reset();
|
||||||
|
output.advanceWrite(bytes);
|
||||||
|
return bytes;
|
||||||
|
} finally {
|
||||||
|
DEFLATER_POOL.add(deflater);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long decompress(long start, long length, NetworkBuffer output) throws DataFormatException {
|
||||||
|
assertDummy();
|
||||||
|
impl(output).assertReadOnly();
|
||||||
|
assertOverflow(start + length);
|
||||||
|
|
||||||
|
ByteBuffer input = bufferSlice((int) start, (int) length);
|
||||||
|
ByteBuffer outputBuffer = impl(output).bufferSlice((int) output.writeIndex(), (int) output.writableBytes());
|
||||||
|
|
||||||
|
Inflater inflater = INFLATER_POOL.get();
|
||||||
|
try {
|
||||||
|
inflater.setInput(input);
|
||||||
|
final int bytes = inflater.inflate(outputBuffer);
|
||||||
|
inflater.reset();
|
||||||
|
output.advanceWrite(bytes);
|
||||||
|
return bytes;
|
||||||
|
} finally {
|
||||||
|
INFLATER_POOL.add(inflater);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Registries registries() {
|
||||||
|
return registries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteBuffer bufferSlice(int position, int length) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocateDirect(0).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
updateAddress(buffer, address);
|
||||||
|
updateCapacity(buffer, (int) capacity);
|
||||||
|
buffer.limit(position + length).position(position);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("NetworkBuffer{r%d|w%d->%d, registries=%s, autoResize=%s, readOnly=%s}",
|
||||||
|
readIndex, writeIndex, capacity, registries != null, autoResize != null, readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean ENDIAN_CONVERSION = ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN;
|
||||||
|
|
||||||
|
private boolean isDummy() {
|
||||||
|
return address == DUMMY_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal writing methods
|
||||||
|
void _putBytes(long index, byte[] value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, value.length, capacity);
|
||||||
|
UNSAFE.copyMemory(value, BYTE_ARRAY_OFFSET, null, address + index, value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getBytes(long index, byte[] value) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, value.length, capacity);
|
||||||
|
UNSAFE.copyMemory(null, address + index, value, BYTE_ARRAY_OFFSET, value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putByte(long index, byte value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Byte.BYTES, capacity);
|
||||||
|
UNSAFE.putByte(address + index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte _getByte(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Byte.BYTES, capacity);
|
||||||
|
return UNSAFE.getByte(address + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putShort(long index, short value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Short.BYTES, capacity);
|
||||||
|
if (ENDIAN_CONVERSION) value = Short.reverseBytes(value);
|
||||||
|
UNSAFE.putShort(address + index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
short _getShort(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Short.BYTES, capacity);
|
||||||
|
final short value = UNSAFE.getShort(address + index);
|
||||||
|
return ENDIAN_CONVERSION ? Short.reverseBytes(value) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putInt(long index, int value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Integer.BYTES, capacity);
|
||||||
|
if (ENDIAN_CONVERSION) value = Integer.reverseBytes(value);
|
||||||
|
UNSAFE.putInt(address + index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getInt(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Integer.BYTES, capacity);
|
||||||
|
final int value = UNSAFE.getInt(address + index);
|
||||||
|
return ENDIAN_CONVERSION ? Integer.reverseBytes(value) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putLong(long index, long value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Long.BYTES, capacity);
|
||||||
|
if (ENDIAN_CONVERSION) value = Long.reverseBytes(value);
|
||||||
|
UNSAFE.putLong(address + index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
long _getLong(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Long.BYTES, capacity);
|
||||||
|
final long value = UNSAFE.getLong(address + index);
|
||||||
|
return ENDIAN_CONVERSION ? Long.reverseBytes(value) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putFloat(long index, float value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Float.BYTES, capacity);
|
||||||
|
int intValue = Float.floatToIntBits(value);
|
||||||
|
if (ENDIAN_CONVERSION) intValue = Integer.reverseBytes(intValue);
|
||||||
|
UNSAFE.putInt(address + index, intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
float _getFloat(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Float.BYTES, capacity);
|
||||||
|
int intValue = UNSAFE.getInt(address + index);
|
||||||
|
if (ENDIAN_CONVERSION) intValue = Integer.reverseBytes(intValue);
|
||||||
|
return Float.intBitsToFloat(intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _putDouble(long index, double value) {
|
||||||
|
if (isDummy()) return;
|
||||||
|
assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(index, Double.BYTES, capacity);
|
||||||
|
long longValue = Double.doubleToLongBits(value);
|
||||||
|
if (ENDIAN_CONVERSION) longValue = Long.reverseBytes(longValue);
|
||||||
|
UNSAFE.putLong(address + index, longValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getDouble(long index) {
|
||||||
|
assertDummy();
|
||||||
|
Objects.checkFromIndexSize(index, Double.BYTES, capacity);
|
||||||
|
long longValue = UNSAFE.getLong(address + index);
|
||||||
|
if (ENDIAN_CONVERSION) longValue = Long.reverseBytes(longValue);
|
||||||
|
return Double.longBitsToDouble(longValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetworkBuffer wrap(byte @NotNull [] bytes, long readIndex, long writeIndex, @Nullable Registries registries) {
|
||||||
|
var buffer = new Builder(bytes.length).registry(registries).build();
|
||||||
|
buffer.writeAt(0, NetworkBuffer.RAW_BYTES, bytes);
|
||||||
|
buffer.index(readIndex, writeIndex);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy(NetworkBuffer srcBuffer, long srcOffset,
|
||||||
|
NetworkBuffer dstBuffer, long dstOffset, long length) {
|
||||||
|
var src = impl(srcBuffer);
|
||||||
|
var dst = impl(dstBuffer);
|
||||||
|
dst.assertReadOnly();
|
||||||
|
Objects.checkFromIndexSize(srcOffset, length, src.capacity);
|
||||||
|
Objects.checkFromIndexSize(dstOffset, length, dst.capacity);
|
||||||
|
final long srcAddress = src.address + srcOffset;
|
||||||
|
final long dstAddress = dst.address + dstOffset;
|
||||||
|
UNSAFE.copyMemory(srcAddress, dstAddress, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean equals(NetworkBuffer buffer1, NetworkBuffer buffer2) {
|
||||||
|
var impl1 = impl(buffer1);
|
||||||
|
var impl2 = impl(buffer2);
|
||||||
|
final int capacity = (int) impl1.capacity;
|
||||||
|
if (capacity != impl2.capacity) return false;
|
||||||
|
final long address1 = impl1.address;
|
||||||
|
final long address2 = impl2.address;
|
||||||
|
for (long i = 0; i < capacity; i++) {
|
||||||
|
if (UNSAFE.getByte(address1 + i) != UNSAFE.getByte(address2 + i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertReadOnly() {
|
||||||
|
if (readOnly) throw new UnsupportedOperationException("Buffer is read-only");
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertDummy() {
|
||||||
|
if (isDummy()) throw new UnsupportedOperationException("Buffer is a dummy buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Builder implements NetworkBuffer.Builder {
|
||||||
|
private final long initialSize;
|
||||||
|
private AutoResize autoResize;
|
||||||
|
private Registries registries;
|
||||||
|
|
||||||
|
public Builder(long initialSize) {
|
||||||
|
this.initialSize = initialSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NetworkBuffer.@NotNull Builder autoResize(@Nullable AutoResize autoResize) {
|
||||||
|
this.autoResize = autoResize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NetworkBuffer.@NotNull Builder registry(Registries registries) {
|
||||||
|
this.registries = registries;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull NetworkBuffer build() {
|
||||||
|
final long address = UNSAFE.allocateMemory(initialSize);
|
||||||
|
return new NetworkBufferImpl(null,
|
||||||
|
address, initialSize,
|
||||||
|
0, 0,
|
||||||
|
autoResize, registries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetworkBufferImpl dummy(Registries registries) {
|
||||||
|
// Dummy buffer with no memory allocated
|
||||||
|
// Useful for size calculations
|
||||||
|
return new NetworkBufferImpl(null,
|
||||||
|
DUMMY_ADDRESS, Long.MAX_VALUE,
|
||||||
|
0, 0,
|
||||||
|
null, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetworkBufferImpl impl(NetworkBuffer buffer) {
|
||||||
|
return (NetworkBufferImpl) buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertOverflow(long value) {
|
||||||
|
try {
|
||||||
|
Math.toIntExact(value); // Check if long is within the bounds of an int
|
||||||
|
} catch (ArithmeticException e) {
|
||||||
|
throw new RuntimeException("Method does not support long values: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,21 @@ public final class NetworkBufferTemplate {
|
|||||||
R apply(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10);
|
R apply(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface F11<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> {
|
||||||
|
R apply(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface F12<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> {
|
||||||
|
R apply(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface F19<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> {
|
||||||
|
R apply(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10, P11 p11, P12 p12, P13 p13, P14 p14, P15 p15, P16 p16, P17 p17, P18 p18, P19 p19);
|
||||||
|
}
|
||||||
|
|
||||||
public static <R> Type<R> template(Supplier<R> supplier) {
|
public static <R> Type<R> template(Supplier<R> supplier) {
|
||||||
return new NetworkBufferTypeImpl<>() {
|
return new NetworkBufferTypeImpl<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -331,4 +346,135 @@ public final class NetworkBufferTemplate {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> Type<R> template(
|
||||||
|
Type<P1> p1, Function<R, P1> g1, Type<P2> p2, Function<R, P2> g2,
|
||||||
|
Type<P3> p3, Function<R, P3> g3, Type<P4> p4, Function<R, P4> g4,
|
||||||
|
Type<P5> p5, Function<R, P5> g5, Type<P6> p6, Function<R, P6> g6,
|
||||||
|
Type<P7> p7, Function<R, P7> g7, Type<P8> p8, Function<R, P8> g8,
|
||||||
|
Type<P9> p9, Function<R, P9> g9, Type<P10> p10, Function<R, P10> g10,
|
||||||
|
Type<P11> p11, Function<R, P11> g11, F11<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> reader
|
||||||
|
) {
|
||||||
|
return new NetworkBufferTypeImpl<>() {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, R value) {
|
||||||
|
p1.write(buffer, g1.apply(value));
|
||||||
|
p2.write(buffer, g2.apply(value));
|
||||||
|
p3.write(buffer, g3.apply(value));
|
||||||
|
p4.write(buffer, g4.apply(value));
|
||||||
|
p5.write(buffer, g5.apply(value));
|
||||||
|
p6.write(buffer, g6.apply(value));
|
||||||
|
p7.write(buffer, g7.apply(value));
|
||||||
|
p8.write(buffer, g8.apply(value));
|
||||||
|
p9.write(buffer, g9.apply(value));
|
||||||
|
p10.write(buffer, g10.apply(value));
|
||||||
|
p11.write(buffer, g11.apply(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R read(@NotNull NetworkBuffer buffer) {
|
||||||
|
return reader.apply(
|
||||||
|
p1.read(buffer), p2.read(buffer),
|
||||||
|
p3.read(buffer), p4.read(buffer),
|
||||||
|
p5.read(buffer), p6.read(buffer),
|
||||||
|
p7.read(buffer), p8.read(buffer),
|
||||||
|
p9.read(buffer), p10.read(buffer),
|
||||||
|
p11.read(buffer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> Type<R> template(
|
||||||
|
Type<P1> p1, Function<R, P1> g1, Type<P2> p2, Function<R, P2> g2,
|
||||||
|
Type<P3> p3, Function<R, P3> g3, Type<P4> p4, Function<R, P4> g4,
|
||||||
|
Type<P5> p5, Function<R, P5> g5, Type<P6> p6, Function<R, P6> g6,
|
||||||
|
Type<P7> p7, Function<R, P7> g7, Type<P8> p8, Function<R, P8> g8,
|
||||||
|
Type<P9> p9, Function<R, P9> g9, Type<P10> p10, Function<R, P10> g10,
|
||||||
|
Type<P11> p11, Function<R, P11> g11, Type<P12> p12, Function<R, P12> g12, F12<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> reader
|
||||||
|
) {
|
||||||
|
return new NetworkBufferTypeImpl<>() {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, R value) {
|
||||||
|
p1.write(buffer, g1.apply(value));
|
||||||
|
p2.write(buffer, g2.apply(value));
|
||||||
|
p3.write(buffer, g3.apply(value));
|
||||||
|
p4.write(buffer, g4.apply(value));
|
||||||
|
p5.write(buffer, g5.apply(value));
|
||||||
|
p6.write(buffer, g6.apply(value));
|
||||||
|
p7.write(buffer, g7.apply(value));
|
||||||
|
p8.write(buffer, g8.apply(value));
|
||||||
|
p9.write(buffer, g9.apply(value));
|
||||||
|
p10.write(buffer, g10.apply(value));
|
||||||
|
p11.write(buffer, g11.apply(value));
|
||||||
|
p12.write(buffer, g12.apply(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R read(@NotNull NetworkBuffer buffer) {
|
||||||
|
return reader.apply(
|
||||||
|
p1.read(buffer), p2.read(buffer),
|
||||||
|
p3.read(buffer), p4.read(buffer),
|
||||||
|
p5.read(buffer), p6.read(buffer),
|
||||||
|
p7.read(buffer), p8.read(buffer),
|
||||||
|
p9.read(buffer), p10.read(buffer),
|
||||||
|
p11.read(buffer), p12.read(buffer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> Type<R> template(
|
||||||
|
Type<P1> p1, Function<R, P1> g1, Type<P2> p2, Function<R, P2> g2,
|
||||||
|
Type<P3> p3, Function<R, P3> g3, Type<P4> p4, Function<R, P4> g4,
|
||||||
|
Type<P5> p5, Function<R, P5> g5, Type<P6> p6, Function<R, P6> g6,
|
||||||
|
Type<P7> p7, Function<R, P7> g7, Type<P8> p8, Function<R, P8> g8,
|
||||||
|
Type<P9> p9, Function<R, P9> g9, Type<P10> p10, Function<R, P10> g10,
|
||||||
|
Type<P11> p11, Function<R, P11> g11, Type<P12> p12, Function<R, P12> g12,
|
||||||
|
Type<P13> p13, Function<R, P13> g13, Type<P14> p14, Function<R, P14> g14,
|
||||||
|
Type<P15> p15, Function<R, P15> g15, Type<P16> p16, Function<R, P16> g16,
|
||||||
|
Type<P17> p17, Function<R, P17> g17, Type<P18> p18, Function<R, P18> g18,
|
||||||
|
Type<P19> p19, Function<R, P19> g19, F19<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> reader
|
||||||
|
) {
|
||||||
|
return new NetworkBufferTypeImpl<>() {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, R value) {
|
||||||
|
p1.write(buffer, g1.apply(value));
|
||||||
|
p2.write(buffer, g2.apply(value));
|
||||||
|
p3.write(buffer, g3.apply(value));
|
||||||
|
p4.write(buffer, g4.apply(value));
|
||||||
|
p5.write(buffer, g5.apply(value));
|
||||||
|
p6.write(buffer, g6.apply(value));
|
||||||
|
p7.write(buffer, g7.apply(value));
|
||||||
|
p8.write(buffer, g8.apply(value));
|
||||||
|
p9.write(buffer, g9.apply(value));
|
||||||
|
p10.write(buffer, g10.apply(value));
|
||||||
|
p11.write(buffer, g11.apply(value));
|
||||||
|
p12.write(buffer, g12.apply(value));
|
||||||
|
p13.write(buffer, g13.apply(value));
|
||||||
|
p14.write(buffer, g14.apply(value));
|
||||||
|
p15.write(buffer, g15.apply(value));
|
||||||
|
p16.write(buffer, g16.apply(value));
|
||||||
|
p17.write(buffer, g17.apply(value));
|
||||||
|
p18.write(buffer, g18.apply(value));
|
||||||
|
p19.write(buffer, g19.apply(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R read(@NotNull NetworkBuffer buffer) {
|
||||||
|
return reader.apply(
|
||||||
|
p1.read(buffer), p2.read(buffer),
|
||||||
|
p3.read(buffer), p4.read(buffer),
|
||||||
|
p5.read(buffer), p6.read(buffer),
|
||||||
|
p7.read(buffer), p8.read(buffer),
|
||||||
|
p9.read(buffer), p10.read(buffer),
|
||||||
|
p11.read(buffer), p12.read(buffer),
|
||||||
|
p13.read(buffer), p14.read(buffer),
|
||||||
|
p15.read(buffer), p16.read(buffer),
|
||||||
|
p17.read(buffer), p18.read(buffer),
|
||||||
|
p19.read(buffer)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.minestom.server.network;
|
package net.minestom.server.network;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||||
import net.kyori.adventure.nbt.BinaryTag;
|
import net.kyori.adventure.nbt.BinaryTag;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.minestom.server.coordinate.Point;
|
import net.minestom.server.coordinate.Point;
|
||||||
import net.minestom.server.coordinate.Pos;
|
import net.minestom.server.coordinate.Pos;
|
||||||
import net.minestom.server.coordinate.Vec;
|
import net.minestom.server.coordinate.Vec;
|
||||||
import net.minestom.server.network.packet.server.play.data.WorldPos;
|
|
||||||
import net.minestom.server.registry.DynamicRegistry;
|
import net.minestom.server.registry.DynamicRegistry;
|
||||||
import net.minestom.server.registry.ProtocolObject;
|
import net.minestom.server.registry.ProtocolObject;
|
||||||
import net.minestom.server.registry.Registries;
|
import net.minestom.server.registry.Registries;
|
||||||
@ -20,12 +20,12 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.*;
|
import static net.minestom.server.network.NetworkBuffer.*;
|
||||||
|
import static net.minestom.server.network.NetworkBufferImpl.impl;
|
||||||
|
|
||||||
interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||||
int SEGMENT_BITS = 0x7F;
|
int SEGMENT_BITS = 0x7F;
|
||||||
@ -45,15 +45,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record BooleanType() implements NetworkBufferTypeImpl<Boolean> {
|
record BooleanType() implements NetworkBufferTypeImpl<Boolean> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Boolean value) {
|
public void write(@NotNull NetworkBuffer buffer, Boolean value) {
|
||||||
buffer.ensureSize(1);
|
buffer.ensureWritable(1);
|
||||||
buffer.nioBuffer.put(buffer.writeIndex(), value ? (byte) 1 : (byte) 0);
|
impl(buffer)._putByte(buffer.writeIndex(), value ? (byte) 1 : (byte) 0);
|
||||||
buffer.writeIndex += 1;
|
buffer.advanceWrite(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean read(@NotNull NetworkBuffer buffer) {
|
public Boolean read(@NotNull NetworkBuffer buffer) {
|
||||||
final byte value = buffer.nioBuffer.get(buffer.readIndex());
|
final byte value = impl(buffer)._getByte(buffer.readIndex());
|
||||||
buffer.readIndex += 1;
|
buffer.advanceRead(1);
|
||||||
return value == 1;
|
return value == 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,15 +61,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record ByteType() implements NetworkBufferTypeImpl<Byte> {
|
record ByteType() implements NetworkBufferTypeImpl<Byte> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Byte value) {
|
public void write(@NotNull NetworkBuffer buffer, Byte value) {
|
||||||
buffer.ensureSize(1);
|
buffer.ensureWritable(1);
|
||||||
buffer.nioBuffer.put(buffer.writeIndex(), value);
|
impl(buffer)._putByte(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 1;
|
buffer.advanceWrite(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Byte read(@NotNull NetworkBuffer buffer) {
|
public Byte read(@NotNull NetworkBuffer buffer) {
|
||||||
final byte value = buffer.nioBuffer.get(buffer.readIndex());
|
final byte value = impl(buffer)._getByte(buffer.readIndex());
|
||||||
buffer.readIndex += 1;
|
buffer.advanceRead(1);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,15 +77,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record ShortType() implements NetworkBufferTypeImpl<Short> {
|
record ShortType() implements NetworkBufferTypeImpl<Short> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Short value) {
|
public void write(@NotNull NetworkBuffer buffer, Short value) {
|
||||||
buffer.ensureSize(2);
|
buffer.ensureWritable(2);
|
||||||
buffer.nioBuffer.putShort(buffer.writeIndex(), value);
|
impl(buffer)._putShort(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 2;
|
buffer.advanceWrite(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Short read(@NotNull NetworkBuffer buffer) {
|
public Short read(@NotNull NetworkBuffer buffer) {
|
||||||
final short value = buffer.nioBuffer.getShort(buffer.readIndex());
|
final short value = impl(buffer)._getShort(buffer.readIndex());
|
||||||
buffer.readIndex += 2;
|
buffer.advanceRead(2);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,15 +93,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record UnsignedShortType() implements NetworkBufferTypeImpl<Integer> {
|
record UnsignedShortType() implements NetworkBufferTypeImpl<Integer> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
||||||
buffer.ensureSize(2);
|
buffer.ensureWritable(2);
|
||||||
buffer.nioBuffer.putShort(buffer.writeIndex(), (short) (value & 0xFFFF));
|
impl(buffer)._putShort(buffer.writeIndex(), (short) (value & 0xFFFF));
|
||||||
buffer.writeIndex += 2;
|
buffer.advanceWrite(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer read(@NotNull NetworkBuffer buffer) {
|
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||||
final short value = buffer.nioBuffer.getShort(buffer.readIndex());
|
final short value = impl(buffer)._getShort(buffer.readIndex());
|
||||||
buffer.readIndex += 2;
|
buffer.advanceRead(2);
|
||||||
return value & 0xFFFF;
|
return value & 0xFFFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,15 +109,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record IntType() implements NetworkBufferTypeImpl<Integer> {
|
record IntType() implements NetworkBufferTypeImpl<Integer> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
||||||
buffer.ensureSize(4);
|
buffer.ensureWritable(4);
|
||||||
buffer.nioBuffer.putInt(buffer.writeIndex(), value);
|
impl(buffer)._putInt(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 4;
|
buffer.advanceWrite(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer read(@NotNull NetworkBuffer buffer) {
|
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||||
final int value = buffer.nioBuffer.getInt(buffer.readIndex());
|
final int value = impl(buffer)._getInt(buffer.readIndex());
|
||||||
buffer.readIndex += 4;
|
buffer.advanceRead(4);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,15 +125,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record LongType() implements NetworkBufferTypeImpl<Long> {
|
record LongType() implements NetworkBufferTypeImpl<Long> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
||||||
buffer.ensureSize(8);
|
buffer.ensureWritable(8);
|
||||||
buffer.nioBuffer.putLong(buffer.writeIndex(), value);
|
impl(buffer)._putLong(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 8;
|
buffer.advanceWrite(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long read(@NotNull NetworkBuffer buffer) {
|
public Long read(@NotNull NetworkBuffer buffer) {
|
||||||
final long value = buffer.nioBuffer.getLong(buffer.readIndex());
|
final long value = impl(buffer)._getLong(buffer.readIndex());
|
||||||
buffer.readIndex += 8;
|
buffer.advanceRead(8);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,15 +141,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record FloatType() implements NetworkBufferTypeImpl<Float> {
|
record FloatType() implements NetworkBufferTypeImpl<Float> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Float value) {
|
public void write(@NotNull NetworkBuffer buffer, Float value) {
|
||||||
buffer.ensureSize(4);
|
buffer.ensureWritable(4);
|
||||||
buffer.nioBuffer.putFloat(buffer.writeIndex(), value);
|
impl(buffer)._putFloat(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 4;
|
buffer.advanceWrite(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Float read(@NotNull NetworkBuffer buffer) {
|
public Float read(@NotNull NetworkBuffer buffer) {
|
||||||
final float value = buffer.nioBuffer.getFloat(buffer.readIndex());
|
final float value = impl(buffer)._getFloat(buffer.readIndex());
|
||||||
buffer.readIndex += 4;
|
buffer.advanceRead(4);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,15 +157,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record DoubleType() implements NetworkBufferTypeImpl<Double> {
|
record DoubleType() implements NetworkBufferTypeImpl<Double> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Double value) {
|
public void write(@NotNull NetworkBuffer buffer, Double value) {
|
||||||
buffer.ensureSize(8);
|
buffer.ensureWritable(8);
|
||||||
buffer.nioBuffer.putDouble(buffer.writeIndex(), value);
|
impl(buffer)._putDouble(buffer.writeIndex(), value);
|
||||||
buffer.writeIndex += 8;
|
buffer.advanceWrite(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double read(@NotNull NetworkBuffer buffer) {
|
public Double read(@NotNull NetworkBuffer buffer) {
|
||||||
final double value = buffer.nioBuffer.getDouble(buffer.readIndex());
|
final double value = impl(buffer)._getDouble(buffer.readIndex());
|
||||||
buffer.readIndex += 8;
|
buffer.advanceRead(8);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,67 +173,73 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record VarIntType() implements NetworkBufferTypeImpl<Integer> {
|
record VarIntType() implements NetworkBufferTypeImpl<Integer> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Integer boxed) {
|
public void write(@NotNull NetworkBuffer buffer, Integer boxed) {
|
||||||
final int value = boxed;
|
buffer.ensureWritable(5);
|
||||||
final int index = buffer.writeIndex();
|
long index = buffer.writeIndex();
|
||||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
int value = boxed;
|
||||||
buffer.ensureSize(1);
|
var nio = impl(buffer);
|
||||||
buffer.nioBuffer.put(index, (byte) value);
|
while (true) {
|
||||||
buffer.writeIndex += 1;
|
if ((value & ~SEGMENT_BITS) == 0) {
|
||||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
nio._putByte(index++, (byte) value);
|
||||||
buffer.ensureSize(2);
|
buffer.advanceWrite(index - buffer.writeIndex());
|
||||||
buffer.nioBuffer.putShort(index, (short) ((value & 0x7F | 0x80) << 8 | (value >>> 7)));
|
return;
|
||||||
buffer.writeIndex += 2;
|
}
|
||||||
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
nio._putByte(index++, (byte) ((byte) (value & SEGMENT_BITS) | CONTINUE_BIT));
|
||||||
buffer.ensureSize(3);
|
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||||
var nio = buffer.nioBuffer;
|
value >>>= 7;
|
||||||
nio.put(index, (byte) (value & 0x7F | 0x80));
|
|
||||||
nio.put(index + 1, (byte) ((value >>> 7) & 0x7F | 0x80));
|
|
||||||
nio.put(index + 2, (byte) (value >>> 14));
|
|
||||||
buffer.writeIndex += 3;
|
|
||||||
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
|
|
||||||
buffer.ensureSize(4);
|
|
||||||
var nio = buffer.nioBuffer;
|
|
||||||
nio.putInt(index, (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
|
|
||||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21));
|
|
||||||
buffer.writeIndex += 4;
|
|
||||||
} else {
|
|
||||||
buffer.ensureSize(5);
|
|
||||||
var nio = buffer.nioBuffer;
|
|
||||||
nio.putInt(index, (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
|
|
||||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80));
|
|
||||||
nio.put(index + 4, (byte) (value >>> 28));
|
|
||||||
buffer.writeIndex += 5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer read(@NotNull NetworkBuffer buffer) {
|
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||||
int index = buffer.readIndex();
|
long index = buffer.readIndex();
|
||||||
// https://github.com/jvm-profiling-tools/async-profiler/blob/a38a375dc62b31a8109f3af97366a307abb0fe6f/src/converter/one/jfr/JfrReader.java#L393
|
// https://github.com/jvm-profiling-tools/async-profiler/blob/a38a375dc62b31a8109f3af97366a307abb0fe6f/src/converter/one/jfr/JfrReader.java#L393
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (int shift = 0; ; shift += 7) {
|
for (int shift = 0; ; shift += 7) {
|
||||||
byte b = buffer.nioBuffer.get(index++);
|
byte b = impl(buffer)._getByte(index++);
|
||||||
result |= (b & 0x7f) << shift;
|
result |= (b & 0x7f) << shift;
|
||||||
if (b >= 0) {
|
if (b >= 0) {
|
||||||
buffer.readIndex += index - buffer.readIndex();
|
buffer.advanceRead(index - buffer.readIndex());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record VarInt3Type() implements NetworkBufferTypeImpl<Integer> {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, Integer boxed) {
|
||||||
|
final int value = boxed;
|
||||||
|
// Value must be between 0 and 2^21
|
||||||
|
Check.argCondition(value < 0 || value >= (1 << 21), "VarInt3 out of bounds: {0}", value);
|
||||||
|
buffer.ensureWritable(3);
|
||||||
|
final long startIndex = buffer.writeIndex();
|
||||||
|
var impl = impl(buffer);
|
||||||
|
impl._putByte(startIndex, (byte) (value & 0x7F | 0x80));
|
||||||
|
impl._putByte(startIndex + 1, (byte) ((value >>> 7) & 0x7F | 0x80));
|
||||||
|
impl._putByte(startIndex + 2, (byte) (value >>> 14));
|
||||||
|
buffer.advanceWrite(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||||
|
// Ensure that the buffer can read other var-int sizes
|
||||||
|
// The optimization is mostly relevant for writing
|
||||||
|
return buffer.read(VAR_INT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
record VarLongType() implements NetworkBufferTypeImpl<Long> {
|
record VarLongType() implements NetworkBufferTypeImpl<Long> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
||||||
buffer.ensureSize(10);
|
buffer.ensureWritable(10);
|
||||||
int size = 0;
|
int size = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if ((value & ~((long) SEGMENT_BITS)) == 0) {
|
if ((value & ~((long) SEGMENT_BITS)) == 0) {
|
||||||
buffer.nioBuffer.put(buffer.writeIndex() + size, (byte) value.intValue());
|
impl(buffer)._putByte(buffer.writeIndex() + size, (byte) value.intValue());
|
||||||
buffer.writeIndex += size + 1;
|
buffer.advanceWrite(size + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buffer.nioBuffer.put(buffer.writeIndex() + size, (byte) (value & SEGMENT_BITS | CONTINUE_BIT));
|
impl(buffer)._putByte(buffer.writeIndex() + size, (byte) (value & SEGMENT_BITS | CONTINUE_BIT));
|
||||||
size++;
|
size++;
|
||||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||||
value >>>= 7;
|
value >>>= 7;
|
||||||
@ -247,34 +253,44 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
int position = 0;
|
int position = 0;
|
||||||
byte currentByte;
|
byte currentByte;
|
||||||
while (true) {
|
while (true) {
|
||||||
currentByte = buffer.nioBuffer.get(buffer.readIndex() + length);
|
currentByte = impl(buffer)._getByte(buffer.readIndex() + length);
|
||||||
length++;
|
length++;
|
||||||
value |= (long) (currentByte & SEGMENT_BITS) << position;
|
value |= (long) (currentByte & SEGMENT_BITS) << position;
|
||||||
if ((currentByte & CONTINUE_BIT) == 0) break;
|
if ((currentByte & CONTINUE_BIT) == 0) break;
|
||||||
position += 7;
|
position += 7;
|
||||||
if (position >= 64) throw new RuntimeException("VarLong is too big");
|
if (position >= 64) throw new RuntimeException("VarLong is too big");
|
||||||
}
|
}
|
||||||
buffer.readIndex += length;
|
buffer.advanceRead(length);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record RawBytesType() implements NetworkBufferTypeImpl<byte[]> {
|
record RawBytesType(int length) implements NetworkBufferTypeImpl<byte[]> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, byte[] value) {
|
public void write(@NotNull NetworkBuffer buffer, byte[] value) {
|
||||||
buffer.ensureSize(value.length);
|
if (length != -1 && value.length != length) {
|
||||||
buffer.nioBuffer.put(buffer.writeIndex(), value);
|
throw new IllegalArgumentException("Invalid length: " + value.length + " != " + length);
|
||||||
buffer.writeIndex += value.length;
|
}
|
||||||
|
final int length = value.length;
|
||||||
|
if (length == 0) return;
|
||||||
|
buffer.ensureWritable(length);
|
||||||
|
impl(buffer)._putBytes(buffer.writeIndex(), value);
|
||||||
|
buffer.advanceWrite(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] read(@NotNull NetworkBuffer buffer) {
|
public byte[] read(@NotNull NetworkBuffer buffer) {
|
||||||
final int limit = buffer.nioBuffer.limit();
|
long length = buffer.readableBytes();
|
||||||
final int length = limit - buffer.readIndex();
|
if (this.length != -1) {
|
||||||
assert length >= 0 : "Invalid remaining: " + length;
|
length = Math.min(length, this.length);
|
||||||
final byte[] bytes = new byte[length];
|
}
|
||||||
buffer.nioBuffer.get(buffer.readIndex(), bytes);
|
if (length == 0) return new byte[0];
|
||||||
buffer.readIndex += length;
|
assert length > 0 : "Invalid remaining: " + length;
|
||||||
|
|
||||||
|
final int arrayLength = Math.toIntExact(length);
|
||||||
|
final byte[] bytes = new byte[arrayLength];
|
||||||
|
impl(buffer)._getBytes(buffer.readIndex(), bytes);
|
||||||
|
buffer.advanceRead(arrayLength);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,18 +299,12 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, String value) {
|
public void write(@NotNull NetworkBuffer buffer, String value) {
|
||||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||||
buffer.write(VAR_INT, bytes.length);
|
buffer.write(BYTE_ARRAY, bytes);
|
||||||
buffer.write(RAW_BYTES, bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String read(@NotNull NetworkBuffer buffer) {
|
public String read(@NotNull NetworkBuffer buffer) {
|
||||||
final int length = buffer.read(VAR_INT);
|
final byte[] bytes = buffer.read(BYTE_ARRAY);
|
||||||
final int remaining = buffer.nioBuffer.limit() - buffer.readIndex();
|
|
||||||
Check.argCondition(length > remaining, "String is too long (length: {0}, readable: {1})", length, remaining);
|
|
||||||
byte[] bytes = new byte[length];
|
|
||||||
buffer.nioBuffer.get(buffer.readIndex(), bytes);
|
|
||||||
buffer.readIndex += length;
|
|
||||||
return new String(bytes, StandardCharsets.UTF_8);
|
return new String(bytes, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,7 +333,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
record NbtType() implements NetworkBufferTypeImpl<BinaryTag> {
|
record NbtType() implements NetworkBufferTypeImpl<BinaryTag> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, BinaryTag value) {
|
public void write(@NotNull NetworkBuffer buffer, BinaryTag value) {
|
||||||
BinaryTagWriter nbtWriter = buffer.nbtWriter;
|
BinaryTagWriter nbtWriter = impl(buffer).nbtWriter;
|
||||||
if (nbtWriter == null) {
|
if (nbtWriter == null) {
|
||||||
nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() {
|
nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() {
|
||||||
@Override
|
@Override
|
||||||
@ -331,7 +341,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
buffer.write(BYTE, (byte) b);
|
buffer.write(BYTE, (byte) b);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
buffer.nbtWriter = nbtWriter;
|
impl(buffer).nbtWriter = nbtWriter;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
nbtWriter.writeNameless(value);
|
nbtWriter.writeNameless(value);
|
||||||
@ -342,7 +352,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BinaryTag read(@NotNull NetworkBuffer buffer) {
|
public BinaryTag read(@NotNull NetworkBuffer buffer) {
|
||||||
BinaryTagReader nbtReader = buffer.nbtReader;
|
BinaryTagReader nbtReader = impl(buffer).nbtReader;
|
||||||
if (nbtReader == null) {
|
if (nbtReader == null) {
|
||||||
nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() {
|
nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() {
|
||||||
@Override
|
@Override
|
||||||
@ -352,10 +362,10 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() {
|
public int available() {
|
||||||
return buffer.readableBytes();
|
return (int) buffer.readableBytes();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
buffer.nbtReader = nbtReader;
|
impl(buffer).nbtReader = nbtReader;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return nbtReader.readNameless();
|
return nbtReader.readNameless();
|
||||||
@ -447,10 +457,10 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] read(@NotNull NetworkBuffer buffer) {
|
public byte[] read(@NotNull NetworkBuffer buffer) {
|
||||||
final int length = buffer.read(VAR_INT);
|
final int length = buffer.read(VAR_INT);
|
||||||
final byte[] bytes = new byte[length];
|
if (length == 0) return new byte[0];
|
||||||
buffer.nioBuffer.get(buffer.readIndex(), bytes);
|
final long remaining = buffer.readableBytes();
|
||||||
buffer.readIndex += length;
|
Check.argCondition(length > remaining, "String is too long (length: {0}, readable: {1})", length, remaining);
|
||||||
return bytes;
|
return buffer.read(FixedRawBytes(length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,18 +544,6 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record DeathLocationType() implements NetworkBufferTypeImpl<WorldPos> {
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer buffer, WorldPos value) {
|
|
||||||
buffer.writeOptional(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldPos read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return buffer.readOptional(WorldPos::new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record Vector3Type() implements NetworkBufferTypeImpl<Point> {
|
record Vector3Type() implements NetworkBufferTypeImpl<Point> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, Point value) {
|
public void write(@NotNull NetworkBuffer buffer, Point value) {
|
||||||
@ -580,6 +578,23 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record Vector3BType() implements NetworkBufferTypeImpl<Point> {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, Point value) {
|
||||||
|
buffer.write(BYTE, (byte) value.x());
|
||||||
|
buffer.write(BYTE, (byte) value.y());
|
||||||
|
buffer.write(BYTE, (byte) value.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Point read(@NotNull NetworkBuffer buffer) {
|
||||||
|
final byte x = buffer.read(BYTE);
|
||||||
|
final byte y = buffer.read(BYTE);
|
||||||
|
final byte z = buffer.read(BYTE);
|
||||||
|
return new Vec(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
record QuaternionType() implements NetworkBufferTypeImpl<float[]> {
|
record QuaternionType() implements NetworkBufferTypeImpl<float[]> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, float[] value) {
|
public void write(@NotNull NetworkBuffer buffer, float[] value) {
|
||||||
@ -601,27 +616,61 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
|
|
||||||
// Combinators
|
// Combinators
|
||||||
|
|
||||||
record EnumType<E extends Enum<?>>(@NotNull Class<E> enumClass) implements NetworkBufferTypeImpl<E> {
|
record EnumSetType<E extends Enum<E>>(@NotNull Class<E> enumType,
|
||||||
|
E[] values) implements NetworkBufferTypeImpl<EnumSet<E>> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, E value) {
|
public void write(@NotNull NetworkBuffer buffer, EnumSet<E> value) {
|
||||||
buffer.writeEnum(enumClass, value);
|
BitSet bitSet = new BitSet(values.length);
|
||||||
|
for (int i = 0; i < values.length; ++i) {
|
||||||
|
bitSet.set(i, value.contains(values[i]));
|
||||||
|
}
|
||||||
|
final byte[] array = bitSet.toByteArray();
|
||||||
|
buffer.write(RAW_BYTES, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E read(@NotNull NetworkBuffer buffer) {
|
public EnumSet<E> read(@NotNull NetworkBuffer buffer) {
|
||||||
return buffer.readEnum(enumClass);
|
final byte[] array = buffer.read(FixedRawBytes((values.length + 7) / 8));
|
||||||
|
BitSet bitSet = BitSet.valueOf(array);
|
||||||
|
EnumSet<E> enumSet = EnumSet.noneOf(enumType);
|
||||||
|
for (int i = 0; i < values.length; ++i) {
|
||||||
|
if (bitSet.get(i)) {
|
||||||
|
enumSet.add(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enumSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record FixedBitSetType(int length) implements NetworkBufferTypeImpl<BitSet> {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, BitSet value) {
|
||||||
|
final int setLength = value.length();
|
||||||
|
if (setLength > length) {
|
||||||
|
throw new IllegalArgumentException("BitSet is larger than expected size (" + setLength + ">" + length + ")");
|
||||||
|
} else {
|
||||||
|
final byte[] array = value.toByteArray();
|
||||||
|
buffer.write(RAW_BYTES, array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BitSet read(@NotNull NetworkBuffer buffer) {
|
||||||
|
final byte[] array = buffer.read(FixedRawBytes((length + 7) / 8));
|
||||||
|
return BitSet.valueOf(array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record OptionalType<T>(@NotNull Type<T> parent) implements NetworkBufferTypeImpl<@Nullable T> {
|
record OptionalType<T>(@NotNull Type<T> parent) implements NetworkBufferTypeImpl<@Nullable T> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, T value) {
|
public void write(@NotNull NetworkBuffer buffer, T value) {
|
||||||
buffer.writeOptional(parent, value);
|
buffer.write(BOOLEAN, value != null);
|
||||||
|
if (value != null) buffer.write(parent, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(@NotNull NetworkBuffer buffer) {
|
public T read(@NotNull NetworkBuffer buffer) {
|
||||||
return buffer.readOptional(parent);
|
return buffer.read(BOOLEAN) ? buffer.read(parent) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,7 +695,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record MappedType<T, S>(@NotNull Type<T> parent, @NotNull Function<T, S> to,
|
record TransformType<T, S>(@NotNull Type<T> parent, @NotNull Function<T, S> to,
|
||||||
@NotNull Function<S, T> from) implements NetworkBufferTypeImpl<S> {
|
@NotNull Function<S, T> from) implements NetworkBufferTypeImpl<S> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, S value) {
|
public void write(@NotNull NetworkBuffer buffer, S value) {
|
||||||
@ -659,27 +708,51 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record MapType<K, V>(@NotNull Type<K> parent, @NotNull NetworkBuffer.Type<V> valueType,
|
||||||
|
int maxSize) implements NetworkBufferTypeImpl<Map<K, V>> {
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull NetworkBuffer buffer, Map<K, V> map) {
|
||||||
|
buffer.write(VAR_INT, map.size());
|
||||||
|
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||||
|
buffer.write(parent, entry.getKey());
|
||||||
|
buffer.write(valueType, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Map<K, V> read(@NotNull NetworkBuffer buffer) {
|
||||||
|
final int size = buffer.read(VAR_INT);
|
||||||
|
Check.argCondition(size > maxSize, "Map size ({0}) is higher than the maximum allowed size ({1})", size, maxSize);
|
||||||
|
K[] keys = (K[]) new Object[size];
|
||||||
|
V[] values = (V[]) new Object[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
keys[i] = buffer.read(parent);
|
||||||
|
values[i] = buffer.read(valueType);
|
||||||
|
}
|
||||||
|
return Map.copyOf(new Object2ObjectArrayMap<>(keys, values, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
record ListType<T>(@NotNull Type<T> parent, int maxSize) implements NetworkBufferTypeImpl<List<T>> {
|
record ListType<T>(@NotNull Type<T> parent, int maxSize) implements NetworkBufferTypeImpl<List<T>> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, List<T> value) {
|
public void write(@NotNull NetworkBuffer buffer, List<T> values) {
|
||||||
buffer.writeCollection(parent, value);
|
if (values == null) {
|
||||||
|
buffer.write(BYTE, (byte) 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer.write(VAR_INT, values.size());
|
||||||
|
for (T value : values) buffer.write(parent, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public List<T> read(@NotNull NetworkBuffer buffer) {
|
public List<T> read(@NotNull NetworkBuffer buffer) {
|
||||||
return buffer.readCollection(parent, maxSize);
|
final int size = buffer.read(VAR_INT);
|
||||||
}
|
Check.argCondition(size > maxSize, "Collection size ({0}) is higher than the maximum allowed size ({1})", size, maxSize);
|
||||||
}
|
T[] values = (T[]) new Object[size];
|
||||||
|
for (int i = 0; i < size; i++) values[i] = buffer.read(parent);
|
||||||
record OptionalTypeImpl<T>(@NotNull Type<T> parent) implements NetworkBufferTypeImpl<T> {
|
return List.of(values);
|
||||||
@Override
|
|
||||||
public void write(@NotNull NetworkBuffer buffer, T value) {
|
|
||||||
buffer.writeOptional(parent, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return buffer.readOptional(parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,23 +760,31 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
|||||||
@NotNull Function<Registries, DynamicRegistry<T>> selector) implements NetworkBufferTypeImpl<DynamicRegistry.Key<T>> {
|
@NotNull Function<Registries, DynamicRegistry<T>> selector) implements NetworkBufferTypeImpl<DynamicRegistry.Key<T>> {
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer buffer, DynamicRegistry.Key<T> value) {
|
public void write(@NotNull NetworkBuffer buffer, DynamicRegistry.Key<T> value) {
|
||||||
Check.stateCondition(buffer.registries == null, "Buffer does not have registries");
|
final Registries registries = impl(buffer).registries;
|
||||||
final DynamicRegistry<T> registry = selector.apply(buffer.registries);
|
Check.stateCondition(registries == null, "Buffer does not have registries");
|
||||||
|
final DynamicRegistry<T> registry = selector.apply(registries);
|
||||||
// Painting variants may be sent in their entirety rather than a registry reference so the ID is offset by 1 to indicate this.
|
// Painting variants may be sent in their entirety rather than a registry reference so the ID is offset by 1 to indicate this.
|
||||||
// FIXME: Support sending the entire registry object instead of an ID reference.
|
// FIXME: Support sending the entire registry object instead of an ID reference.
|
||||||
final int id = registry.id().equals("minecraft:painting_variant")?registry.getId(value)+1:registry.getId(value);
|
final int id = registry.id().equals("minecraft:painting_variant") ? registry.getId(value) + 1 : registry.getId(value);
|
||||||
Check.argCondition(id == -1, "Key is not registered: {0} > {1}", registry, value);
|
Check.argCondition(id == -1, "Key is not registered: {0} > {1}", registry, value);
|
||||||
buffer.write(VAR_INT, id);
|
buffer.write(VAR_INT, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynamicRegistry.Key<T> read(@NotNull NetworkBuffer buffer) {
|
public DynamicRegistry.Key<T> read(@NotNull NetworkBuffer buffer) {
|
||||||
Check.stateCondition(buffer.registries == null, "Buffer does not have registries");
|
final Registries registries = impl(buffer).registries;
|
||||||
DynamicRegistry<T> registry = selector.apply(buffer.registries);
|
Check.stateCondition(registries == null, "Buffer does not have registries");
|
||||||
|
DynamicRegistry<T> registry = selector.apply(registries);
|
||||||
final int id = buffer.read(VAR_INT);
|
final int id = buffer.read(VAR_INT);
|
||||||
final DynamicRegistry.Key<T> key = registry.getKey(id);
|
final DynamicRegistry.Key<T> key = registry.getKey(id);
|
||||||
Check.argCondition(key == null, "No such ID in registry: {0} > {1}", registry, id);
|
Check.argCondition(key == null, "No such ID in registry: {0} > {1}", registry, id);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <T> long sizeOf(Type<T> type, T value, Registries registries) {
|
||||||
|
NetworkBuffer buffer = NetworkBufferImpl.dummy(registries);
|
||||||
|
type.write(buffer, value);
|
||||||
|
return buffer.writeIndex();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package net.minestom.server.network;
|
||||||
|
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
final class NetworkBufferUnsafe {
|
||||||
|
static final Unsafe UNSAFE;
|
||||||
|
|
||||||
|
static final Field ADDRESS, CAPACITY;
|
||||||
|
static final long ADDRESS_OFFSET, CAPACITY_OFFSET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||||
|
theUnsafe.setAccessible(true);
|
||||||
|
UNSAFE = (Unsafe) theUnsafe.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ADDRESS = Buffer.class.getDeclaredField("address");
|
||||||
|
CAPACITY = Buffer.class.getDeclaredField("capacity");
|
||||||
|
// Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
|
||||||
|
// will forbid changing the access level via reflection.
|
||||||
|
ADDRESS_OFFSET = UNSAFE.objectFieldOffset(ADDRESS);
|
||||||
|
CAPACITY_OFFSET = UNSAFE.objectFieldOffset(CAPACITY);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset, in bytes, between the base memory address of a byte array and its first element.
|
||||||
|
*/
|
||||||
|
static final long BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
|
||||||
|
|
||||||
|
static void updateAddress(ByteBuffer buffer, long address) {
|
||||||
|
UNSAFE.putLong(buffer, ADDRESS_OFFSET, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateCapacity(ByteBuffer buffer, int capacity) {
|
||||||
|
UNSAFE.putInt(buffer, CAPACITY_OFFSET, capacity);
|
||||||
|
}
|
||||||
|
}
|
@ -14,15 +14,15 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
*/
|
*/
|
||||||
public sealed interface PacketParser<T> {
|
public sealed interface PacketParser<T> {
|
||||||
|
|
||||||
@NotNull PacketRegistry<T> handshakeRegistry();
|
@NotNull PacketRegistry<T> handshake();
|
||||||
|
|
||||||
@NotNull PacketRegistry<T> statusRegistry();
|
@NotNull PacketRegistry<T> status();
|
||||||
|
|
||||||
@NotNull PacketRegistry<T> loginRegistry();
|
@NotNull PacketRegistry<T> login();
|
||||||
|
|
||||||
@NotNull PacketRegistry<T> configurationRegistry();
|
@NotNull PacketRegistry<T> configuration();
|
||||||
|
|
||||||
@NotNull PacketRegistry<T> playRegistry();
|
@NotNull PacketRegistry<T> play();
|
||||||
|
|
||||||
default @NotNull T parse(@NotNull ConnectionState connectionState,
|
default @NotNull T parse(@NotNull ConnectionState connectionState,
|
||||||
int packetId, @NotNull NetworkBuffer buffer) {
|
int packetId, @NotNull NetworkBuffer buffer) {
|
||||||
@ -32,20 +32,20 @@ public sealed interface PacketParser<T> {
|
|||||||
|
|
||||||
default @NotNull PacketRegistry<T> stateRegistry(@NotNull ConnectionState connectionState) {
|
default @NotNull PacketRegistry<T> stateRegistry(@NotNull ConnectionState connectionState) {
|
||||||
return switch (connectionState) {
|
return switch (connectionState) {
|
||||||
case HANDSHAKE -> handshakeRegistry();
|
case HANDSHAKE -> handshake();
|
||||||
case STATUS -> statusRegistry();
|
case STATUS -> status();
|
||||||
case LOGIN -> loginRegistry();
|
case LOGIN -> login();
|
||||||
case CONFIGURATION -> configurationRegistry();
|
case CONFIGURATION -> configuration();
|
||||||
case PLAY -> playRegistry();
|
case PLAY -> play();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
record Client(
|
record Client(
|
||||||
PacketRegistry<ClientPacket> handshakeRegistry,
|
PacketRegistry<ClientPacket> handshake,
|
||||||
PacketRegistry<ClientPacket> statusRegistry,
|
PacketRegistry<ClientPacket> status,
|
||||||
PacketRegistry<ClientPacket> loginRegistry,
|
PacketRegistry<ClientPacket> login,
|
||||||
PacketRegistry<ClientPacket> configurationRegistry,
|
PacketRegistry<ClientPacket> configuration,
|
||||||
PacketRegistry<ClientPacket> playRegistry
|
PacketRegistry<ClientPacket> play
|
||||||
) implements PacketParser<ClientPacket> {
|
) implements PacketParser<ClientPacket> {
|
||||||
public Client() {
|
public Client() {
|
||||||
this(
|
this(
|
||||||
@ -59,11 +59,11 @@ public sealed interface PacketParser<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
record Server(
|
record Server(
|
||||||
PacketRegistry<ServerPacket> handshakeRegistry,
|
PacketRegistry<ServerPacket> handshake,
|
||||||
PacketRegistry<ServerPacket> statusRegistry,
|
PacketRegistry<ServerPacket> status,
|
||||||
PacketRegistry<ServerPacket> loginRegistry,
|
PacketRegistry<ServerPacket> login,
|
||||||
PacketRegistry<ServerPacket> configurationRegistry,
|
PacketRegistry<ServerPacket> configuration,
|
||||||
PacketRegistry<ServerPacket> playRegistry
|
PacketRegistry<ServerPacket> play
|
||||||
) implements PacketParser<ServerPacket> {
|
) implements PacketParser<ServerPacket> {
|
||||||
public Server() {
|
public Server() {
|
||||||
this(
|
this(
|
||||||
|
@ -0,0 +1,224 @@
|
|||||||
|
package net.minestom.server.network.packet;
|
||||||
|
|
||||||
|
import net.minestom.server.ServerFlag;
|
||||||
|
import net.minestom.server.network.ConnectionState;
|
||||||
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tools to read packets from a {@link NetworkBuffer} for network processing.
|
||||||
|
* <p>
|
||||||
|
* Fairly internal and performance sensitive.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public final class PacketReading {
|
||||||
|
private final static Logger LOGGER = LoggerFactory.getLogger(PacketReading.class);
|
||||||
|
|
||||||
|
private static final int MAX_VAR_INT_SIZE = 5;
|
||||||
|
private static final Result.Empty EMPTY_CLIENT_PACKET = new Result.Empty<>();
|
||||||
|
|
||||||
|
public sealed interface Result<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At least one packet was read.
|
||||||
|
* The buffer may still contain half-read packets and should therefore be compacted for next read.
|
||||||
|
*/
|
||||||
|
record Success<T>(List<T> packets, ConnectionState newState) implements Result<T> {
|
||||||
|
public Success {
|
||||||
|
if (packets.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Empty packets");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Success(T packet, ConnectionState newState) {
|
||||||
|
this(List.of(packet), newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents no packet to read. Can generally be ignored.
|
||||||
|
* <p>
|
||||||
|
* Happens when a packet length or payload couldn't be read, but the buffer has enough capacity.
|
||||||
|
*/
|
||||||
|
record Empty<T>() implements Result<T> {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a failure to read a packet due to insufficient buffer capacity.
|
||||||
|
* <p>
|
||||||
|
* Buffer should be expanded to at least {@code requiredCapacity} bytes.
|
||||||
|
* <p>
|
||||||
|
* If the buffer does not allow to read the packet length, max var-int length is returned.
|
||||||
|
*/
|
||||||
|
record Failure<T>(long requiredCapacity) implements Result<T> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<ClientPacket> readClients(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
return readPackets(buffer, PacketVanilla.CLIENT_PACKET_PARSER, state, PacketVanilla::nextClientState, compressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<ServerPacket> readServers(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
return readPackets(buffer, PacketVanilla.SERVER_PACKET_PARSER, state, PacketVanilla::nextServerState, compressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> readPackets(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull PacketParser<T> parser,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull BiFunction<T, ConnectionState, ConnectionState> stateUpdater,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
List<T> packets = new ArrayList<>();
|
||||||
|
readLoop:
|
||||||
|
while (buffer.readableBytes() > 0) {
|
||||||
|
final Result<T> result = readPacket(buffer, parser, state, stateUpdater, compressed);
|
||||||
|
if (buffer.readableBytes() == 0 && packets.isEmpty()) return result;
|
||||||
|
switch (result) {
|
||||||
|
case Result.Success<T> success -> {
|
||||||
|
assert success.packets().size() == 1;
|
||||||
|
packets.add(success.packets().getFirst());
|
||||||
|
state = success.newState();
|
||||||
|
}
|
||||||
|
case Result.Empty<T> ignored -> {
|
||||||
|
break readLoop;
|
||||||
|
}
|
||||||
|
case Result.Failure<T> failure -> {
|
||||||
|
return packets.isEmpty() ? failure : new Result.Success<>(packets, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !packets.isEmpty() ? new Result.Success<>(packets, state) : EMPTY_CLIENT_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<ClientPacket> readClient(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
return readPacket(buffer, PacketVanilla.CLIENT_PACKET_PARSER, state, PacketVanilla::nextClientState, compressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<ServerPacket> readServer(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
return readPacket(buffer, PacketVanilla.SERVER_PACKET_PARSER, state, PacketVanilla::nextServerState, compressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> readPacket(
|
||||||
|
@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull PacketParser<T> parser,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull BiFunction<T, ConnectionState, ConnectionState> stateUpdater,
|
||||||
|
boolean compressed
|
||||||
|
) throws DataFormatException {
|
||||||
|
final long beginMark = buffer.readIndex();
|
||||||
|
// READ PACKET LENGTH
|
||||||
|
final int packetLength;
|
||||||
|
try {
|
||||||
|
packetLength = buffer.read(VAR_INT);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
// Couldn't read a single var-int
|
||||||
|
return new Result.Failure<>(MAX_VAR_INT_SIZE);
|
||||||
|
}
|
||||||
|
final long readerStart = buffer.readIndex();
|
||||||
|
if (readerStart > buffer.writeIndex()) {
|
||||||
|
// Can't read the packet length, buffer has enough capacity
|
||||||
|
buffer.readIndex(beginMark);
|
||||||
|
return EMPTY_CLIENT_PACKET;
|
||||||
|
}
|
||||||
|
final int maxPacketSize = maxPacketSize(state);
|
||||||
|
if (packetLength > maxPacketSize) {
|
||||||
|
throw new DataFormatException("Packet too large: " + packetLength);
|
||||||
|
}
|
||||||
|
// READ PAYLOAD https://wiki.vg/Protocol#Packet_format
|
||||||
|
if (buffer.readableBytes() < packetLength) {
|
||||||
|
// Can't read the full packet
|
||||||
|
buffer.readIndex(beginMark);
|
||||||
|
final long packetLengthVarIntSize = readerStart - beginMark;
|
||||||
|
final long requiredCapacity = packetLengthVarIntSize + packetLength;
|
||||||
|
// Must return a failure if the buffer is too small
|
||||||
|
// Otherwise do nothing, and hope to read the packet remains next time
|
||||||
|
if (requiredCapacity > buffer.capacity()) return new Result.Failure<>(requiredCapacity);
|
||||||
|
else return EMPTY_CLIENT_PACKET;
|
||||||
|
}
|
||||||
|
NetworkBuffer content = buffer.slice(buffer.readIndex(), packetLength, 0, packetLength);
|
||||||
|
final PacketRegistry<T> registry = parser.stateRegistry(state);
|
||||||
|
final T packet = readFramedPacket(content, registry, compressed);
|
||||||
|
final ConnectionState nextState = stateUpdater.apply(packet, state);
|
||||||
|
buffer.readIndex(readerStart + packetLength);
|
||||||
|
return new Result.Success<>(packet, nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T readFramedPacket(NetworkBuffer buffer,
|
||||||
|
PacketRegistry<T> registry,
|
||||||
|
boolean compressed) throws DataFormatException {
|
||||||
|
if (!compressed) {
|
||||||
|
// No compression format
|
||||||
|
return readPayload(buffer, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int dataLength = buffer.read(VAR_INT);
|
||||||
|
if (dataLength == 0) {
|
||||||
|
// Uncompressed packet
|
||||||
|
return readPayload(buffer, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress the packet into the pooled buffer
|
||||||
|
// and read the uncompressed packet from it
|
||||||
|
NetworkBuffer decompressed = PacketVanilla.PACKET_POOL.get();
|
||||||
|
try {
|
||||||
|
if (decompressed.capacity() < dataLength) decompressed.resize(dataLength);
|
||||||
|
buffer.decompress(buffer.readIndex(), buffer.readableBytes(), decompressed);
|
||||||
|
return readPayload(decompressed, registry);
|
||||||
|
} finally {
|
||||||
|
PacketVanilla.PACKET_POOL.add(decompressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T readPayload(NetworkBuffer buffer, PacketRegistry<T> registry) {
|
||||||
|
final int packetId = buffer.read(VAR_INT);
|
||||||
|
final PacketRegistry.PacketInfo<T> packetInfo = registry.packetInfo(packetId);
|
||||||
|
final NetworkBuffer.Type<T> serializer = packetInfo.serializer();
|
||||||
|
try {
|
||||||
|
final T packet = serializer.read(buffer);
|
||||||
|
if (buffer.readableBytes() != 0) {
|
||||||
|
LOGGER.warn("WARNING: Packet ({}) 0x{} not fully read ({})",
|
||||||
|
packetInfo.packetClass().getSimpleName(), Integer.toHexString(packetId), buffer);
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int maxPacketSize(ConnectionState state) {
|
||||||
|
return switch (state) {
|
||||||
|
case HANDSHAKE, LOGIN -> ServerFlag.MAX_PACKET_SIZE_PRE_AUTH;
|
||||||
|
default -> ServerFlag.MAX_PACKET_SIZE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,15 @@ public interface PacketRegistry<T> {
|
|||||||
@UnknownNullability
|
@UnknownNullability
|
||||||
T create(int packetId, @NotNull NetworkBuffer reader);
|
T create(int packetId, @NotNull NetworkBuffer reader);
|
||||||
|
|
||||||
PacketInfo<T> packetInfo(Class<? extends T> packetClass);
|
PacketInfo<T> packetInfo(@NotNull Class<?> packetClass);
|
||||||
|
|
||||||
record PacketInfo<T>(Class<? extends T> packetClass, int id, NetworkBuffer.Type<T> serializer) {
|
default PacketInfo<T> packetInfo(@NotNull T packet) {
|
||||||
|
return packetInfo(packet.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketInfo<T> packetInfo(int packetId);
|
||||||
|
|
||||||
|
record PacketInfo<T>(Class<T> packetClass, int id, NetworkBuffer.Type<T> serializer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Client extends PacketRegistryTemplate<ClientPacket> {
|
sealed class Client extends PacketRegistryTemplate<ClientPacket> {
|
||||||
@ -155,7 +161,9 @@ public interface PacketRegistry<T> {
|
|||||||
|
|
||||||
final class ServerHandshake extends Server {
|
final class ServerHandshake extends Server {
|
||||||
public ServerHandshake() {
|
public ServerHandshake() {
|
||||||
super();
|
super(
|
||||||
|
// Empty
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,16 +344,15 @@ public interface PacketRegistry<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
sealed class PacketRegistryTemplate<T> implements PacketRegistry<T> {
|
sealed class PacketRegistryTemplate<T> implements PacketRegistry<T> {
|
||||||
private final Entry<? extends T>[] suppliers;
|
private final PacketInfo<? extends T>[] suppliers;
|
||||||
private final ClassValue<PacketInfo<T>> packetIds = new ClassValue<>() {
|
private final ClassValue<PacketInfo<T>> packetIds = new ClassValue<>() {
|
||||||
@Override
|
@Override
|
||||||
protected PacketInfo<T> computeValue(@NotNull Class<?> type) {
|
protected PacketInfo<T> computeValue(@NotNull Class<?> type) {
|
||||||
for (int i = 0; i < suppliers.length; i++) {
|
for (PacketInfo<? extends T> info : suppliers) {
|
||||||
final Entry<? extends T> entry = suppliers[i];
|
if (info != null && info.packetClass == type) {
|
||||||
if (entry != null && entry.type == type) {
|
return (PacketInfo<T>) info;
|
||||||
//noinspection unchecked
|
|
||||||
return new PacketInfo<T>(entry.type, i, (NetworkBuffer.Type<T>) entry.reader);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("Packet type " + type + " isn't registered!");
|
throw new IllegalStateException("Packet type " + type + " isn't registered!");
|
||||||
@ -354,26 +361,39 @@ public interface PacketRegistry<T> {
|
|||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
PacketRegistryTemplate(Entry<? extends T>... suppliers) {
|
PacketRegistryTemplate(Entry<? extends T>... suppliers) {
|
||||||
this.suppliers = suppliers;
|
PacketInfo<? extends T>[] packetInfos = new PacketInfo[suppliers.length];
|
||||||
|
for (int i = 0; i < suppliers.length; i++) {
|
||||||
|
final Entry<? extends T> entry = suppliers[i];
|
||||||
|
if (entry == null) continue;
|
||||||
|
packetInfos[i] = new PacketInfo(entry.type, i, entry.reader);
|
||||||
|
}
|
||||||
|
this.suppliers = packetInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @UnknownNullability T create(int packetId, @NotNull NetworkBuffer reader) {
|
public @UnknownNullability T create(int packetId, @NotNull NetworkBuffer reader) {
|
||||||
final Entry<? extends T> entry = suppliers[packetId];
|
final PacketInfo<T> info = packetInfo(packetId);
|
||||||
if (entry == null)
|
final NetworkBuffer.Type<T> supplier = info.serializer;
|
||||||
throw new IllegalStateException("Packet id 0x" + Integer.toHexString(packetId) + " isn't registered!");
|
|
||||||
final NetworkBuffer.Type<? extends T> supplier = entry.reader;
|
|
||||||
final T packet = supplier.read(reader);
|
final T packet = supplier.read(reader);
|
||||||
if (packet == null) {
|
if (packet == null) {
|
||||||
throw new IllegalStateException("Packet " + entry.type + " failed to read!");
|
throw new IllegalStateException("Packet " + info.packetClass + " failed to read!");
|
||||||
}
|
}
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PacketInfo<T> packetInfo(Class<? extends T> packetClass) {
|
public PacketInfo<T> packetInfo(@NotNull Class<?> packetClass) {
|
||||||
return packetIds.get(packetClass);
|
return packetIds.get(packetClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketInfo<T> packetInfo(int packetId) {
|
||||||
|
final PacketInfo<T> info;
|
||||||
|
if (packetId < 0 || packetId >= suppliers.length || (info = (PacketInfo<T>) suppliers[packetId]) == null) {
|
||||||
|
throw new IllegalStateException("Packet id 0x" + Integer.toHexString(packetId) + " isn't registered!");
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
record Entry<T>(Class<T> type, NetworkBuffer.Type<T> reader) {
|
record Entry<T>(Class<T> type, NetworkBuffer.Type<T> reader) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package net.minestom.server.network.packet;
|
||||||
|
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.ServerFlag;
|
||||||
|
import net.minestom.server.network.ConnectionState;
|
||||||
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
|
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
|
||||||
|
import net.minestom.server.network.packet.client.handshake.ClientHandshakePacket;
|
||||||
|
import net.minestom.server.network.packet.client.login.ClientLoginAcknowledgedPacket;
|
||||||
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
import net.minestom.server.network.packet.server.configuration.FinishConfigurationPacket;
|
||||||
|
import net.minestom.server.network.packet.server.login.LoginSuccessPacket;
|
||||||
|
import net.minestom.server.network.packet.server.play.StartConfigurationPacket;
|
||||||
|
import net.minestom.server.utils.ObjectPool;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants and utilities for vanilla packets.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public final class PacketVanilla {
|
||||||
|
public static final PacketParser<ClientPacket> CLIENT_PACKET_PARSER = new PacketParser.Client();
|
||||||
|
public static final PacketParser<ServerPacket> SERVER_PACKET_PARSER = new PacketParser.Server();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pool containing a buffer able to hold the largest packet.
|
||||||
|
* <p>
|
||||||
|
* Size starts with {@link ServerFlag#POOLED_BUFFER_SIZE} and doubles until {@link ServerFlag#MAX_PACKET_SIZE}.
|
||||||
|
*/
|
||||||
|
public static final ObjectPool<NetworkBuffer> PACKET_POOL = ObjectPool.pool(
|
||||||
|
() -> NetworkBuffer.staticBuffer(ServerFlag.POOLED_BUFFER_SIZE, MinecraftServer.process()),
|
||||||
|
NetworkBuffer::clear);
|
||||||
|
|
||||||
|
public static ConnectionState nextClientState(ClientPacket packet, ConnectionState currentState) {
|
||||||
|
return switch (packet) {
|
||||||
|
case ClientHandshakePacket handshakePacket -> switch (handshakePacket.intent()) {
|
||||||
|
case STATUS -> ConnectionState.STATUS;
|
||||||
|
case LOGIN, TRANSFER -> ConnectionState.LOGIN;
|
||||||
|
};
|
||||||
|
case ClientLoginAcknowledgedPacket ignored -> ConnectionState.CONFIGURATION;
|
||||||
|
case ClientFinishConfigurationPacket ignored -> ConnectionState.PLAY;
|
||||||
|
default -> currentState;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionState nextServerState(ServerPacket packet, ConnectionState currentState) {
|
||||||
|
// Client chooses between STATUS or LOGIN state directly after the first handshake packet
|
||||||
|
if (currentState == ConnectionState.HANDSHAKE)
|
||||||
|
throw new IllegalStateException("No server Handshake packet exists");
|
||||||
|
return switch (packet) {
|
||||||
|
case LoginSuccessPacket ignored -> ConnectionState.CONFIGURATION;
|
||||||
|
case StartConfigurationPacket ignored -> ConnectionState.CONFIGURATION;
|
||||||
|
case FinishConfigurationPacket ignored -> ConnectionState.PLAY;
|
||||||
|
default -> currentState;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
package net.minestom.server.network.packet;
|
||||||
|
|
||||||
|
import net.minestom.server.ServerFlag;
|
||||||
|
import net.minestom.server.network.ConnectionState;
|
||||||
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tools to write packets into a {@link NetworkBuffer} for network processing.
|
||||||
|
* <p>
|
||||||
|
* Fairly internal and performance sensitive.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public final class PacketWriting {
|
||||||
|
public static void writeFramedPacket(@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull ClientPacket packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
writeFramedPacket(buffer, PacketVanilla.CLIENT_PACKET_PARSER, state, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeFramedPacket(@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull ServerPacket packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
writeFramedPacket(buffer, PacketVanilla.SERVER_PACKET_PARSER, state, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void writeFramedPacket(@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull PacketParser<T> parser,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull T packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
final PacketRegistry<T> registry = parser.stateRegistry(state);
|
||||||
|
writeFramedPacket(buffer, registry, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void writeFramedPacket(@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull PacketRegistry<T> registry,
|
||||||
|
@NotNull T packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
final PacketRegistry.PacketInfo<T> packetInfo = registry.packetInfo(packet);
|
||||||
|
final int id = packetInfo.id();
|
||||||
|
final NetworkBuffer.Type<T> serializer = packetInfo.serializer();
|
||||||
|
writeFramedPacket(
|
||||||
|
buffer, serializer,
|
||||||
|
id, packet,
|
||||||
|
compressionThreshold
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void writeFramedPacket(@NotNull NetworkBuffer buffer,
|
||||||
|
@NotNull NetworkBuffer.Type<T> type,
|
||||||
|
int id, @NotNull T packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
if (compressionThreshold <= 0) writeUncompressedFormat(buffer, type, id, packet);
|
||||||
|
else writeCompressedFormat(buffer, type, id, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void writeUncompressedFormat(NetworkBuffer buffer,
|
||||||
|
NetworkBuffer.Type<T> type,
|
||||||
|
int id, T packet) throws IndexOutOfBoundsException {
|
||||||
|
// Uncompressed format https://wiki.vg/Protocol#Without_compression
|
||||||
|
final long lengthIndex = buffer.advanceWrite(3);
|
||||||
|
buffer.write(NetworkBuffer.VAR_INT, id);
|
||||||
|
buffer.write(type, packet);
|
||||||
|
final long finalSize = buffer.writeIndex() - (lengthIndex + 3);
|
||||||
|
buffer.writeAt(lengthIndex, NetworkBuffer.VAR_INT_3, (int) finalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void writeCompressedFormat(NetworkBuffer buffer,
|
||||||
|
NetworkBuffer.Type<T> type,
|
||||||
|
int id, T packet,
|
||||||
|
int compressionThreshold) throws IndexOutOfBoundsException {
|
||||||
|
// Compressed format https://wiki.vg/Protocol#With_compression
|
||||||
|
final long compressedIndex = buffer.advanceWrite(3);
|
||||||
|
final long uncompressedIndex = buffer.advanceWrite(3);
|
||||||
|
final long contentStart = buffer.writeIndex();
|
||||||
|
buffer.write(NetworkBuffer.VAR_INT, id);
|
||||||
|
buffer.write(type, packet);
|
||||||
|
final long packetSize = buffer.writeIndex() - contentStart;
|
||||||
|
final boolean compressed = packetSize >= compressionThreshold;
|
||||||
|
if (compressed) {
|
||||||
|
// Write the compressed content into the pooled buffer
|
||||||
|
// and compress it into the current buffer
|
||||||
|
NetworkBuffer input = PacketVanilla.PACKET_POOL.get();
|
||||||
|
try {
|
||||||
|
if (input.capacity() < packetSize) input.resize(packetSize);
|
||||||
|
NetworkBuffer.copy(buffer, contentStart, input, 0, packetSize);
|
||||||
|
buffer.writeIndex(contentStart);
|
||||||
|
input.compress(0, packetSize, buffer);
|
||||||
|
} finally {
|
||||||
|
PacketVanilla.PACKET_POOL.add(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Packet header (Packet + Data Length)
|
||||||
|
buffer.writeAt(compressedIndex, NetworkBuffer.VAR_INT_3, (int) (buffer.writeIndex() - uncompressedIndex));
|
||||||
|
buffer.writeAt(uncompressedIndex, NetworkBuffer.VAR_INT_3, compressed ? (int) packetSize : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NetworkBuffer allocateTrimmedPacket(@NotNull ConnectionState state,
|
||||||
|
@NotNull ClientPacket packet,
|
||||||
|
int compressionThreshold) {
|
||||||
|
return allocateTrimmedPacket(PacketVanilla.CLIENT_PACKET_PARSER, state, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NetworkBuffer allocateTrimmedPacket(@NotNull ConnectionState state,
|
||||||
|
@NotNull ServerPacket packet,
|
||||||
|
int compressionThreshold) {
|
||||||
|
return allocateTrimmedPacket(PacketVanilla.SERVER_PACKET_PARSER, state, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> NetworkBuffer allocateTrimmedPacket(
|
||||||
|
@NotNull PacketParser<T> parser,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull T packet,
|
||||||
|
int compressionThreshold) {
|
||||||
|
NetworkBuffer buffer = PacketVanilla.PACKET_POOL.get();
|
||||||
|
try {
|
||||||
|
return allocateTrimmedPacket(buffer, parser, state, packet, compressionThreshold);
|
||||||
|
} finally {
|
||||||
|
PacketVanilla.PACKET_POOL.add(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> NetworkBuffer allocateTrimmedPacket(
|
||||||
|
@NotNull NetworkBuffer tmpBuffer,
|
||||||
|
@NotNull PacketParser<T> parser,
|
||||||
|
@NotNull ConnectionState state,
|
||||||
|
@NotNull T packet,
|
||||||
|
int compressionThreshold) {
|
||||||
|
final PacketRegistry<T> registry = parser.stateRegistry(state);
|
||||||
|
return allocateTrimmedPacket(tmpBuffer, registry, packet, compressionThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> NetworkBuffer allocateTrimmedPacket(
|
||||||
|
@NotNull NetworkBuffer tmpBuffer,
|
||||||
|
@NotNull PacketRegistry<T> registry,
|
||||||
|
@NotNull T packet,
|
||||||
|
int compressionThreshold) {
|
||||||
|
final PacketRegistry.PacketInfo<T> packetInfo = registry.packetInfo(packet);
|
||||||
|
final int id = packetInfo.id();
|
||||||
|
final NetworkBuffer.Type<T> serializer = packetInfo.serializer();
|
||||||
|
try {
|
||||||
|
writeFramedPacket(tmpBuffer, serializer, id, packet, compressionThreshold);
|
||||||
|
return tmpBuffer.copy(0, tmpBuffer.writeIndex());
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
final long sizeOf = serializer.sizeOf(packet, tmpBuffer.registries());
|
||||||
|
if (sizeOf > ServerFlag.MAX_PACKET_SIZE) {
|
||||||
|
throw new IllegalStateException("Packet too large: " + sizeOf);
|
||||||
|
}
|
||||||
|
// Add 15 bytes to account for the 3 potential varints in the packet header
|
||||||
|
// Packet Length - Data Length - Packet ID
|
||||||
|
tmpBuffer.resize(sizeOf + 15);
|
||||||
|
tmpBuffer.writeIndex(0);
|
||||||
|
writeFramedPacket(tmpBuffer, serializer, id, packet, compressionThreshold);
|
||||||
|
return tmpBuffer.copy(0, tmpBuffer.writeIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,9 @@
|
|||||||
package net.minestom.server.network.packet.client;
|
package net.minestom.server.network.packet.client;
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a packet received from a client.
|
* Represents a packet received from a client.
|
||||||
* <p>
|
* <p>
|
||||||
* Packets are value-based, and should therefore not be reliant on identity.
|
* Packets are value-based, and should therefore not be reliant on identity.
|
||||||
*/
|
*/
|
||||||
public interface ClientPacket {
|
public interface ClientPacket {
|
||||||
/**
|
|
||||||
* Determines whether this packet should be processed immediately
|
|
||||||
* or wait until the next server tick.
|
|
||||||
*
|
|
||||||
* @return true if this packet should process immediately
|
|
||||||
*/
|
|
||||||
@ApiStatus.Internal
|
|
||||||
default boolean processImmediately() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,4 @@ public record ClientCookieResponsePacket(
|
|||||||
Check.argCondition(value != null && value.length > CookieStorePacket.MAX_VALUE_LENGTH,
|
Check.argCondition(value != null && value.length > CookieStorePacket.MAX_VALUE_LENGTH,
|
||||||
"Value is too long: {0} > {1}", value != null ? value.length : 0, CookieStorePacket.MAX_VALUE_LENGTH);
|
"Value is too long: {0} > {1}", value != null ? value.length : 0, CookieStorePacket.MAX_VALUE_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processImmediately() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,4 @@ import static net.minestom.server.network.NetworkBuffer.LONG;
|
|||||||
public record ClientKeepAlivePacket(long id) implements ClientPacket {
|
public record ClientKeepAlivePacket(long id) implements ClientPacket {
|
||||||
public static final NetworkBuffer.Type<ClientKeepAlivePacket> SERIALIZER = NetworkBufferTemplate.template(
|
public static final NetworkBuffer.Type<ClientKeepAlivePacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
LONG, ClientKeepAlivePacket::id, ClientKeepAlivePacket::new);
|
LONG, ClientKeepAlivePacket::id, ClientKeepAlivePacket::new);
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processImmediately() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,4 @@ import static net.minestom.server.network.NetworkBuffer.LONG;
|
|||||||
public record ClientPingRequestPacket(long number) implements ClientPacket {
|
public record ClientPingRequestPacket(long number) implements ClientPacket {
|
||||||
public static final NetworkBuffer.Type<ClientPingRequestPacket> SERIALIZER = NetworkBufferTemplate.template(
|
public static final NetworkBuffer.Type<ClientPingRequestPacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
LONG, ClientPingRequestPacket::number, ClientPingRequestPacket::new);
|
LONG, ClientPingRequestPacket::number, ClientPingRequestPacket::new);
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processImmediately() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ import net.minestom.server.network.NetworkBufferTemplate;
|
|||||||
import net.minestom.server.network.packet.client.ClientPacket;
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static net.minestom.server.network.NetworkBuffer.RAW_BYTES;
|
import static net.minestom.server.network.NetworkBuffer.RAW_BYTES;
|
||||||
import static net.minestom.server.network.NetworkBuffer.STRING;
|
import static net.minestom.server.network.NetworkBuffer.STRING;
|
||||||
|
|
||||||
@ -18,4 +21,17 @@ public record ClientPluginMessagePacket(@NotNull String channel, byte[] data) im
|
|||||||
if (channel.length() > 256)
|
if (channel.length() > 256)
|
||||||
throw new IllegalArgumentException("Channel cannot be more than 256 characters long");
|
throw new IllegalArgumentException("Channel cannot be more than 256 characters long");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ClientPluginMessagePacket that = (ClientPluginMessagePacket) o;
|
||||||
|
return Objects.deepEquals(data, that.data) && Objects.equals(channel, that.channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(channel, Arrays.hashCode(data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,27 @@ package net.minestom.server.network.packet.client.common;
|
|||||||
|
|
||||||
import net.kyori.adventure.resource.ResourcePackStatus;
|
import net.kyori.adventure.resource.ResourcePackStatus;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.network.packet.client.ClientPacket;
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.UUID;
|
||||||
|
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||||
|
|
||||||
public record ClientResourcePackStatusPacket(
|
public record ClientResourcePackStatusPacket(
|
||||||
@NotNull UUID id,
|
@NotNull UUID id,
|
||||||
@NotNull ResourcePackStatus status
|
@NotNull ResourcePackStatus status
|
||||||
) implements ClientPacket {
|
) implements ClientPacket {
|
||||||
public static NetworkBuffer.Type<ClientResourcePackStatusPacket> SERIALIZER = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<ClientResourcePackStatusPacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
@Override
|
UUID, ClientResourcePackStatusPacket::id,
|
||||||
public void write(@NotNull NetworkBuffer buffer, ClientResourcePackStatusPacket value) {
|
VAR_INT.transform(ClientResourcePackStatusPacket::readStatus, ClientResourcePackStatusPacket::statusId), ClientResourcePackStatusPacket::status,
|
||||||
buffer.write(NetworkBuffer.UUID, value.id);
|
ClientResourcePackStatusPacket::new
|
||||||
buffer.write(NetworkBuffer.VAR_INT, statusId(value.status));
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private static @NotNull ResourcePackStatus readStatus(int id) {
|
||||||
public ClientResourcePackStatusPacket read(@NotNull NetworkBuffer buffer) {
|
return switch (id) {
|
||||||
return new ClientResourcePackStatusPacket(buffer.read(NetworkBuffer.UUID), readStatus(buffer));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static @NotNull ResourcePackStatus readStatus(@NotNull NetworkBuffer reader) {
|
|
||||||
var ordinal = reader.read(NetworkBuffer.VAR_INT);
|
|
||||||
return switch (ordinal) {
|
|
||||||
case 0 -> ResourcePackStatus.SUCCESSFULLY_LOADED;
|
case 0 -> ResourcePackStatus.SUCCESSFULLY_LOADED;
|
||||||
case 1 -> ResourcePackStatus.DECLINED;
|
case 1 -> ResourcePackStatus.DECLINED;
|
||||||
case 2 -> ResourcePackStatus.FAILED_DOWNLOAD;
|
case 2 -> ResourcePackStatus.FAILED_DOWNLOAD;
|
||||||
@ -35,7 +31,7 @@ public record ClientResourcePackStatusPacket(
|
|||||||
case 5 -> ResourcePackStatus.INVALID_URL;
|
case 5 -> ResourcePackStatus.INVALID_URL;
|
||||||
case 6 -> ResourcePackStatus.FAILED_RELOAD;
|
case 6 -> ResourcePackStatus.FAILED_RELOAD;
|
||||||
case 7 -> ResourcePackStatus.DISCARDED;
|
case 7 -> ResourcePackStatus.DISCARDED;
|
||||||
default -> throw new IllegalStateException("Unexpected resource pack status: " + ordinal);
|
default -> throw new IllegalStateException("Unexpected resource pack status: " + id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package net.minestom.server.network.packet.client.handshake;
|
|||||||
|
|
||||||
import net.minestom.server.extras.bungee.BungeeCordProxy;
|
import net.minestom.server.extras.bungee.BungeeCordProxy;
|
||||||
import net.minestom.server.network.NetworkBuffer;
|
import net.minestom.server.network.NetworkBuffer;
|
||||||
|
import net.minestom.server.network.NetworkBufferTemplate;
|
||||||
import net.minestom.server.network.packet.client.ClientPacket;
|
import net.minestom.server.network.packet.client.ClientPacket;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -11,40 +12,19 @@ public record ClientHandshakePacket(int protocolVersion, @NotNull String serverA
|
|||||||
int serverPort, @NotNull Intent intent) implements ClientPacket {
|
int serverPort, @NotNull Intent intent) implements ClientPacket {
|
||||||
|
|
||||||
public ClientHandshakePacket {
|
public ClientHandshakePacket {
|
||||||
if (serverAddress.length() > getMaxHandshakeLength()) {
|
if (serverAddress.length() > maxHandshakeLength()) {
|
||||||
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
|
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NetworkBuffer.Type<ClientHandshakePacket> SERIALIZER = new NetworkBuffer.Type<>() {
|
public static final NetworkBuffer.Type<ClientHandshakePacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||||
@Override
|
VAR_INT, ClientHandshakePacket::protocolVersion,
|
||||||
public void write(@NotNull NetworkBuffer buffer, ClientHandshakePacket value) {
|
STRING, ClientHandshakePacket::serverAddress,
|
||||||
buffer.write(VAR_INT, value.protocolVersion);
|
UNSIGNED_SHORT, ClientHandshakePacket::serverPort,
|
||||||
int maxLength = getMaxHandshakeLength();
|
VAR_INT.transform(Intent::fromId, Intent::id), ClientHandshakePacket::intent,
|
||||||
if (value.serverAddress.length() > maxLength) {
|
ClientHandshakePacket::new);
|
||||||
throw new IllegalArgumentException("serverAddress is " + value.serverAddress.length() + " characters long, maximum allowed is " + maxLength);
|
|
||||||
}
|
|
||||||
buffer.write(STRING, value.serverAddress);
|
|
||||||
buffer.write(UNSIGNED_SHORT, value.serverPort);
|
|
||||||
// Not a writeEnum call because the indices are not 0-based
|
|
||||||
buffer.write(VAR_INT, value.intent.id());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private static int maxHandshakeLength() {
|
||||||
public @NotNull ClientHandshakePacket read(@NotNull NetworkBuffer buffer) {
|
|
||||||
return new ClientHandshakePacket(buffer.read(VAR_INT), buffer.read(STRING),
|
|
||||||
buffer.read(UNSIGNED_SHORT),
|
|
||||||
// Not a readEnum call because the indices are not 0-based
|
|
||||||
Intent.fromId(buffer.read(VAR_INT)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processImmediately() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getMaxHandshakeLength() {
|
|
||||||
// BungeeGuard limits handshake length to 2500 characters, while vanilla limits it to 255
|
// BungeeGuard limits handshake length to 2500 characters, while vanilla limits it to 255
|
||||||
return BungeeCordProxy.isEnabled() ? (BungeeCordProxy.isBungeeGuardEnabled() ? 2500 : Short.MAX_VALUE) : 255;
|
return BungeeCordProxy.isEnabled() ? (BungeeCordProxy.isBungeeGuardEnabled() ? 2500 : Short.MAX_VALUE) : 255;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,4 @@ public record ClientEncryptionResponsePacket(byte[] sharedSecret,
|
|||||||
BYTE_ARRAY, ClientEncryptionResponsePacket::sharedSecret,
|
BYTE_ARRAY, ClientEncryptionResponsePacket::sharedSecret,
|
||||||
BYTE_ARRAY, ClientEncryptionResponsePacket::encryptedVerifyToken,
|
BYTE_ARRAY, ClientEncryptionResponsePacket::encryptedVerifyToken,
|
||||||
ClientEncryptionResponsePacket::new);
|
ClientEncryptionResponsePacket::new);
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processImmediately() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user