From b716d8a47a3397397683202c43a10a6ee5859670 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Sat, 15 Aug 2020 04:05:15 +0200 Subject: [PATCH 1/4] Comments + cleanup --- .../minestom/server/instance/Explosion.java | 6 +- .../server/instance/ExplosionSupplier.java | 6 +- .../instance/MinestomBasicChunkLoader.java | 3 +- .../minestom/server/instance/WorldBorder.java | 26 +++- .../server/network/PacketWriterUtils.java | 40 +++++- .../network/player/NettyPlayerConnection.java | 115 +++++++++--------- .../minestom/server/utils/PacketUtils.java | 15 +++ 7 files changed, 138 insertions(+), 73 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Explosion.java b/src/main/java/net/minestom/server/instance/Explosion.java index 0753c6d3c..b10da5581 100644 --- a/src/main/java/net/minestom/server/instance/Explosion.java +++ b/src/main/java/net/minestom/server/instance/Explosion.java @@ -1,6 +1,7 @@ package net.minestom.server.instance; import net.minestom.server.instance.block.Block; +import net.minestom.server.network.PacketWriterUtils; import net.minestom.server.network.packet.server.play.ExplosionPacket; import net.minestom.server.utils.BlockPosition; @@ -78,9 +79,8 @@ public abstract class Explosion { postExplosion(instance, blocks, packet); - instance.getPlayers().forEach(player -> { - player.sendPacketToViewersAndSelf(packet); - }); + // TODO send only to close players + PacketWriterUtils.writeAndSend(instance.getPlayers(), packet); postSend(instance, blocks); } diff --git a/src/main/java/net/minestom/server/instance/ExplosionSupplier.java b/src/main/java/net/minestom/server/instance/ExplosionSupplier.java index 1c295d6a2..a3d5b3ae3 100644 --- a/src/main/java/net/minestom/server/instance/ExplosionSupplier.java +++ b/src/main/java/net/minestom/server/instance/ExplosionSupplier.java @@ -8,9 +8,9 @@ public interface ExplosionSupplier { /** * Creates a new explosion * - * @param centerX center of the explosion - * @param centerY center of the explosion - * @param centerZ center of the explosion + * @param centerX center X of the explosion + * @param centerY center Y of the explosion + * @param centerZ center Z of the explosion * @param strength strength of the explosion * @param additionalData data passed via {@link Instance#explode)}. Can be null * @return Explosion object representing the algorithm to use diff --git a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java index a7135cdd9..28254babc 100644 --- a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java @@ -28,8 +28,9 @@ public class MinestomBasicChunkLoader implements IChunkLoader { final int chunkZ = chunk.getChunkZ(); try { + final String key = getChunkKey(chunkX, chunkZ); final byte[] data = chunk.getSerializedData(); - storageFolder.set(getChunkKey(chunkX, chunkZ), data); + storageFolder.set(key, data); if (callback != null) callback.run(); diff --git a/src/main/java/net/minestom/server/instance/WorldBorder.java b/src/main/java/net/minestom/server/instance/WorldBorder.java index a099d13ca..a1bc0a505 100644 --- a/src/main/java/net/minestom/server/instance/WorldBorder.java +++ b/src/main/java/net/minestom/server/instance/WorldBorder.java @@ -2,6 +2,7 @@ package net.minestom.server.instance; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; +import net.minestom.server.network.PacketWriterUtils; import net.minestom.server.network.packet.server.play.WorldBorderPacket; import net.minestom.server.utils.Position; @@ -59,6 +60,11 @@ public class WorldBorder { return centerX; } + /** + * Change the center X of the world border + * + * @param centerX the new center X + */ public void setCenterX(float centerX) { this.centerX = centerX; refreshCenter(); @@ -73,6 +79,11 @@ public class WorldBorder { return centerZ; } + /** + * Change the center Z of the world border + * + * @param centerZ the new center Z + */ public void setCenterZ(float centerZ) { this.centerZ = centerZ; refreshCenter(); @@ -220,6 +231,11 @@ public class WorldBorder { } } + /** + * Send the world border init packet to a player + * + * @param player the player to send the packet to + */ protected void init(Player player) { WorldBorderPacket worldBorderPacket = new WorldBorderPacket(); worldBorderPacket.action = WorldBorderPacket.Action.INITIALIZE; @@ -237,6 +253,9 @@ public class WorldBorder { return instance; } + /** + * Send the new world border centers to all instance players + */ private void refreshCenter() { WorldBorderPacket worldBorderPacket = new WorldBorderPacket(); worldBorderPacket.action = WorldBorderPacket.Action.SET_CENTER; @@ -244,8 +263,13 @@ public class WorldBorder { sendPacket(worldBorderPacket); } + /** + * Send a {@link WorldBorderPacket} to all the instance players + * + * @param worldBorderPacket the packet to send + */ private void sendPacket(WorldBorderPacket worldBorderPacket) { - instance.getPlayers().forEach(player -> player.getPlayerConnection().sendPacket(worldBorderPacket)); + PacketWriterUtils.writeAndSend(instance.getPlayers(), worldBorderPacket); } public enum CollisionAxis { diff --git a/src/main/java/net/minestom/server/network/PacketWriterUtils.java b/src/main/java/net/minestom/server/network/PacketWriterUtils.java index 27eb51615..2adcab42b 100644 --- a/src/main/java/net/minestom/server/network/PacketWriterUtils.java +++ b/src/main/java/net/minestom/server/network/PacketWriterUtils.java @@ -13,19 +13,34 @@ import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; -public class PacketWriterUtils { +/** + * Utils class used to write packets in the appropriate thread pool + */ +public final class PacketWriterUtils { - private static ExecutorService batchesPool = new MinestomThread(MinecraftServer.THREAD_COUNT_PACKET_WRITER, MinecraftServer.THREAD_NAME_PACKET_WRITER); + private static final ExecutorService PACKET_WRITER_POOL = new MinestomThread(MinecraftServer.THREAD_COUNT_PACKET_WRITER, MinecraftServer.THREAD_NAME_PACKET_WRITER); + /** + * Write the packet in the writer thread pool + * + * @param serverPacket the packet to write + * @param consumer the consumer called once the packet has been written + */ public static void writeCallbackPacket(ServerPacket serverPacket, Consumer consumer) { - batchesPool.execute(() -> { + PACKET_WRITER_POOL.execute(() -> { final ByteBuf buffer = PacketUtils.writePacket(serverPacket); consumer.accept(buffer); }); } + /** + * Write a packet in the writer thread pool and send it to every players in {@code players} + * + * @param players the players list to send the packet to + * @param serverPacket the packet to write and send + */ public static void writeAndSend(Collection players, ServerPacket serverPacket) { - batchesPool.execute(() -> { + PACKET_WRITER_POOL.execute(() -> { if (players.isEmpty()) return; @@ -42,14 +57,27 @@ public class PacketWriterUtils { }); } + /** + * Write a packet and send it to a player connection + * + * @param playerConnection the connection to send the packet to + * @param serverPacket the packet to write and send + */ public static void writeAndSend(PlayerConnection playerConnection, ServerPacket serverPacket) { - batchesPool.execute(() -> { + PACKET_WRITER_POOL.execute(() -> { playerConnection.sendPacket(serverPacket); }); } + /** + * Write a packet and send it to a player + * + * @param player the player to send the packet to + * @param serverPacket the packet to write and send + */ public static void writeAndSend(Player player, ServerPacket serverPacket) { - writeAndSend(player.getPlayerConnection(), serverPacket); + final PlayerConnection playerConnection = player.getPlayerConnection(); + writeAndSend(playerConnection, serverPacket); } } diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 9d4fbe78c..9055ba074 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -2,11 +2,11 @@ package net.minestom.server.network.player; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; import lombok.Getter; import net.minestom.server.extras.mojangAuth.Decrypter; import net.minestom.server.extras.mojangAuth.Encrypter; import net.minestom.server.extras.mojangAuth.MojangCrypt; -import io.netty.channel.socket.SocketChannel; import net.minestom.server.network.netty.codec.PacketCompressor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.login.SetCompressionPacket; @@ -20,79 +20,76 @@ import java.net.SocketAddress; */ public class NettyPlayerConnection extends PlayerConnection { - private final SocketChannel channel; - @Getter - private boolean encrypted = false; - @Getter - private boolean compressed = false; + private final SocketChannel channel; + @Getter + private boolean encrypted = false; + @Getter + private boolean compressed = false; - public NettyPlayerConnection(SocketChannel channel) { - super(); - this.channel = channel; - } + public NettyPlayerConnection(SocketChannel channel) { + super(); + this.channel = channel; + } - public void setEncryptionKey(SecretKey secretKey) { - this.encrypted = true; - getChannel().pipeline().addBefore("framer", "decrypt", new Decrypter(MojangCrypt.getCipher(2, secretKey))); - getChannel().pipeline().addBefore("framer", "encrypt", new Encrypter(MojangCrypt.getCipher(1, secretKey))); - } + public void setEncryptionKey(SecretKey secretKey) { + this.encrypted = true; + getChannel().pipeline().addBefore("framer", "decrypt", new Decrypter(MojangCrypt.getCipher(2, secretKey))); + getChannel().pipeline().addBefore("framer", "encrypt", new Encrypter(MojangCrypt.getCipher(1, secretKey))); + } - @Override + @Override public void enableCompression(int threshold) { - this.compressed = true; + this.compressed = true; sendPacket(new SetCompressionPacket(threshold)); channel.pipeline().addAfter("framer", "compressor", new PacketCompressor(threshold)); } @Override - public void sendPacket(ByteBuf buffer, boolean copy) { - //System.out.println(getConnectionState() + " out"); - if ((encrypted || compressed) && copy) { - buffer = buffer.copy(); - buffer.retain(); - channel.writeAndFlush(buffer); - buffer.release(); - } else { - getChannel().writeAndFlush(buffer); - } - } + public void sendPacket(ByteBuf buffer, boolean copy) { + if ((encrypted || compressed) && copy) { + buffer = buffer.copy(); + buffer.retain(); + channel.writeAndFlush(buffer); + buffer.release(); + } else { + getChannel().writeAndFlush(buffer); + } + } - @Override - public void writePacket(ByteBuf buffer, boolean copy) { - //System.out.println(getConnectionState() + " out"); - if ((encrypted || compressed) && copy) { - buffer = buffer.copy(); - buffer.retain(); - channel.write(buffer); - buffer.release(); - } else { - getChannel().write(buffer); - } - } + @Override + public void writePacket(ByteBuf buffer, boolean copy) { + if ((encrypted || compressed) && copy) { + buffer = buffer.copy(); + buffer.retain(); + channel.write(buffer); + buffer.release(); + } else { + getChannel().write(buffer); + } + } - @Override - public void sendPacket(ServerPacket serverPacket) { - //System.out.println(serverPacket.getClass().getName() + " out"); + @Override + public void sendPacket(ServerPacket serverPacket) { channel.writeAndFlush(serverPacket); - } + } - @Override - public void flush() { - getChannel().flush(); - } + @Override + public void flush() { + getChannel().flush(); + } - @Override - public SocketAddress getRemoteAddress() { - return getChannel().remoteAddress(); - } + @Override + public SocketAddress getRemoteAddress() { + return getChannel().remoteAddress(); + } - @Override - public void disconnect() { - getChannel().close(); - } + @Override + public void disconnect() { + getChannel().close(); + } - public Channel getChannel() { - return channel; - } + public Channel getChannel() { + return channel; + } } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 5c82581b5..e7f3bd242 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -5,12 +5,21 @@ import io.netty.buffer.Unpooled; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.server.ServerPacket; +/** + * Class used to write packets + */ public final class PacketUtils { private PacketUtils() { } + /** + * Write a {@link ServerPacket} into a {@link ByteBuf} + * + * @param buf the recipient of {@code packet} + * @param packet the packet to write into {@code buf} + */ public static void writePacket(ByteBuf buf, ServerPacket packet) { PacketWriter writer = new PacketWriter(); @@ -19,6 +28,12 @@ public final class PacketUtils { buf.writeBytes(writer.toByteArray()); } + /** + * Write a {@link ServerPacket} into a newly created {@link ByteBuf} + * + * @param packet the packet to write + * @return a {@link ByteBuf} containing {@code packet} + */ public static ByteBuf writePacket(ServerPacket packet) { ByteBuf buffer = Unpooled.buffer(); From 92f914ef2d40269606a4849e4bc0becf241b5d77 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Sat, 15 Aug 2020 13:32:36 +0200 Subject: [PATCH 2/4] Cleanup --- .../net/minestom/server/MinecraftServer.java | 161 ++++++++++++++++-- .../net/minestom/server/entity/Entity.java | 11 +- .../minestom/server/instance/Instance.java | 22 +++ .../server/instance/InstanceContainer.java | 7 + .../server/instance/InstanceManager.java | 83 ++++++++- .../instance/MinestomBasicChunkLoader.java | 7 + .../network/netty/channel/ClientChannel.java | 5 +- .../network/player/PlayerConnection.java | 35 +++- 8 files changed, 301 insertions(+), 30 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index c8ee70346..fe291a08f 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -9,9 +9,10 @@ import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.command.CommandManager; import net.minestom.server.data.DataManager; +import net.minestom.server.data.DataType; +import net.minestom.server.data.SerializableData; import net.minestom.server.entity.EntityManager; import net.minestom.server.entity.EntityType; -import net.minestom.server.entity.Player; import net.minestom.server.extras.mojangAuth.MojangCrypt; import net.minestom.server.fluids.Fluid; import net.minestom.server.gamedata.loottables.LootTableManager; @@ -20,6 +21,8 @@ import net.minestom.server.instance.Biome; import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockManager; +import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.instance.block.rule.BlockPlacementRule; import net.minestom.server.item.Enchantment; import net.minestom.server.item.Material; import net.minestom.server.listener.manager.PacketListenerManager; @@ -80,10 +83,10 @@ public class MinecraftServer { public static final int CHUNK_VIEW_DISTANCE = 10; public static final int ENTITY_VIEW_DISTANCE = 5; public static final int COMPRESSION_THRESHOLD = 256; - // Can be modified at performance cost when decreased + // Can be modified at performance cost when increased + public static final int TICK_PER_SECOND = 20; private static final int MS_TO_SEC = 1000; - public static final int TICK_MS = MS_TO_SEC / 20; - public static final int TICK_PER_SECOND = MS_TO_SEC / TICK_MS; + public static final int TICK_MS = MS_TO_SEC / TICK_PER_SECOND; //Extras @Getter @@ -91,7 +94,6 @@ public class MinecraftServer { private static boolean fixLighting = true; //Rate Limiting - @Getter @Setter private static int rateLimit = 0; // Networking @@ -208,96 +210,227 @@ public class MinecraftServer { PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), brandMessage); } + /** + * Get the max number of packets a client can send over 1 second + * + * @return the packet count limit over 1 second + */ + public static int getRateLimit() { + return rateLimit; + } + + /** + * Change the number of packet a client can send over 1 second without being disconnected + * + * @param rateLimit the number of packet, 0 to disable + */ + public static void setRateLimit(int rateLimit) { + MinecraftServer.rateLimit = rateLimit; + } + + /** + * Get the server difficulty showed in game option + * + * @return the server difficulty + */ public static Difficulty getDifficulty() { return difficulty; } + /** + * Change the server difficulty and send the appropriate packet to all connected clients + * + * @param difficulty the new server difficulty + */ public static void setDifficulty(Difficulty difficulty) { MinecraftServer.difficulty = difficulty; - for (Player player : connectionManager.getOnlinePlayers()) { - ServerDifficultyPacket serverDifficultyPacket = new ServerDifficultyPacket(); - serverDifficultyPacket.difficulty = difficulty; - serverDifficultyPacket.locked = true; - player.getPlayerConnection().sendPacket(serverDifficultyPacket); - } + + // The difficulty packet + ServerDifficultyPacket serverDifficultyPacket = new ServerDifficultyPacket(); + serverDifficultyPacket.difficulty = difficulty; + serverDifficultyPacket.locked = true; // Can only be modified on singleplayer + // Send the packet to all online players + PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), serverDifficultyPacket); } + /** + * Get the manager handling all incoming packets + * + * @return the packet listener manager + */ public static PacketListenerManager getPacketListenerManager() { return packetListenerManager; } + /** + * Get the netty server + * + * @return the netty server + */ public static NettyServer getNettyServer() { return nettyServer; } + /** + * Get the manager handling all registered instances + * + * @return the instance manager + */ public static InstanceManager getInstanceManager() { return instanceManager; } + /** + * Get the manager handling {@link CustomBlock} and {@link BlockPlacementRule} + * + * @return the block manager + */ public static BlockManager getBlockManager() { return blockManager; } + /** + * Get the manager handling waiting players + * + * @return the entity manager + */ public static EntityManager getEntityManager() { return entityManager; } + /** + * Get the manager handling commands + * + * @return the command manager + */ public static CommandManager getCommandManager() { return commandManager; } + /** + * Get the manager handling recipes show to the clients + * + * @return the recipe manager + */ public static RecipeManager getRecipeManager() { return recipeManager; } + /** + * Get the manager handling storage + * + * @return the storage manager + */ public static StorageManager getStorageManager() { return storageManager; } + /** + * Get the manager handling {@link DataType} used by {@link SerializableData} + * + * @return the data manager + */ public static DataManager getDataManager() { return dataManager; } + /** + * Get the manager handling teams + * + * @return the team manager + */ public static TeamManager getTeamManager() { return teamManager; } + /** + * Get the manager handling scheduled tasks + * + * @return the scheduler manager + */ public static SchedulerManager getSchedulerManager() { return schedulerManager; } + /** + * Get the manager handling server monitoring + * + * @return the benchmark manager + */ public static BenchmarkManager getBenchmarkManager() { return benchmarkManager; } + /** + * Get the manager handling server connections + * + * @return the connection manager + */ public static ConnectionManager getConnectionManager() { return connectionManager; } + /** + * Get the consumer executed to show server-list data + * + * @return the response data consumer + */ public static ResponseDataConsumer getResponseDataConsumer() { return responseDataConsumer; } + /** + * Get the manager handling loot tables + * + * @return the loot table manager + */ public static LootTableManager getLootTableManager() { return lootTableManager; } + /** + * Get the manager handling dimensions + * + * @return the dimension manager + */ public static DimensionTypeManager getDimensionTypeManager() { return dimensionTypeManager; } + /** + * Get the manager handling advancements + * + * @return the advancement manager + */ public static AdvancementManager getAdvancementManager() { return advancementManager; } + /** + * Get the manager handling tags + * + * @return the tag manager + */ public static TagManager getTagManager() { return tagManager; } + /** + * Get the manager handling the server ticks + * + * @return the update manager + */ public static UpdateManager getUpdateManager() { return updateManager; } + /** + * Start the server + * + * @param address the server address + * @param port the server port + * @param responseDataConsumer the response data consumer, can be null + */ public void start(String address, int port, ResponseDataConsumer responseDataConsumer) { LOGGER.info("Starting Minestom server."); MinecraftServer.responseDataConsumer = responseDataConsumer; @@ -306,6 +439,12 @@ public class MinecraftServer { LOGGER.info("Minestom server started successfully."); } + /** + * Start the server + * + * @param address the server address + * @param port the server port + */ public void start(String address, int port) { start(address, port, null); } diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 242f8be4d..6e4ff7561 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -14,7 +14,10 @@ import net.minestom.server.event.entity.EntitySpawnEvent; import net.minestom.server.event.entity.EntityTickEvent; import net.minestom.server.event.entity.EntityVelocityEvent; import net.minestom.server.event.handler.EventHandler; -import net.minestom.server.instance.*; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.InstanceManager; +import net.minestom.server.instance.WorldBorder; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.server.play.*; @@ -634,13 +637,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { * * @param instance the new instance of the entity * @throws NullPointerException if {@code instance} is null - * @throws IllegalStateException if {@code instance} has not been registered in - * {@link InstanceManager#createInstanceContainer()} or - * {@link InstanceManager#createSharedInstance(InstanceContainer)} + * @throws IllegalStateException if {@code instance} has not been registered in {@link InstanceManager} */ public void setInstance(Instance instance) { Check.notNull(instance, "instance cannot be null!"); - Check.stateCondition(!MinecraftServer.getInstanceManager().getInstances().contains(instance), + Check.stateCondition(!instance.isRegistered(), "Instances need to be registered with InstanceManager#createInstanceContainer or InstanceManager#createSharedInstance"); if (this.instance != null) { diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index fbca1d2da..a86c8274d 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -49,6 +49,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager(); + private boolean registered; + private DimensionType dimensionType; private WorldBorder worldBorder; @@ -306,6 +308,26 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta // + /** + * Get if the instance has been registered in {@link InstanceManager} + * + * @return true if the instance has been registered + */ + public boolean isRegistered() { + return registered; + } + + /** + * Change the registered field + *

+ * WARNING: should only be used by {@link InstanceManager} + * + * @param registered true to mark the instance as registered + */ + protected void setRegistered(boolean registered) { + this.registered = registered; + } + /** * Get the instance dimension * diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 0bf10a310..b7b812ceb 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -518,6 +518,13 @@ public class InstanceContainer extends Instance { return position.getY() < -64; } + /** + * Assign a {@link SharedInstance} to this container + *

+ * Only used by {@link InstanceManager} + * + * @param sharedInstance the shared instance to assign to this container + */ protected void addSharedInstance(SharedInstance sharedInstance) { this.sharedInstances.add(sharedInstance); } diff --git a/src/main/java/net/minestom/server/instance/InstanceManager.java b/src/main/java/net/minestom/server/instance/InstanceManager.java index 3fda23367..fe8d298fd 100644 --- a/src/main/java/net/minestom/server/instance/InstanceManager.java +++ b/src/main/java/net/minestom/server/instance/InstanceManager.java @@ -5,54 +5,119 @@ import net.minestom.server.utils.validate.Check; import net.minestom.server.world.DimensionType; import java.util.Collections; -import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; +/** + * Used to register instances + */ public final class InstanceManager { - private Set instances = Collections.synchronizedSet(new HashSet<>()); + private final Set instances = new CopyOnWriteArraySet<>(); - public InstanceContainer createInstanceContainer(InstanceContainer instanceContainer) { - this.instances.add(instanceContainer); + /** + * Register an {@link InstanceContainer} + * + * @param instanceContainer the instance to register + * @return the registered {@link InstanceContainer} + */ + public InstanceContainer registerInstanceContainer(InstanceContainer instanceContainer) { + registerInstance(instanceContainer); return instanceContainer; } + /** + * Create and register an {@link InstanceContainer} with the specified dimension and storage folder + * + * @param dimensionType the dimension of the instance + * @param storageFolder the storage folder of the instance, can be null + * @return the created {@link InstanceContainer} + */ public InstanceContainer createInstanceContainer(DimensionType dimensionType, StorageFolder storageFolder) { - InstanceContainer instance = new InstanceContainer(UUID.randomUUID(), dimensionType, storageFolder); - return createInstanceContainer(instance); + final InstanceContainer instance = new InstanceContainer(UUID.randomUUID(), dimensionType, storageFolder); + return registerInstanceContainer(instance); } + /** + * Create and register an {@link InstanceContainer} with the specified storage folder + * + * @param storageFolder the storage folder of the instance, can be null + * @return the created {@link InstanceContainer} + */ public InstanceContainer createInstanceContainer(StorageFolder storageFolder) { return createInstanceContainer(DimensionType.OVERWORLD, storageFolder); } + /** + * Create and register an {@link InstanceContainer} with the specified dimension + * + * @param dimensionType the dimension of the instance + * @return the created {@link InstanceContainer} + */ public InstanceContainer createInstanceContainer(DimensionType dimensionType) { return createInstanceContainer(dimensionType, null); } + /** + * Create and register an {@link InstanceContainer} + * + * @return the created {@link InstanceContainer} + */ public InstanceContainer createInstanceContainer() { return createInstanceContainer(DimensionType.OVERWORLD); } - public SharedInstance createSharedInstance(SharedInstance sharedInstance) { + /** + * Register a {@link SharedInstance} + *

+ * WARNING: the shared instance needs to have an {@link InstanceContainer} assigned to it + * + * @param sharedInstance the instance to register + * @return the registered {@link SharedInstance} + * @throws NullPointerException if the shared instance doesn't have an {@link InstanceContainer} assigned to it + */ + public SharedInstance registerSharedInstance(SharedInstance sharedInstance) { final InstanceContainer instanceContainer = sharedInstance.getInstanceContainer(); Check.notNull(instanceContainer, "SharedInstance needs to have an InstanceContainer to be created!"); instanceContainer.addSharedInstance(sharedInstance); - this.instances.add(sharedInstance); + registerInstance(sharedInstance); return sharedInstance; } + /** + * Create and register a {@link SharedInstance} + * + * @param instanceContainer the container assigned to the shared instance + * @return the created {@link SharedInstance} + * @throws IllegalStateException if {@code instanceContainer} is not registered + */ public SharedInstance createSharedInstance(InstanceContainer instanceContainer) { Check.notNull(instanceContainer, "Instance container cannot be null when creating a SharedInstance!"); + Check.stateCondition(!instanceContainer.isRegistered(), "The container needs to be register in the InstanceManager"); final SharedInstance sharedInstance = new SharedInstance(UUID.randomUUID(), instanceContainer); - return createSharedInstance(sharedInstance); + return registerSharedInstance(sharedInstance); } + /** + * Get all the registered instances + * + * @return an unmodifiable set containing all the registered instances + */ public Set getInstances() { return Collections.unmodifiableSet(instances); } + /** + * Register the instance internally + * + * @param instance the instance to register + */ + private void registerInstance(Instance instance) { + instance.setRegistered(true); + this.instances.add(instance); + } + } diff --git a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java index 28254babc..0bf6b11f1 100644 --- a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java @@ -52,6 +52,13 @@ public class MinestomBasicChunkLoader implements IChunkLoader { } } + /** + * Get the chunk key used by the {@link StorageFolder} + * + * @param chunkX the chunk X + * @param chunkZ the chunk Z + * @return the chunk key + */ private static String getChunkKey(int chunkX, int chunkZ) { return chunkX + "." + chunkZ; } diff --git a/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java b/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java index dc4edbbb2..1f21e2fb1 100644 --- a/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java +++ b/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java @@ -20,7 +20,7 @@ public class ClientChannel extends SimpleChannelInboundHandler { @Override public void channelActive(ChannelHandlerContext ctx) { - System.out.println("CONNECTION"); + //System.out.println("CONNECTION"); } @Override @@ -28,7 +28,7 @@ public class ClientChannel extends SimpleChannelInboundHandler { try { packetProcessor.process(ctx, packet); } finally { - int availableBytes = packet.body.readableBytes(); + final int availableBytes = packet.body.readableBytes(); if (availableBytes > 0) { // TODO log4j2 @@ -44,6 +44,7 @@ public class ClientChannel extends SimpleChannelInboundHandler { public void channelInactive(ChannelHandlerContext ctx) { PlayerConnection playerConnection = packetProcessor.getPlayerConnection(ctx); if (playerConnection != null) { + // Remove the connection playerConnection.refreshOnline(false); Player player = playerConnection.getPlayer(); if (player != null) { diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index f37ded7f2..d19a8d2ec 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import lombok.Getter; import lombok.Setter; import net.minestom.server.MinecraftServer; +import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.Player; import net.minestom.server.network.ConnectionState; @@ -32,11 +33,13 @@ public abstract class PlayerConnection { private ConnectionState connectionState; private boolean online; - private static final ColoredText rateLimitKickMessage = ColoredText.of("Too Many Packets"); + // Text used to kick client sending too many packets + private static final ColoredText rateLimitKickMessage = ColoredText.of(ChatColor.RED + "Too Many Packets"); //Connection Stats @Getter private final AtomicInteger packetCounter = new AtomicInteger(0); + private final AtomicInteger lastPacketCounter = new AtomicInteger(0); private short tickCounter = 0; public PlayerConnection() { @@ -44,14 +47,21 @@ public abstract class PlayerConnection { this.connectionState = ConnectionState.UNKNOWN; } + /** + * Update values related to the network connection + */ public void updateStats() { + // Check rate limit if (MinecraftServer.getRateLimit() > 0) { tickCounter++; - if (tickCounter % 20 == 0 && tickCounter > 0) { + if (tickCounter % MinecraftServer.TICK_PER_SECOND == 0 && tickCounter > 0) { tickCounter = 0; + // Retrieve the packet count final int count = packetCounter.get(); - packetCounter.set(0); + this.lastPacketCounter.set(count); + this.packetCounter.set(0); if (count > MinecraftServer.getRateLimit()) { + // Sent too many packets if (connectionState == ConnectionState.LOGIN) { sendPacket(new LoginDisconnect("Too Many Packets")); } else { @@ -96,6 +106,11 @@ public abstract class PlayerConnection { */ public abstract void flush(); + /** + * Get the remote address of the client + * + * @return the remote address + */ public abstract SocketAddress getRemoteAddress(); /** @@ -140,7 +155,21 @@ public abstract class PlayerConnection { this.connectionState = connectionState; } + /** + * Get the client connection state + * + * @return the client connection state + */ public ConnectionState getConnectionState() { return connectionState; } + + /** + * Get the number of packet the client sent over the last second + * + * @return the number of packet sent over the last second + */ + public int getLastPacketCounter() { + return lastPacketCounter.get(); + } } From 939d06cfb56c06bbe87712a5878726f7339796b9 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Sat, 15 Aug 2020 13:38:57 +0200 Subject: [PATCH 3/4] Added Player#sendPluginMessage --- .../net/minestom/server/entity/Player.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index e948c69f3..e5c0c127b 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -604,6 +604,29 @@ public class Player extends LivingEntity implements CommandSender { } } + /** + * Send a plugin message to the player + * + * @param channel the message channel + * @param data the message data + */ + public void sendPluginMessage(String channel, byte[] data) { + PluginMessagePacket pluginMessagePacket = new PluginMessagePacket(); + pluginMessagePacket.channel = channel; + pluginMessagePacket.data = data; + playerConnection.sendPacket(pluginMessagePacket); + } + + /** + * Send a plugin message to the player + * + * @param channel the message channel + * @param message the message + */ + public void sendPluginMessage(String channel, String message) { + sendPluginMessage(channel, message.getBytes()); + } + /** * Send a {@link BlockBreakAnimationPacket} packet to the player and his viewers * Setting {@code destroyStage} to -1 resets the break animation From ab86400e80c9a90f7242a626223de1f42fd3cf17 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Sat, 15 Aug 2020 13:42:27 +0200 Subject: [PATCH 4/4] Fix Player#sendPluginMessage --- src/main/java/net/minestom/server/entity/Player.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index e5c0c127b..9a8bffb00 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -624,7 +624,13 @@ public class Player extends LivingEntity implements CommandSender { * @param message the message */ public void sendPluginMessage(String channel, String message) { - sendPluginMessage(channel, message.getBytes()); + // Write the data + PacketWriter writer = new PacketWriter(); + writer.writeSizedString(message); + // Retrieve the data + final byte[] data = writer.toByteArray(); + + sendPluginMessage(channel, data); } /**