mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-04 22:41:26 +01:00
Network improvements (#2324)
This commit is contained in:
parent
561470c82a
commit
3f079d252e
@ -1,6 +1,7 @@
|
||||
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.infra.results.L_Result;
|
||||
|
||||
@ -11,7 +12,7 @@ import static org.openjdk.jcstress.annotations.Expect.ACCEPTABLE;
|
||||
@Outcome(id = "2", expect = ACCEPTABLE)
|
||||
@State
|
||||
public class ObjectPoolTest {
|
||||
private final ObjectPool<BinaryBuffer> pool = ObjectPool.BUFFER_POOL;
|
||||
private final ObjectPool<NetworkBuffer> pool = PacketVanilla.PACKET_POOL;
|
||||
|
||||
@Actor
|
||||
public void actor1() {
|
||||
|
@ -22,6 +22,7 @@ import net.minestom.server.message.ChatType;
|
||||
import net.minestom.server.monitoring.BenchmarkManager;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
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.play.ServerDifficultyPacket;
|
||||
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.thread.TickSchedulerThread;
|
||||
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.validate.Check;
|
||||
import net.minestom.server.world.Difficulty;
|
||||
@ -40,7 +41,6 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
@ -80,13 +80,8 @@ public final class MinecraftServer implements MinecraftConstants {
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static ServerProcess updateProcess() {
|
||||
ServerProcess process;
|
||||
try {
|
||||
process = new ServerProcessImpl();
|
||||
serverProcess = process;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ServerProcess process = new ServerProcessImpl();
|
||||
serverProcess = process;
|
||||
return process;
|
||||
}
|
||||
|
||||
@ -108,7 +103,7 @@ public final class MinecraftServer implements MinecraftConstants {
|
||||
*/
|
||||
public static void setBrandName(@NotNull String 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) {
|
||||
MinecraftServer.difficulty = difficulty;
|
||||
PacketUtils.broadcastPlayPacket(new ServerDifficultyPacket(difficulty, true));
|
||||
PacketSendingUtils.broadcastPlayPacket(new ServerDifficultyPacket(difficulty, true));
|
||||
}
|
||||
|
||||
public static @UnknownNullability ServerProcess process() {
|
||||
@ -188,7 +183,7 @@ public final class MinecraftServer implements MinecraftConstants {
|
||||
return serverProcess.bossBar();
|
||||
}
|
||||
|
||||
public static @NotNull PacketParser.Client getPacketParser() {
|
||||
public static @NotNull PacketParser<ClientPacket> getPacketParser() {
|
||||
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 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 WORKER_COUNT = intProperty("minestom.workers", Runtime.getRuntime().availableProcessors());
|
||||
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 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_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
|
||||
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);
|
||||
|
@ -14,6 +14,7 @@ import net.minestom.server.listener.manager.PacketListenerManager;
|
||||
import net.minestom.server.monitoring.BenchmarkManager;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
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.recipe.RecipeManager;
|
||||
import net.minestom.server.registry.Registries;
|
||||
@ -103,7 +104,7 @@ public interface ServerProcess extends Registries, Snapshotable {
|
||||
* <p>
|
||||
* 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.
|
||||
|
@ -28,6 +28,8 @@ import net.minestom.server.monitoring.BenchmarkManager;
|
||||
import net.minestom.server.monitoring.TickMonitor;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
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.recipe.RecipeManager;
|
||||
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.ThreadProvider;
|
||||
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.nbt.BinaryTagSerializer;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
@ -77,7 +79,7 @@ final class ServerProcessImpl implements ServerProcess {
|
||||
|
||||
private final ConnectionManager connection;
|
||||
private final PacketListenerManager packetListener;
|
||||
private final PacketParser.Client packetParser;
|
||||
private final PacketParser<ClientPacket> packetParser;
|
||||
private final InstanceManager instance;
|
||||
private final BlockManager block;
|
||||
private final CommandManager command;
|
||||
@ -98,7 +100,7 @@ final class ServerProcessImpl implements ServerProcess {
|
||||
private final AtomicBoolean started = new AtomicBoolean();
|
||||
private final AtomicBoolean stopped = new AtomicBoolean();
|
||||
|
||||
public ServerProcessImpl() throws IOException {
|
||||
public ServerProcessImpl() {
|
||||
this.exception = new ExceptionManager();
|
||||
|
||||
// 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.packetListener = new PacketListenerManager();
|
||||
this.packetParser = new PacketParser.Client();
|
||||
this.packetParser = PacketVanilla.CLIENT_PACKET_PARSER;
|
||||
this.instance = new InstanceManager(this);
|
||||
this.block = new BlockManager();
|
||||
this.command = new CommandManager();
|
||||
@ -287,7 +289,7 @@ final class ServerProcessImpl implements ServerProcess {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PacketParser.Client packetParser() {
|
||||
public @NotNull PacketParser<ClientPacket> packetParser() {
|
||||
return packetParser;
|
||||
}
|
||||
|
||||
@ -379,10 +381,7 @@ final class ServerProcessImpl implements ServerProcess {
|
||||
scheduler().processTickEnd();
|
||||
|
||||
// Flush all waiting packets
|
||||
PacketUtils.flush();
|
||||
|
||||
// Server connection tick
|
||||
server().tick();
|
||||
PacketViewableUtils.flush();
|
||||
|
||||
// Monitoring
|
||||
{
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.SendablePacket;
|
||||
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 java.util.Collection;
|
||||
@ -60,7 +60,7 @@ public interface Viewable {
|
||||
*/
|
||||
default void sendPacketToViewers(@NotNull SendablePacket packet) {
|
||||
if (packet instanceof ServerPacket serverPacket) {
|
||||
PacketUtils.sendGroupedPacket(getViewers(), serverPacket);
|
||||
PacketSendingUtils.sendGroupedPacket(getViewers(), serverPacket);
|
||||
} else {
|
||||
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.ClearTitlesPacket;
|
||||
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 java.util.Collection;
|
||||
@ -54,7 +54,7 @@ public interface PacketGroupingAudience extends ForwardingAudience {
|
||||
* @param packet the packet to broadcast
|
||||
*/
|
||||
default void sendGroupedPacket(@NotNull ServerPacket packet) {
|
||||
PacketUtils.sendGroupedPacket(getPlayers(), packet);
|
||||
PacketSendingUtils.sendGroupedPacket(getPlayers(), packet);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
@ -3,7 +3,7 @@ package net.minestom.server.adventure.bossbar;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.bossbar.BossBar;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import net.minestom.server.utils.PacketSendingUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
@ -29,33 +29,32 @@ class BossBarListener implements BossBar.Listener {
|
||||
|
||||
@Override
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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) {
|
||||
BossBarHolder holder = this.manager.bars.get(bar);
|
||||
|
||||
if (holder != null) {
|
||||
consumer.accept(holder);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.bossbar.BossBar;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import net.minestom.server.utils.PacketSendingUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
@ -75,7 +75,7 @@ public class BossBarManager {
|
||||
BossBarHolder holder = this.getOrCreateHandler(bar);
|
||||
Collection<Player> addedPlayers = players.stream().filter(holder::addViewer).toList();
|
||||
if (!addedPlayers.isEmpty()) {
|
||||
PacketUtils.sendGroupedPacket(addedPlayers, holder.createAddPacket());
|
||||
PacketSendingUtils.sendGroupedPacket(addedPlayers, holder.createAddPacket());
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ public class BossBarManager {
|
||||
if (holder != null) {
|
||||
Collection<Player> removedPlayers = players.stream().filter(holder::removeViewer).toList();
|
||||
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) {
|
||||
BossBarHolder holder = this.bars.remove(bossBar);
|
||||
if (holder != null) {
|
||||
PacketUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
|
||||
PacketSendingUtils.sendGroupedPacket(holder.players, holder.createRemovePacket());
|
||||
for (Player player : holder.players) {
|
||||
this.removePlayer(player, holder);
|
||||
}
|
||||
|
@ -16,17 +16,7 @@ import java.util.Objects;
|
||||
public final class AlphaColor extends Color {
|
||||
private static final int BIT_MASK = 0xff;
|
||||
|
||||
public static final NetworkBuffer.Type<AlphaColor> NETWORK_TYPE = new NetworkBuffer.Type<AlphaColor>() {
|
||||
@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));
|
||||
}
|
||||
};
|
||||
public static final NetworkBuffer.Type<AlphaColor> NETWORK_TYPE = NetworkBuffer.INT.transform(AlphaColor::new, AlphaColor::asARGB);
|
||||
private final int alpha;
|
||||
|
||||
public AlphaColor(int alpha, int red, int green, int blue) {
|
||||
|
@ -17,17 +17,10 @@ import java.util.Objects;
|
||||
public class Color implements RGBLike {
|
||||
private static final int BIT_MASK = 0xff;
|
||||
|
||||
public static final NetworkBuffer.Type<RGBLike> NETWORK_TYPE = new NetworkBuffer.Type<RGBLike>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, RGBLike value) {
|
||||
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 NetworkBuffer.Type<RGBLike> NETWORK_TYPE = NetworkBuffer.INT.transform(
|
||||
Color::new,
|
||||
color -> Color.fromRGBLike(color).asRGB()
|
||||
);
|
||||
public static final BinaryTagSerializer<RGBLike> NBT_TYPE = BinaryTagSerializer.INT
|
||||
.map(Color::new, color -> Color.fromRGBLike(color).asRGB());
|
||||
private final int red;
|
||||
|
@ -37,9 +37,7 @@ public class ArgumentString extends Argument<String> {
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] nodeProperties() {
|
||||
return NetworkBuffer.makeArray(buffer -> {
|
||||
buffer.write(NetworkBuffer.VAR_INT, 1); // Quotable phrase
|
||||
});
|
||||
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 1); // Quotable phrase
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,9 +32,7 @@ public class ArgumentStringArray extends Argument<String[]> {
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] nodeProperties() {
|
||||
return NetworkBuffer.makeArray(buffer -> {
|
||||
buffer.write(NetworkBuffer.VAR_INT, 2); // Greedy phrase
|
||||
});
|
||||
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 2); // Greedy phrase
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,9 +75,7 @@ public class ArgumentWord extends Argument<String> {
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] nodeProperties() {
|
||||
return NetworkBuffer.makeArray(buffer -> {
|
||||
buffer.write(NetworkBuffer.VAR_INT, 0); // Single word
|
||||
});
|
||||
return NetworkBuffer.makeArray(NetworkBuffer.VAR_INT, 0); // Single word
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,6 @@ public class ArgumentResource extends Argument<String> {
|
||||
|
||||
@Override
|
||||
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
|
||||
public byte @Nullable [] nodeProperties() {
|
||||
return NetworkBuffer.makeArray(buffer ->
|
||||
buffer.write(NetworkBuffer.STRING, this.identifier)
|
||||
);
|
||||
return NetworkBuffer.makeArray(NetworkBuffer.STRING, identifier);
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ public class ArgumentTime extends Argument<Duration> {
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] nodeProperties() {
|
||||
return NetworkBuffer.makeArray(buffer -> buffer.write(NetworkBuffer.INT, min));
|
||||
return NetworkBuffer.makeArray(NetworkBuffer.INT, min);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,37 +1,30 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 ArgumentSignatures {
|
||||
entries = List.copyOf(entries);
|
||||
}
|
||||
|
||||
public ArgumentSignatures(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readCollection(Entry::new, MAX_ENTRIES));
|
||||
}
|
||||
public static final NetworkBuffer.Type<ArgumentSignatures> SERIALIZER = NetworkBufferTemplate.template(
|
||||
Entry.SERIALIZER.list(MAX_ENTRIES), ArgumentSignatures::entries,
|
||||
ArgumentSignatures::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.writeCollection(entries);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
public record Entry(@NotNull String name, @NotNull MessageSignature signature) {
|
||||
public static final NetworkBuffer.Type<Entry> SERIALIZER = NetworkBufferTemplate.template(
|
||||
STRING, Entry::name,
|
||||
MessageSignature.SERIALIZER, Entry::signature,
|
||||
Entry::new
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record ChatSession(@NotNull UUID sessionId, @NotNull PlayerPublicKey publicKey) implements NetworkBuffer.Writer {
|
||||
public ChatSession(@NotNull NetworkBuffer reader) {
|
||||
this(reader.read(NetworkBuffer.UUID), new PlayerPublicKey(reader));
|
||||
}
|
||||
import static net.minestom.server.network.NetworkBuffer.UUID;
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(NetworkBuffer.UUID, sessionId);
|
||||
writer.write(publicKey);
|
||||
}
|
||||
public record ChatSession(@NotNull UUID sessionId, @NotNull PlayerPublicKey publicKey) {
|
||||
public static final NetworkBuffer.Type<ChatSession> SERIALIZER = NetworkBufferTemplate.template(
|
||||
UUID, ChatSession::sessionId,
|
||||
PlayerPublicKey.SERIALIZER, ChatSession::publicKey,
|
||||
ChatSession::new
|
||||
);
|
||||
}
|
||||
|
@ -5,20 +5,25 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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 FilterMask(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readEnum(Type.class), BitSet.valueOf(reader.read(LONG_ARRAY)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.writeEnum(Type.class, type);
|
||||
if (type == Type.PARTIALLY_FILTERED) {
|
||||
writer.write(LONG_ARRAY, mask.toLongArray());
|
||||
public record FilterMask(@NotNull Type type, @NotNull BitSet mask) {
|
||||
public static final NetworkBuffer.Type<FilterMask> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@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
|
||||
public FilterMask read(@NotNull NetworkBuffer buffer) {
|
||||
Type type = buffer.read(NetworkBuffer.Enum(Type.class));
|
||||
BitSet mask = type == Type.PARTIALLY_FILTERED ? buffer.read(BITSET) : new BitSet();
|
||||
return new FilterMask(type, mask);
|
||||
}
|
||||
};
|
||||
|
||||
public enum Type {
|
||||
PASS_THROUGH,
|
||||
|
@ -1,50 +1,41 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.FixedBitSet;
|
||||
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 LastSeenMessages {
|
||||
entries = List.copyOf(entries);
|
||||
}
|
||||
|
||||
public LastSeenMessages(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readCollection(MessageSignature::new, MAX_ENTRIES));
|
||||
}
|
||||
public static final NetworkBuffer.Type<LastSeenMessages> SERIALIZER = NetworkBufferTemplate.template(
|
||||
MessageSignature.SERIALIZER.list(MAX_ENTRIES), LastSeenMessages::entries,
|
||||
LastSeenMessages::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
}
|
||||
|
||||
public record Packed(@NotNull List<MessageSignature.@NotNull Packed> entries) implements NetworkBuffer.Writer {
|
||||
public record Packed(@NotNull List<MessageSignature.@NotNull Packed> entries) {
|
||||
public static final Packed EMPTY = new Packed(List.of());
|
||||
|
||||
public Packed(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readCollection(MessageSignature.Packed::new, MAX_ENTRIES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.writeCollection(entries);
|
||||
}
|
||||
public static final NetworkBuffer.Type<Packed> SERIALIZER = NetworkBufferTemplate.template(
|
||||
MessageSignature.Packed.SERIALIZER.list(MAX_ENTRIES), Packed::entries,
|
||||
Packed::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);
|
||||
}
|
||||
public record Update(int offset, @NotNull BitSet acknowledged) {
|
||||
public static final NetworkBuffer.Type<Update> SERIALIZER = NetworkBufferTemplate.template(
|
||||
VAR_INT, Update::offset,
|
||||
FixedBitSet(20), Update::acknowledged,
|
||||
Update::new
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.RAW_BYTES;
|
||||
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||
|
||||
public record MessageSignature(byte @NotNull [] signature) implements NetworkBuffer.Writer {
|
||||
|
||||
public record MessageSignature(byte @NotNull [] signature) {
|
||||
static final int SIGNATURE_BYTE_LENGTH = 256;
|
||||
|
||||
public MessageSignature {
|
||||
@ -17,33 +16,28 @@ public record MessageSignature(byte @NotNull [] signature) implements NetworkBuf
|
||||
}
|
||||
}
|
||||
|
||||
public MessageSignature(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readBytes(SIGNATURE_BYTE_LENGTH));
|
||||
}
|
||||
|
||||
@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 static final NetworkBuffer.Type<MessageSignature> SERIALIZER = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.RAW_BYTES, MessageSignature::signature,
|
||||
MessageSignature::new
|
||||
);
|
||||
|
||||
public record Packed(int id, @UnknownNullability MessageSignature fullSignature) {
|
||||
private Packed(@NotNull Packed packed) {
|
||||
this(packed.id, packed.fullSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(VAR_INT, id + 1);
|
||||
if (id == 0) writer.write(fullSignature);
|
||||
}
|
||||
public static final NetworkBuffer.Type<Packed> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Packed value) {
|
||||
buffer.write(VAR_INT, value.id + 1);
|
||||
if (value.id == 0) buffer.write(MessageSignature.SERIALIZER, value.fullSignature);
|
||||
}
|
||||
|
||||
private static Packed read(NetworkBuffer reader) {
|
||||
final int id = reader.read(VAR_INT) - 1;
|
||||
return new Packed(id, id == -1 ? new MessageSignature(reader) : null);
|
||||
}
|
||||
@Override
|
||||
public Packed read(@NotNull NetworkBuffer buffer) {
|
||||
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;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.utils.crypto.KeyUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
|
||||
import static net.minestom.server.network.NetworkBuffer.LONG;
|
||||
import static net.minestom.server.network.NetworkBuffer.*;
|
||||
|
||||
/**
|
||||
* Player's public key used to sign chat messages
|
||||
*/
|
||||
public record PlayerPublicKey(Instant expiresAt, PublicKey publicKey,
|
||||
byte[] signature) implements NetworkBuffer.Writer {
|
||||
public PlayerPublicKey(@NotNull NetworkBuffer reader) {
|
||||
this(Instant.ofEpochMilli(reader.read(LONG)),
|
||||
KeyUtils.publicRSAKeyFrom(reader.read(BYTE_ARRAY)), reader.read(BYTE_ARRAY));
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
public record PlayerPublicKey(Instant expiresAt, PublicKey publicKey, byte[] signature) {
|
||||
public static final NetworkBuffer.Type<PlayerPublicKey> SERIALIZER = NetworkBufferTemplate.template(
|
||||
INSTANT_MS, PlayerPublicKey::expiresAt,
|
||||
PUBLIC_KEY, PlayerPublicKey::publicKey,
|
||||
BYTE_ARRAY, PlayerPublicKey::signature,
|
||||
PlayerPublicKey::new
|
||||
);
|
||||
}
|
||||
|
@ -1,19 +1,12 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
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;
|
||||
import static net.minestom.server.network.NetworkBuffer.LONG;
|
||||
|
||||
public record SaltSignaturePair(long salt, byte[] signature) implements NetworkBuffer.Writer {
|
||||
public SaltSignaturePair(@NotNull NetworkBuffer reader) {
|
||||
this(reader.read(LONG), reader.read(BYTE_ARRAY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(LONG, salt);
|
||||
writer.write(BYTE_ARRAY, signature);
|
||||
}
|
||||
public record SaltSignaturePair(long salt, byte[] signature) {
|
||||
public static final NetworkBuffer.Type<SaltSignaturePair> SERIALIZER = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.LONG, SaltSignaturePair::salt,
|
||||
NetworkBuffer.BYTE_ARRAY, SaltSignaturePair::signature,
|
||||
SaltSignaturePair::new
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
@ -8,24 +9,19 @@ import java.time.Instant;
|
||||
public final class SignedMessageBody {
|
||||
|
||||
public record Packed(@NotNull String content, @NotNull Instant timeStamp, long salt,
|
||||
LastSeenMessages.@NotNull Packed lastSeen) implements NetworkBuffer.Writer {
|
||||
LastSeenMessages.@NotNull Packed lastSeen) {
|
||||
public Packed {
|
||||
if (content.length() > MessageSignature.SIGNATURE_BYTE_LENGTH) {
|
||||
throw new IllegalArgumentException("Message content too long");
|
||||
}
|
||||
}
|
||||
|
||||
public Packed(@NotNull NetworkBuffer reader) {
|
||||
this(reader.read(NetworkBuffer.STRING), Instant.ofEpochMilli(reader.read(NetworkBuffer.LONG)),
|
||||
reader.read(NetworkBuffer.LONG), new LastSeenMessages.Packed(reader));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(NetworkBuffer.STRING, content);
|
||||
writer.write(NetworkBuffer.LONG, timeStamp.toEpochMilli());
|
||||
writer.write(NetworkBuffer.LONG, salt);
|
||||
writer.write(lastSeen);
|
||||
}
|
||||
public static final NetworkBuffer.Type<Packed> SERIALIZER = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.STRING, Packed::content,
|
||||
NetworkBuffer.INSTANT_MS, Packed::timeStamp,
|
||||
NetworkBuffer.LONG, Packed::salt,
|
||||
LastSeenMessages.Packed.SERIALIZER, Packed::lastSeen,
|
||||
Packed::new
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,16 @@
|
||||
package net.minestom.server.crypto;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record SignedMessageHeader(@Nullable MessageSignature previousSignature,
|
||||
@NotNull UUID sender) implements NetworkBuffer.Writer {
|
||||
public SignedMessageHeader(@NotNull NetworkBuffer reader) {
|
||||
this(reader.readOptional(MessageSignature::new), reader.read(NetworkBuffer.UUID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.writeOptional(previousSignature);
|
||||
writer.write(NetworkBuffer.UUID, sender);
|
||||
}
|
||||
public record SignedMessageHeader(@Nullable MessageSignature previousSignature, @NotNull UUID sender) {
|
||||
public static final NetworkBuffer.Type<SignedMessageHeader> SERIALIZER = NetworkBufferTemplate.template(
|
||||
MessageSignature.SERIALIZER.optional(), SignedMessageHeader::previousSignature,
|
||||
NetworkBuffer.UUID, SignedMessageHeader::sender,
|
||||
SignedMessageHeader::new
|
||||
);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ import net.minestom.server.timer.Schedulable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
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.block.BlockIterator;
|
||||
import net.minestom.server.utils.chunk.ChunkCache;
|
||||
@ -1244,22 +1244,21 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
final Chunk chunk = getChunk();
|
||||
assert chunk != null;
|
||||
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;
|
||||
} else if (positionChange && viewChange) {
|
||||
// PacketUtils.prepareViewablePacket(chunk, new EntityVelocityPacket(getEntityId(), new Vec(distanceX, distanceY, distanceZ).div(20 * 8000)));
|
||||
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||
PacketViewableUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||
lastSyncedPosition, isOnGround()), this);
|
||||
// Fix head rotation
|
||||
PacketUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
PacketViewableUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
} 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
|
||||
// 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);
|
||||
} else if (viewChange) {
|
||||
PacketUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||
PacketViewableUtils.prepareViewablePacket(chunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
PacketViewableUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
|
||||
lastSyncedPosition, isOnGround()), this);
|
||||
}
|
||||
this.lastSyncedPosition = position;
|
||||
@ -1535,9 +1534,9 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
@ApiStatus.Internal
|
||||
protected void synchronizePosition() {
|
||||
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()) {
|
||||
PacketUtils.prepareViewablePacket(currentChunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
PacketViewableUtils.prepareViewablePacket(currentChunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
|
||||
}
|
||||
nextSynchronizationTick = ticks + synchronizationTicks;
|
||||
this.lastSyncedPosition = posCache;
|
||||
|
@ -32,15 +32,18 @@ public enum GameMode {
|
||||
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
|
||||
public void write(@NotNull NetworkBuffer buffer, GameMode value) {
|
||||
buffer.write(BYTE, value.id());
|
||||
buffer.write(BYTE, value != null ? value.id() : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.utils.Direction;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
@ -213,15 +212,13 @@ public final class Metadata {
|
||||
return (byte) NEXT_ID.getAndIncrement();
|
||||
}
|
||||
|
||||
public sealed interface Entry<T> extends NetworkBuffer.Writer
|
||||
permits MetadataImpl.EntryImpl {
|
||||
public sealed interface Entry<T> permits MetadataImpl.EntryImpl {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
NetworkBuffer.Type<Entry<?>> SERIALIZER = (NetworkBuffer.Type) MetadataImpl.EntryImpl.SERIALIZER;
|
||||
|
||||
int type();
|
||||
|
||||
@UnknownNullability T value();
|
||||
|
||||
@ApiStatus.Internal
|
||||
static @NotNull Entry<?> read(int type, @NotNull NetworkBuffer reader) {
|
||||
return MetadataImpl.EntryImpl.read(type, reader);
|
||||
}
|
||||
@UnknownNullability
|
||||
T value();
|
||||
}
|
||||
}
|
||||
|
@ -61,22 +61,23 @@ final class MetadataImpl {
|
||||
EMPTY_VALUES.trim();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
record EntryImpl<T>(int type, @UnknownNullability T value,
|
||||
@NotNull NetworkBuffer.Type<T> serializer) implements Metadata.Entry<T> {
|
||||
static Entry<?> read(int type, @NotNull NetworkBuffer reader) {
|
||||
final EntryImpl<?> value = (EntryImpl<?>) EMPTY_VALUES.get(type);
|
||||
if (value == null) throw new UnsupportedOperationException("Unknown value type: " + type);
|
||||
return value.withValue(reader);
|
||||
}
|
||||
static final NetworkBuffer.Type<EntryImpl<?>> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, EntryImpl value) {
|
||||
buffer.write(VAR_INT, value.type);
|
||||
buffer.write(value.serializer, value.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(VAR_INT, type);
|
||||
writer.write(serializer, value);
|
||||
}
|
||||
|
||||
private EntryImpl<T> withValue(@NotNull NetworkBuffer reader) {
|
||||
return new EntryImpl<>(type, reader.read(serializer), serializer);
|
||||
}
|
||||
@Override
|
||||
public EntryImpl read(@NotNull NetworkBuffer buffer) {
|
||||
final int type = buffer.read(VAR_INT);
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ import net.minestom.server.statistic.PlayerStatistic;
|
||||
import net.minestom.server.thread.Acquirable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
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.chunk.ChunkUpdateLimitChecker;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
@ -328,7 +328,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
EventDispatcher.call(skinInitEvent);
|
||||
this.skin = skinInitEvent.getSkin();
|
||||
// FIXME: when using Geyser, this line remove the skin of the client
|
||||
PacketUtils.broadcastPlayPacket(getAddPlayerToList());
|
||||
PacketSendingUtils.broadcastPlayPacket(getAddPlayerToList());
|
||||
|
||||
var connectionManager = MinecraftServer.getConnectionManager();
|
||||
for (var player : connectionManager.getOnlinePlayers()) {
|
||||
@ -604,7 +604,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Clear all viewable chunks
|
||||
ChunkUtils.forChunksInRange(chunkX, chunkZ, settings.getEffectiveViewDistance(), chunkRemover);
|
||||
// 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
|
||||
// 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 author = PlainTextComponentSerializer.plainText().serialize(book.author());
|
||||
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();
|
||||
|
||||
// Set book in offhand
|
||||
@ -1176,7 +1176,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
*/
|
||||
public void setDisplayName(@Nullable Component 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
|
||||
PacketUtils.broadcastPlayPacket(removePlayerPacket);
|
||||
PacketSendingUtils.broadcastPlayPacket(removePlayerPacket);
|
||||
sendPacketToViewers(destroyEntitiesPacket);
|
||||
|
||||
// Show player again
|
||||
PacketUtils.broadcastPlayPacket(addPlayerPacket);
|
||||
PacketSendingUtils.broadcastPlayPacket(addPlayerPacket);
|
||||
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
|
||||
if (isActive()) {
|
||||
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
|
||||
@ -2138,7 +2138,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
public void refreshLatency(int latency) {
|
||||
this.latency = latency;
|
||||
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.
|
||||
*
|
||||
* @param notification the {@link Notification} to send
|
||||
*/
|
||||
public void sendNotification(@NotNull Notification notification) {
|
||||
|
@ -12,7 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
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);
|
||||
|
||||
@Contract(pure = true)
|
||||
|
@ -22,13 +22,13 @@ public final class AttributeInstance {
|
||||
public void write(@NotNull NetworkBuffer buffer, AttributeInstance value) {
|
||||
buffer.write(Attribute.NETWORK_TYPE, value.attribute());
|
||||
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
|
||||
public AttributeInstance read(@NotNull NetworkBuffer buffer) {
|
||||
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.event.trait.PlayerEvent;
|
||||
import net.minestom.server.network.plugin.LoginPlugin;
|
||||
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
|
||||
import net.minestom.server.network.plugin.LoginPluginResponse;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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.
|
||||
*/
|
||||
public @NotNull CompletableFuture<LoginPluginResponse> sendPluginRequest(String channel, byte[] requestPayload) {
|
||||
public @NotNull CompletableFuture<LoginPlugin.Response> sendPluginRequest(String channel, byte[] 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.extras.query.event.BasicQueryEvent;
|
||||
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.timer.Task;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
@ -183,24 +185,24 @@ public class Query {
|
||||
if (remaining == 0) { // basic
|
||||
BasicQueryEvent event = new BasicQueryEvent(sender, sessionID);
|
||||
EventDispatcher.callCancellable(event, () ->
|
||||
sendResponse(event.getQueryResponse(), sessionID, sender));
|
||||
sendResponse(BasicQueryResponse.SERIALIZER, event.getQueryResponse(), sessionID, sender));
|
||||
} else if (remaining == 5) { // full
|
||||
FullQueryEvent event = new FullQueryEvent(sender, sessionID);
|
||||
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 -> {
|
||||
// header
|
||||
buffer.write(NetworkBuffer.BYTE, (byte) 0);
|
||||
buffer.write(NetworkBuffer.INT, sessionID);
|
||||
// payload
|
||||
buffer.write(queryResponse);
|
||||
buffer.write(type, queryResponse);
|
||||
});
|
||||
try {
|
||||
socket.send(new DatagramPacket(responseData, responseData.length, sender));
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.minestom.server.extras.query.event;
|
||||
|
||||
import net.minestom.server.event.trait.CancellableEvent;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
@ -12,7 +11,7 @@ import java.util.Objects;
|
||||
*
|
||||
* @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 int sessionID;
|
||||
|
||||
|
@ -2,145 +2,43 @@ package net.minestom.server.extras.query.response;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class BasicQueryResponse implements NetworkBuffer.Writer {
|
||||
private String motd, gametype, map, numPlayers, maxPlayers;
|
||||
|
||||
public record BasicQueryResponse(String motd, String gameType,
|
||||
String map,
|
||||
String numPlayers, String maxPlayers,
|
||||
short port, String address) {
|
||||
/**
|
||||
* Creates a new basic query response with pre-filled default values.
|
||||
*/
|
||||
public BasicQueryResponse() {
|
||||
this.motd = "A Minestom Server";
|
||||
this.gametype = "SMP";
|
||||
this.map = "world";
|
||||
this.numPlayers = String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayerCount());
|
||||
this.maxPlayers = String.valueOf(Integer.parseInt(this.numPlayers) + 1);
|
||||
this(
|
||||
"A Minestom Server",
|
||||
"SMP",
|
||||
"world",
|
||||
String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayerCount()),
|
||||
"9999",
|
||||
(short) MinecraftServer.getServer().getPort(),
|
||||
Objects.requireNonNullElse(MinecraftServer.getServer().getAddress(), "")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MoTD.
|
||||
*
|
||||
* @return the motd
|
||||
*/
|
||||
public @NotNull String getMotd() {
|
||||
return this.motd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(), ""));
|
||||
}
|
||||
public static final NetworkBuffer.Type<BasicQueryResponse> SERIALIZER = NetworkBufferTemplate.template(
|
||||
STRING_TERMINATED, BasicQueryResponse::motd,
|
||||
STRING_TERMINATED, BasicQueryResponse::gameType,
|
||||
STRING_TERMINATED, BasicQueryResponse::map,
|
||||
STRING_TERMINATED, BasicQueryResponse::numPlayers,
|
||||
STRING_TERMINATED, BasicQueryResponse::maxPlayers,
|
||||
SHORT, BasicQueryResponse::port, // TODO little endian?
|
||||
STRING_TERMINATED, BasicQueryResponse::address,
|
||||
BasicQueryResponse::new
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import java.util.*;
|
||||
/**
|
||||
* 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 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};
|
||||
@ -123,20 +123,27 @@ public class FullQueryResponse implements NetworkBuffer.Writer {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(NetworkBuffer.RAW_BYTES, PADDING_11);
|
||||
// key-values
|
||||
for (var entry : this.kv.entrySet()) {
|
||||
writer.write(NetworkBuffer.STRING_TERMINATED, entry.getKey());
|
||||
writer.write(NetworkBuffer.STRING_TERMINATED, entry.getValue());
|
||||
public static final NetworkBuffer.Type<FullQueryResponse> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, FullQueryResponse value) {
|
||||
buffer.write(NetworkBuffer.RAW_BYTES, PADDING_11);
|
||||
// key-values
|
||||
for (var entry : value.kv.entrySet()) {
|
||||
buffer.write(NetworkBuffer.STRING_TERMINATED, entry.getKey());
|
||||
buffer.write(NetworkBuffer.STRING_TERMINATED, entry.getValue());
|
||||
}
|
||||
buffer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||
buffer.write(NetworkBuffer.RAW_BYTES, PADDING_10);
|
||||
// players
|
||||
for (String player : value.players) {
|
||||
buffer.write(NetworkBuffer.STRING_TERMINATED, player);
|
||||
}
|
||||
buffer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||
}
|
||||
writer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||
writer.write(NetworkBuffer.RAW_BYTES, PADDING_10);
|
||||
// players
|
||||
for (String player : this.players) {
|
||||
writer.write(NetworkBuffer.STRING_TERMINATED, player);
|
||||
|
||||
@Override
|
||||
public FullQueryResponse read(@NotNull NetworkBuffer buffer) {
|
||||
throw new UnsupportedOperationException("FullQueryResponse is write-only");
|
||||
}
|
||||
writer.write(NetworkBuffer.STRING_TERMINATED, "");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public final class VelocityProxy {
|
||||
for (int i = 0; i < signature.length; i++) {
|
||||
signature[i] = buffer.read(BYTE);
|
||||
}
|
||||
final int index = buffer.readIndex();
|
||||
final long index = buffer.readIndex();
|
||||
final byte[] data = buffer.read(RAW_BYTES);
|
||||
buffer.readIndex(index);
|
||||
try {
|
||||
@ -66,7 +66,7 @@ public final class VelocityProxy {
|
||||
return false;
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
final int version = buffer.read(VAR_INT);
|
||||
return version == SUPPORTED_FORWARDING_VERSION;
|
||||
|
@ -1,6 +1,6 @@
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -18,7 +18,7 @@ public final class TagManager {
|
||||
// Load required tags from files
|
||||
for (var type : Tag.BasicType.values()) {
|
||||
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<>());
|
||||
json.keySet().forEach(tagName -> {
|
||||
final var tag = new Tag(NamespaceID.from(tagName), getValues(json, tagName));
|
||||
@ -40,6 +40,22 @@ public final class TagManager {
|
||||
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) {
|
||||
Map<String, Object> tagObject = main.get(value);
|
||||
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.MotionBlockingHeightmap;
|
||||
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.packet.server.CachedPacket;
|
||||
import net.minestom.server.network.packet.server.SendablePacket;
|
||||
@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.SHORT;
|
||||
import static net.minestom.server.utils.chunk.ChunkUtils.toSectionRelativeCoordinate;
|
||||
|
||||
/**
|
||||
@ -260,7 +262,11 @@ public class DynamicChunk extends Chunk {
|
||||
heightmapsNBT = getHeightmapNBT();
|
||||
|
||||
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.instance.block.Block;
|
||||
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 java.util.List;
|
||||
@ -73,7 +73,7 @@ public abstract class Explosion {
|
||||
ExplosionPacket packet = new ExplosionPacket(centerX, centerY, centerZ, strength,
|
||||
records, 0, 0, 0);
|
||||
postExplosion(instance, blocks, packet);
|
||||
PacketUtils.sendGroupedPacket(instance.getPlayers(), packet);
|
||||
PacketSendingUtils.sendGroupedPacket(instance.getPlayers(), packet);
|
||||
|
||||
postSend(instance, blocks);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ import net.minestom.server.timer.Schedulable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
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.ChunkSupplier;
|
||||
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) {
|
||||
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;
|
||||
// time needs to be sent to players
|
||||
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.registry.DynamicRegistry;
|
||||
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.block.BlockUtils;
|
||||
import net.minestom.server.utils.chunk.ChunkCache;
|
||||
@ -240,7 +240,7 @@ public class InstanceContainer extends Instance {
|
||||
UNSAFE_setBlock(chunk, x, y, z, resultBlock, null,
|
||||
new BlockHandler.PlayerDestroy(block, this, blockPosition, player), doBlockUpdates, 0);
|
||||
// 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),
|
||||
// Prevent the block breaker to play the particles and sound two times
|
||||
(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.palette.Palette;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
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.network.NetworkBuffer.SHORT;
|
||||
|
||||
public final class Section implements NetworkBuffer.Writer {
|
||||
public final class Section {
|
||||
private final Palette blockPalette;
|
||||
private final Palette biomePalette;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer writer) {
|
||||
writer.write(SHORT, (short) blockPalette.count());
|
||||
writer.write(blockPalette);
|
||||
writer.write(biomePalette);
|
||||
}
|
||||
|
||||
public void setSkyLight(byte[] copyArray) {
|
||||
if (copyArray == null || copyArray.length == 0) 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.block.Block;
|
||||
import net.minestom.server.instance.block.BlockHandler;
|
||||
import net.minestom.server.instance.palette.Palettes;
|
||||
import net.minestom.server.registry.DynamicRegistry;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
@ -196,7 +196,7 @@ public class AnvilLoader implements IChunkLoader {
|
||||
|
||||
int bitsPerEntry = packedIndices.length * 64 / biomeIndices.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) -> {
|
||||
final int index = x + z * 4 + y * 16;
|
||||
@ -216,7 +216,7 @@ public class AnvilLoader implements IChunkLoader {
|
||||
final long[] packedStates = blockStatesTag.getLongArray("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];
|
||||
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 z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
|
||||
@ -465,7 +465,7 @@ public class AnvilLoader implements IChunkLoader {
|
||||
if (blockPaletteEntries.size() > 1) {
|
||||
// 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)));
|
||||
blockStates.putLongArray("data", ArrayUtils.pack(blockIndices, bitsPerEntry));
|
||||
blockStates.putLongArray("data", Palettes.pack(blockIndices, bitsPerEntry));
|
||||
}
|
||||
sectionData.put("block_states", blockStates.build());
|
||||
|
||||
@ -474,7 +474,7 @@ public class AnvilLoader implements IChunkLoader {
|
||||
if (biomePalette.size() > 1) {
|
||||
// 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)));
|
||||
biomes.putLongArray("data", ArrayUtils.pack(biomeIndices, bitsPerEntry));
|
||||
biomes.putLongArray("data", Palettes.pack(biomeIndices, bitsPerEntry));
|
||||
}
|
||||
sectionData.put("biomes", biomes.build());
|
||||
|
||||
|
@ -27,7 +27,7 @@ import java.util.function.BiPredicate;
|
||||
public sealed interface Block extends StaticProtocolObject, TagReadable, Blocks permits BlockImpl {
|
||||
|
||||
@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}.
|
||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockHandler;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.block.BlockUtils;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -14,6 +15,8 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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>
|
||||
*
|
||||
@ -25,8 +28,8 @@ import java.util.function.Predicate;
|
||||
* is used for matching adventure mode blocks and must line up with client prediction.</p>
|
||||
*
|
||||
* @param blocks The block names/tags to match.
|
||||
* @param state The block properties to match.
|
||||
* @param nbt The block nbt to match.
|
||||
* @param state The block properties to match.
|
||||
* @param nbt The block nbt to match.
|
||||
*/
|
||||
public record BlockPredicate(
|
||||
@Nullable BlockTypeFilter blocks,
|
||||
@ -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 NetworkBuffer.Type<BlockPredicate> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, BlockPredicate value) {
|
||||
buffer.writeOptional(BlockTypeFilter.NETWORK_TYPE, value.blocks);
|
||||
buffer.writeOptional(PropertiesPredicate.NETWORK_TYPE, value.state);
|
||||
buffer.writeOptional(NetworkBuffer.NBT, value.nbt);
|
||||
}
|
||||
public static final NetworkBuffer.Type<BlockPredicate> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
BlockTypeFilter.NETWORK_TYPE.optional(), BlockPredicate::blocks,
|
||||
PropertiesPredicate.NETWORK_TYPE.optional(), BlockPredicate::state,
|
||||
NBT_COMPOUND.optional(), BlockPredicate::nbt,
|
||||
BlockPredicate::new
|
||||
);
|
||||
|
||||
@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<>() {
|
||||
@Override
|
||||
public @NotNull BinaryTag write(@NotNull BlockPredicate value) {
|
||||
@ -116,5 +109,4 @@ public record BlockPredicate(
|
||||
return false;
|
||||
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.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -13,28 +14,14 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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 static final NetworkBuffer.Type<PropertiesPredicate> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, PropertiesPredicate value) {
|
||||
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 NetworkBuffer.Type<PropertiesPredicate> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.STRING.mapValue(ValuePredicate.NETWORK_TYPE), PropertiesPredicate::properties,
|
||||
PropertiesPredicate::new
|
||||
);
|
||||
public static final BinaryTagSerializer<PropertiesPredicate> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> {
|
||||
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 {
|
||||
|
||||
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);
|
||||
|
||||
@Override
|
||||
@ -94,19 +81,12 @@ public record PropertiesPredicate(@NotNull Map<String, ValuePredicate> propertie
|
||||
* @param max The max value to match, exclusive
|
||||
*/
|
||||
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(
|
||||
tag -> new Range(
|
||||
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.IntSet;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -20,7 +19,7 @@ final class AdaptivePalette implements Palette, Cloneable {
|
||||
this.dimension = dimension;
|
||||
this.maxBitsPerEntry = maxBitsPerEntry;
|
||||
this.defaultBitsPerEntry = bitsPerEntry;
|
||||
this.palette = new FilledPalette(dimension, 0);
|
||||
this.palette = new PaletteSingle(dimension, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -51,12 +50,12 @@ final class AdaptivePalette implements Palette, Cloneable {
|
||||
|
||||
@Override
|
||||
public void fill(int value) {
|
||||
this.palette = new FilledPalette(dimension, value);
|
||||
this.palette = new PaletteSingle(dimension, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAll(@NotNull EntrySupplier supplier) {
|
||||
SpecializedPalette newPalette = new FlexiblePalette(this);
|
||||
SpecializedPalette newPalette = new PaletteIndirect(this);
|
||||
newPalette.setAll(supplier);
|
||||
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() {
|
||||
var currentPalette = this.palette;
|
||||
if (currentPalette instanceof FlexiblePalette flexiblePalette) {
|
||||
final int count = flexiblePalette.count();
|
||||
if (currentPalette instanceof PaletteIndirect paletteIndirect) {
|
||||
final int count = paletteIndirect.count();
|
||||
if (count == 0) {
|
||||
return new FilledPalette(dimension, 0);
|
||||
return new PaletteSingle(dimension, 0);
|
||||
} else {
|
||||
// Find all entries and compress the palette
|
||||
IntSet entries = new IntOpenHashSet(flexiblePalette.paletteToValueList.size());
|
||||
flexiblePalette.getAll((x, y, z, value) -> entries.add(value));
|
||||
final int currentBitsPerEntry = flexiblePalette.bitsPerEntry();
|
||||
IntSet entries = new IntOpenHashSet(paletteIndirect.paletteToValueList.size());
|
||||
paletteIndirect.getAll((x, y, z, value) -> entries.add(value));
|
||||
final int currentBitsPerEntry = paletteIndirect.bitsPerEntry();
|
||||
final int bitsPerEntry;
|
||||
if (entries.size() == 1) {
|
||||
return new FilledPalette(dimension, entries.iterator().nextInt());
|
||||
return new PaletteSingle(dimension, entries.iterator().nextInt());
|
||||
} else if (currentBitsPerEntry > defaultBitsPerEntry &&
|
||||
(bitsPerEntry = MathUtils.bitsToRepresent(entries.size() - 1)) < currentBitsPerEntry) {
|
||||
flexiblePalette.resize((byte) bitsPerEntry);
|
||||
return flexiblePalette;
|
||||
paletteIndirect.resize((byte) bitsPerEntry);
|
||||
return paletteIndirect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,9 +130,9 @@ final class AdaptivePalette implements Palette, Cloneable {
|
||||
|
||||
Palette flexiblePalette() {
|
||||
SpecializedPalette currentPalette = this.palette;
|
||||
if (currentPalette instanceof FilledPalette filledPalette) {
|
||||
currentPalette = new FlexiblePalette(this);
|
||||
currentPalette.fill(filledPalette.value());
|
||||
if (currentPalette instanceof PaletteSingle paletteSingle) {
|
||||
currentPalette = new PaletteIndirect(this);
|
||||
currentPalette.fill(paletteSingle.value());
|
||||
this.palette = currentPalette;
|
||||
}
|
||||
return currentPalette;
|
||||
|
@ -5,12 +5,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.*;
|
||||
|
||||
/**
|
||||
* Represents a palette used to store blocks and biomes.
|
||||
* <p>
|
||||
* 0 is the default value.
|
||||
*/
|
||||
public interface Palette extends NetworkBuffer.Writer {
|
||||
public interface Palette {
|
||||
static Palette blocks() {
|
||||
return newPalette(16, 8, 4);
|
||||
}
|
||||
@ -77,4 +79,58 @@ public interface Palette extends NetworkBuffer.Writer {
|
||||
interface EntryFunction {
|
||||
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.IntArrayList;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -11,52 +10,64 @@ import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
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.
|
||||
*/
|
||||
final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
final class PaletteIndirect implements SpecializedPalette, Cloneable {
|
||||
private static final ThreadLocal<int[]> WRITE_CACHE = ThreadLocal.withInitial(() -> new int[4096]);
|
||||
|
||||
// Specific to this palette type
|
||||
private final AdaptivePalette adaptivePalette;
|
||||
private final int dimension;
|
||||
private final int maxBitsPerEntry;
|
||||
|
||||
private byte bitsPerEntry;
|
||||
private int count;
|
||||
|
||||
private long[] values;
|
||||
long[] values;
|
||||
// palette index = value
|
||||
IntArrayList paletteToValueList;
|
||||
// value = palette index
|
||||
private Int2IntOpenHashMap valueToPaletteMap;
|
||||
|
||||
FlexiblePalette(AdaptivePalette adaptivePalette, byte bitsPerEntry) {
|
||||
this.adaptivePalette = adaptivePalette;
|
||||
|
||||
PaletteIndirect(int dimension, int maxBitsPerEntry, byte bitsPerEntry,
|
||||
int count, int[] palette, long[] values) {
|
||||
this.dimension = dimension;
|
||||
this.maxBitsPerEntry = maxBitsPerEntry;
|
||||
this.bitsPerEntry = bitsPerEntry;
|
||||
|
||||
this.paletteToValueList = new IntArrayList(1);
|
||||
this.paletteToValueList.add(0);
|
||||
this.valueToPaletteMap = new Int2IntOpenHashMap(1);
|
||||
this.valueToPaletteMap.put(0, 0);
|
||||
this.count = count;
|
||||
this.values = values;
|
||||
|
||||
this.paletteToValueList = new IntArrayList(palette.length);
|
||||
this.valueToPaletteMap = new Int2IntOpenHashMap(palette.length);
|
||||
this.valueToPaletteMap.defaultReturnValue(-1);
|
||||
|
||||
final int valuesPerLong = 64 / bitsPerEntry;
|
||||
this.values = new long[(maxSize() + valuesPerLong - 1) / valuesPerLong];
|
||||
for (int i = 0; i < palette.length; i++) {
|
||||
this.paletteToValueList.add(palette[i]);
|
||||
this.valueToPaletteMap.put(palette[i], i);
|
||||
}
|
||||
|
||||
this.values = new long[arrayLength(dimension(), bitsPerEntry)];
|
||||
}
|
||||
|
||||
FlexiblePalette(AdaptivePalette adaptivePalette) {
|
||||
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
|
||||
public int get(int x, int y, int z) {
|
||||
final int bitsPerEntry = this.bitsPerEntry;
|
||||
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);
|
||||
final int value = read(dimension(), bitsPerEntry, values, x, y, z);
|
||||
// Change to palette value and return
|
||||
return hasPalette() ? paletteToValueList.getInt(value) : value;
|
||||
}
|
||||
@ -74,20 +85,9 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
@Override
|
||||
public void set(int x, int y, int z, int value) {
|
||||
value = getPaletteIndex(value);
|
||||
final int bitsPerEntry = this.bitsPerEntry;
|
||||
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);
|
||||
final int oldValue = Palettes.write(dimension(), bitsPerEntry, values, x, y, z, value);
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -99,13 +99,7 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
return;
|
||||
}
|
||||
value = getPaletteIndex(value);
|
||||
final int bitsPerEntry = this.bitsPerEntry;
|
||||
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);
|
||||
Palettes.fill(bitsPerEntry, values, value);
|
||||
this.count = maxSize();
|
||||
}
|
||||
|
||||
@ -185,18 +179,18 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
|
||||
@Override
|
||||
public int maxBitsPerEntry() {
|
||||
return adaptivePalette.maxBitsPerEntry();
|
||||
return maxBitsPerEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dimension() {
|
||||
return adaptivePalette.dimension();
|
||||
return dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SpecializedPalette clone() {
|
||||
try {
|
||||
FlexiblePalette palette = (FlexiblePalette) super.clone();
|
||||
PaletteIndirect palette = (PaletteIndirect) super.clone();
|
||||
palette.values = values != null ? values.clone() : null;
|
||||
palette.paletteToValueList = paletteToValueList.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) {
|
||||
if (!consumeEmpty && count == 0) return;
|
||||
final long[] values = this.values;
|
||||
@ -268,7 +253,7 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
|
||||
void resize(byte newBitsPerEntry) {
|
||||
newBitsPerEntry = newBitsPerEntry > maxBitsPerEntry() ? 15 : newBitsPerEntry;
|
||||
FlexiblePalette palette = new FlexiblePalette(adaptivePalette, newBitsPerEntry);
|
||||
PaletteIndirect palette = new PaletteIndirect(dimension, maxBitsPerEntry, newBitsPerEntry);
|
||||
palette.paletteToValueList = paletteToValueList;
|
||||
palette.valueToPaletteMap = valueToPaletteMap;
|
||||
getAll(palette::set);
|
||||
@ -297,14 +282,6 @@ final class FlexiblePalette implements SpecializedPalette, Cloneable {
|
||||
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) {
|
||||
return 1 << bitsPerEntry;
|
||||
}
|
@ -1,15 +1,11 @@
|
||||
package net.minestom.server.instance.palette;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
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.
|
||||
*/
|
||||
record FilledPalette(byte dim, int value) implements SpecializedPalette.Immutable {
|
||||
record PaletteSingle(byte dim, int value) implements SpecializedPalette.Immutable {
|
||||
@Override
|
||||
public int get(int x, int y, int z) {
|
||||
return value;
|
||||
@ -44,11 +40,4 @@ record FilledPalette(byte dim, int value) implements SpecializedPalette.Immutabl
|
||||
public @NotNull SpecializedPalette clone() {
|
||||
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);
|
||||
}
|
||||
};
|
||||
@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");
|
||||
return itemStack;
|
||||
}, itemStack -> {
|
||||
|
@ -16,7 +16,7 @@ import java.util.Collection;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
|
@ -21,12 +21,12 @@ public record FilteredText<T>(@NotNull T text, @Nullable T filtered) {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, FilteredText<T> value) {
|
||||
buffer.write(inner, value.text);
|
||||
buffer.writeOptional(inner, value.filtered);
|
||||
buffer.write(inner.optional(), value.filtered);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.TrimPattern;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.registry.DynamicRegistry;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
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<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, ArmorTrim value) {
|
||||
buffer.write(TrimMaterial.NETWORK_TYPE, value.material);
|
||||
buffer.write(TrimPattern.NETWORK_TYPE, value.pattern);
|
||||
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 NetworkBuffer.Type<ArmorTrim> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
TrimMaterial.NETWORK_TYPE, ArmorTrim::material,
|
||||
TrimPattern.NETWORK_TYPE, ArmorTrim::pattern,
|
||||
NetworkBuffer.BOOLEAN, ArmorTrim::showInTooltip,
|
||||
ArmorTrim::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<ArmorTrim> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> {
|
||||
@ -43,5 +36,4 @@ public record ArmorTrim(@NotNull DynamicRegistry.Key<TrimMaterial> material, @No
|
||||
public @NotNull ArmorTrim withTooltip(boolean 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.AttributeModifier;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
|
||||
|
||||
public record AttributeList(@NotNull List<Modifier> modifiers, boolean showInTooltip) {
|
||||
public static final AttributeList EMPTY = new AttributeList(List.of(), true);
|
||||
|
||||
public static final NetworkBuffer.Type<AttributeList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, AttributeList value) {
|
||||
buffer.writeCollection(Modifier.NETWORK_TYPE, value.modifiers);
|
||||
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 NetworkBuffer.Type<AttributeList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
Modifier.NETWORK_TYPE.list(Short.MAX_VALUE), AttributeList::modifiers,
|
||||
BOOLEAN, AttributeList::showInTooltip,
|
||||
AttributeList::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<AttributeList> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||
@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 static final NetworkBuffer.Type<Modifier> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Modifier value) {
|
||||
buffer.write(Attribute.NETWORK_TYPE, value.attribute);
|
||||
buffer.write(AttributeModifier.NETWORK_TYPE, value.modifier);
|
||||
buffer.writeEnum(EquipmentSlotGroup.class, value.slot);
|
||||
}
|
||||
|
||||
@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 record Modifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier,
|
||||
@NotNull EquipmentSlotGroup slot) {
|
||||
public static final NetworkBuffer.Type<Modifier> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
Attribute.NETWORK_TYPE, Modifier::attribute,
|
||||
AttributeModifier.NETWORK_TYPE, Modifier::modifier,
|
||||
NetworkBuffer.Enum(EquipmentSlotGroup.class), Modifier::slot,
|
||||
Modifier::new
|
||||
);
|
||||
public static final BinaryTagSerializer<Modifier> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> new Modifier(
|
||||
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.instance.block.banner.BannerPattern;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.registry.DynamicRegistry;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -15,22 +16,15 @@ import java.util.List;
|
||||
public record BannerPatterns(@NotNull List<Layer> layers) {
|
||||
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 record Layer(@NotNull DynamicRegistry.Key<BannerPattern> pattern, @NotNull DyeColor color) {
|
||||
public static final NetworkBuffer.Type<Layer> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Layer value) {
|
||||
buffer.write(BannerPattern.NETWORK_TYPE, value.pattern);
|
||||
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 NetworkBuffer.Type<Layer> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
BannerPattern.NETWORK_TYPE, Layer::pattern,
|
||||
DyeColor.NETWORK_TYPE, Layer::color,
|
||||
Layer::new
|
||||
);
|
||||
public static final BinaryTagSerializer<Layer> NBT_TYPE = new BinaryTagSerializer<Layer>() {
|
||||
@Override
|
||||
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.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record Bee(@NotNull CustomData entityData, int ticksInHive, int minTicksInHive) {
|
||||
|
||||
public static @NotNull NetworkBuffer.Type<Bee> NETWORK_TYPE = new NetworkBuffer.Type<Bee>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Bee value) {
|
||||
buffer.write(CustomData.NETWORK_TYPE, value.entityData);
|
||||
buffer.write(NetworkBuffer.VAR_INT, value.ticksInHive);
|
||||
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 final NetworkBuffer.Type<Bee> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
CustomData.NETWORK_TYPE, Bee::entityData,
|
||||
NetworkBuffer.VAR_INT, Bee::ticksInHive,
|
||||
NetworkBuffer.VAR_INT, Bee::minTicksInHive,
|
||||
Bee::new
|
||||
);
|
||||
public static @NotNull BinaryTagSerializer<Bee> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> new Bee(CustomData.NBT_TYPE.read(tag.getCompound("entity_data")),
|
||||
tag.getInt("ticks_in_hive"),
|
||||
|
@ -6,24 +6,18 @@ import net.kyori.adventure.nbt.IntBinaryTag;
|
||||
import net.kyori.adventure.util.RGBLike;
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record DyedItemColor(@NotNull RGBLike color, boolean showInTooltip) {
|
||||
public static DyedItemColor LEATHER = new DyedItemColor(new Color(-6265536), true);
|
||||
|
||||
public static final NetworkBuffer.Type<DyedItemColor> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, DyedItemColor value) {
|
||||
buffer.write(Color.NETWORK_TYPE, value.color);
|
||||
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 NetworkBuffer.Type<DyedItemColor> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
Color.NETWORK_TYPE, DyedItemColor::color,
|
||||
NetworkBuffer.BOOLEAN, DyedItemColor::showInTooltip,
|
||||
DyedItemColor::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<DyedItemColor> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||
@Override
|
||||
|
@ -4,9 +4,9 @@ import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.minestom.server.item.enchant.Enchantment;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.registry.DynamicRegistry;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -18,30 +18,11 @@ public record EnchantmentList(@NotNull Map<DynamicRegistry.Key<Enchantment>, Int
|
||||
boolean showInTooltip) {
|
||||
public static final EnchantmentList EMPTY = new EnchantmentList(Map.of(), true);
|
||||
|
||||
public static NetworkBuffer.Type<EnchantmentList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, @NotNull EnchantmentList value) {
|
||||
buffer.write(NetworkBuffer.VAR_INT, value.enchantments.size());
|
||||
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 final NetworkBuffer.Type<EnchantmentList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
Enchantment.NETWORK_TYPE.mapValue(NetworkBuffer.VAR_INT, Short.MAX_VALUE), EnchantmentList::enchantments,
|
||||
NetworkBuffer.BOOLEAN, EnchantmentList::showInTooltip,
|
||||
EnchantmentList::new
|
||||
);
|
||||
public static BinaryTagSerializer<EnchantmentList> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||
@Override
|
||||
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.minestom.server.color.Color;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -11,6 +12,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
|
||||
|
||||
public record FireworkExplosion(
|
||||
@NotNull Shape shape,
|
||||
@NotNull List<RGBLike> colors,
|
||||
@ -27,27 +30,14 @@ public record FireworkExplosion(
|
||||
BURST
|
||||
}
|
||||
|
||||
public static final NetworkBuffer.Type<FireworkExplosion> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, FireworkExplosion value) {
|
||||
buffer.writeEnum(Shape.class, value.shape);
|
||||
buffer.writeCollection(Color.NETWORK_TYPE, value.colors);
|
||||
buffer.writeCollection(Color.NETWORK_TYPE, value.fadeColors);
|
||||
buffer.write(NetworkBuffer.BOOLEAN, value.hasTrail);
|
||||
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 NetworkBuffer.Type<FireworkExplosion> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.Enum(Shape.class), FireworkExplosion::shape,
|
||||
Color.NETWORK_TYPE.list(Short.MAX_VALUE), FireworkExplosion::colors,
|
||||
Color.NETWORK_TYPE.list(Short.MAX_VALUE), FireworkExplosion::fadeColors,
|
||||
BOOLEAN, FireworkExplosion::hasTrail,
|
||||
BOOLEAN, FireworkExplosion::hasTwinkle,
|
||||
FireworkExplosion::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<FireworkExplosion> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> {
|
||||
|
@ -2,6 +2,7 @@ package net.minestom.server.item.component;
|
||||
|
||||
import net.kyori.adventure.nbt.*;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -11,19 +12,11 @@ import java.util.List;
|
||||
public record FireworkList(int flightDuration, @NotNull List<FireworkExplosion> explosions) {
|
||||
public static final FireworkList EMPTY = new FireworkList(0, List.of());
|
||||
|
||||
public static final NetworkBuffer.Type<FireworkList> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, FireworkList value) {
|
||||
buffer.write(NetworkBuffer.VAR_INT, value.flightDuration);
|
||||
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 NetworkBuffer.Type<FireworkList> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.VAR_INT, FireworkList::flightDuration,
|
||||
FireworkExplosion.NETWORK_TYPE.list(256), FireworkList::explosions,
|
||||
FireworkList::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<FireworkList> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> {
|
||||
|
@ -6,43 +6,33 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.minestom.server.ServerFlag;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.potion.CustomPotionEffect;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.*;
|
||||
|
||||
public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat, float eatSeconds,
|
||||
@NotNull ItemStack usingConvertsTo, @NotNull List<EffectChance> effects) {
|
||||
public static final float DEFAULT_EAT_SECONDS = 1.6f;
|
||||
|
||||
public static final NetworkBuffer.Type<Food> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Food value) {
|
||||
buffer.write(NetworkBuffer.VAR_INT, value.nutrition);
|
||||
buffer.write(NetworkBuffer.FLOAT, value.saturationModifier);
|
||||
buffer.write(NetworkBuffer.BOOLEAN, value.canAlwaysEat);
|
||||
buffer.write(NetworkBuffer.FLOAT, value.eatSeconds);
|
||||
buffer.write(ItemStack.NETWORK_TYPE, value.usingConvertsTo);
|
||||
buffer.writeCollection(EffectChance.NETWORK_TYPE, value.effects);
|
||||
}
|
||||
public static final NetworkBuffer.Type<Food> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
VAR_INT, Food::nutrition,
|
||||
FLOAT, Food::saturationModifier,
|
||||
BOOLEAN, Food::canAlwaysEat,
|
||||
FLOAT, Food::eatSeconds,
|
||||
ItemStack.NETWORK_TYPE, Food::usingConvertsTo,
|
||||
EffectChance.NETWORK_TYPE.list(Short.MAX_VALUE), Food::effects,
|
||||
Food::new
|
||||
);
|
||||
|
||||
@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(
|
||||
tag -> new Food(
|
||||
tag.getInt("nutrition"),
|
||||
tag.getFloat("saturation"),
|
||||
tag.getFloat("saturation_modifier"),
|
||||
tag.getBoolean("can_always_eat"),
|
||||
tag.getFloat("eat_seconds", DEFAULT_EAT_SECONDS),
|
||||
tag.get("using_converts_to") instanceof BinaryTag usingConvertsTo
|
||||
@ -51,7 +41,7 @@ public record Food(int nutrition, float saturationModifier, boolean canAlwaysEat
|
||||
value -> {
|
||||
CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
|
||||
.putInt("nutrition", value.nutrition)
|
||||
.putFloat("saturation", value.saturationModifier)
|
||||
.putFloat("saturation_odifier", value.saturationModifier)
|
||||
.putBoolean("can_always_eat", value.canAlwaysEat)
|
||||
.putFloat("eat_seconds", value.eatSeconds)
|
||||
.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 static final NetworkBuffer.Type<EffectChance> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, EffectChance value) {
|
||||
CustomPotionEffect.NETWORK_TYPE.write(buffer, value.effect);
|
||||
buffer.write(NetworkBuffer.FLOAT, value.probability);
|
||||
}
|
||||
public static final NetworkBuffer.Type<EffectChance> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
CustomPotionEffect.NETWORK_TYPE, EffectChance::effect,
|
||||
FLOAT, EffectChance::probability,
|
||||
EffectChance::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public EffectChance read(@NotNull NetworkBuffer buffer) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
public static final BinaryTagSerializer<EffectChance> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> new EffectChance(
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import net.kyori.adventure.nbt.IntArrayBinaryTag;
|
||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||
import net.minestom.server.entity.PlayerSkin;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -12,22 +13,19 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.List;
|
||||
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 static final HeadProfile EMPTY = new HeadProfile(null, null, List.of());
|
||||
|
||||
public static final NetworkBuffer.Type<HeadProfile> NETWORK_TYPE = new NetworkBuffer.Type<HeadProfile>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, HeadProfile value) {
|
||||
buffer.writeOptional(NetworkBuffer.STRING, value.name);
|
||||
buffer.writeOptional(NetworkBuffer.UUID, value.uuid);
|
||||
buffer.writeCollection(Property.NETWORK_TYPE, value.properties);
|
||||
}
|
||||
public static final NetworkBuffer.Type<HeadProfile> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
STRING.optional(), HeadProfile::name,
|
||||
UUID.optional(), HeadProfile::uuid,
|
||||
Property.NETWORK_TYPE.list(Short.MAX_VALUE), HeadProfile::properties,
|
||||
HeadProfile::new
|
||||
);
|
||||
|
||||
@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(
|
||||
tag -> new HeadProfile(
|
||||
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();
|
||||
if (profile.name != null) builder.putString("name", profile.name);
|
||||
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();
|
||||
}
|
||||
);
|
||||
@ -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 static final NetworkBuffer.Type<Property> NETWORK_TYPE = new NetworkBuffer.Type<Property>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Property value) {
|
||||
buffer.write(NetworkBuffer.STRING, value.name);
|
||||
buffer.write(NetworkBuffer.STRING, value.value);
|
||||
buffer.writeOptional(NetworkBuffer.STRING, value.signature);
|
||||
}
|
||||
public static final NetworkBuffer.Type<Property> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
STRING, Property::name,
|
||||
STRING, Property::value,
|
||||
STRING.optional(), Property::signature,
|
||||
Property::new
|
||||
);
|
||||
|
||||
@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(
|
||||
tag -> new Property(tag.getString("name"), tag.getString("value"),
|
||||
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.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -14,26 +15,10 @@ import java.util.Map;
|
||||
public record ItemBlockState(@NotNull Map<String, String> properties) {
|
||||
public static final ItemBlockState EMPTY = new ItemBlockState(Map.of());
|
||||
|
||||
public static final NetworkBuffer.Type<ItemBlockState> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, ItemBlockState value) {
|
||||
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 NetworkBuffer.Type<ItemBlockState> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.STRING.mapValue(NetworkBuffer.STRING), ItemBlockState::properties,
|
||||
ItemBlockState::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<ItemBlockState> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> {
|
||||
|
@ -3,6 +3,7 @@ package net.minestom.server.item.component;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
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.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -10,21 +11,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record LodestoneTracker(@Nullable WorldPos target, boolean tracked) {
|
||||
|
||||
public static final NetworkBuffer.Type<LodestoneTracker> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, @NotNull LodestoneTracker value) {
|
||||
buffer.writeOptional(WorldPos.NETWORK_TYPE, value.target);
|
||||
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 NetworkBuffer.Type<LodestoneTracker> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
WorldPos.NETWORK_TYPE.optional(), LodestoneTracker::target,
|
||||
NetworkBuffer.BOOLEAN, LodestoneTracker::tracked,
|
||||
LodestoneTracker::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<LodestoneTracker> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> new LodestoneTracker(
|
||||
|
@ -1,22 +1,10 @@
|
||||
package net.minestom.server.item.component;
|
||||
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public enum MapPostProcessing {
|
||||
LOCK,
|
||||
SCALE;
|
||||
private static final MapPostProcessing[] VALUES = values();
|
||||
|
||||
public static final NetworkBuffer.Type<MapPostProcessing> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@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)];
|
||||
}
|
||||
};
|
||||
public static final NetworkBuffer.Type<MapPostProcessing> NETWORK_TYPE = NetworkBuffer.Enum(MapPostProcessing.class);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public record PotDecorations(
|
||||
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 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 PotDecorations(@NotNull List<Material> list) {
|
||||
|
@ -25,18 +25,18 @@ public record PotionContents(
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, PotionContents value) {
|
||||
Integer typeId = value.potion == null ? null : value.potion.id();
|
||||
buffer.writeOptional(NetworkBuffer.VAR_INT, typeId);
|
||||
buffer.writeOptional(Color.NETWORK_TYPE, value.customColor);
|
||||
buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects);
|
||||
buffer.write(NetworkBuffer.VAR_INT.optional(), typeId);
|
||||
buffer.write(Color.NETWORK_TYPE.optional(), value.customColor);
|
||||
buffer.write(CustomPotionEffect.NETWORK_TYPE.list(), value.customEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PotionContents read(@NotNull NetworkBuffer buffer) {
|
||||
Integer typeId = buffer.readOptional(NetworkBuffer.VAR_INT);
|
||||
Integer typeId = buffer.read(NetworkBuffer.VAR_INT.optional());
|
||||
return new PotionContents(
|
||||
typeId == null ? null : PotionType.fromId(typeId),
|
||||
buffer.readOptional(Color.NETWORK_TYPE),
|
||||
buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE)
|
||||
buffer.read(Color.NETWORK_TYPE.optional()),
|
||||
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.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.potion.PotionEffect;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
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 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 SuspiciousStewEffects {
|
||||
@ -32,18 +33,11 @@ public record SuspiciousStewEffects(@NotNull List<Effect> effects) {
|
||||
|
||||
public record Effect(@NotNull PotionEffect id, int durationTicks) {
|
||||
|
||||
public static final NetworkBuffer.Type<Effect> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Effect value) {
|
||||
buffer.write(PotionEffect.NETWORK_TYPE, value.id);
|
||||
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 NetworkBuffer.Type<Effect> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
PotionEffect.NETWORK_TYPE, Effect::id,
|
||||
NetworkBuffer.VAR_INT, Effect::durationTicks,
|
||||
Effect::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<Effect> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
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.minestom.server.instance.block.predicate.BlockTypeFilter;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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 static final NetworkBuffer.Type<Rule> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Rule value) {
|
||||
buffer.write(BlockTypeFilter.NETWORK_TYPE, value.blocks());
|
||||
buffer.writeOptional(NetworkBuffer.FLOAT, value.speed());
|
||||
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 NetworkBuffer.Type<Rule> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
BlockTypeFilter.NETWORK_TYPE, Rule::blocks,
|
||||
NetworkBuffer.FLOAT, Rule::speed,
|
||||
NetworkBuffer.BOOLEAN.optional(), Rule::correctForDrops,
|
||||
Rule::new
|
||||
);
|
||||
public static final BinaryTagSerializer<Rule> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
tag -> new Rule(
|
||||
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.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record Unbreakable(boolean showInTooltip) {
|
||||
public static final Unbreakable DEFAULT = new Unbreakable();
|
||||
|
||||
public static final NetworkBuffer.Type<Unbreakable> NETWORK_TYPE = new NetworkBuffer.Type<Unbreakable>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Unbreakable value) {
|
||||
buffer.write(NetworkBuffer.BOOLEAN, value.showInTooltip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unbreakable read(@NotNull NetworkBuffer buffer) {
|
||||
return new Unbreakable(buffer.read(NetworkBuffer.BOOLEAN));
|
||||
}
|
||||
};
|
||||
public static final NetworkBuffer.Type<Unbreakable> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
NetworkBuffer.BOOLEAN, Unbreakable::showInTooltip,
|
||||
Unbreakable::new
|
||||
);
|
||||
|
||||
public Unbreakable() {
|
||||
this(true);
|
||||
@ -28,5 +21,4 @@ public record Unbreakable(boolean showInTooltip) {
|
||||
tag -> new Unbreakable(tag.getBoolean("showInTooltip", true)),
|
||||
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.minestom.server.item.book.FilteredText;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -14,17 +15,10 @@ import java.util.List;
|
||||
public record WritableBookContent(@NotNull List<FilteredText<String>> pages) {
|
||||
public static final WritableBookContent EMPTY = new WritableBookContent(List.of());
|
||||
|
||||
public static final NetworkBuffer.Type<WritableBookContent> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, WritableBookContent value) {
|
||||
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 NetworkBuffer.Type<WritableBookContent> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
FilteredText.STRING_NETWORK_TYPE.list(100), WritableBookContent::pages,
|
||||
WritableBookContent::new
|
||||
);
|
||||
|
||||
public static final BinaryTagSerializer<WritableBookContent> NBT_TYPE = new BinaryTagSerializer<>() {
|
||||
@Override
|
||||
|
@ -6,34 +6,26 @@ import net.kyori.adventure.nbt.ListBinaryTag;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.item.book.FilteredText;
|
||||
import net.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.utils.nbt.BinaryTagSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @NotNull FilteredText<String> title, @NotNull String author, int generation, boolean resolved) {
|
||||
public static final WrittenBookContent EMPTY = new WrittenBookContent(List.of(), new FilteredText<>("", null), "", 0, true);
|
||||
import static net.minestom.server.network.NetworkBuffer.*;
|
||||
|
||||
public static final @NotNull NetworkBuffer.Type<WrittenBookContent> NETWORK_TYPE = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, WrittenBookContent value) {
|
||||
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);
|
||||
}
|
||||
public record WrittenBookContent(@NotNull FilteredText<String> title, @NotNull String author, int generation,
|
||||
@NotNull List<FilteredText<Component>> pages, boolean resolved) {
|
||||
public static final WrittenBookContent EMPTY = new WrittenBookContent(new FilteredText<>("", null), "", 0, List.of(), true);
|
||||
|
||||
@Override
|
||||
public WrittenBookContent read(@NotNull NetworkBuffer buffer) {
|
||||
FilteredText<String> title = buffer.read(FilteredText.STRING_NETWORK_TYPE);
|
||||
String author = buffer.read(NetworkBuffer.STRING);
|
||||
int generation = buffer.read(NetworkBuffer.VAR_INT);
|
||||
List<FilteredText<Component>> pages = buffer.readCollection(FilteredText.COMPONENT_NETWORK_TYPE, 100);
|
||||
boolean resolved = buffer.read(NetworkBuffer.BOOLEAN);
|
||||
return new WrittenBookContent(pages, title, author, generation, resolved);
|
||||
}
|
||||
};
|
||||
public static final NetworkBuffer.Type<WrittenBookContent> NETWORK_TYPE = NetworkBufferTemplate.template(
|
||||
FilteredText.STRING_NETWORK_TYPE, WrittenBookContent::title,
|
||||
STRING, WrittenBookContent::author,
|
||||
VAR_INT, WrittenBookContent::generation,
|
||||
FilteredText.COMPONENT_NETWORK_TYPE.list(100), WrittenBookContent::pages,
|
||||
BOOLEAN, WrittenBookContent::resolved,
|
||||
WrittenBookContent::new
|
||||
);
|
||||
|
||||
public static final @NotNull BinaryTagSerializer<WrittenBookContent> NBT_TYPE = BinaryTagSerializer.COMPOUND.map(
|
||||
compound -> {
|
||||
@ -45,7 +37,7 @@ public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @
|
||||
String author = compound.getString("author");
|
||||
int generation = compound.getInt("generation");
|
||||
boolean resolved = compound.getBoolean("resolved");
|
||||
return new WrittenBookContent(pages, title, author, generation, resolved);
|
||||
return new WrittenBookContent(title, author, generation, pages, resolved);
|
||||
},
|
||||
value -> {
|
||||
ListBinaryTag.Builder<BinaryTag> pagesTag = ListBinaryTag.builder();
|
||||
@ -66,11 +58,11 @@ public record WrittenBookContent(@NotNull List<FilteredText<Component>> pages, @
|
||||
pages = List.copyOf(pages);
|
||||
}
|
||||
|
||||
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author) {
|
||||
this(pages, title, author, 0, true);
|
||||
public WrittenBookContent(@NotNull String title, @NotNull String author, @NotNull List<Component> pages) {
|
||||
this(title, author, 0, pages, true);
|
||||
}
|
||||
|
||||
public WrittenBookContent(@NotNull List<Component> pages, @NotNull String title, @NotNull String author, int generation, boolean resolved) {
|
||||
this(pages.stream().map(page -> new FilteredText<>(page, null)).toList(), new FilteredText<>(title, null), author, generation, resolved);
|
||||
public WrittenBookContent(@NotNull String title, @NotNull String author, int generation, @NotNull List<Component> pages, boolean 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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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.PlayerConnection;
|
||||
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.LoginPluginResponse;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -33,7 +33,6 @@ import java.net.*;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
@ -56,7 +55,7 @@ public final class LoginListener {
|
||||
socketConnection.UNSAFE_setLoginUsername(packet.username());
|
||||
// Velocity support
|
||||
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));
|
||||
return;
|
||||
}
|
||||
@ -171,14 +170,13 @@ public final class LoginListener {
|
||||
return MojangCrypt.decryptByteToSecretKey(MojangAuth.getKeyPair().getPrivate(), sharedSecret);
|
||||
}
|
||||
|
||||
private static void handleVelocityProxyResponse(PlayerSocketConnection socketConnection, LoginPluginResponse response) {
|
||||
byte[] data = response.getPayload();
|
||||
|
||||
private static void handleVelocityProxyResponse(PlayerSocketConnection socketConnection, LoginPlugin.Response response) {
|
||||
final byte[] data = response.payload();
|
||||
SocketAddress socketAddress = null;
|
||||
GameProfile gameProfile = null;
|
||||
boolean success = false;
|
||||
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);
|
||||
if (success) {
|
||||
// Get the real connection address
|
||||
@ -191,7 +189,7 @@ public final class LoginListener {
|
||||
}
|
||||
final int port = ((java.net.InetSocketAddress) socketConnection.getRemoteAddress()).getPort();
|
||||
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.minestom.server.entity.Player;
|
||||
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.Nullable;
|
||||
|
||||
@ -50,7 +50,7 @@ public final class Messenger {
|
||||
*/
|
||||
public static void sendMessage(@NotNull Collection<Player> players, @NotNull Component message,
|
||||
@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));
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,8 @@ public final class ConnectionManager {
|
||||
private CachedPacket getDefaultTags() {
|
||||
var defaultTags = this.defaultTags;
|
||||
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;
|
||||
}
|
||||
|
@ -1,8 +1,28 @@
|
||||
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 {
|
||||
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;
|
||||
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
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.utils.Direction;
|
||||
import net.minestom.server.utils.Unit;
|
||||
import net.minestom.server.utils.nbt.BinaryTagReader;
|
||||
import net.minestom.server.utils.nbt.BinaryTagWriter;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import net.minestom.server.utils.crypto.KeyUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import javax.crypto.Cipher;
|
||||
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.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
public final class NetworkBuffer {
|
||||
public static final Type<Unit> UNIT = new NetworkBufferTypeImpl.UnitType();
|
||||
public static final Type<Boolean> BOOLEAN = new NetworkBufferTypeImpl.BooleanType();
|
||||
public static final Type<Byte> BYTE = new NetworkBufferTypeImpl.ByteType();
|
||||
public static final Type<Short> SHORT = new NetworkBufferTypeImpl.ShortType();
|
||||
public static final Type<Integer> UNSIGNED_SHORT = new NetworkBufferTypeImpl.UnsignedShortType();
|
||||
public static final Type<Integer> INT = new NetworkBufferTypeImpl.IntType();
|
||||
public static final Type<Long> LONG = new NetworkBufferTypeImpl.LongType();
|
||||
public static final Type<Float> FLOAT = new NetworkBufferTypeImpl.FloatType();
|
||||
public static final Type<Double> DOUBLE = new NetworkBufferTypeImpl.DoubleType();
|
||||
public static final Type<Integer> VAR_INT = new NetworkBufferTypeImpl.VarIntType();
|
||||
public static final Type<Long> VAR_LONG = new NetworkBufferTypeImpl.VarLongType();
|
||||
public static final Type<byte[]> RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType();
|
||||
public static final Type<String> STRING = new NetworkBufferTypeImpl.StringType();
|
||||
public static final Type<String> STRING_TERMINATED = new NetworkBufferTypeImpl.StringTerminatedType();
|
||||
public static final Type<BinaryTag> NBT = new NetworkBufferTypeImpl.NbtType();
|
||||
public static final Type<Point> BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType();
|
||||
public static final Type<Component> COMPONENT = new ComponentNetworkBufferTypeImpl();
|
||||
public static final Type<Component> JSON_COMPONENT = new NetworkBufferTypeImpl.JsonComponentType();
|
||||
public static final Type<UUID> UUID = new NetworkBufferTypeImpl.UUIDType();
|
||||
public static final Type<Pos> POS = new NetworkBufferTypeImpl.PosType();
|
||||
public sealed interface NetworkBuffer permits NetworkBufferImpl {
|
||||
Type<Unit> UNIT = new NetworkBufferTypeImpl.UnitType();
|
||||
Type<Boolean> BOOLEAN = new NetworkBufferTypeImpl.BooleanType();
|
||||
Type<Byte> BYTE = new NetworkBufferTypeImpl.ByteType();
|
||||
Type<Short> SHORT = new NetworkBufferTypeImpl.ShortType();
|
||||
Type<Integer> UNSIGNED_SHORT = new NetworkBufferTypeImpl.UnsignedShortType();
|
||||
Type<Integer> INT = new NetworkBufferTypeImpl.IntType();
|
||||
Type<Long> LONG = new NetworkBufferTypeImpl.LongType();
|
||||
Type<Float> FLOAT = new NetworkBufferTypeImpl.FloatType();
|
||||
Type<Double> DOUBLE = new NetworkBufferTypeImpl.DoubleType();
|
||||
Type<Integer> VAR_INT = new NetworkBufferTypeImpl.VarIntType();
|
||||
Type<Integer> VAR_INT_3 = new NetworkBufferTypeImpl.VarInt3Type();
|
||||
Type<Long> VAR_LONG = new NetworkBufferTypeImpl.VarLongType();
|
||||
Type<byte[]> RAW_BYTES = new NetworkBufferTypeImpl.RawBytesType(-1);
|
||||
Type<String> STRING = new NetworkBufferTypeImpl.StringType();
|
||||
Type<String> STRING_TERMINATED = new NetworkBufferTypeImpl.StringTerminatedType();
|
||||
Type<BinaryTag> NBT = new NetworkBufferTypeImpl.NbtType();
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
Type<CompoundBinaryTag> NBT_COMPOUND = (Type) new NetworkBufferTypeImpl.NbtType();
|
||||
Type<Point> BLOCK_POSITION = new NetworkBufferTypeImpl.BlockPositionType();
|
||||
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();
|
||||
public static final Type<long[]> LONG_ARRAY = new NetworkBufferTypeImpl.LongArrayType();
|
||||
public static final Type<int[]> VAR_INT_ARRAY = new NetworkBufferTypeImpl.VarIntArrayType();
|
||||
public static final Type<long[]> VAR_LONG_ARRAY = new NetworkBufferTypeImpl.VarLongArrayType();
|
||||
Type<byte[]> BYTE_ARRAY = new NetworkBufferTypeImpl.ByteArrayType();
|
||||
Type<long[]> LONG_ARRAY = new NetworkBufferTypeImpl.LongArrayType();
|
||||
Type<int[]> VAR_INT_ARRAY = new NetworkBufferTypeImpl.VarIntArrayType();
|
||||
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);
|
||||
}
|
||||
|
||||
// METADATA
|
||||
public static final Type<int[]> VILLAGER_DATA = new NetworkBufferTypeImpl.VillagerDataType();
|
||||
public static final Type<Point> VECTOR3 = new NetworkBufferTypeImpl.Vector3Type();
|
||||
public static final Type<Point> VECTOR3D = new NetworkBufferTypeImpl.Vector3DType();
|
||||
public static final Type<float[]> QUATERNION = new NetworkBufferTypeImpl.QuaternionType();
|
||||
Type<int[]> VILLAGER_DATA = new NetworkBufferTypeImpl.VillagerDataType();
|
||||
Type<Point> VECTOR3 = new NetworkBufferTypeImpl.Vector3Type();
|
||||
Type<Point> VECTOR3D = new NetworkBufferTypeImpl.Vector3DType();
|
||||
Type<Point> VECTOR3B = new NetworkBufferTypeImpl.Vector3BType();
|
||||
Type<float[]> QUATERNION = new NetworkBufferTypeImpl.QuaternionType();
|
||||
|
||||
public static final Type<@Nullable Component> OPT_CHAT = Optional(COMPONENT);
|
||||
public static final Type<@Nullable Point> OPT_BLOCK_POSITION = Optional(BLOCK_POSITION);
|
||||
public static final Type<@Nullable UUID> OPT_UUID = Optional(UUID);
|
||||
Type<@Nullable Component> OPT_CHAT = COMPONENT.optional();
|
||||
Type<@Nullable Point> OPT_BLOCK_POSITION = BLOCK_POSITION.optional();
|
||||
Type<@Nullable UUID> OPT_UUID = UUID.optional();
|
||||
|
||||
public static final Type<Direction> DIRECTION = new NetworkBufferTypeImpl.EnumType<>(Direction.class);
|
||||
public static final Type<EntityPose> POSE = new NetworkBufferTypeImpl.EnumType<>(EntityPose.class);
|
||||
Type<Direction> DIRECTION = Enum(Direction.class);
|
||||
Type<EntityPose> POSE = Enum(EntityPose.class);
|
||||
|
||||
// Combinators
|
||||
|
||||
public static <T> @NotNull Type<@Nullable T> Optional(@NotNull Type<T> type) {
|
||||
return new NetworkBufferTypeImpl.OptionalType<>(type);
|
||||
static <E extends Enum<E>> @NotNull Type<E> Enum(@NotNull Class<E> enumClass) {
|
||||
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) {
|
||||
return new NetworkBufferTypeImpl.EnumType<>(enumClass);
|
||||
static <E extends Enum<E>> @NotNull Type<EnumSet<E>> EnumSet(@NotNull Class<E> 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);
|
||||
}
|
||||
|
||||
<T> void write(@NotNull Type<T> type, @UnknownNullability T value);
|
||||
|
||||
ByteBuffer nioBuffer;
|
||||
final boolean resizable;
|
||||
int writeIndex;
|
||||
int readIndex;
|
||||
<T> @UnknownNullability T read(@NotNull Type<T> type);
|
||||
|
||||
BinaryTagWriter nbtWriter;
|
||||
BinaryTagReader nbtReader;
|
||||
<T> void writeAt(long index, @NotNull Type<T> type, @UnknownNullability T value);
|
||||
|
||||
// In the future, this should be passed as a parameter.
|
||||
final Registries registries = MinecraftServer.process();
|
||||
<T> @UnknownNullability T readAt(long index, @NotNull Type<T> type);
|
||||
|
||||
public NetworkBuffer(@NotNull ByteBuffer buffer, boolean resizable) {
|
||||
this.nioBuffer = buffer.order(ByteOrder.BIG_ENDIAN);
|
||||
this.resizable = resizable;
|
||||
void copyTo(long srcOffset, byte @NotNull [] dest, long destOffset, long length);
|
||||
|
||||
this.writeIndex = buffer.position();
|
||||
this.readIndex = buffer.position();
|
||||
byte @NotNull [] extractBytes(@NotNull Consumer<@NotNull NetworkBuffer> extractor);
|
||||
|
||||
@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) {
|
||||
this(buffer, true);
|
||||
}
|
||||
int readChannel(ReadableByteChannel channel) throws IOException;
|
||||
|
||||
public NetworkBuffer(int initialCapacity) {
|
||||
this(ByteBuffer.allocateDirect(initialCapacity), true);
|
||||
}
|
||||
boolean writeChannel(SocketChannel channel) throws IOException;
|
||||
|
||||
public NetworkBuffer() {
|
||||
this(1024);
|
||||
}
|
||||
void cipher(Cipher cipher, long start, long length);
|
||||
|
||||
public <T> void write(@NotNull Type<T> type, @NotNull T value) {
|
||||
type.write(this, value);
|
||||
}
|
||||
long compress(long start, long length, NetworkBuffer output);
|
||||
|
||||
public <T> void write(@NotNull Writer writer) {
|
||||
writer.write(this);
|
||||
}
|
||||
long decompress(long start, long length, NetworkBuffer output) throws DataFormatException;
|
||||
|
||||
public <T> @NotNull T read(@NotNull Type<T> type) {
|
||||
return type.read(this);
|
||||
}
|
||||
@Nullable Registries registries();
|
||||
|
||||
public <T> void writeOptional(@NotNull Type<T> type, @Nullable T value) {
|
||||
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> {
|
||||
interface Type<T> {
|
||||
void write(@NotNull NetworkBuffer buffer, T value);
|
||||
|
||||
T read(@NotNull NetworkBuffer buffer);
|
||||
|
||||
default <S> @NotNull Type<S> map(@NotNull Function<T, S> to, @NotNull Function<S, T> from) {
|
||||
return new NetworkBufferTypeImpl.MappedType<>(this, to, from);
|
||||
default long sizeOf(@NotNull T value, @Nullable Registries registries) {
|
||||
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) {
|
||||
return new NetworkBufferTypeImpl.ListType<>(this, maxSize);
|
||||
}
|
||||
|
||||
default @NotNull Type<List<T>> list() {
|
||||
return list(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
default @NotNull Type<T> optional() {
|
||||
return new NetworkBufferTypeImpl.OptionalTypeImpl<>(this);
|
||||
return new NetworkBufferTypeImpl.OptionalType<>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Writer {
|
||||
void write(@NotNull NetworkBuffer writer);
|
||||
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
|
||||
public interface Reader<T> {
|
||||
@NotNull T read(@NotNull NetworkBuffer reader);
|
||||
interface AutoResize {
|
||||
AutoResize DOUBLE = (capacity, targetSize) -> Math.max(capacity * 2, targetSize);
|
||||
|
||||
long resize(long capacity, long targetSize);
|
||||
}
|
||||
|
||||
public static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) {
|
||||
NetworkBuffer writer = new NetworkBuffer();
|
||||
writing.accept(writer);
|
||||
byte[] bytes = new byte[writer.writeIndex];
|
||||
writer.copyTo(0, bytes, 0, bytes.length);
|
||||
return bytes;
|
||||
static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing, @Nullable Registries registries) {
|
||||
NetworkBuffer buffer = resizableBuffer(256, registries);
|
||||
writing.accept(buffer);
|
||||
return buffer.read(RAW_BYTES);
|
||||
}
|
||||
|
||||
static byte[] makeArray(@NotNull Consumer<@NotNull NetworkBuffer> writing) {
|
||||
return makeArray(writing, null);
|
||||
}
|
||||
|
||||
static <T> byte[] makeArray(@NotNull Type<T> type, @NotNull T value, @Nullable Registries registries) {
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
return new NetworkBufferTypeImpl<>() {
|
||||
@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;
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
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.ProtocolObject;
|
||||
import net.minestom.server.registry.Registries;
|
||||
@ -20,12 +20,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.*;
|
||||
import static net.minestom.server.network.NetworkBufferImpl.impl;
|
||||
|
||||
interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
int SEGMENT_BITS = 0x7F;
|
||||
@ -45,15 +45,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record BooleanType() implements NetworkBufferTypeImpl<Boolean> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Boolean value) {
|
||||
buffer.ensureSize(1);
|
||||
buffer.nioBuffer.put(buffer.writeIndex(), value ? (byte) 1 : (byte) 0);
|
||||
buffer.writeIndex += 1;
|
||||
buffer.ensureWritable(1);
|
||||
impl(buffer)._putByte(buffer.writeIndex(), value ? (byte) 1 : (byte) 0);
|
||||
buffer.advanceWrite(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean read(@NotNull NetworkBuffer buffer) {
|
||||
final byte value = buffer.nioBuffer.get(buffer.readIndex());
|
||||
buffer.readIndex += 1;
|
||||
final byte value = impl(buffer)._getByte(buffer.readIndex());
|
||||
buffer.advanceRead(1);
|
||||
return value == 1;
|
||||
}
|
||||
}
|
||||
@ -61,15 +61,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record ByteType() implements NetworkBufferTypeImpl<Byte> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Byte value) {
|
||||
buffer.ensureSize(1);
|
||||
buffer.nioBuffer.put(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 1;
|
||||
buffer.ensureWritable(1);
|
||||
impl(buffer)._putByte(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte read(@NotNull NetworkBuffer buffer) {
|
||||
final byte value = buffer.nioBuffer.get(buffer.readIndex());
|
||||
buffer.readIndex += 1;
|
||||
final byte value = impl(buffer)._getByte(buffer.readIndex());
|
||||
buffer.advanceRead(1);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -77,15 +77,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record ShortType() implements NetworkBufferTypeImpl<Short> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Short value) {
|
||||
buffer.ensureSize(2);
|
||||
buffer.nioBuffer.putShort(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 2;
|
||||
buffer.ensureWritable(2);
|
||||
impl(buffer)._putShort(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short read(@NotNull NetworkBuffer buffer) {
|
||||
final short value = buffer.nioBuffer.getShort(buffer.readIndex());
|
||||
buffer.readIndex += 2;
|
||||
final short value = impl(buffer)._getShort(buffer.readIndex());
|
||||
buffer.advanceRead(2);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -93,15 +93,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record UnsignedShortType() implements NetworkBufferTypeImpl<Integer> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
||||
buffer.ensureSize(2);
|
||||
buffer.nioBuffer.putShort(buffer.writeIndex(), (short) (value & 0xFFFF));
|
||||
buffer.writeIndex += 2;
|
||||
buffer.ensureWritable(2);
|
||||
impl(buffer)._putShort(buffer.writeIndex(), (short) (value & 0xFFFF));
|
||||
buffer.advanceWrite(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||
final short value = buffer.nioBuffer.getShort(buffer.readIndex());
|
||||
buffer.readIndex += 2;
|
||||
final short value = impl(buffer)._getShort(buffer.readIndex());
|
||||
buffer.advanceRead(2);
|
||||
return value & 0xFFFF;
|
||||
}
|
||||
}
|
||||
@ -109,15 +109,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record IntType() implements NetworkBufferTypeImpl<Integer> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Integer value) {
|
||||
buffer.ensureSize(4);
|
||||
buffer.nioBuffer.putInt(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 4;
|
||||
buffer.ensureWritable(4);
|
||||
impl(buffer)._putInt(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer read(@NotNull NetworkBuffer buffer) {
|
||||
final int value = buffer.nioBuffer.getInt(buffer.readIndex());
|
||||
buffer.readIndex += 4;
|
||||
final int value = impl(buffer)._getInt(buffer.readIndex());
|
||||
buffer.advanceRead(4);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -125,15 +125,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record LongType() implements NetworkBufferTypeImpl<Long> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
||||
buffer.ensureSize(8);
|
||||
buffer.nioBuffer.putLong(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 8;
|
||||
buffer.ensureWritable(8);
|
||||
impl(buffer)._putLong(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long read(@NotNull NetworkBuffer buffer) {
|
||||
final long value = buffer.nioBuffer.getLong(buffer.readIndex());
|
||||
buffer.readIndex += 8;
|
||||
final long value = impl(buffer)._getLong(buffer.readIndex());
|
||||
buffer.advanceRead(8);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -141,15 +141,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record FloatType() implements NetworkBufferTypeImpl<Float> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Float value) {
|
||||
buffer.ensureSize(4);
|
||||
buffer.nioBuffer.putFloat(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 4;
|
||||
buffer.ensureWritable(4);
|
||||
impl(buffer)._putFloat(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float read(@NotNull NetworkBuffer buffer) {
|
||||
final float value = buffer.nioBuffer.getFloat(buffer.readIndex());
|
||||
buffer.readIndex += 4;
|
||||
final float value = impl(buffer)._getFloat(buffer.readIndex());
|
||||
buffer.advanceRead(4);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -157,15 +157,15 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record DoubleType() implements NetworkBufferTypeImpl<Double> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Double value) {
|
||||
buffer.ensureSize(8);
|
||||
buffer.nioBuffer.putDouble(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += 8;
|
||||
buffer.ensureWritable(8);
|
||||
impl(buffer)._putDouble(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double read(@NotNull NetworkBuffer buffer) {
|
||||
final double value = buffer.nioBuffer.getDouble(buffer.readIndex());
|
||||
buffer.readIndex += 8;
|
||||
final double value = impl(buffer)._getDouble(buffer.readIndex());
|
||||
buffer.advanceRead(8);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -173,67 +173,73 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record VarIntType() implements NetworkBufferTypeImpl<Integer> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Integer boxed) {
|
||||
final int value = boxed;
|
||||
final int index = buffer.writeIndex();
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
buffer.ensureSize(1);
|
||||
buffer.nioBuffer.put(index, (byte) value);
|
||||
buffer.writeIndex += 1;
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
buffer.ensureSize(2);
|
||||
buffer.nioBuffer.putShort(index, (short) ((value & 0x7F | 0x80) << 8 | (value >>> 7)));
|
||||
buffer.writeIndex += 2;
|
||||
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
||||
buffer.ensureSize(3);
|
||||
var nio = buffer.nioBuffer;
|
||||
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;
|
||||
buffer.ensureWritable(5);
|
||||
long index = buffer.writeIndex();
|
||||
int value = boxed;
|
||||
var nio = impl(buffer);
|
||||
while (true) {
|
||||
if ((value & ~SEGMENT_BITS) == 0) {
|
||||
nio._putByte(index++, (byte) value);
|
||||
buffer.advanceWrite(index - buffer.writeIndex());
|
||||
return;
|
||||
}
|
||||
nio._putByte(index++, (byte) ((byte) (value & SEGMENT_BITS) | CONTINUE_BIT));
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
int result = 0;
|
||||
for (int shift = 0; ; shift += 7) {
|
||||
byte b = buffer.nioBuffer.get(index++);
|
||||
byte b = impl(buffer)._getByte(index++);
|
||||
result |= (b & 0x7f) << shift;
|
||||
if (b >= 0) {
|
||||
buffer.readIndex += index - buffer.readIndex();
|
||||
buffer.advanceRead(index - buffer.readIndex());
|
||||
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> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, Long value) {
|
||||
buffer.ensureSize(10);
|
||||
buffer.ensureWritable(10);
|
||||
int size = 0;
|
||||
while (true) {
|
||||
if ((value & ~((long) SEGMENT_BITS)) == 0) {
|
||||
buffer.nioBuffer.put(buffer.writeIndex() + size, (byte) value.intValue());
|
||||
buffer.writeIndex += size + 1;
|
||||
impl(buffer)._putByte(buffer.writeIndex() + size, (byte) value.intValue());
|
||||
buffer.advanceWrite(size + 1);
|
||||
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++;
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
@ -247,34 +253,44 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
int position = 0;
|
||||
byte currentByte;
|
||||
while (true) {
|
||||
currentByte = buffer.nioBuffer.get(buffer.readIndex() + length);
|
||||
currentByte = impl(buffer)._getByte(buffer.readIndex() + length);
|
||||
length++;
|
||||
value |= (long) (currentByte & SEGMENT_BITS) << position;
|
||||
if ((currentByte & CONTINUE_BIT) == 0) break;
|
||||
position += 7;
|
||||
if (position >= 64) throw new RuntimeException("VarLong is too big");
|
||||
}
|
||||
buffer.readIndex += length;
|
||||
buffer.advanceRead(length);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
record RawBytesType() implements NetworkBufferTypeImpl<byte[]> {
|
||||
record RawBytesType(int length) implements NetworkBufferTypeImpl<byte[]> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, byte[] value) {
|
||||
buffer.ensureSize(value.length);
|
||||
buffer.nioBuffer.put(buffer.writeIndex(), value);
|
||||
buffer.writeIndex += value.length;
|
||||
if (length != -1 && value.length != length) {
|
||||
throw new IllegalArgumentException("Invalid length: " + value.length + " != " + length);
|
||||
}
|
||||
final int length = value.length;
|
||||
if (length == 0) return;
|
||||
buffer.ensureWritable(length);
|
||||
impl(buffer)._putBytes(buffer.writeIndex(), value);
|
||||
buffer.advanceWrite(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] read(@NotNull NetworkBuffer buffer) {
|
||||
final int limit = buffer.nioBuffer.limit();
|
||||
final int length = limit - buffer.readIndex();
|
||||
assert length >= 0 : "Invalid remaining: " + length;
|
||||
final byte[] bytes = new byte[length];
|
||||
buffer.nioBuffer.get(buffer.readIndex(), bytes);
|
||||
buffer.readIndex += length;
|
||||
long length = buffer.readableBytes();
|
||||
if (this.length != -1) {
|
||||
length = Math.min(length, this.length);
|
||||
}
|
||||
if (length == 0) return new byte[0];
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -283,18 +299,12 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, String value) {
|
||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
buffer.write(VAR_INT, bytes.length);
|
||||
buffer.write(RAW_BYTES, bytes);
|
||||
buffer.write(BYTE_ARRAY, bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String read(@NotNull NetworkBuffer buffer) {
|
||||
final int length = buffer.read(VAR_INT);
|
||||
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;
|
||||
final byte[] bytes = buffer.read(BYTE_ARRAY);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@ -323,7 +333,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
record NbtType() implements NetworkBufferTypeImpl<BinaryTag> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, BinaryTag value) {
|
||||
BinaryTagWriter nbtWriter = buffer.nbtWriter;
|
||||
BinaryTagWriter nbtWriter = impl(buffer).nbtWriter;
|
||||
if (nbtWriter == null) {
|
||||
nbtWriter = new BinaryTagWriter(new DataOutputStream(new OutputStream() {
|
||||
@Override
|
||||
@ -331,7 +341,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
buffer.write(BYTE, (byte) b);
|
||||
}
|
||||
}));
|
||||
buffer.nbtWriter = nbtWriter;
|
||||
impl(buffer).nbtWriter = nbtWriter;
|
||||
}
|
||||
try {
|
||||
nbtWriter.writeNameless(value);
|
||||
@ -342,7 +352,7 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
|
||||
@Override
|
||||
public BinaryTag read(@NotNull NetworkBuffer buffer) {
|
||||
BinaryTagReader nbtReader = buffer.nbtReader;
|
||||
BinaryTagReader nbtReader = impl(buffer).nbtReader;
|
||||
if (nbtReader == null) {
|
||||
nbtReader = new BinaryTagReader(new DataInputStream(new InputStream() {
|
||||
@Override
|
||||
@ -352,10 +362,10 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
|
||||
@Override
|
||||
public int available() {
|
||||
return buffer.readableBytes();
|
||||
return (int) buffer.readableBytes();
|
||||
}
|
||||
}));
|
||||
buffer.nbtReader = nbtReader;
|
||||
impl(buffer).nbtReader = nbtReader;
|
||||
}
|
||||
try {
|
||||
return nbtReader.readNameless();
|
||||
@ -447,10 +457,10 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
@Override
|
||||
public byte[] read(@NotNull NetworkBuffer buffer) {
|
||||
final int length = buffer.read(VAR_INT);
|
||||
final byte[] bytes = new byte[length];
|
||||
buffer.nioBuffer.get(buffer.readIndex(), bytes);
|
||||
buffer.readIndex += length;
|
||||
return bytes;
|
||||
if (length == 0) return new byte[0];
|
||||
final long remaining = buffer.readableBytes();
|
||||
Check.argCondition(length > remaining, "String is too long (length: {0}, readable: {1})", length, remaining);
|
||||
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> {
|
||||
@Override
|
||||
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[]> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, float[] value) {
|
||||
@ -601,27 +616,61 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
|
||||
// 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
|
||||
public void write(@NotNull NetworkBuffer buffer, E value) {
|
||||
buffer.writeEnum(enumClass, value);
|
||||
public void write(@NotNull NetworkBuffer buffer, EnumSet<E> 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
|
||||
public E read(@NotNull NetworkBuffer buffer) {
|
||||
return buffer.readEnum(enumClass);
|
||||
public EnumSet<E> read(@NotNull NetworkBuffer buffer) {
|
||||
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> {
|
||||
@Override
|
||||
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
|
||||
public T read(@NotNull NetworkBuffer buffer) {
|
||||
return buffer.readOptional(parent);
|
||||
return buffer.read(BOOLEAN) ? buffer.read(parent) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,8 +695,8 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
}
|
||||
}
|
||||
|
||||
record MappedType<T, S>(@NotNull Type<T> parent, @NotNull Function<T, S> to,
|
||||
@NotNull Function<S, T> from) implements NetworkBufferTypeImpl<S> {
|
||||
record TransformType<T, S>(@NotNull Type<T> parent, @NotNull Function<T, S> to,
|
||||
@NotNull Function<S, T> from) implements NetworkBufferTypeImpl<S> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, S value) {
|
||||
parent.write(buffer, from.apply(value));
|
||||
@ -659,27 +708,51 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
}
|
||||
}
|
||||
|
||||
record ListType<T>(@NotNull Type<T> parent, int maxSize) implements NetworkBufferTypeImpl<List<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, List<T> value) {
|
||||
buffer.writeCollection(parent, value);
|
||||
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 List<T> read(@NotNull NetworkBuffer buffer) {
|
||||
return buffer.readCollection(parent, maxSize);
|
||||
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 OptionalTypeImpl<T>(@NotNull Type<T> parent) implements NetworkBufferTypeImpl<T> {
|
||||
record ListType<T>(@NotNull Type<T> parent, int maxSize) implements NetworkBufferTypeImpl<List<T>> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, T value) {
|
||||
buffer.writeOptional(parent, value);
|
||||
public void write(@NotNull NetworkBuffer buffer, List<T> values) {
|
||||
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
|
||||
public T read(@NotNull NetworkBuffer buffer) {
|
||||
return buffer.readOptional(parent);
|
||||
public List<T> read(@NotNull NetworkBuffer buffer) {
|
||||
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);
|
||||
return List.of(values);
|
||||
}
|
||||
}
|
||||
|
||||
@ -687,23 +760,31 @@ interface NetworkBufferTypeImpl<T> extends NetworkBuffer.Type<T> {
|
||||
@NotNull Function<Registries, DynamicRegistry<T>> selector) implements NetworkBufferTypeImpl<DynamicRegistry.Key<T>> {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, DynamicRegistry.Key<T> value) {
|
||||
Check.stateCondition(buffer.registries == null, "Buffer does not have registries");
|
||||
final DynamicRegistry<T> registry = selector.apply(buffer.registries);
|
||||
final Registries registries = impl(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.
|
||||
// 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);
|
||||
buffer.write(VAR_INT, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicRegistry.Key<T> read(@NotNull NetworkBuffer buffer) {
|
||||
Check.stateCondition(buffer.registries == null, "Buffer does not have registries");
|
||||
DynamicRegistry<T> registry = selector.apply(buffer.registries);
|
||||
final Registries registries = impl(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 DynamicRegistry.Key<T> key = registry.getKey(id);
|
||||
Check.argCondition(key == null, "No such ID in registry: {0} > {1}", registry, id);
|
||||
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> {
|
||||
|
||||
@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,
|
||||
int packetId, @NotNull NetworkBuffer buffer) {
|
||||
@ -32,20 +32,20 @@ public sealed interface PacketParser<T> {
|
||||
|
||||
default @NotNull PacketRegistry<T> stateRegistry(@NotNull ConnectionState connectionState) {
|
||||
return switch (connectionState) {
|
||||
case HANDSHAKE -> handshakeRegistry();
|
||||
case STATUS -> statusRegistry();
|
||||
case LOGIN -> loginRegistry();
|
||||
case CONFIGURATION -> configurationRegistry();
|
||||
case PLAY -> playRegistry();
|
||||
case HANDSHAKE -> handshake();
|
||||
case STATUS -> status();
|
||||
case LOGIN -> login();
|
||||
case CONFIGURATION -> configuration();
|
||||
case PLAY -> play();
|
||||
};
|
||||
}
|
||||
|
||||
record Client(
|
||||
PacketRegistry<ClientPacket> handshakeRegistry,
|
||||
PacketRegistry<ClientPacket> statusRegistry,
|
||||
PacketRegistry<ClientPacket> loginRegistry,
|
||||
PacketRegistry<ClientPacket> configurationRegistry,
|
||||
PacketRegistry<ClientPacket> playRegistry
|
||||
PacketRegistry<ClientPacket> handshake,
|
||||
PacketRegistry<ClientPacket> status,
|
||||
PacketRegistry<ClientPacket> login,
|
||||
PacketRegistry<ClientPacket> configuration,
|
||||
PacketRegistry<ClientPacket> play
|
||||
) implements PacketParser<ClientPacket> {
|
||||
public Client() {
|
||||
this(
|
||||
@ -59,11 +59,11 @@ public sealed interface PacketParser<T> {
|
||||
}
|
||||
|
||||
record Server(
|
||||
PacketRegistry<ServerPacket> handshakeRegistry,
|
||||
PacketRegistry<ServerPacket> statusRegistry,
|
||||
PacketRegistry<ServerPacket> loginRegistry,
|
||||
PacketRegistry<ServerPacket> configurationRegistry,
|
||||
PacketRegistry<ServerPacket> playRegistry
|
||||
PacketRegistry<ServerPacket> handshake,
|
||||
PacketRegistry<ServerPacket> status,
|
||||
PacketRegistry<ServerPacket> login,
|
||||
PacketRegistry<ServerPacket> configuration,
|
||||
PacketRegistry<ServerPacket> play
|
||||
) implements PacketParser<ServerPacket> {
|
||||
public Server() {
|
||||
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
|
||||
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> {
|
||||
@ -155,7 +161,9 @@ public interface PacketRegistry<T> {
|
||||
|
||||
final class ServerHandshake extends Server {
|
||||
public ServerHandshake() {
|
||||
super();
|
||||
super(
|
||||
// Empty
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,16 +344,15 @@ public interface PacketRegistry<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
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<>() {
|
||||
@Override
|
||||
protected PacketInfo<T> computeValue(@NotNull Class<?> type) {
|
||||
for (int i = 0; i < suppliers.length; i++) {
|
||||
final Entry<? extends T> entry = suppliers[i];
|
||||
if (entry != null && entry.type == type) {
|
||||
//noinspection unchecked
|
||||
return new PacketInfo<T>(entry.type, i, (NetworkBuffer.Type<T>) entry.reader);
|
||||
for (PacketInfo<? extends T> info : suppliers) {
|
||||
if (info != null && info.packetClass == type) {
|
||||
return (PacketInfo<T>) info;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Packet type " + type + " isn't registered!");
|
||||
@ -354,26 +361,39 @@ public interface PacketRegistry<T> {
|
||||
|
||||
@SafeVarargs
|
||||
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) {
|
||||
final Entry<? extends T> entry = suppliers[packetId];
|
||||
if (entry == null)
|
||||
throw new IllegalStateException("Packet id 0x" + Integer.toHexString(packetId) + " isn't registered!");
|
||||
final NetworkBuffer.Type<? extends T> supplier = entry.reader;
|
||||
final PacketInfo<T> info = packetInfo(packetId);
|
||||
final NetworkBuffer.Type<T> supplier = info.serializer;
|
||||
final T packet = supplier.read(reader);
|
||||
if (packet == null) {
|
||||
throw new IllegalStateException("Packet " + entry.type + " failed to read!");
|
||||
throw new IllegalStateException("Packet " + info.packetClass + " failed to read!");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketInfo<T> packetInfo(Class<? extends T> packetClass) {
|
||||
public PacketInfo<T> packetInfo(@NotNull Class<?> 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) {
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents a packet received from a client.
|
||||
* <p>
|
||||
* Packets are value-based, and should therefore not be reliant on identity.
|
||||
*/
|
||||
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,
|
||||
"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 static final NetworkBuffer.Type<ClientKeepAlivePacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||
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 static final NetworkBuffer.Type<ClientPingRequestPacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||
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 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.STRING;
|
||||
|
||||
@ -18,4 +21,17 @@ public record ClientPluginMessagePacket(@NotNull String channel, byte[] data) im
|
||||
if (channel.length() > 256)
|
||||
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.minestom.server.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.network.packet.client.ClientPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.minestom.server.network.NetworkBuffer.UUID;
|
||||
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
|
||||
|
||||
public record ClientResourcePackStatusPacket(
|
||||
@NotNull UUID id,
|
||||
@NotNull ResourcePackStatus status
|
||||
) implements ClientPacket {
|
||||
public static NetworkBuffer.Type<ClientResourcePackStatusPacket> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, ClientResourcePackStatusPacket value) {
|
||||
buffer.write(NetworkBuffer.UUID, value.id);
|
||||
buffer.write(NetworkBuffer.VAR_INT, statusId(value.status));
|
||||
}
|
||||
public static final NetworkBuffer.Type<ClientResourcePackStatusPacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||
UUID, ClientResourcePackStatusPacket::id,
|
||||
VAR_INT.transform(ClientResourcePackStatusPacket::readStatus, ClientResourcePackStatusPacket::statusId), ClientResourcePackStatusPacket::status,
|
||||
ClientResourcePackStatusPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public ClientResourcePackStatusPacket read(@NotNull NetworkBuffer buffer) {
|
||||
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) {
|
||||
private static @NotNull ResourcePackStatus readStatus(int id) {
|
||||
return switch (id) {
|
||||
case 0 -> ResourcePackStatus.SUCCESSFULLY_LOADED;
|
||||
case 1 -> ResourcePackStatus.DECLINED;
|
||||
case 2 -> ResourcePackStatus.FAILED_DOWNLOAD;
|
||||
@ -35,7 +31,7 @@ public record ClientResourcePackStatusPacket(
|
||||
case 5 -> ResourcePackStatus.INVALID_URL;
|
||||
case 6 -> ResourcePackStatus.FAILED_RELOAD;
|
||||
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.network.NetworkBuffer;
|
||||
import net.minestom.server.network.NetworkBufferTemplate;
|
||||
import net.minestom.server.network.packet.client.ClientPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -11,40 +12,19 @@ public record ClientHandshakePacket(int protocolVersion, @NotNull String serverA
|
||||
int serverPort, @NotNull Intent intent) implements ClientPacket {
|
||||
|
||||
public ClientHandshakePacket {
|
||||
if (serverAddress.length() > getMaxHandshakeLength()) {
|
||||
if (serverAddress.length() > maxHandshakeLength()) {
|
||||
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
|
||||
}
|
||||
}
|
||||
|
||||
public static NetworkBuffer.Type<ClientHandshakePacket> SERIALIZER = new NetworkBuffer.Type<>() {
|
||||
@Override
|
||||
public void write(@NotNull NetworkBuffer buffer, ClientHandshakePacket value) {
|
||||
buffer.write(VAR_INT, value.protocolVersion);
|
||||
int maxLength = getMaxHandshakeLength();
|
||||
if (value.serverAddress.length() > maxLength) {
|
||||
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());
|
||||
}
|
||||
public static final NetworkBuffer.Type<ClientHandshakePacket> SERIALIZER = NetworkBufferTemplate.template(
|
||||
VAR_INT, ClientHandshakePacket::protocolVersion,
|
||||
STRING, ClientHandshakePacket::serverAddress,
|
||||
UNSIGNED_SHORT, ClientHandshakePacket::serverPort,
|
||||
VAR_INT.transform(Intent::fromId, Intent::id), ClientHandshakePacket::intent,
|
||||
ClientHandshakePacket::new);
|
||||
|
||||
@Override
|
||||
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() {
|
||||
private static int maxHandshakeLength() {
|
||||
// BungeeGuard limits handshake length to 2500 characters, while vanilla limits it to 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::encryptedVerifyToken,
|
||||
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