From 2e0c5a72b75c2191f8e52d97563bcb32f0a07492 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 2 Nov 2020 02:53:12 +0100 Subject: [PATCH 01/55] Added optional additional data in Permission#isValidFor --- .../server/command/CommandSender.java | 29 +++++++++++++++---- .../server/permission/BasicPermission.java | 5 ++-- .../server/permission/Permission.java | 19 +++++++----- src/test/java/demo/Main.java | 8 ++--- src/test/java/demo/commands/TestCommand.java | 1 + .../java/permissions/TestPermissions.java | 22 +++++++------- 6 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/minestom/server/command/CommandSender.java b/src/main/java/net/minestom/server/command/CommandSender.java index 40c2807ec..a8602093d 100644 --- a/src/main/java/net/minestom/server/command/CommandSender.java +++ b/src/main/java/net/minestom/server/command/CommandSender.java @@ -3,6 +3,7 @@ package net.minestom.server.command; import net.minestom.server.entity.Player; import net.minestom.server.permission.Permission; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -63,19 +64,23 @@ public interface CommandSender { * Simple shortcut to
getAllPermissions().contains(permission) && permission.isValidFor(this)
for readability. * * @param p permission to check against - * @return true if the sender has the permission and validate {@link Permission#isValidFor(CommandSender)} + * @return true if the sender has the permission and validate {@link Permission#isValidFor(CommandSender, Object)} */ default boolean hasPermission(@NotNull Permission p) { - return getAllPermissions().contains(p) && p.isValidFor(this); + return hasPermission(p, null); + } + + default boolean hasPermission(@NotNull Permission p, @Nullable T data) { + return getAllPermissions().contains(p) && p.isValidFor(this, data); } /** * Checks if the given {@link Permission} is possessed by this command sender. - * Will call {@link Permission#isValidFor(CommandSender)} on all permissions that are an instance of {@code permissionClass}. + * Will call {@link Permission#isValidFor(CommandSender, Object)} on all permissions that are an instance of {@code permissionClass}. * If no matching permission is found, this result returns false. * * @param permissionClass the permission class to check - * @return true if the sender has the permission and validate {@link Permission#isValidFor(CommandSender)} + * @return true if the sender has the permission and validate {@link Permission#isValidFor(CommandSender, Object)} * @see #getAllPermissions() */ default boolean hasPermission(@NotNull Class permissionClass) { @@ -84,7 +89,21 @@ public interface CommandSender { for (Permission p : getAllPermissions()) { if (permissionClass.isInstance(p)) { foundPerm = true; - result &= p.isValidFor(this); + result &= p.isValidFor(this, null); + } + } + if (!foundPerm) + return false; + return result; + } + + default boolean hasPermission(@NotNull Class> permissionClass, @Nullable T data) { + boolean result = true; + boolean foundPerm = false; + for (Permission p : getAllPermissions()) { + if (permissionClass.isInstance(p)) { + foundPerm = true; + result &= p.isValidFor(this, data); } } if (!foundPerm) diff --git a/src/main/java/net/minestom/server/permission/BasicPermission.java b/src/main/java/net/minestom/server/permission/BasicPermission.java index 168f74fa4..779fbc9c7 100644 --- a/src/main/java/net/minestom/server/permission/BasicPermission.java +++ b/src/main/java/net/minestom/server/permission/BasicPermission.java @@ -1,14 +1,15 @@ package net.minestom.server.permission; import net.minestom.server.command.CommandSender; +import org.jetbrains.annotations.NotNull; /** * Basic {@link Permission} implementation that only requires the permission to be given to the {@link CommandSender} to be considered applied * (eg. no arguments) */ -public class BasicPermission implements Permission { +public class BasicPermission implements Permission { @Override - public boolean isValidFor(CommandSender commandSender) { + public boolean isValidFor(@NotNull CommandSender commandSender, Object data) { return true; } } diff --git a/src/main/java/net/minestom/server/permission/Permission.java b/src/main/java/net/minestom/server/permission/Permission.java index 04a95e63c..b5cebae82 100644 --- a/src/main/java/net/minestom/server/permission/Permission.java +++ b/src/main/java/net/minestom/server/permission/Permission.java @@ -6,10 +6,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * Representation of a permission granted to a {@link CommandSender} + * Representation of a permission granted to a {@link CommandSender}. + * + * @param the type of data that this permission can handle in {@link #isValidFor(CommandSender, Object)}. + * Used if you want to allow passing additional data to check if the permission is valid in a certain situation, + * you can default it to {@link Object} if you do not need it. */ @FunctionalInterface -public interface Permission { +public interface Permission { /** * Does the given {@link CommandSender} have the permission represented by this object? @@ -18,22 +22,23 @@ public interface Permission { * have this permission and validate the condition in this method. * * @param commandSender the command sender + * @param data the optional data (eg the number of home possible, placing a block at X position) * @return true if the commandSender possesses this permission */ - boolean isValidFor(CommandSender commandSender); + boolean isValidFor(@NotNull CommandSender commandSender, @Nullable T data); /** - * Writes any required data for this permission inside the given destination + * Writes any required data for this permission inside the given destination. * - * @param destination {@link Data} to write to + * @param destination the {@link Data} to write to */ default void write(@NotNull Data destination) { } /** - * Reads any required data for this permission from the given destination + * Reads any required data for this permission from the given destination. * - * @param source {@link Data} to read from + * @param source the {@link Data} to read from * @return this for chaining */ default Permission read(@Nullable Data source) { diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index b841c74b2..36c883119 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -3,7 +3,7 @@ package demo; import demo.blocks.BurningTorchBlock; import demo.blocks.StoneBlock; import demo.blocks.UpdatableBlockDemo; -import demo.commands.TestCommand; +import demo.commands.*; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; import net.minestom.server.instance.block.BlockManager; @@ -27,14 +27,14 @@ public class Main { blockManager.registerBlockPlacementRule(new RedstonePlacementRule()); CommandManager commandManager = MinecraftServer.getCommandManager(); - //commandManager.register(new EntitySelectorCommand()); + commandManager.register(new EntitySelectorCommand()); commandManager.register(new TestCommand()); - /*commandManager.register(new HealthCommand()); + commandManager.register(new HealthCommand()); commandManager.register(new SimpleCommand()); commandManager.register(new GamemodeCommand()); commandManager.register(new DimensionCommand()); commandManager.register(new ShutdownCommand()); - commandManager.register(new TeleportCommand());*/ + commandManager.register(new TeleportCommand()); StorageManager storageManager = MinecraftServer.getStorageManager(); diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index db426ad50..219ddc972 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -22,6 +22,7 @@ public class TestCommand extends Command { setDefaultExecutor((source, args) -> { System.out.println("DEFAULT"); + System.gc(); }); addSyntax((source, args) -> { diff --git a/src/test/java/permissions/TestPermissions.java b/src/test/java/permissions/TestPermissions.java index e2b87e416..a66152fcb 100644 --- a/src/test/java/permissions/TestPermissions.java +++ b/src/test/java/permissions/TestPermissions.java @@ -2,11 +2,9 @@ package permissions; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; -import net.minestom.server.data.Data; import net.minestom.server.entity.Player; import net.minestom.server.permission.Permission; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,7 +24,8 @@ public class TestPermissions { MinecraftServer.init(); // for entity manager player = new Player(UUID.randomUUID(), "TestPlayer", null) { @Override - protected void playerConnectionInit() {} + protected void playerConnectionInit() { + } @Override public boolean isOnline() { @@ -40,15 +39,16 @@ public class TestPermissions { assertFalse(player.hasPermission(Permission.class)); } - class PermTest1 implements Permission { + class PermTest1 implements Permission { @Override - public boolean isValidFor(CommandSender commandSender) { + public boolean isValidFor(@NotNull CommandSender commandSender, Object data) { return true; } } - class PermTest2 implements Permission { + + class PermTest2 implements Permission { @Override - public boolean isValidFor(CommandSender commandSender) { + public boolean isValidFor(@NotNull CommandSender commandSender, Object data) { return true; } } @@ -65,7 +65,7 @@ public class TestPermissions { assertTrue(player.hasPermission(PermTest2.class)); } - class BooleanPerm implements Permission { + class BooleanPerm implements Permission { private final boolean value; BooleanPerm(boolean v) { @@ -73,7 +73,7 @@ public class TestPermissions { } @Override - public boolean isValidFor(CommandSender commandSender) { + public boolean isValidFor(@NotNull CommandSender commandSender, Object data) { return value; } } @@ -88,9 +88,9 @@ public class TestPermissions { @Test public void singlePermission() { - Permission p = commandSender -> true; + Permission p = (commandSender, data) -> true; player.addPermission(p); - assertTrue(p.isValidFor(player)); + assertTrue(p.isValidFor(player, null)); assertTrue(player.hasPermission(p)); assertTrue(player.hasPermission(Permission.class)); } From 1c6070a1b22ecf80defcc75ee682ba849b0affc0 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 2 Nov 2020 04:13:43 +0100 Subject: [PATCH 02/55] Added an unknown command callback --- .../server/command/CommandManager.java | 29 ++++++++++++++++++- .../utils/callback/CommandCallback.java | 22 ++++++++++++++ src/test/java/demo/Main.java | 2 ++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/minestom/server/utils/callback/CommandCallback.java diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 9b7aa25da..376ab1ce7 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -20,6 +20,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerCommandEvent; import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import net.minestom.server.utils.ArrayUtils; +import net.minestom.server.utils.callback.CommandCallback; import net.minestom.server.utils.validate.Check; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; @@ -44,6 +45,8 @@ public final class CommandManager { private final CommandDispatcher dispatcher = new CommandDispatcher(); private final Map commandProcessorMap = new HashMap<>(); + private CommandCallback unknownCommandCallback; + public CommandManager() { running = true; // Setup console thread @@ -153,8 +156,12 @@ public final class CommandManager { final String[] splitCommand = command.split(" "); final String commandName = splitCommand[0]; final CommandProcessor commandProcessor = commandProcessorMap.get(commandName.toLowerCase()); - if (commandProcessor == null) + if (commandProcessor == null) { + if (unknownCommandCallback != null) { + this.unknownCommandCallback.apply(sender, command); + } return false; + } // Execute the legacy-command final String[] args = command.substring(command.indexOf(" ") + 1).split(" "); @@ -164,6 +171,26 @@ public final class CommandManager { } } + /** + * Gets the callback executed once an unknown command is run. + * + * @return the unknown command callback, null if not any + */ + @Nullable + public CommandCallback getUnknownCommandCallback() { + return unknownCommandCallback; + } + + /** + * Sets the callback executed once an unknown command is run. + * + * @param unknownCommandCallback the new unknown command callback, + * setting it to null mean that nothing will be executed + */ + public void setUnknownCommandCallback(@Nullable CommandCallback unknownCommandCallback) { + this.unknownCommandCallback = unknownCommandCallback; + } + /** * Gets the {@link ConsoleSender} (which is used as a {@link CommandSender}). * diff --git a/src/main/java/net/minestom/server/utils/callback/CommandCallback.java b/src/main/java/net/minestom/server/utils/callback/CommandCallback.java new file mode 100644 index 000000000..b19eb9017 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/CommandCallback.java @@ -0,0 +1,22 @@ +package net.minestom.server.utils.callback; + +import net.minestom.server.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +/** + * Functional interface used by the {@link net.minestom.server.command.CommandManager} + * to execute a callback if an unknown command is run. + * You can set it with {@link net.minestom.server.command.CommandManager#setUnknownCommandCallback(CommandCallback)}. + */ +@FunctionalInterface +public interface CommandCallback { + + /** + * Executed if an unknown command is run. + * + * @param sender the command sender + * @param command the complete command string + */ + void apply(@NotNull CommandSender sender, @NotNull String command); + +} diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index 36c883119..e49d54cb1 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -36,6 +36,8 @@ public class Main { commandManager.register(new ShutdownCommand()); commandManager.register(new TeleportCommand()); + commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command")); + StorageManager storageManager = MinecraftServer.getStorageManager(); storageManager.defineDefaultStorageSystem(FileStorageSystem::new); From 96dcd0e27f6ad4bb6bef404ba3eadcdc42058689 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 3 Nov 2020 02:16:32 +0100 Subject: [PATCH 03/55] Incredibly hard update to 1.16.4 --- src/main/java/net/minestom/server/MinecraftServer.java | 4 ++-- src/main/java/net/minestom/server/command/CommandManager.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 0ac2cf69d..97de2621f 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -74,8 +74,8 @@ public class MinecraftServer { @Getter private final static Logger LOGGER = LoggerFactory.getLogger(MinecraftServer.class); - public static final String VERSION_NAME = "1.16.3"; - public static final int PROTOCOL_VERSION = 753; + public static final String VERSION_NAME = "1.16.4"; + public static final int PROTOCOL_VERSION = 754; // Threads public static final String THREAD_NAME_BENCHMARK = "Ms-Benchmark"; diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 376ab1ce7..502d0cead 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -38,7 +38,7 @@ public final class CommandManager { public static final String COMMAND_PREFIX = "/"; - private boolean running; + private volatile boolean running; private final ConsoleSender consoleSender = new ConsoleSender(); From 547e7cf6e6628cf27d7d88778b17586d510e1326 Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Sun, 18 Oct 2020 18:38:25 +0200 Subject: [PATCH 04/55] Cleanup TODOs --- .../net/minestom/server/map/framebuffers/GLFWCapableBuffer.java | 1 - src/main/java/net/minestom/server/map/MapColors.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java index a40bf0270..fed4ede7b 100644 --- a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java +++ b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java @@ -96,7 +96,6 @@ public abstract class GLFWCapableBuffer { prepareMapColors(); } - // TODO: provide shader that performs the conversion automatically, would be a lot faster /** * Called in render after glFlush to read the pixel buffer contents and convert it to map colors. * Only call if you do not use {@link #render(Runnable)} nor {@link #setupRenderLoop(long, TimeUnit, Runnable)} diff --git a/src/main/java/net/minestom/server/map/MapColors.java b/src/main/java/net/minestom/server/map/MapColors.java index 7d9453205..f158d73fd 100644 --- a/src/main/java/net/minestom/server/map/MapColors.java +++ b/src/main/java/net/minestom/server/map/MapColors.java @@ -197,7 +197,6 @@ public enum MapColors { } catch (Throwable t) { t.printStackTrace(); } - System.out.println("done mapping."); // todo: remove, debug only } public static PreciseMapColor closestColor(int argb) { From d09c2756aac18827c076c69319a220bc4db0cc67 Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Tue, 3 Nov 2020 09:03:54 +0100 Subject: [PATCH 05/55] Fix ChunkDataPacket using doubles for block entity data instead of ints --- .../server/network/packet/server/play/ChunkDataPacket.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index d16aa6499..9a5c454fe 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -102,9 +102,9 @@ public class ChunkDataPacket implements ServerPacket { final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ); NBTCompound nbt = new NBTCompound() - .setDouble("x", blockPosition.getX()) - .setDouble("y", blockPosition.getY()) - .setDouble("z", blockPosition.getZ()); + .setInt("x", blockPosition.getX()) + .setInt("y", blockPosition.getY()) + .setInt("z", blockPosition.getZ()); final short customBlockId = customBlocksId[index]; final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); From b575d6df57959572c9a030c5bc1ffbfb8e5031b2 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 3 Nov 2020 18:22:36 +0100 Subject: [PATCH 06/55] Changed the wiki link in README.md --- .github/README.md | 2 +- src/main/java/net/minestom/server/MinecraftServer.java | 2 +- .../minestom/server/network/player/NettyPlayerConnection.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/README.md b/.github/README.md index 228c4a712..2807efc96 100644 --- a/.github/README.md +++ b/.github/README.md @@ -30,7 +30,7 @@ Our own expanded version for Vanilla can be found [here](https://github.com/Mine # Usage An example of how to use the Minestom library is available [here](/src/test/java/demo). -Alternatively you can check the official wiki [here](https://github.com/Minestom/Minestom/wiki). +Alternatively you can check the official wiki [here](https://wiki.minestom.com/). # Why Minestom? Minecraft evolved a lot since its release, most of the servers today do not take advantage of vanilla features and even have to struggle because of them. Our target audience is those who want to make a completely different server compared to default Minecraft gamemode such as survival or creative building. diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 97de2621f..babdd7155 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -442,7 +442,7 @@ public class MinecraftServer { * @throws IllegalStateException if this is called after the server started */ public static void setChunkViewDistance(int chunkViewDistance) { - Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), "The chunk view distance needs to be between 2 and 32"); + Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), "The chunk view distance must be between 2 and 32"); MinecraftServer.chunkViewDistance = chunkViewDistance; if (started) { UpdateViewDistancePacket updateViewDistancePacket = new UpdateViewDistancePacket(); 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 139924583..c13ec2b42 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -115,7 +115,7 @@ public class NettyPlayerConnection extends PlayerConnection { } /** - * Get the server address that the client used to connect. + * Gets the server address that the client used to connect. *

* WARNING: it is given by the client, it is possible for it to be wrong. * @@ -127,7 +127,7 @@ public class NettyPlayerConnection extends PlayerConnection { } /** - * Get the server port that the client used to connect. + * Gets the server port that the client used to connect. *

* WARNING: it is given by the client, it is possible for it to be wrong. * From b84bcde84d1aebf7a555839cae481412e11a5a6f Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 3 Nov 2020 23:31:26 +0100 Subject: [PATCH 07/55] Fixed Argument#useRemaining creating an empty array --- .../command/builder/CommandDispatcher.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java index b175fc1dc..4a3c78c88 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java +++ b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java @@ -110,6 +110,7 @@ public class CommandDispatcher { final String[] argsValues = new String[arguments.length]; boolean syntaxCorrect = true; + // The current index in the raw command string arguments int argIndex = 0; boolean useRemaining = false; @@ -126,20 +127,24 @@ public class CommandDispatcher { StringBuilder argValue = new StringBuilder(); if (useRemaining) { - // Argument is supposed to take the rest of the command input - for (int i = argIndex; i < args.length; i++) { - final String arg = args[i]; - if (argValue.length() > 0) - argValue.append(" "); - argValue.append(arg); - } + final boolean hasArgs = args.length > argIndex; + // Verify if there is any string part available + if (hasArgs) { + // Argument is supposed to take the rest of the command input + for (int i = argIndex; i < args.length; i++) { + final String arg = args[i]; + if (argValue.length() > 0) + argValue.append(" "); + argValue.append(arg); + } - final String argValueString = argValue.toString(); + final String argValueString = argValue.toString(); - correctionResult = argument.getCorrectionResult(argValueString); - if (correctionResult == Argument.SUCCESS) { - correct = true; - argsValues[argIndex] = argValueString; + correctionResult = argument.getCorrectionResult(argValueString); + if (correctionResult == Argument.SUCCESS) { + correct = true; + argsValues[argIndex] = argValueString; + } } } else { // Argument is either single-word or can accept optional delimited space(s) From 9781e380b91525736476638b78b5d1673d3f32c7 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 04:45:46 +0100 Subject: [PATCH 08/55] Command syntaxes can now contain a CommandCondition --- .../server/command/CommandManager.java | 17 +++++-- .../server/command/builder/Command.java | 23 ++++++++-- .../command/builder/CommandDispatcher.java | 19 ++++++-- .../server/command/builder/CommandSyntax.java | 44 ++++++++++++++++--- .../builder/condition/CommandCondition.java | 23 +++++++++- src/test/java/demo/Main.java | 6 +-- src/test/java/demo/PlayerInit.java | 2 +- .../java/demo/commands/GamemodeCommand.java | 2 +- .../java/demo/commands/HealthCommand.java | 2 +- src/test/java/demo/commands/TestCommand.java | 7 +-- 10 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 502d0cead..45213edd7 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -204,7 +204,7 @@ public final class CommandManager { /** * Gets the {@link DeclareCommandsPacket} for a specific player. *

- * Can be used to update the {@link Player} auto-completion list. + * Can be used to update a player auto-completion list. * * @param player the player to get the commands packet * @return the {@link DeclareCommandsPacket} for {@code player} @@ -234,7 +234,7 @@ public final class CommandManager { final CommandCondition commandCondition = command.getCondition(); if (commandCondition != null) { // Do not show command if return false - if (!commandCondition.apply(player)) { + if (!commandCondition.canUse(player, null)) { continue; } } @@ -247,7 +247,7 @@ public final class CommandManager { names.add(command.getName()); names.addAll(Arrays.asList(command.getAliases())); for (String name : names) { - createCommand(nodes, cmdChildren, name, syntaxes, rootChildren); + createCommand(player, nodes, cmdChildren, name, syntaxes, rootChildren); } } @@ -311,13 +311,15 @@ public final class CommandManager { /** * Adds the command's syntaxes to the nodes list. * + * @param sender the potential sender of the command * @param nodes the nodes of the packet * @param cmdChildren the main root of this command * @param name the name of the command (or the alias) * @param syntaxes the syntaxes of the command * @param rootChildren the children of the main node (all commands name) */ - private void createCommand(@NotNull List nodes, + private void createCommand(@NotNull CommandSender sender, + @NotNull List nodes, @NotNull IntList cmdChildren, @NotNull String name, @NotNull Collection syntaxes, @@ -334,6 +336,13 @@ public final class CommandManager { Map> storedArgumentsNodes = new HashMap<>(); for (CommandSyntax syntax : syntaxes) { + final CommandCondition commandCondition = syntax.getCommandCondition(); + if (commandCondition != null && !commandCondition.canUse(sender, null)) { + // Sender does not have the right to use this syntax, ignore it + continue; + } + + // Represent the last nodes computed in the last iteration List lastNodes = null; diff --git a/src/main/java/net/minestom/server/command/builder/Command.java b/src/main/java/net/minestom/server/command/builder/Command.java index 68807565b..811d7d6ae 100644 --- a/src/main/java/net/minestom/server/command/builder/Command.java +++ b/src/main/java/net/minestom/server/command/builder/Command.java @@ -104,16 +104,31 @@ public class Command { /** * Adds a new syntax in the command. *

- * A syntax is simply a list of arguments + * A syntax is simply a list of arguments. + * + * @param commandCondition the condition to use the syntax + * @param executor the executor to call when the syntax is successfully received + * @param args all the arguments of the syntax + * @return the created {@link CommandSyntax} + */ + public CommandSyntax addSyntax(@Nullable CommandCondition commandCondition, + @NotNull CommandExecutor executor, + @NotNull Argument... args) { + final CommandSyntax syntax = new CommandSyntax(commandCondition, executor, args); + this.syntaxes.add(syntax); + return syntax; + } + + /** + * Adds a new syntax in the command without any condition. * * @param executor the executor to call when the syntax is successfully received * @param args all the arguments of the syntax * @return the created {@link CommandSyntax} + * @see #addSyntax(CommandCondition, CommandExecutor, Argument[]) */ public CommandSyntax addSyntax(@NotNull CommandExecutor executor, @NotNull Argument... args) { - final CommandSyntax syntax = new CommandSyntax(executor, args); - this.syntaxes.add(syntax); - return syntax; + return addSyntax(null, executor, args); } /** diff --git a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java index 4a3c78c88..fb812291a 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java +++ b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java @@ -198,6 +198,7 @@ public class CommandDispatcher { final CommandSyntax finalSyntax = findMostCorrectSyntax(validSyntaxes, syntaxesValues, executorArgs); if (finalSyntax != null) { // A fully correct syntax has been found, use it + result.syntax = finalSyntax; result.executor = finalSyntax.getExecutor(); result.arguments = executorArgs; return result; @@ -333,6 +334,8 @@ public class CommandDispatcher { private Command command; // Command Executor + private CommandSyntax syntax; + private CommandExecutor executor; private Arguments arguments; @@ -353,17 +356,27 @@ public class CommandDispatcher { public void execute(@NotNull CommandSender source, @NotNull String commandString) { // Global listener command.globalListener(source, arguments, commandString); - // Condition check + // Command condition check final CommandCondition condition = command.getCondition(); if (condition != null) { - final boolean result = condition.apply(source); + final boolean result = condition.canUse(source, commandString); if (!result) return; } // Condition is respected if (executor != null) { // An executor has been found - executor.apply(source, arguments); + + if (syntax != null) { + // The executor is from a syntax + final CommandCondition commandCondition = syntax.getCommandCondition(); + if (commandCondition == null || commandCondition.canUse(source, commandString)) { + executor.apply(source, arguments); + } + } else { + // The executor is probably the default one + executor.apply(source, arguments); + } } else if (callback != null) { // No syntax has been validated but the faulty argument with a callback has been found // Execute the faulty argument callback diff --git a/src/main/java/net/minestom/server/command/builder/CommandSyntax.java b/src/main/java/net/minestom/server/command/builder/CommandSyntax.java index 0d518a6a2..803fa9a78 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandSyntax.java +++ b/src/main/java/net/minestom/server/command/builder/CommandSyntax.java @@ -1,7 +1,10 @@ package net.minestom.server.command.builder; import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.condition.CommandCondition; +import net.minestom.server.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents a syntax in {@link Command} @@ -9,22 +12,39 @@ import org.jetbrains.annotations.NotNull; */ public class CommandSyntax { - private final Argument[] args; + private CommandCondition commandCondition; private CommandExecutor executor; + private final Argument[] args; - protected CommandSyntax(@NotNull CommandExecutor commandExecutor, @NotNull Argument... args) { + protected CommandSyntax(@Nullable CommandCondition commandCondition, + @NotNull CommandExecutor commandExecutor, + @NotNull Argument... args) { + this.commandCondition = commandCondition; this.executor = commandExecutor; this.args = args; } /** - * Gets all the required {@link Argument} for this syntax. + * Gets the condition to use this syntax. * - * @return the required arguments + * @return this command condition, null if none */ - @NotNull - public Argument[] getArguments() { - return args; + @Nullable + public CommandCondition getCommandCondition() { + return commandCondition; + } + + /** + * Changes the command condition of this syntax. + *

+ * Be aware that changing the command condition will not automatically update players auto-completion. + * You can create a new packet containing the changes with + * {@link net.minestom.server.command.CommandManager#createDeclareCommandsPacket(Player)}. + * + * @param commandCondition the new command condition, null to remove it + */ + public void setCommandCondition(@Nullable CommandCondition commandCondition) { + this.commandCondition = commandCondition; } /** @@ -46,4 +66,14 @@ public class CommandSyntax { this.executor = executor; } + /** + * Gets all the required {@link Argument} for this syntax. + * + * @return the required arguments + */ + @NotNull + public Argument[] getArguments() { + return args; + } + } diff --git a/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java b/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java index 243872ba9..751e3ef5d 100644 --- a/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java +++ b/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java @@ -2,10 +2,29 @@ package net.minestom.server.command.builder.condition; import net.minestom.server.command.CommandSender; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** - * Used to know if the {@link CommandSender} is allowed to run the command. + * Used to know if the {@link CommandSender} is allowed to run the command or a specific syntax. */ +@FunctionalInterface public interface CommandCondition { - boolean apply(@NotNull CommandSender source); + + /** + * Called when the sender permission needs to be checked. + *

+ * The first time will be during player connection in order to know + * if the command/syntax should be displayed as tab-completion suggestion, + * {@code commandString} will be null in this case. + *

+ * Otherwise, {@code commandString} will never be null + * but will instead be the raw command string given by the sender. + * You should in this case warn the sender (eg by sending a message) if the condition is unsuccessful. + * + * @param source the sender of the command + * @param commandString the raw command string, + * null if the method has been called at player login + * @return true if the sender has the right to use the command, false otherwise + */ + boolean canUse(@NotNull CommandSender source, @Nullable String commandString); } diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index e49d54cb1..ba8a5f9d5 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -3,7 +3,7 @@ package demo; import demo.blocks.BurningTorchBlock; import demo.blocks.StoneBlock; import demo.blocks.UpdatableBlockDemo; -import demo.commands.*; +import demo.commands.TestCommand; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; import net.minestom.server.instance.block.BlockManager; @@ -27,14 +27,14 @@ public class Main { blockManager.registerBlockPlacementRule(new RedstonePlacementRule()); CommandManager commandManager = MinecraftServer.getCommandManager(); - commandManager.register(new EntitySelectorCommand()); commandManager.register(new TestCommand()); + /*commandManager.register(new EntitySelectorCommand()); commandManager.register(new HealthCommand()); commandManager.register(new SimpleCommand()); commandManager.register(new GamemodeCommand()); commandManager.register(new DimensionCommand()); commandManager.register(new ShutdownCommand()); - commandManager.register(new TeleportCommand()); + commandManager.register(new TeleportCommand());*/ commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command")); diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 31d437949..1c1bd80cb 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -172,7 +172,7 @@ public class PlayerInit { player.addEventCallback(PlayerSpawnEvent.class, event -> { player.setGameMode(GameMode.SURVIVAL); - if(event.isFirstSpawn()){ + if (event.isFirstSpawn()) { player.teleport(new Position(0, 64f, 0)); } diff --git a/src/test/java/demo/commands/GamemodeCommand.java b/src/test/java/demo/commands/GamemodeCommand.java index 8b715a19c..3518a2e10 100644 --- a/src/test/java/demo/commands/GamemodeCommand.java +++ b/src/test/java/demo/commands/GamemodeCommand.java @@ -70,7 +70,7 @@ public class GamemodeCommand extends Command { sender.sendMessage("'" + gamemode + "' is not a valid gamemode!"); } - private boolean isAllowed(CommandSender sender) { + private boolean isAllowed(CommandSender sender, String commandString) { if (!sender.isPlayer()) { sender.sendMessage("The command is only available for player"); return false; diff --git a/src/test/java/demo/commands/HealthCommand.java b/src/test/java/demo/commands/HealthCommand.java index 93533f89f..5bbbf3668 100644 --- a/src/test/java/demo/commands/HealthCommand.java +++ b/src/test/java/demo/commands/HealthCommand.java @@ -28,7 +28,7 @@ public class HealthCommand extends Command { addSyntax(this::execute, arg0); } - private boolean condition(CommandSender sender) { + private boolean condition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { sender.sendMessage("The command is only available for player"); return false; diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index 219ddc972..66fdd970c 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -17,8 +17,9 @@ public class TestCommand extends Command { //addSyntax(this::execute, dynamicWord); } - Argument test = ArgumentType.Word("test").from("hey"); - Argument num = ArgumentType.Integer("num"); + Argument test = ArgumentType.Word("wordT"); + Argument testt = ArgumentType.Word("wordTt"); + Argument test2 = ArgumentType.StringArray("array"); setDefaultExecutor((source, args) -> { System.out.println("DEFAULT"); @@ -31,7 +32,7 @@ public class TestCommand extends Command { addSyntax((source, args) -> { System.out.println(2); - }, test, num); + }, test, test2); } private void usage(CommandSender sender, Arguments arguments) { From b296b103c11efe56ff3c027d5487f93177a0a94f Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 05:19:30 +0100 Subject: [PATCH 09/55] CommandCondition command string can be null if a new command packet is built --- .../server/command/builder/condition/CommandCondition.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java b/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java index 751e3ef5d..181379166 100644 --- a/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java +++ b/src/main/java/net/minestom/server/command/builder/condition/CommandCondition.java @@ -15,7 +15,8 @@ public interface CommandCondition { *

* The first time will be during player connection in order to know * if the command/syntax should be displayed as tab-completion suggestion, - * {@code commandString} will be null in this case. + * {@code commandString} will be null in this case. (It is also possible for the command string + * to be null if a new command packet is built) *

* Otherwise, {@code commandString} will never be null * but will instead be the raw command string given by the sender. @@ -23,7 +24,7 @@ public interface CommandCondition { * * @param source the sender of the command * @param commandString the raw command string, - * null if the method has been called at player login + * null if this is an access request * @return true if the sender has the right to use the command, false otherwise */ boolean canUse(@NotNull CommandSender source, @Nullable String commandString); From 097dcf0f6c3115215560da1348c5eecf05342816 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 14:18:02 +0100 Subject: [PATCH 10/55] Log the proper warning message instead of a NPE --- .../minestom/server/instance/MinestomBasicChunkLoader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java index d358cc5d4..c0539d2e3 100644 --- a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java @@ -6,6 +6,7 @@ import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkSupplier; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,10 +38,10 @@ public class MinestomBasicChunkLoader implements IChunkLoader { } @Override - public void saveChunk(@NotNull Chunk chunk, Runnable callback) { + public void saveChunk(@NotNull Chunk chunk, @Nullable Runnable callback) { final StorageLocation storageLocation = instanceContainer.getStorageLocation(); if (storageLocation == null) { - callback.run(); + OptionalCallback.execute(callback); LOGGER.warn("No storage location to save chunk!"); return; } From c03a2992ef1987ff1025e65a12759c398b17df76 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 15:41:56 +0100 Subject: [PATCH 11/55] Stop the whole click process if the event is canceled --- src/main/java/net/minestom/server/instance/Chunk.java | 2 ++ .../server/instance/MinestomBasicChunkLoader.java | 2 +- .../inventory/click/InventoryClickProcessor.java | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index 959edf807..6f144b39b 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -293,6 +293,8 @@ public abstract class Chunk implements Viewable, DataContainer { /** * Gets if this chunk will or had been loaded with a {@link ChunkGenerator}. + *

+ * If false, the chunk will be entirely empty when loaded. * * @return true if this chunk is affected by a {@link ChunkGenerator} */ diff --git a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java index c0539d2e3..f87b33c1c 100644 --- a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java @@ -66,7 +66,7 @@ public class MinestomBasicChunkLoader implements IChunkLoader { } @Override - public boolean loadChunk(@NotNull Instance instance, int chunkX, int chunkZ, ChunkCallback callback) { + public boolean loadChunk(@NotNull Instance instance, int chunkX, int chunkZ, @Nullable ChunkCallback callback) { final StorageLocation storageLocation = instanceContainer.getStorageLocation(); final byte[] bytes = storageLocation == null ? null : storageLocation.get(getChunkKey(chunkX, chunkZ)); diff --git a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java index 7304b8b6e..946751fae 100644 --- a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java +++ b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java @@ -199,7 +199,7 @@ public class InventoryClickProcessor { clickResult = startCondition(clickResult, inventory, player, index, ClickType.SHIFT_CLICK, item, cursor); if (clickResult.isCancel()) - continue; + break; final int amount = itemRule.getAmount(item); if (!clickedRule.canApply(clicked, amount + 1)) @@ -230,7 +230,7 @@ public class InventoryClickProcessor { clickResult = startCondition(clickResult, inventory, player, index, ClickType.SHIFT_CLICK, item, cursor); if (clickResult.isCancel()) - continue; + break; // Switch itemSetter.accept(index, resultClicked); @@ -283,7 +283,7 @@ public class InventoryClickProcessor { clickResult = startCondition(clickResult, inventory, player, s, ClickType.DRAGGING, slotItem, cursor); if (clickResult.isCancel()) - continue; + break; final int maxSize = stackingRule.getMaxSize(); if (stackingRule.canBeStacked(draggedItem, slotItem)) { @@ -323,7 +323,7 @@ public class InventoryClickProcessor { clickResult = startCondition(clickResult, inventory, player, s, ClickType.DRAGGING, slotItem, cursor); if (clickResult.isCancel()) - continue; + break; if (stackingRule.canBeStacked(draggedItem, slotItem)) { final int amount = slotItem.getAmount() + 1; @@ -399,7 +399,7 @@ public class InventoryClickProcessor { if (cursorRule.canBeStacked(cursor, item)) { clickResult = startCondition(clickResult, inventory, player, index, ClickType.DOUBLE_CLICK, item, cursor); if (clickResult.isCancel()) - continue; + break; final int totalAmount = amount + cursorRule.getAmount(item); if (!cursorRule.canApply(cursor, totalAmount)) { From 8612245ff973bdbe3539d5c27c99ad384b203235 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 18:22:42 +0100 Subject: [PATCH 12/55] Added precison to the InventoryModifier comments --- .../minestom/server/inventory/InventoryModifier.java | 12 +++++++----- .../inventory/click/InventoryClickProcessor.java | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/inventory/InventoryModifier.java b/src/main/java/net/minestom/server/inventory/InventoryModifier.java index fc32f1558..1220ec155 100644 --- a/src/main/java/net/minestom/server/inventory/InventoryModifier.java +++ b/src/main/java/net/minestom/server/inventory/InventoryModifier.java @@ -7,12 +7,12 @@ import org.jetbrains.annotations.NotNull; import java.util.List; /** - * Represents an inventory where its items can be modified/retrieved. + * Represents an inventory where items can be modified/retrieved. */ public interface InventoryModifier { /** - * Sets an {@link ItemStack} at the specified slot. + * Sets an {@link ItemStack} at the specified slot and send relevant update to the viewer(s). * * @param slot the slot to set the item * @param itemStack the item to set @@ -20,7 +20,9 @@ public interface InventoryModifier { void setItemStack(int slot, @NotNull ItemStack itemStack); /** - * Adds an {@link ItemStack} to the inventory. + * Adds an {@link ItemStack} to the inventory and send relevant update to the viewer(s). + *

+ * Even the item cannot be fully added, the amount of {@code itemStack} will be updated. * * @param itemStack the item to add * @return true if the item has been successfully fully added, false otherwise @@ -28,7 +30,7 @@ public interface InventoryModifier { boolean addItemStack(@NotNull ItemStack itemStack); /** - * Clears the inventory. + * Clears the inventory and send relevant update to the viewer(s). */ void clear(); @@ -59,7 +61,7 @@ public interface InventoryModifier { /** * Gets all the {@link InventoryCondition} of this inventory. * - * @return the inventory conditions + * @return a modifiable {@link List} containing all the inventory conditions */ @NotNull List getInventoryConditions(); diff --git a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java index 946751fae..28e6f3f08 100644 --- a/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java +++ b/src/main/java/net/minestom/server/inventory/click/InventoryClickProcessor.java @@ -205,7 +205,7 @@ public class InventoryClickProcessor { if (!clickedRule.canApply(clicked, amount + 1)) continue; - int totalAmount = clickedRule.getAmount(resultClicked) + amount; + final int totalAmount = clickedRule.getAmount(resultClicked) + amount; if (!clickedRule.canApply(clicked, totalAmount)) { item = itemRule.apply(item, itemRule.getMaxSize()); itemSetter.accept(index, item); @@ -278,7 +278,7 @@ public class InventoryClickProcessor { int finalCursorAmount = cursorAmount; for (Integer s : slots) { - ItemStack draggedItem = cursor.copy(); + final ItemStack draggedItem = cursor.copy(); ItemStack slotItem = itemGetter.apply(s); clickResult = startCondition(clickResult, inventory, player, s, ClickType.DRAGGING, slotItem, cursor); From ae1d089603225560b751c94b168b7bd6f20a5290 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 18:27:18 +0100 Subject: [PATCH 13/55] Rounded extensions loading time for readability purpose --- src/main/java/net/minestom/server/MinecraftServer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index babdd7155..13c64d911 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -607,7 +607,8 @@ public class MinecraftServer { extensionManager.getExtensions().forEach(Extension::initialize); extensionManager.getExtensions().forEach(Extension::postInitialize); - LOGGER.info("Extensions loaded in " + (t1 + System.nanoTime()) / 1_000_000D + "ms"); + final double loadTime = MathUtils.round((t1 + System.nanoTime()) / 1_000_000D, 2); + LOGGER.info("Extensions loaded in " + loadTime + "ms"); LOGGER.info("Minestom server started successfully."); MinecraftServer.started = true; From f85b2c4aad9d9ea40507d0cb8813bc95d863d4e1 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 19:14:04 +0100 Subject: [PATCH 14/55] Allow dynamic entity view distance change --- .../net/minestom/server/MinecraftServer.java | 18 ++++- .../net/minestom/server/entity/Entity.java | 76 ++----------------- .../net/minestom/server/entity/Player.java | 53 ++++++++++++- .../net/minestom/server/instance/Chunk.java | 10 +++ src/test/java/demo/PlayerInit.java | 11 +-- src/test/java/demo/commands/TestCommand.java | 13 ++-- 6 files changed, 89 insertions(+), 92 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 13c64d911..b16342323 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -439,10 +439,11 @@ public class MinecraftServer { * Changes the chunk view distance of the server. * * @param chunkViewDistance the new chunk view distance - * @throws IllegalStateException if this is called after the server started + * @throws IllegalArgumentException if {@code chunkViewDistance} is not between 2 and 32 */ public static void setChunkViewDistance(int chunkViewDistance) { - Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), "The chunk view distance must be between 2 and 32"); + Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), + "The chunk view distance must be between 2 and 32"); MinecraftServer.chunkViewDistance = chunkViewDistance; if (started) { UpdateViewDistancePacket updateViewDistancePacket = new UpdateViewDistancePacket(); @@ -476,11 +477,20 @@ public class MinecraftServer { * WARNING: this need to be called before {@link #start(String, int, ResponseDataConsumer)}. * * @param entityViewDistance the new entity view distance - * @throws IllegalStateException if this is called after the server started + * @throws IllegalArgumentException if {@code entityViewDistance} is not between 0 and 32 */ public static void setEntityViewDistance(int entityViewDistance) { - Check.stateCondition(started, "The entity view distance cannot be changed after the server has been started."); + Check.argCondition(!MathUtils.isBetween(entityViewDistance, 0, 32), + "The entity view distance must be between 0 and 32"); MinecraftServer.entityViewDistance = entityViewDistance; + if (started) { + connectionManager.getOnlinePlayers().forEach(player -> { + final Chunk playerChunk = player.getChunk(); + if (playerChunk != null) { + player.refreshVisibleEntities(playerChunk); + } + }); + } } /** diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index f2b192f59..eaef06087 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -21,7 +21,6 @@ import net.minestom.server.instance.WorldBorder; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.network.packet.server.play.*; import net.minestom.server.thread.ThreadProvider; -import net.minestom.server.utils.ArrayUtils; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; import net.minestom.server.utils.Vector; @@ -84,7 +83,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { private boolean autoViewable; private final int id; private Data data; - private final Set viewers = new CopyOnWriteArraySet<>(); + protected final Set viewers = new CopyOnWriteArraySet<>(); protected UUID uuid; private boolean isActive; // False if entity has only been instanced without being added somewhere @@ -1007,7 +1006,12 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { instance.removeEntityFromChunk(this, lastChunk); instance.addEntityToChunk(this, newChunk); } - updateView(lastChunk, newChunk); + if (this instanceof Player) { + // Refresh player view + final Player player = (Player) this; + player.refreshVisibleChunks(newChunk); + player.refreshVisibleEntities(newChunk); + } } } } @@ -1020,72 +1024,6 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { refreshPosition(position.getX(), position.getY(), position.getZ()); } - /** - * Manages viewable entities automatically if {@link #isAutoViewable()} is enabled. - *

- * Called by {@link #refreshPosition(float, float, float)} when the new position is in a different {@link Chunk}. - * - * @param lastChunk the previous {@link Chunk} of this entity - * @param newChunk the new {@link Chunk} of this entity - */ - private void updateView(@NotNull Chunk lastChunk, @NotNull Chunk newChunk) { - final boolean isPlayer = this instanceof Player; - - if (isPlayer) - ((Player) this).refreshVisibleChunks(newChunk); // Refresh loaded chunk - - // Refresh entity viewable list - final int entityViewDistance = MinecraftServer.getEntityViewDistance(); - final long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), entityViewDistance); - final long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), entityViewDistance); - - // Remove from previous chunks - final int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity); - for (int index : oldChunksEntity) { - final long chunkIndex = lastVisibleChunksEntity[index]; - final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex); - final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex); - final Chunk chunk = instance.getChunk(chunkX, chunkZ); - if (chunk == null) - continue; - instance.getChunkEntities(chunk).forEach(ent -> { - if (ent instanceof Player) { - final Player player = (Player) ent; - if (isAutoViewable()) - removeViewer(player); - if (isPlayer) { - player.removeViewer((Player) this); - } - } else if (isPlayer) { - ent.removeViewer((Player) this); - } - }); - } - - // Add to new chunks - final int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity); - for (int index : newChunksEntity) { - final long chunkIndex = updatedVisibleChunksEntity[index]; - final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex); - final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex); - final Chunk chunk = instance.getChunk(chunkX, chunkZ); - if (chunk == null) - continue; - instance.getChunkEntities(chunk).forEach(ent -> { - if (ent instanceof Player) { - Player player = (Player) ent; - if (isAutoViewable()) - addViewer(player); - if (this instanceof Player) { - player.addViewer((Player) this); - } - } else if (isPlayer) { - ent.addViewer((Player) this); - } - }); - } - } - /** * Updates the entity view internally. *

diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 11f6ba3e8..425a01b9c 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -80,6 +80,7 @@ public class Player extends LivingEntity implements CommandSender { private String username; protected final PlayerConnection playerConnection; + // All the entities that this player can see protected final Set viewableEntities = new CopyOnWriteArraySet<>(); private int latency; @@ -1406,12 +1407,12 @@ public class Player extends LivingEntity implements CommandSender { /** * Called when the player changes chunk (move from one to another). - * Can also be used to refresh the list of chunks that the client should see. + * Can also be used to refresh the list of chunks that the client should see based on {@link #getChunkRange()}. *

* It does remove and add the player from the chunks viewers list when removed or added. * It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}. * - * @param newChunk the current/new player chunk + * @param newChunk the current/new player chunk (can be the current one) */ public void refreshVisibleChunks(@NotNull Chunk newChunk) { // Previous chunks indexes @@ -1420,7 +1421,7 @@ public class Player extends LivingEntity implements CommandSender { ).toArray(); // New chunks indexes - final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), getChunkRange()); + final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(newChunk.toPosition(), getChunkRange()); // Find the difference between the two arrays¬ final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks); @@ -1461,6 +1462,52 @@ public class Player extends LivingEntity implements CommandSender { } } + /** + * Refreshes the list of entities that the player should be able to see based on {@link MinecraftServer#getEntityViewDistance()} + * and {@link Entity#isAutoViewable()}. + * + * @param newChunk the new chunk of the player (can be the current one) + */ + public void refreshVisibleEntities(@NotNull Chunk newChunk) { + final int entityViewDistance = MinecraftServer.getEntityViewDistance(); + final float maximalDistance = entityViewDistance * Chunk.CHUNK_SECTION_SIZE; + + // Manage already viewable entities + this.viewableEntities.forEach(entity -> { + final float distance = entity.getDistance(this); + if (distance > maximalDistance) { + // Entity shouldn't be viewable anymore + if (isAutoViewable()) { + entity.removeViewer(this); + } + if (entity instanceof Player && entity.isAutoViewable()) { + removeViewer((Player) entity); + } + } + }); + + // Manage entities in unchecked chunks + final long[] chunksInRange = ChunkUtils.getChunksInRange(newChunk.toPosition(), entityViewDistance); + + for (long chunkIndex : chunksInRange) { + final int chunkX = ChunkUtils.getChunkCoordX(chunkIndex); + final int chunkZ = ChunkUtils.getChunkCoordZ(chunkIndex); + final Chunk chunk = instance.getChunk(chunkX, chunkZ); + if (chunk == null) + continue; + instance.getChunkEntities(chunk).forEach(entity -> { + if (isAutoViewable() && !entity.viewers.contains(this)) { + entity.addViewer(this); + } + + if (entity instanceof Player && entity.isAutoViewable()) { + addViewer((Player) entity); + } + }); + } + + } + @Override public void teleport(@NotNull Position position, @Nullable Runnable callback) { super.teleport(position, () -> { diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index 6f144b39b..dc0816e36 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -19,6 +19,7 @@ import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.network.packet.server.play.UpdateLightPacket; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.MathUtils; +import net.minestom.server.utils.Position; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkSupplier; @@ -291,6 +292,15 @@ public abstract class Chunk implements Viewable, DataContainer { return chunkZ; } + /** + * Creates a {@link Position} object based on this chunk. + * + * @return the position of this chunk + */ + public Position toPosition() { + return new Position(CHUNK_SIZE_Z * getChunkX(), 0, CHUNK_SIZE_Z * getChunkZ()); + } + /** * Gets if this chunk will or had been loaded with a {@link ChunkGenerator}. *

diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 1c1bd80cb..4705aecbd 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -7,6 +7,7 @@ import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.*; import net.minestom.server.entity.damage.DamageType; +import net.minestom.server.entity.type.monster.EntityZombie; import net.minestom.server.event.entity.EntityAttackEvent; import net.minestom.server.event.item.ItemDropEvent; import net.minestom.server.event.item.PickupItemEvent; @@ -14,7 +15,6 @@ import net.minestom.server.event.player.*; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceContainer; -import net.minestom.server.instance.SharedInstance; import net.minestom.server.instance.block.Block; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; @@ -144,13 +144,8 @@ public class PlayerInit { Vector velocity = player.getPosition().copy().getDirection().multiply(6); itemEntity.setVelocity(velocity); - Instance instance = player.getInstance(); - InstanceContainer instanceContainer = instance instanceof InstanceContainer ? (InstanceContainer) instance : - ((SharedInstance) instance).getInstanceContainer(); - SharedInstance sharedInstance = MinecraftServer.getInstanceManager().createSharedInstance(instanceContainer); - player.setInstance(sharedInstance); - player.sendMessage("New instance"); - + EntityZombie entityZombie = new EntityZombie(player.getPosition()); + entityZombie.setInstance(player.getInstance()); }); player.addEventCallback(PlayerDisconnectEvent.class, event -> { diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index 66fdd970c..e1396cbea 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Arguments; import net.minestom.server.command.builder.Command; @@ -17,9 +18,7 @@ public class TestCommand extends Command { //addSyntax(this::execute, dynamicWord); } - Argument test = ArgumentType.Word("wordT"); - Argument testt = ArgumentType.Word("wordTt"); - Argument test2 = ArgumentType.StringArray("array"); + Argument test = ArgumentType.Integer("number"); setDefaultExecutor((source, args) -> { System.out.println("DEFAULT"); @@ -27,12 +26,10 @@ public class TestCommand extends Command { }); addSyntax((source, args) -> { - System.out.println(1); + int number = args.getInteger("number"); + source.sendMessage("set view to " + number); + MinecraftServer.setEntityViewDistance(number); }, test); - - addSyntax((source, args) -> { - System.out.println(2); - }, test, test2); } private void usage(CommandSender sender, Arguments arguments) { From 7b6f4b365b9edc45c76bc1b60699e4d91fe1508f Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 19:16:47 +0100 Subject: [PATCH 15/55] Do not add the entity as viewer multiple time --- src/main/java/net/minestom/server/entity/Player.java | 2 +- 1 file changed, 1 insertion(+), 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 425a01b9c..929a3928e 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1500,7 +1500,7 @@ public class Player extends LivingEntity implements CommandSender { entity.addViewer(this); } - if (entity instanceof Player && entity.isAutoViewable()) { + if (entity instanceof Player && entity.isAutoViewable() && !viewers.contains(entity)) { addViewer((Player) entity); } }); From 2c0f0a8f9ebef1414ee8545f6e8859f4f77fa1d1 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 Nov 2020 19:20:45 +0100 Subject: [PATCH 16/55] Remove warning about MinecraftServer#setEntityViewDistance --- src/main/java/net/minestom/server/MinecraftServer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index b16342323..1e611764b 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -473,8 +473,6 @@ public class MinecraftServer { /** * Changes the entity view distance of the server. - *

- * WARNING: this need to be called before {@link #start(String, int, ResponseDataConsumer)}. * * @param entityViewDistance the new entity view distance * @throws IllegalArgumentException if {@code entityViewDistance} is not between 0 and 32 From 3be560512627b1c7f5a11d4d754d1bb3c673e45c Mon Sep 17 00:00:00 2001 From: Geolykt Date: Wed, 4 Nov 2020 20:31:29 +0100 Subject: [PATCH 17/55] Added distanceSquared where applicable Warning: Also breaks current API-Implementation (API-specification remains the same) in a way that BlockPosition#getDistance(BlockPosition) no longer returns the manhattan distance, but the distance according to pythagoras. --- .../minestom/server/utils/BlockPosition.java | 28 +++++++++++++++++-- .../net/minestom/server/utils/Position.java | 14 ++++++++++ .../net/minestom/server/utils/Vector.java | 10 +++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/BlockPosition.java b/src/main/java/net/minestom/server/utils/BlockPosition.java index b597012d8..3e819dadb 100644 --- a/src/main/java/net/minestom/server/utils/BlockPosition.java +++ b/src/main/java/net/minestom/server/utils/BlockPosition.java @@ -174,17 +174,41 @@ public class BlockPosition { } /** - * Gets the distance to another block position. + * Gets the manhattan distance to another block position. * * @param blockPosition the block position to check the distance * @return the distance between 'this' and {@code blockPosition} */ - public int getDistance(@NotNull BlockPosition blockPosition) { + public int getManhattanDistance(@NotNull BlockPosition blockPosition) { return Math.abs(getX() - blockPosition.getX()) + Math.abs(getY() - blockPosition.getY()) + Math.abs(getZ() - blockPosition.getZ()); } + /** + * Gets the distance to another block position. + * In cases where performance matters, {@link #getDistanceSquared(BlockPosition)} should be used + * as it does not perform the expensive Math.sqrt method. + * + * @param blockPosition the block position to check the distance + * @return the distance between 'this' and {@code blockPosition} + */ + public double getDistance(@NotNull BlockPosition blockPosition) { + return Math.sqrt(getDistanceSquared(blockPosition)); + } + + /** + * Gets the square distance to another block position. + * + * @param blockPosition the block position to check the distance + * @return the distance between 'this' and {@code blockPosition} + */ + public int getDistanceSquared(@NotNull BlockPosition blockPosition) { + return MathUtils.square(getX() - blockPosition.getX()) + + MathUtils.square(getY() - blockPosition.getY()) + + MathUtils.square(getZ() - blockPosition.getZ()); + } + /** * Copies this block position. * diff --git a/src/main/java/net/minestom/server/utils/Position.java b/src/main/java/net/minestom/server/utils/Position.java index c587384a3..66b1d9978 100644 --- a/src/main/java/net/minestom/server/utils/Position.java +++ b/src/main/java/net/minestom/server/utils/Position.java @@ -72,6 +72,8 @@ public class Position { /** * Gets the distance between 2 positions. + * In cases where performance matters, {@link #getDistanceSquared(Position)} should be used + * as it does not perform the expensive Math.sqrt method. * * @param position the second position * @return the distance between {@code this} and {@code position} @@ -82,6 +84,18 @@ public class Position { MathUtils.square(position.getZ() - getZ())); } + /** + * Gets the square distance to another position. + * + * @param position the second position + * @return the squared distance between {@code this} and {@code position} + */ + public float getDistanceSquared(Position position) { + return MathUtils.square(getX() - position.getX()) + + MathUtils.square(getY() - position.getY()) + + MathUtils.square(getZ() - position.getZ()); + } + /** * Gets a unit-vector pointing in the direction that this Location is * facing. diff --git a/src/main/java/net/minestom/server/utils/Vector.java b/src/main/java/net/minestom/server/utils/Vector.java index e4542a379..f88382a8f 100644 --- a/src/main/java/net/minestom/server/utils/Vector.java +++ b/src/main/java/net/minestom/server/utils/Vector.java @@ -118,6 +118,16 @@ public class Vector { return Math.sqrt(MathUtils.square(x - o.x) + MathUtils.square(y - o.y) + MathUtils.square(z - o.z)); } + /** + * Gets the squared distance between this vector and another. + * + * @param o The other vector + * @return the squared distance + */ + public double distanceSquared(Vector o) { + return MathUtils.square(x - o.x) + MathUtils.square(y - o.y) + MathUtils.square(z - o.z); + } + /** * Performs scalar multiplication, multiplying all components with a * scalar. From 6ed1a7d462040f5e8d2a746926095ab0dae0c2ca Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 13:57:54 +0100 Subject: [PATCH 18/55] Added more usage info in README.md --- .github/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 2807efc96..16d0d6282 100644 --- a/.github/README.md +++ b/.github/README.md @@ -11,7 +11,7 @@ Minestom is an alternative to the popular Minecraft server API called Bukkit. The main difference is that our implementation of the Notchian server does not contain any features by default! However, we have a complete API which allows you to make anything possible with current spigot plugins. -This is a developer API not meant to be used by the end-users. Replacing Spigot/Paper with this will **not** work since we do not implement the Bukkit API +This is a developer API not meant to be used by the end-users. Replacing Spigot/Paper with this will **not** work since we do not implement the Bukkit API. # Table of contents - [Install](#install) @@ -32,6 +32,8 @@ Our own expanded version for Vanilla can be found [here](https://github.com/Mine An example of how to use the Minestom library is available [here](/src/test/java/demo). Alternatively you can check the official wiki [here](https://wiki.minestom.com/). +This mean you need to add Minestom as a dependency, add your code and compile by yourself. + # Why Minestom? Minecraft evolved a lot since its release, most of the servers today do not take advantage of vanilla features and even have to struggle because of them. Our target audience is those who want to make a completely different server compared to default Minecraft gamemode such as survival or creative building. The goal is to offer more performance for those who need it, Minecraft being single-threaded is the most important problem for them. From a8cdefc09eda522fd802345c461871166050e299 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 13:58:27 +0100 Subject: [PATCH 19/55] Added more usage info in README.md --- .github/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/README.md b/.github/README.md index 16d0d6282..c5af0c555 100644 --- a/.github/README.md +++ b/.github/README.md @@ -28,12 +28,12 @@ Minestom is similar to Bukkit in the fact that it is not a standlone program, it It is the base for interfacing between the server and client. Our own expanded version for Vanilla can be found [here](https://github.com/Minestom/VanillaReimplementation). +This mean you need to add Minestom as a dependency, add your code and compile by yourself. + # Usage An example of how to use the Minestom library is available [here](/src/test/java/demo). Alternatively you can check the official wiki [here](https://wiki.minestom.com/). -This mean you need to add Minestom as a dependency, add your code and compile by yourself. - # Why Minestom? Minecraft evolved a lot since its release, most of the servers today do not take advantage of vanilla features and even have to struggle because of them. Our target audience is those who want to make a completely different server compared to default Minecraft gamemode such as survival or creative building. The goal is to offer more performance for those who need it, Minecraft being single-threaded is the most important problem for them. From b50530b4e7c5f072fd1bf90236e843385343b626 Mon Sep 17 00:00:00 2001 From: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> Date: Fri, 6 Nov 2020 00:36:45 +1000 Subject: [PATCH 20/55] Fix rgb byte overflow Fix rgb byte overflow: Changed from concatenation and logic to bitwise ops --- .../java/net/minestom/server/chat/ChatColor.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index 73435bec4..a43e16f8f 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -263,19 +263,9 @@ public class ChatColor { code = codeName; } else { // RGB color - String redH = Integer.toHexString(red); - if (redH.length() == 1) - redH = "0" + redH; + int color = (red & 0xFF) << 16 | (green & 0xFF) << 8 | blue & 0xFF; - String greenH = Integer.toHexString(green); - if (greenH.length() == 1) - greenH = "0" + greenH; - - String blueH = Integer.toHexString(blue); - if (blueH.length() == 1) - blueH = "0" + blueH; - - code = redH + greenH + blueH; + code = "#"+Integer.toHexString(color); } return header + code + footer; From 16051e09de01ac7bbd194525ca877edd514252d6 Mon Sep 17 00:00:00 2001 From: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> Date: Fri, 6 Nov 2020 01:15:06 +1000 Subject: [PATCH 21/55] Minor typo fix Forgot to remove reduntant hashtag --- src/main/java/net/minestom/server/chat/ChatColor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index a43e16f8f..4a4e5a710 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -262,10 +262,10 @@ public class ChatColor { // color or special code (white/red/reset/bold/etc...) code = codeName; } else { - // RGB color + // RGB color (special code not set) int color = (red & 0xFF) << 16 | (green & 0xFF) << 8 | blue & 0xFF; - code = "#"+Integer.toHexString(color); + code = Integer.toHexString(color); } return header + code + footer; From 428cea625e2f9390c3b9d1a0f610c043badb4824 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 16:37:16 +0100 Subject: [PATCH 22/55] Removed Block empty duplicate --- src/main/java/net/minestom/server/instance/BlockModifier.java | 2 +- src/main/java/net/minestom/server/instance/block/Block.java | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/main/java/net/minestom/server/instance/block/Block.java diff --git a/src/main/java/net/minestom/server/instance/BlockModifier.java b/src/main/java/net/minestom/server/instance/BlockModifier.java index 5146c1d37..3cfb2b852 100644 --- a/src/main/java/net/minestom/server/instance/BlockModifier.java +++ b/src/main/java/net/minestom/server/instance/BlockModifier.java @@ -76,7 +76,7 @@ public interface BlockModifier { setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), block); } - default void setBlockStateId(BlockPosition blockPosition, short blockStateId) { + default void setBlockStateId(@NotNull BlockPosition blockPosition, short blockStateId) { Check.notNull(blockPosition, "The block position cannot be null"); setBlockStateId(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockStateId); } diff --git a/src/main/java/net/minestom/server/instance/block/Block.java b/src/main/java/net/minestom/server/instance/block/Block.java deleted file mode 100644 index e69de29bb..000000000 From 46d008b59541494d6c5675c31ad6956c011e26e8 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 22:20:51 +0100 Subject: [PATCH 23/55] Added annotations in PacketUtils --- .../java/net/minestom/server/utils/PacketUtils.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 20a707b3f..d526127cd 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; /** * Class used to write packets. @@ -20,7 +21,7 @@ public final class PacketUtils { * @param buf the recipient of {@code packet} * @param packet the packet to write into {@code buf} */ - public static void writePacket(ByteBuf buf, ServerPacket packet) { + public static void writePacket(@NotNull ByteBuf buf, @NotNull ServerPacket packet) { final ByteBuf packetBuffer = getPacketBuffer(packet); @@ -33,7 +34,8 @@ public final class PacketUtils { * @param packet the packet to write * @return a {@link ByteBuf} containing {@code packet} */ - public static ByteBuf writePacket(ServerPacket packet) { + @NotNull + public static ByteBuf writePacket(@NotNull ServerPacket packet) { final ByteBuf packetBuffer = getPacketBuffer(packet); // Add 5 for the packet id and for the packet size @@ -52,7 +54,7 @@ public final class PacketUtils { * @param packetBuffer the buffer containing the raw packet data * @param packetId the packet id */ - private static void writePacket(ByteBuf buf, ByteBuf packetBuffer, int packetId) { + private static void writePacket(@NotNull ByteBuf buf, @NotNull ByteBuf packetBuffer, int packetId) { Utils.writeVarIntBuf(buf, packetId); buf.writeBytes(packetBuffer); } @@ -63,7 +65,8 @@ public final class PacketUtils { * @param packet the packet to write * @return the {@link ByteBuf} containing the raw packet data */ - private static ByteBuf getPacketBuffer(ServerPacket packet) { + @NotNull + private static ByteBuf getPacketBuffer(@NotNull ServerPacket packet) { BinaryWriter writer = new BinaryWriter(); packet.write(writer); From 0ee8eb7d459c7c9975818e341f64533ee0e3999b Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 22:37:04 +0100 Subject: [PATCH 24/55] Removed the instance from Chunk constructor --- .../java/net/minestom/server/instance/Chunk.java | 12 ++++-------- .../minestom/server/instance/DynamicChunk.java | 8 ++++---- .../server/instance/InstanceContainer.java | 6 +++--- .../server/instance/MinestomBasicChunkLoader.java | 2 +- .../net/minestom/server/instance/StaticChunk.java | 8 ++++---- .../server/utils/chunk/ChunkSupplier.java | 15 ++++++++------- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index dc0816e36..c52170bc5 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -60,8 +60,6 @@ public abstract class Chunk implements Viewable, DataContainer { public static final int BIOME_COUNT = 1024; // 4x4x4 blocks group - @NotNull - protected final Instance instance; @NotNull protected final Biome[] biomes; protected final int chunkX, chunkZ; @@ -84,8 +82,7 @@ public abstract class Chunk implements Viewable, DataContainer { // Data protected Data data; - public Chunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ, boolean shouldGenerate) { - this.instance = instance; + public Chunk(@Nullable Biome[] biomes, int chunkX, int chunkZ, boolean shouldGenerate) { this.chunkX = chunkX; this.chunkZ = chunkZ; this.shouldGenerate = shouldGenerate; @@ -236,13 +233,12 @@ public abstract class Chunk implements Viewable, DataContainer { *

* The instance and chunk position (X/Z) can be modified using the given arguments. * - * @param instance the instance of the new chunk - * @param chunkX the new chunk X - * @param chunkZ the new chunK Z + * @param chunkX the new chunk X + * @param chunkZ the new chunk Z * @return a copy of this chunk with a potentially new instance and position */ @NotNull - public abstract Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ); + public abstract Chunk copy(int chunkX, int chunkZ); /** * Gets the {@link CustomBlock} at a position. diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index f378a3b7f..2bf81c6a9 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -60,8 +60,8 @@ public class DynamicChunk extends Chunk { // Block entities protected final Set blockEntities = new CopyOnWriteArraySet<>(); - public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ) { - super(instance, biomes, chunkX, chunkZ, true); + public DynamicChunk(@Nullable Biome[] biomes, int chunkX, int chunkZ) { + super(biomes, chunkX, chunkZ, true); } @Override @@ -407,8 +407,8 @@ public class DynamicChunk extends Chunk { @NotNull @Override - public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) { - DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ); + public Chunk copy(int chunkX, int chunkZ) { + DynamicChunk dynamicChunk = new DynamicChunk(biomes.clone(), chunkX, chunkZ); ArrayUtils.copyToDestination(blocksStateId, dynamicChunk.blocksStateId); ArrayUtils.copyToDestination(customBlocksId, dynamicChunk.customBlocksId); dynamicChunk.blocksData.putAll(blocksData); diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 7328e94f0..14bcabf6f 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -533,7 +533,7 @@ public class InstanceContainer extends Instance { chunkGenerator.fillBiomes(biomes, chunkX, chunkZ); } - final Chunk chunk = chunkSupplier.getChunk(this, biomes, chunkX, chunkZ); + final Chunk chunk = chunkSupplier.getChunk(biomes, chunkX, chunkZ); Check.notNull(chunk, "Chunks supplied by a ChunkSupplier cannot be null."); cacheChunk(chunk); @@ -619,7 +619,7 @@ public class InstanceContainer extends Instance { /** * Copies all the chunks of this instance and create a new instance container with all of them. *

- * Chunks are copied with {@link Chunk#copy(Instance, int, int)}, + * Chunks are copied with {@link Chunk#copy(int, int)}, * {@link UUID} is randomized, {@link DimensionType} is passed over and the {@link StorageLocation} is null. * * @return an {@link InstanceContainer} with the exact same chunks as 'this' @@ -635,7 +635,7 @@ public class InstanceContainer extends Instance { final long index = entry.getKey(); final Chunk chunk = entry.getValue(); - final Chunk copiedChunk = chunk.copy(copiedInstance, chunk.getChunkX(), chunk.getChunkZ()); + final Chunk copiedChunk = chunk.copy(chunk.getChunkX(), chunk.getChunkZ()); copiedChunks.put(index, copiedChunk); UPDATE_MANAGER.signalChunkLoad(copiedInstance, chunk.getChunkX(), chunk.getChunkZ()); diff --git a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java index f87b33c1c..50ee59561 100644 --- a/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java +++ b/src/main/java/net/minestom/server/instance/MinestomBasicChunkLoader.java @@ -77,7 +77,7 @@ public class MinestomBasicChunkLoader implements IChunkLoader { // Found, load from result bytes BinaryReader reader = new BinaryReader(bytes); // Create the chunk object using the instance's ChunkSupplier to support multiple implementations - Chunk chunk = instanceContainer.getChunkSupplier().getChunk(instance, null, chunkX, chunkZ); + Chunk chunk = instanceContainer.getChunkSupplier().getChunk(null, chunkX, chunkZ); // Execute the callback once all blocks are placed (allow for multithreaded implementations) chunk.readChunk(reader, callback); return true; diff --git a/src/main/java/net/minestom/server/instance/StaticChunk.java b/src/main/java/net/minestom/server/instance/StaticChunk.java index 7fd749d26..7a6fccd43 100644 --- a/src/main/java/net/minestom/server/instance/StaticChunk.java +++ b/src/main/java/net/minestom/server/instance/StaticChunk.java @@ -28,8 +28,8 @@ public class StaticChunk extends Chunk { protected final BlockProvider blockProvider; - public StaticChunk(Instance instance, Biome[] biomes, int chunkX, int chunkZ, BlockProvider blockProvider) { - super(instance, biomes, chunkX, chunkZ, false); + public StaticChunk(Biome[] biomes, int chunkX, int chunkZ, BlockProvider blockProvider) { + super(biomes, chunkX, chunkZ, false); this.blockProvider = blockProvider; setReadOnly(true); } @@ -114,8 +114,8 @@ public class StaticChunk extends Chunk { @NotNull @Override - public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) { - StaticChunk staticChunk = new StaticChunk(instance, biomes.clone(), chunkX, chunkZ, blockProvider); + public Chunk copy(int chunkX, int chunkZ) { + StaticChunk staticChunk = new StaticChunk(biomes.clone(), chunkX, chunkZ, blockProvider); // Prevent re-writing the whole packet since it is static anyway final ByteBuf packetBuffer = getFullDataPacket(); if (packetBuffer != null) { diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkSupplier.java b/src/main/java/net/minestom/server/utils/chunk/ChunkSupplier.java index 78c3ca742..56d11b687 100644 --- a/src/main/java/net/minestom/server/utils/chunk/ChunkSupplier.java +++ b/src/main/java/net/minestom/server/utils/chunk/ChunkSupplier.java @@ -1,8 +1,9 @@ package net.minestom.server.utils.chunk; import net.minestom.server.instance.Chunk; -import net.minestom.server.instance.Instance; import net.minestom.server.world.biomes.Biome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Used to customize which type of {@link Chunk} an implementation should use. @@ -11,13 +12,13 @@ import net.minestom.server.world.biomes.Biome; public interface ChunkSupplier { /** - * Create a {@link Chunk} object. + * Creates a {@link Chunk} object. * - * @param instance the {@link Instance} assigned to the chunk - * @param biomes the biomes of the chunk, can be null - * @param chunkX the chunk X - * @param chunkZ the chunk Z + * @param biomes the biomes of the chunk, can be null + * @param chunkX the chunk X + * @param chunkZ the chunk Z * @return a newly {@link Chunk} object, cannot be null */ - Chunk getChunk(Instance instance, Biome[] biomes, int chunkX, int chunkZ); + @NotNull + Chunk getChunk(@Nullable Biome[] biomes, int chunkX, int chunkZ); } From 73d99bf4624cff7fde219e3c318314ea9cd03fa6 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 22:42:03 +0100 Subject: [PATCH 25/55] Made InstanceContainer#cacheChunk public --- .../java/net/minestom/server/instance/InstanceContainer.java | 4 +++- src/main/java/net/minestom/server/instance/StaticChunk.java | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 14bcabf6f..1812ecec9 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -677,10 +677,12 @@ public class InstanceContainer extends Instance { /** * Adds a {@link Chunk} to the internal instance map. + *

+ * WARNING: the chunk will not automatically be sent to players * * @param chunk the chunk to cache */ - private void cacheChunk(Chunk chunk) { + public void cacheChunk(@NotNull Chunk chunk) { final long index = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ()); this.chunks.put(index, chunk); } diff --git a/src/main/java/net/minestom/server/instance/StaticChunk.java b/src/main/java/net/minestom/server/instance/StaticChunk.java index 7a6fccd43..eaa8558ca 100644 --- a/src/main/java/net/minestom/server/instance/StaticChunk.java +++ b/src/main/java/net/minestom/server/instance/StaticChunk.java @@ -28,14 +28,15 @@ public class StaticChunk extends Chunk { protected final BlockProvider blockProvider; - public StaticChunk(Biome[] biomes, int chunkX, int chunkZ, BlockProvider blockProvider) { + public StaticChunk(@Nullable Biome[] biomes, int chunkX, int chunkZ, + @NotNull BlockProvider blockProvider) { super(biomes, chunkX, chunkZ, false); this.blockProvider = blockProvider; setReadOnly(true); } @Override - public void UNSAFE_setBlock(int x, int y, int z, short blockStateId, short customBlockId, Data data, boolean updatable) { + public void UNSAFE_setBlock(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data, boolean updatable) { //noop } From 7ba1b9e174d3d211694555b8b8849c4687efc6a3 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 5 Nov 2020 22:53:46 +0100 Subject: [PATCH 26/55] Added additional information in InstanceContainer#cacheChunk about signaling --- .../java/net/minestom/server/instance/InstanceContainer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 1812ecec9..dfc12eaf8 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -678,7 +678,8 @@ public class InstanceContainer extends Instance { /** * Adds a {@link Chunk} to the internal instance map. *

- * WARNING: the chunk will not automatically be sent to players + * WARNING: the chunk will not automatically be sent to players and + * {@link net.minestom.server.UpdateManager#signalChunkLoad(Instance, int, int)} must be called manually. * * @param chunk the chunk to cache */ From c2136fb379fe017fef63de73c6efffbca0358b18 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 6 Nov 2020 15:24:04 +0100 Subject: [PATCH 27/55] Fixed English --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index c5af0c555..2b3f4101c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -28,7 +28,7 @@ Minestom is similar to Bukkit in the fact that it is not a standlone program, it It is the base for interfacing between the server and client. Our own expanded version for Vanilla can be found [here](https://github.com/Minestom/VanillaReimplementation). -This mean you need to add Minestom as a dependency, add your code and compile by yourself. +This means you need to add Minestom as a dependency, add your code and compile by yourself. # Usage An example of how to use the Minestom library is available [here](/src/test/java/demo). From fc4501501b9454c7ab446507b6b031a351decb4e Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 6 Nov 2020 22:13:52 +0100 Subject: [PATCH 28/55] Fix tasks not being canceled properly + annotations --- .../server/timer/SchedulerManager.java | 26 ++++++++----------- .../java/net/minestom/server/timer/Task.java | 17 ++++++++---- .../minestom/server/timer/TaskBuilder.java | 16 ++++++++---- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/minestom/server/timer/SchedulerManager.java b/src/main/java/net/minestom/server/timer/SchedulerManager.java index 155e145a5..caf0833e3 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerManager.java +++ b/src/main/java/net/minestom/server/timer/SchedulerManager.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectCollection; import net.minestom.server.MinecraftServer; import net.minestom.server.utils.thread.MinestomThread; +import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.concurrent.ExecutorService; @@ -64,7 +65,8 @@ public final class SchedulerManager { * @param runnable The {@link Task} to run when scheduled * @return the {@link TaskBuilder} */ - public TaskBuilder buildTask(Runnable runnable) { + @NotNull + public TaskBuilder buildTask(@NotNull Runnable runnable) { return new TaskBuilder(this, runnable); } @@ -74,7 +76,8 @@ public final class SchedulerManager { * @param runnable The shutdown {@link Task} to run when scheduled * @return the {@link TaskBuilder} */ - public TaskBuilder buildShutdownTask(Runnable runnable) { + @NotNull + public TaskBuilder buildShutdownTask(@NotNull Runnable runnable) { return new TaskBuilder(this, runnable, true); } @@ -83,19 +86,8 @@ public final class SchedulerManager { * * @param task The {@link Task} to remove */ - public void removeTask(Task task) { - synchronized (tasks) { - this.tasks.remove(task.getId()); - } - } - - /** - * Removes/Forces the end of a {@link Task}. - * - * @param task The {@link Task} to remove - */ - public void removeShutdownTask(Task task) { - this.shutdownTasks.remove(task.getId()); + public void removeTask(@NotNull Task task) { + task.cancel(); } /** @@ -141,6 +133,7 @@ public final class SchedulerManager { * * @return a {@link Collection} with all the registered {@link Task} */ + @NotNull public ObjectCollection getTasks() { return tasks.values(); } @@ -150,6 +143,7 @@ public final class SchedulerManager { * * @return a {@link Collection} with all the registered shutdown {@link Task} */ + @NotNull public ObjectCollection getShutdownTasks() { return shutdownTasks.values(); } @@ -159,6 +153,7 @@ public final class SchedulerManager { * * @return the execution service for all the registered {@link Task} */ + @NotNull public ExecutorService getBatchesPool() { return batchesPool; } @@ -168,6 +163,7 @@ public final class SchedulerManager { * * @return the scheduled execution service for all the registered {@link Task} */ + @NotNull public ScheduledExecutorService getTimerExecutionService() { return timerExecutionService; } diff --git a/src/main/java/net/minestom/server/timer/Task.java b/src/main/java/net/minestom/server/timer/Task.java index 9200e9346..ee5afad92 100644 --- a/src/main/java/net/minestom/server/timer/Task.java +++ b/src/main/java/net/minestom/server/timer/Task.java @@ -1,5 +1,8 @@ package net.minestom.server.timer; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.jetbrains.annotations.NotNull; + import java.util.Objects; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -37,7 +40,7 @@ public class Task implements Runnable { * @param delay The time to delay * @param repeat The time until the repetition */ - public Task(SchedulerManager schedulerManager, Runnable runnable, boolean shutdown, long delay, long repeat) { + public Task(@NotNull SchedulerManager schedulerManager, @NotNull Runnable runnable, boolean shutdown, long delay, long repeat) { this.schedulerManager = schedulerManager; this.runnable = runnable; this.shutdown = shutdown; @@ -84,6 +87,7 @@ public class Task implements Runnable { * * @return the current stats of the task */ + @NotNull public TaskStatus getStatus() { if (this.future == null) return TaskStatus.SCHEDULED; if (this.future.isCancelled()) return TaskStatus.CANCELLED; @@ -119,10 +123,13 @@ public class Task implements Runnable { * Removes the task. */ private void finish() { - if (this.shutdown) - this.schedulerManager.removeShutdownTask(this); - else - this.schedulerManager.removeTask(this); + Int2ObjectMap taskMap = shutdown ? + this.schedulerManager.shutdownTasks : + this.schedulerManager.tasks; + + synchronized (taskMap) { + taskMap.remove(getId()); + } } @Override diff --git a/src/main/java/net/minestom/server/timer/TaskBuilder.java b/src/main/java/net/minestom/server/timer/TaskBuilder.java index 8c967d276..a127513e2 100644 --- a/src/main/java/net/minestom/server/timer/TaskBuilder.java +++ b/src/main/java/net/minestom/server/timer/TaskBuilder.java @@ -2,6 +2,7 @@ package net.minestom.server.timer; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.NotNull; /** * A builder which represents a fluent Object to schedule tasks. @@ -30,7 +31,7 @@ public class TaskBuilder { * @param schedulerManager The manager for the tasks * @param runnable The task to run when scheduled */ - public TaskBuilder(SchedulerManager schedulerManager, Runnable runnable) { + public TaskBuilder(@NotNull SchedulerManager schedulerManager, @NotNull Runnable runnable) { this(schedulerManager, runnable, false); } @@ -41,7 +42,7 @@ public class TaskBuilder { * @param runnable The task to run when scheduled * @param shutdown Defines whether the task is a shutdown task */ - public TaskBuilder(SchedulerManager schedulerManager, Runnable runnable, boolean shutdown) { + public TaskBuilder(@NotNull SchedulerManager schedulerManager, @NotNull Runnable runnable, boolean shutdown) { this.schedulerManager = schedulerManager; this.runnable = runnable; this.shutdown = shutdown; @@ -54,7 +55,8 @@ public class TaskBuilder { * @param unit The unit of time for {@code time} * @return this builder, for chaining */ - public TaskBuilder delay(long time, TimeUnit unit) { + @NotNull + public TaskBuilder delay(long time, @NotNull TimeUnit unit) { this.delay = unit.toMilliseconds(time); return this; } @@ -66,7 +68,8 @@ public class TaskBuilder { * @param unit The {@link TimeUnit} for {@code time} * @return this builder, for chaining */ - public TaskBuilder repeat(long time, TimeUnit unit) { + @NotNull + public TaskBuilder repeat(long time, @NotNull TimeUnit unit) { this.repeat = unit.toMilliseconds(time); return this; } @@ -76,6 +79,7 @@ public class TaskBuilder { * * @return this builder, for chaining */ + @NotNull public TaskBuilder clearDelay() { this.delay = 0L; return this; @@ -86,16 +90,18 @@ public class TaskBuilder { * * @return this builder, for chaining */ + @NotNull public TaskBuilder clearRepeat() { this.repeat = 0L; return this; } /** - * Schedule this {@link Task} for execution. + * Schedules this {@link Task} for execution. * * @return the built {@link Task} */ + @NotNull public Task schedule() { Task task = new Task( this.schedulerManager, From cf6fbd3d346bd81aa8fe902606dca58591690339 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 6 Nov 2020 22:58:38 +0100 Subject: [PATCH 29/55] Cleanup --- .../java/net/minestom/server/instance/WorldBorder.java | 4 ++-- .../server/listener/manager/PacketController.java | 2 +- .../java/net/minestom/server/timer/SchedulerManager.java | 8 +++++--- src/main/java/net/minestom/server/timer/Task.java | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/WorldBorder.java b/src/main/java/net/minestom/server/instance/WorldBorder.java index 74d64d310..813eec4e4 100644 --- a/src/main/java/net/minestom/server/instance/WorldBorder.java +++ b/src/main/java/net/minestom/server/instance/WorldBorder.java @@ -29,7 +29,7 @@ public class WorldBorder { private int warningTime; private int warningBlocks; - protected WorldBorder(Instance instance) { + protected WorldBorder(@NotNull Instance instance) { this.instance = instance; this.oldDiameter = Double.MAX_VALUE; @@ -272,7 +272,7 @@ public class WorldBorder { * * @param worldBorderPacket the packet to send */ - private void sendPacket(WorldBorderPacket worldBorderPacket) { + private void sendPacket(@NotNull WorldBorderPacket worldBorderPacket) { PacketWriterUtils.writeAndSend(instance.getPlayers(), worldBorderPacket); } diff --git a/src/main/java/net/minestom/server/listener/manager/PacketController.java b/src/main/java/net/minestom/server/listener/manager/PacketController.java index 926dce935..6c3c02a28 100644 --- a/src/main/java/net/minestom/server/listener/manager/PacketController.java +++ b/src/main/java/net/minestom/server/listener/manager/PacketController.java @@ -47,7 +47,7 @@ public class PacketController { /** * Changes the packet listener, setting it to null cancel the listener. *

- * WARNING: this will overwrite the default minestom listener. + * WARNING: this will overwrite the default minestom listener, be sure to know what you are doing. * * @param packetListenerConsumer the new packet listener, can be null */ diff --git a/src/main/java/net/minestom/server/timer/SchedulerManager.java b/src/main/java/net/minestom/server/timer/SchedulerManager.java index caf0833e3..407cd42c6 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerManager.java +++ b/src/main/java/net/minestom/server/timer/SchedulerManager.java @@ -19,10 +19,10 @@ import java.util.concurrent.atomic.AtomicInteger; *

* {@link Task} first need to be built with {@link #buildTask(Runnable)}, you can then specify a delay with as example * {@link TaskBuilder#delay(long, net.minestom.server.utils.time.TimeUnit)} - * or {@link TaskBuilder#repeat(long, net.minestom.server.utils.time.TimeUnit)} - * and to finally schedule {@link TaskBuilder#schedule()}. + * or {@link TaskBuilder#repeat(long, net.minestom.server.utils.time.TimeUnit)}, + * and to finally schedule: {@link TaskBuilder#schedule()}. *

- * Shutdown {@link Task} are built with {@link #buildShutdownTask(Runnable)}. + * Shutdown tasks are built with {@link #buildShutdownTask(Runnable)} and are executed, as the name implies, when the server stops. */ public final class SchedulerManager { @@ -83,6 +83,8 @@ public final class SchedulerManager { /** * Removes/Forces the end of a {@link Task}. + *

+ * {@link Task#cancel()} can also be used instead. * * @param task The {@link Task} to remove */ diff --git a/src/main/java/net/minestom/server/timer/Task.java b/src/main/java/net/minestom/server/timer/Task.java index ee5afad92..f9899d0c5 100644 --- a/src/main/java/net/minestom/server/timer/Task.java +++ b/src/main/java/net/minestom/server/timer/Task.java @@ -120,7 +120,7 @@ public class Task implements Runnable { } /** - * Removes the task. + * Removes the task from the {@link SchedulerManager} map. */ private void finish() { Int2ObjectMap taskMap = shutdown ? From 66fc6779b64e10050c9b94b3b8a1cabe4075d5d1 Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 03:40:37 +0100 Subject: [PATCH 30/55] Dynamic arguments now also have an optional dynamic restriction --- .../arguments/ArgumentDynamicStringArray.java | 29 +++++++++++++++++ .../arguments/ArgumentDynamicWord.java | 32 +++++++++++++++++++ .../builder/arguments/ArgumentWord.java | 2 +- .../validator/StringArrayValidator.java | 8 +++++ .../callback/validator/StringValidator.java | 8 +++++ .../utils/callback/validator/Validator.java | 19 +++++++++++ src/test/java/demo/commands/TestCommand.java | 11 ++++--- 7 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/callback/validator/StringArrayValidator.java create mode 100644 src/main/java/net/minestom/server/utils/callback/validator/StringValidator.java create mode 100644 src/main/java/net/minestom/server/utils/callback/validator/Validator.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java index e254e07f4..71689b35e 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java @@ -1,6 +1,8 @@ package net.minestom.server.command.builder.arguments; +import net.minestom.server.utils.callback.validator.StringArrayValidator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.regex.Pattern; @@ -10,6 +12,10 @@ import java.util.regex.Pattern; */ public class ArgumentDynamicStringArray extends Argument { + public static final int RESTRICTION_ERROR = 1; + + private StringArrayValidator dynamicRestriction; + public ArgumentDynamicStringArray(String id) { super(id, true, true); } @@ -27,6 +33,29 @@ public class ArgumentDynamicStringArray extends Argument { @Override public int getConditionResult(@NotNull String[] value) { + + // true if 'value' is valid based on the dynamic restriction + final boolean restrictionCheck = dynamicRestriction != null ? + dynamicRestriction.isValid(value) : true; + + if (!restrictionCheck) { + return RESTRICTION_ERROR; + } + return SUCCESS; } + + /** + * Sets the dynamic restriction of this dynamic argument. + *

+ * Will be called once the argument condition is checked. + * + * @param dynamicRestriction the dynamic restriction, can be null to disable + * @return 'this' for chaining + */ + public ArgumentDynamicStringArray fromRestrictions(@Nullable StringArrayValidator dynamicRestriction) { + this.dynamicRestriction = dynamicRestriction; + return this; + } + } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java index c4b88384e..df259bebe 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java @@ -1,6 +1,8 @@ package net.minestom.server.command.builder.arguments; +import net.minestom.server.utils.callback.validator.StringValidator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Same as {@link ArgumentWord} with the exception @@ -8,12 +10,20 @@ import org.jetbrains.annotations.NotNull; */ public class ArgumentDynamicWord extends Argument { + public static final int SPACE_ERROR = 1; + public static final int RESTRICTION_ERROR = 2; + + private StringValidator dynamicRestriction; + public ArgumentDynamicWord(String id) { super(id); } @Override public int getCorrectionResult(@NotNull String value) { + if (value.contains(" ")) + return SPACE_ERROR; + return SUCCESS; } @@ -25,6 +35,28 @@ public class ArgumentDynamicWord extends Argument { @Override public int getConditionResult(@NotNull String value) { + + // true if 'value' is valid based on the dynamic restriction + final boolean restrictionCheck = dynamicRestriction != null ? + dynamicRestriction.isValid(value) : true; + + if (!restrictionCheck) { + return RESTRICTION_ERROR; + } + return SUCCESS; } + + /** + * Sets the dynamic restriction of this dynamic argument. + *

+ * Will be called once the argument condition is checked. + * + * @param dynamicRestriction the dynamic restriction, can be null to disable + * @return 'this' for chaining + */ + public ArgumentDynamicWord fromRestrictions(@Nullable StringValidator dynamicRestriction) { + this.dynamicRestriction = dynamicRestriction; + return this; + } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java index d596d3266..5a28ec3e2 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java @@ -20,7 +20,7 @@ public class ArgumentWord extends Argument { protected String[] restrictions; public ArgumentWord(String id) { - super(id, false); + super(id); } /** diff --git a/src/main/java/net/minestom/server/utils/callback/validator/StringArrayValidator.java b/src/main/java/net/minestom/server/utils/callback/validator/StringArrayValidator.java new file mode 100644 index 000000000..a818399d8 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/validator/StringArrayValidator.java @@ -0,0 +1,8 @@ +package net.minestom.server.utils.callback.validator; + +/** + * Interface used when a string array needs to be validated dynamically. + */ +@FunctionalInterface +public interface StringArrayValidator extends Validator { +} diff --git a/src/main/java/net/minestom/server/utils/callback/validator/StringValidator.java b/src/main/java/net/minestom/server/utils/callback/validator/StringValidator.java new file mode 100644 index 000000000..ca9e1a133 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/validator/StringValidator.java @@ -0,0 +1,8 @@ +package net.minestom.server.utils.callback.validator; + +/** + * Interface used when a string needs to be validated dynamically. + */ +@FunctionalInterface +public interface StringValidator extends Validator { +} diff --git a/src/main/java/net/minestom/server/utils/callback/validator/Validator.java b/src/main/java/net/minestom/server/utils/callback/validator/Validator.java new file mode 100644 index 000000000..081f77971 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/validator/Validator.java @@ -0,0 +1,19 @@ +package net.minestom.server.utils.callback.validator; + +import org.jetbrains.annotations.NotNull; + +/** + * Interface used when a value needs to be validated dynamically. + */ +@FunctionalInterface +public interface Validator { + + /** + * Gets if a value is valid based on a condition. + * + * @param value the value to check + * @return true if the value is valid, false otherwise + */ + boolean isValid(@NotNull T value); + +} diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index e1396cbea..456458317 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -1,6 +1,5 @@ package demo.commands; -import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Arguments; import net.minestom.server.command.builder.Command; @@ -18,7 +17,11 @@ public class TestCommand extends Command { //addSyntax(this::execute, dynamicWord); } - Argument test = ArgumentType.Integer("number"); + Argument test = ArgumentType.DynamicWord("testArg").fromRestrictions(value -> value.contains("a")); + + test.setCallback((source, value, error) -> { + System.out.println("ERROR " + error); + }); setDefaultExecutor((source, args) -> { System.out.println("DEFAULT"); @@ -26,9 +29,7 @@ public class TestCommand extends Command { }); addSyntax((source, args) -> { - int number = args.getInteger("number"); - source.sendMessage("set view to " + number); - MinecraftServer.setEntityViewDistance(number); + System.out.println("HEY IT WORKS"); }, test); } From b2097a338d432b3c51c0b53b49473c3349125eac Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 03:45:25 +0100 Subject: [PATCH 31/55] Added comments for Argument constructors --- .../command/builder/arguments/Argument.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java index 59187fa09..c8b0bfa92 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/Argument.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/Argument.java @@ -29,16 +29,34 @@ public abstract class Argument { private ArgumentCallback callback; + /** + * Creates a new argument. + * + * @param id the id of the argument, used to retrieve the parsed value + * @param allowSpace true if the argument can/should have spaces in it + * @param useRemaining true if the argument will always take the rest of the command arguments + */ public Argument(@NotNull String id, boolean allowSpace, boolean useRemaining) { this.id = id; this.allowSpace = allowSpace; this.useRemaining = useRemaining; } + /** + * Creates a new argument with {@code useRemaining} sets to false. + * + * @param id the id of the argument, used to retrieve the parsed value + * @param allowSpace true if the argument can/should have spaces in it + */ public Argument(@NotNull String id, boolean allowSpace) { this(id, allowSpace, false); } + /** + * Creates a new argument with {@code useRemaining} and {@code allowSpace} sets to false. + * + * @param id the id of the argument, used to retrieve the parsed value + */ public Argument(@NotNull String id) { this(id, false, false); } @@ -100,8 +118,8 @@ public abstract class Argument { /** * Gets if the argument always use all the remaining characters. *

- * ex: /help I am a test - would get you "I am a test" - * if the sole argument does use the remaining. + * ex: /help I am a test - will always give you "I am a test" + * if the first and single argument does use the remaining. * * @return true if the argument use all the remaining characters, false otherwise */ From 1d30e6e953ada786e58c3736b27ac23771b0c250 Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 04:28:50 +0100 Subject: [PATCH 32/55] Use the Validator interface when possible for readability --- .../server/command/builder/arguments/ArgumentBoolean.java | 2 +- .../command/builder/arguments/number/ArgumentNumber.java | 2 +- .../net/minestom/server/network/ConnectionManager.java | 8 ++++---- .../java/net/minestom/server/thread/ThreadProvider.java | 6 +++--- .../server/utils/callback/validator/EntityValidator.java | 7 +++++++ .../server/utils/callback/validator/PlayerValidator.java | 7 +++++++ 6 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/callback/validator/EntityValidator.java create mode 100644 src/main/java/net/minestom/server/utils/callback/validator/PlayerValidator.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentBoolean.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentBoolean.java index 7020bd490..5b3b002fb 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentBoolean.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentBoolean.java @@ -12,7 +12,7 @@ public class ArgumentBoolean extends Argument { public static final int NOT_BOOLEAN_ERROR = 1; public ArgumentBoolean(String id) { - super(id, false); + super(id); } @Override diff --git a/src/main/java/net/minestom/server/command/builder/arguments/number/ArgumentNumber.java b/src/main/java/net/minestom/server/command/builder/arguments/number/ArgumentNumber.java index a51b94864..fba4a701e 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/number/ArgumentNumber.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/number/ArgumentNumber.java @@ -16,7 +16,7 @@ public abstract class ArgumentNumber extends Argument { protected T min, max; public ArgumentNumber(String id) { - super(id, false); + super(id); } @NotNull diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 1b8015b36..14054c42d 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -7,6 +7,7 @@ import net.minestom.server.listener.manager.PacketConsumer; import net.minestom.server.network.packet.client.login.LoginStartPacket; import net.minestom.server.network.packet.server.play.ChatMessagePacket; import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.utils.callback.validator.PlayerValidator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +15,6 @@ import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Consumer; -import java.util.function.Function; /** * Manages the connected clients. @@ -93,7 +93,7 @@ public final class ConnectionManager { * @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage} * @param condition the condition to receive the message */ - public void broadcastMessage(@NotNull JsonMessage jsonMessage, @Nullable Function condition) { + public void broadcastMessage(@NotNull JsonMessage jsonMessage, @Nullable PlayerValidator condition) { final Collection recipients = getRecipients(condition); if (!recipients.isEmpty()) { @@ -117,7 +117,7 @@ public final class ConnectionManager { PacketWriterUtils.writeAndSend(recipients, chatMessagePacket); } - private Collection getRecipients(@Nullable Function condition) { + private Collection getRecipients(@Nullable PlayerValidator condition) { Collection recipients; // Get the recipients @@ -126,7 +126,7 @@ public final class ConnectionManager { } else { recipients = new ArrayList<>(); getOnlinePlayers().forEach(player -> { - final boolean result = condition.apply(player); + final boolean result = condition.isValid(player); if (result) recipients.add(player); }); diff --git a/src/main/java/net/minestom/server/thread/ThreadProvider.java b/src/main/java/net/minestom/server/thread/ThreadProvider.java index 7d5adb32f..a5f869a5e 100644 --- a/src/main/java/net/minestom/server/thread/ThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/ThreadProvider.java @@ -6,6 +6,7 @@ import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.SharedInstance; +import net.minestom.server.utils.callback.validator.EntityValidator; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.thread.MinestomThread; import org.jetbrains.annotations.NotNull; @@ -16,7 +17,6 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.function.Consumer; -import java.util.function.Function; /** * Used to link chunks into multiple groups. @@ -216,12 +216,12 @@ public abstract class ThreadProvider { * @param condition the condition which confirm if the update happens or not */ protected void conditionalEntityUpdate(@NotNull Instance instance, @NotNull Chunk chunk, long time, - @Nullable Function condition) { + @Nullable EntityValidator condition) { final Set entities = instance.getChunkEntities(chunk); if (!entities.isEmpty()) { for (Entity entity : entities) { - if (condition != null && !condition.apply(entity)) + if (condition != null && !condition.isValid(entity)) continue; entity.tick(time); } diff --git a/src/main/java/net/minestom/server/utils/callback/validator/EntityValidator.java b/src/main/java/net/minestom/server/utils/callback/validator/EntityValidator.java new file mode 100644 index 000000000..d7fb26c60 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/validator/EntityValidator.java @@ -0,0 +1,7 @@ +package net.minestom.server.utils.callback.validator; + +import net.minestom.server.entity.Entity; + +@FunctionalInterface +public interface EntityValidator extends Validator { +} diff --git a/src/main/java/net/minestom/server/utils/callback/validator/PlayerValidator.java b/src/main/java/net/minestom/server/utils/callback/validator/PlayerValidator.java new file mode 100644 index 000000000..2991b98ba --- /dev/null +++ b/src/main/java/net/minestom/server/utils/callback/validator/PlayerValidator.java @@ -0,0 +1,7 @@ +package net.minestom.server.utils.callback.validator; + +import net.minestom.server.entity.Player; + +@FunctionalInterface +public interface PlayerValidator extends Validator { +} From 7bdfc933341436b5bd520df3596dd46b8ce70f6e Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 04:42:48 +0100 Subject: [PATCH 33/55] Added some comments about exceptions --- src/main/java/net/minestom/server/MinecraftServer.java | 4 ++++ src/main/java/net/minestom/server/chat/ChatColor.java | 10 +++++++++- src/main/java/net/minestom/server/entity/Player.java | 6 +++++- .../net/minestom/server/network/ConnectionManager.java | 1 + .../java/net/minestom/server/scoreboard/Sidebar.java | 9 ++++++--- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 1e611764b..68c6a7dd1 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -595,10 +595,13 @@ public class MinecraftServer { /** * Starts the server. + *

+ * It should be called after {@link #init()} and probably your own initialization code. * * @param address the server address * @param port the server port * @param responseDataConsumer the response data consumer, can be null + * @throws IllegalStateException if called before {@link #init()} or if the server is already running */ public void start(String address, int port, ResponseDataConsumer responseDataConsumer) { Check.stateCondition(!initialized, "#start can only be called after #init"); @@ -627,6 +630,7 @@ public class MinecraftServer { * * @param address the server address * @param port the server port + * @see #start(String, int, ResponseDataConsumer) */ public void start(String address, int port) { start(address, port, null); diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index 4a4e5a710..1abc71865 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -242,6 +242,14 @@ public class ChatColor { return codeName; } + /** + * Gets the color id, only present if this color has been retrieved from {@link ChatColor} constants. + *

+ * Should only be used for some special packets which require it. + * + * @return the color id + * @throws IllegalStateException if the color is not from the class constants + */ public int getId() { Check.stateCondition(id == -1, "Please use one of the ChatColor constant instead"); return id; @@ -263,7 +271,7 @@ public class ChatColor { code = codeName; } else { // RGB color (special code not set) - int color = (red & 0xFF) << 16 | (green & 0xFF) << 8 | blue & 0xFF; + final int color = (red & 0xFF) << 16 | (green & 0xFF) << 8 | blue & 0xFF; code = Integer.toHexString(color); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 929a3928e..0a574039e 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1029,6 +1029,7 @@ public class Player extends LivingEntity implements CommandSender { * Sets and refresh client food bar. * * @param food the new food value + * @throws IllegalArgumentException if {@code food} is not between 0 and 20 */ public void setFood(int food) { Check.argCondition(!MathUtils.isBetween(food, 0, 20), "Food has to be between 0 and 20"); @@ -1044,6 +1045,7 @@ public class Player extends LivingEntity implements CommandSender { * Sets and refresh client food saturation. * * @param foodSaturation the food saturation + * @throws IllegalArgumentException if {@code foodSaturation} is not between 0 and 5 */ public void setFoodSaturation(float foodSaturation) { Check.argCondition(!MathUtils.isBetween(foodSaturation, 0, 5), "Food saturation has to be between 0 and 5"); @@ -1218,7 +1220,7 @@ public class Player extends LivingEntity implements CommandSender { * * @param resourcePack the resource pack */ - public void setResourcePack(ResourcePack resourcePack) { + public void setResourcePack(@NotNull ResourcePack resourcePack) { Check.notNull(resourcePack, "The resource pack cannot be null"); final String url = resourcePack.getUrl(); final String hash = resourcePack.getHash(); @@ -1367,6 +1369,7 @@ public class Player extends LivingEntity implements CommandSender { * This cannot change the displayed level, see {@link #setLevel(int)}. * * @param exp a percentage between 0 and 1 + * @throws IllegalArgumentException if {@code exp} is not between 0 and 1 */ public void setExp(float exp) { Check.argCondition(!MathUtils.isBetween(exp, 0, 1), "Exp should be between 0 and 1"); @@ -1880,6 +1883,7 @@ public class Player extends LivingEntity implements CommandSender { * Changes the player permission level. * * @param permissionLevel the new player permission level + * @throws IllegalArgumentException if {@code permissionLevel} is not between 0 and 4 */ public void setPermissionLevel(int permissionLevel) { Check.argCondition(!MathUtils.isBetween(permissionLevel, 0, 4), "permissionLevel has to be between 0 and 4"); diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 14054c42d..9aeae34bc 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -259,6 +259,7 @@ public final class ConnectionManager { * Used during disconnection, you shouldn't have to do it manually. * * @param connection the player connection + * @see PlayerConnection#disconnect() to properly disconnect a player */ public void removePlayer(@NotNull PlayerConnection connection) { final Player player = this.connectionPlayerMap.get(connection); diff --git a/src/main/java/net/minestom/server/scoreboard/Sidebar.java b/src/main/java/net/minestom/server/scoreboard/Sidebar.java index 9e150b978..22bba6dbb 100644 --- a/src/main/java/net/minestom/server/scoreboard/Sidebar.java +++ b/src/main/java/net/minestom/server/scoreboard/Sidebar.java @@ -86,9 +86,12 @@ public class Sidebar implements Scoreboard { } /** - * Creates a new {@link ScoreboardLine} + * Creates a new {@link ScoreboardLine}. * - * @param scoreboardLine The new scoreboard line + * @param scoreboardLine the new scoreboard line + * @throws IllegalStateException if the sidebar cannot take more line + * @throws IllegalArgumentException if the sidebar already contains the line {@code scoreboardLine} + * or has a line with the same id */ public void createLine(ScoreboardLine scoreboardLine) { synchronized (lines) { @@ -114,7 +117,7 @@ public class Sidebar implements Scoreboard { } /** - * Updates a {@link ScoreboardLine} content through the given identifier + * Updates a {@link ScoreboardLine} content through the given identifier. * * @param id The identifier of the {@link ScoreboardLine} * @param content The new content for the {@link ScoreboardLine} From 224626bdbda608e014524d2d7585ea39b776fa90 Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 19:39:22 +0100 Subject: [PATCH 34/55] Added NbtDataImpl to support writing custom item NBT data to the client --- gradle.properties | 2 +- .../java/net/minestom/server/data/Data.java | 16 ++- .../minestom/server/data/DataContainer.java | 2 +- .../net/minestom/server/data/DataImpl.java | 9 ++ .../net/minestom/server/data/DataManager.java | 3 +- .../net/minestom/server/data/NbtDataImpl.java | 52 ++++++++ .../server/data/SerializableDataImpl.java | 28 ++--- .../net/minestom/server/item/ItemStack.java | 22 +++- .../net/minestom/server/utils/NBTUtils.java | 117 ++++++++++++++++-- .../server/utils/PrimitiveConversion.java | 53 +++++--- .../minestom/server/utils/validate/Check.java | 2 +- 11 files changed, 252 insertions(+), 54 deletions(-) create mode 100644 src/main/java/net/minestom/server/data/NbtDataImpl.java diff --git a/gradle.properties b/gradle.properties index d4178c6c0..97206a8bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ asmVersion=8.0.1 mixinVersion=0.8 -hephaistos_version=v1.1.4 \ No newline at end of file +hephaistos_version=v1.1.5 \ No newline at end of file diff --git a/src/main/java/net/minestom/server/data/Data.java b/src/main/java/net/minestom/server/data/Data.java index 650ff6e45..66a9087a9 100644 --- a/src/main/java/net/minestom/server/data/Data.java +++ b/src/main/java/net/minestom/server/data/Data.java @@ -56,11 +56,25 @@ public interface Data { * * @param key the key * @param value the value object, null to remove the key - * @param type the value type, can be null if not in a {@link SerializableData} + * @param type the value type, {@link #set(String, Object)} can be used instead. + * null if {@code value} is also null * @param the value generic */ void set(@NotNull String key, @Nullable T value, @Nullable Class type); + /** + * Assigns a value to a specific key. + *

+ * Will by default call {@link #set(String, Object, Class)} with the type sets to {@link T#getClass()}. + * + * @param key the key + * @param value the value object, null to remove the key + * @param the value generic + */ + default void set(@NotNull String key, @Nullable T value) { + set(key, value, value != null ? (Class) value.getClass() : null); + } + /** * Retrieves a value based on its key. * diff --git a/src/main/java/net/minestom/server/data/DataContainer.java b/src/main/java/net/minestom/server/data/DataContainer.java index f5c4d0086..e5dbddbde 100644 --- a/src/main/java/net/minestom/server/data/DataContainer.java +++ b/src/main/java/net/minestom/server/data/DataContainer.java @@ -26,7 +26,7 @@ public interface DataContainer { * Default implementations are {@link DataImpl} and {@link SerializableDataImpl} depending * on your use-case. * - * @param data the {@link Data} of this container, null to remove it + * @param data the new {@link Data} of this container, null to remove it */ void setData(@Nullable Data data); diff --git a/src/main/java/net/minestom/server/data/DataImpl.java b/src/main/java/net/minestom/server/data/DataImpl.java index 7bc5b312c..0c1a373cb 100644 --- a/src/main/java/net/minestom/server/data/DataImpl.java +++ b/src/main/java/net/minestom/server/data/DataImpl.java @@ -14,12 +14,20 @@ public class DataImpl implements Data { protected final ConcurrentHashMap data = new ConcurrentHashMap<>(); + /** + * Data key -> Class + * Used to know the type of an element of this data object (for serialization purpose) + */ + protected final ConcurrentHashMap dataType = new ConcurrentHashMap<>(); + @Override public void set(@NotNull String key, @Nullable T value, @Nullable Class type) { if (value != null) { this.data.put(key, value); + this.dataType.put(key, type); } else { this.data.remove(key); + this.dataType.remove(key); } } @@ -54,6 +62,7 @@ public class DataImpl implements Data { public Data copy() { DataImpl data = new DataImpl(); data.data.putAll(this.data); + data.dataType.putAll(this.dataType); return data; } diff --git a/src/main/java/net/minestom/server/data/DataManager.java b/src/main/java/net/minestom/server/data/DataManager.java index 718d30f3a..2803dc8f0 100644 --- a/src/main/java/net/minestom/server/data/DataManager.java +++ b/src/main/java/net/minestom/server/data/DataManager.java @@ -86,8 +86,7 @@ public final class DataManager { * * @param clazz the data class * @param the data type - * @return the {@link DataType} associated to the class - * @throws NullPointerException if none is found + * @return the {@link DataType} associated to the class, null if not found */ @Nullable public DataType getDataType(@NotNull Class clazz) { diff --git a/src/main/java/net/minestom/server/data/NbtDataImpl.java b/src/main/java/net/minestom/server/data/NbtDataImpl.java new file mode 100644 index 000000000..fb479fda8 --- /dev/null +++ b/src/main/java/net/minestom/server/data/NbtDataImpl.java @@ -0,0 +1,52 @@ +package net.minestom.server.data; + +import net.minestom.server.utils.NBTUtils; +import net.minestom.server.utils.binary.BinaryWriter; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +import java.util.Map; + +/** + * A data implementation backed by a {@link org.jglrxavpok.hephaistos.nbt.NBTCompound}. + */ +public class NbtDataImpl extends DataImpl { + + // Used to know if a nbt key is from a Data object, should NOT be changed + public static final String KEY_PREFIX = "nbtdata_"; + + @NotNull + @Override + public Data copy() { + DataImpl data = new NbtDataImpl(); + data.data.putAll(this.data); + data.dataType.putAll(this.dataType); + return data; + } + + /** + * Writes all the data into a {@link NBTCompound}. + * + * @param nbtCompound the nbt compound to write to + * @throws NullPointerException if the type of a data is not a primitive nbt type and therefore not supported + * (you can use {@link DataType#encode(BinaryWriter, Object)} to use byte array instead) + */ + public void writeToNbt(@NotNull NBTCompound nbtCompound) { + for (Map.Entry entry : data.entrySet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + + final Class type = dataType.get(key); + + final NBT nbt = NBTUtils.toNBT(value, type, false); + + Check.notNull(nbt, + "The type '" + type + "' is not supported within NbtDataImpl, if you wish to use a custom type you can encode the value into a byte array using a DataType"); + + nbtCompound.set(KEY_PREFIX + key, nbt); + } + } + +} diff --git a/src/main/java/net/minestom/server/data/SerializableDataImpl.java b/src/main/java/net/minestom/server/data/SerializableDataImpl.java index a8431352d..959236494 100644 --- a/src/main/java/net/minestom/server/data/SerializableDataImpl.java +++ b/src/main/java/net/minestom/server/data/SerializableDataImpl.java @@ -14,7 +14,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * {@link SerializableData} implementation based on {@link DataImpl} + * {@link SerializableData} implementation based on {@link DataImpl}. */ public class SerializableDataImpl extends DataImpl implements SerializableData { @@ -25,15 +25,9 @@ public class SerializableDataImpl extends DataImpl implements SerializableData { private static final ConcurrentHashMap nameToClassMap = new ConcurrentHashMap<>(); /** - * Data key -> Class - * Used to know the type of an element of this data object (for serialization purpose) - */ - private final ConcurrentHashMap dataType = new ConcurrentHashMap<>(); - - /** - * Set a value to a specific key + * Sets a value to a specific key. *

- * WARNING: the type needs to be registered in {@link DataManager} + * WARNING: the type needs to be registered in {@link DataManager}. * * @param key the key * @param value the value object @@ -42,18 +36,12 @@ public class SerializableDataImpl extends DataImpl implements SerializableData { * @throws UnsupportedOperationException if {@code type} is not registered in {@link DataManager} */ @Override - public void set(@NotNull String key, @Nullable T value, @NotNull Class type) { - if (value != null) { - if (DATA_MANAGER.getDataType(type) == null) { - throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); - } - - this.data.put(key, value); - this.dataType.put(key, type); - } else { - this.data.remove(key); - this.dataType.remove(key); + public void set(@NotNull String key, @Nullable T value, @Nullable Class type) { + if (type != null && DATA_MANAGER.getDataType(type) == null) { + throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); } + + super.set(key, value, type); } @NotNull diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index 6ffe70e80..f299cb679 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -3,6 +3,7 @@ package net.minestom.server.item; import net.minestom.server.chat.ColoredText; import net.minestom.server.data.Data; import net.minestom.server.data.DataContainer; +import net.minestom.server.data.NbtDataImpl; import net.minestom.server.entity.ItemEntity; import net.minestom.server.entity.Player; import net.minestom.server.inventory.Inventory; @@ -525,7 +526,8 @@ public class ItemStack implements DataContainer { !attributes.isEmpty() || hideFlag != 0 || customModelData != 0 || - (itemMeta != null && itemMeta.hasNbt()); + (itemMeta != null && itemMeta.hasNbt()) || + (data instanceof NbtDataImpl && !data.isEmpty()); } /** @@ -557,16 +559,25 @@ public class ItemStack implements DataContainer { final Data data = getData(); if (data != null) itemStack.setData(data.copy()); + return itemStack; } + @Nullable @Override public Data getData() { return data; } + /** + * Sets the data of this item. + *

+ * It is recommended to use {@link NbtDataImpl} if you want the data to be passed to the client. + * + * @param data the new {@link Data} of this container, null to remove it + */ @Override - public void setData(Data data) { + public void setData(@Nullable Data data) { this.data = data; } @@ -676,6 +687,13 @@ public class ItemStack implements DataContainer { return null; } + /** + * Creates a {@link NBTCompound} containing the data of this item. + *

+ * WARNING: modifying the returned nbt will not affect the item. + * + * @return this item nbt + */ @NotNull public NBTCompound toNBT() { NBTCompound compound = new NBTCompound() diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index 245d85482..4de564447 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -1,9 +1,13 @@ package net.minestom.server.utils; +import net.minestom.server.MinecraftServer; import net.minestom.server.attribute.Attribute; import net.minestom.server.attribute.AttributeOperation; import net.minestom.server.chat.ChatParser; import net.minestom.server.chat.ColoredText; +import net.minestom.server.data.Data; +import net.minestom.server.data.DataType; +import net.minestom.server.data.NbtDataImpl; import net.minestom.server.inventory.Inventory; import net.minestom.server.item.Enchantment; import net.minestom.server.item.ItemStack; @@ -15,6 +19,9 @@ import net.minestom.server.item.metadata.ItemMeta; import net.minestom.server.registry.Registries; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +47,7 @@ public final class NBTUtils { * @param items the items to save * @param destination the inventory destination */ - public static void loadAllItems(NBTList items, Inventory destination) { + public static void loadAllItems(@NotNull NBTList items, @NotNull Inventory destination) { destination.clear(); for (NBTCompound tag : items) { Material item = Registries.getMaterial(tag.getString("id")); @@ -55,7 +62,7 @@ public final class NBTUtils { } } - public static void saveAllItems(NBTList list, Inventory inventory) { + public static void saveAllItems(@NotNull NBTList list, @NotNull Inventory inventory) { for (int i = 0; i < inventory.getSize(); i++) { final ItemStack stack = inventory.getItemStack(i); NBTCompound nbt = new NBTCompound(); @@ -72,7 +79,8 @@ public final class NBTUtils { } } - public static void writeEnchant(NBTCompound nbt, String listName, Map enchantmentMap) { + public static void writeEnchant(@NotNull NBTCompound nbt, @NotNull String listName, + @NotNull Map enchantmentMap) { NBTList enchantList = new NBTList<>(NBTTypes.TAG_Compound); for (Map.Entry entry : enchantmentMap.entrySet()) { final Enchantment enchantment = entry.getKey(); @@ -86,7 +94,8 @@ public final class NBTUtils { nbt.set(listName, enchantList); } - public static ItemStack readItemStack(BinaryReader reader) { + @NotNull + public static ItemStack readItemStack(@NotNull BinaryReader reader) { final boolean present = reader.readBoolean(); if (!present) { @@ -116,7 +125,7 @@ public final class NBTUtils { return item; } - public static void loadDataIntoItem(ItemStack item, NBTCompound nbt) { + public static void loadDataIntoItem(@NotNull ItemStack item, @NotNull NBTCompound nbt) { if (nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage")); if (nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getInt("Unbreakable") == 1); if (nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags")); @@ -182,6 +191,21 @@ public final class NBTUtils { if (itemMeta == null) return; itemMeta.read(nbt); + + NbtDataImpl customData = null; + for (String key : nbt.getKeys()) { + if (key.startsWith(NbtDataImpl.KEY_PREFIX)) { + if (customData == null) { + customData = new NbtDataImpl(); + } + final NBT keyNbt = nbt.get(key); + + final String dataKey = key.replaceFirst(NbtDataImpl.KEY_PREFIX, ""); + final Object dataValue = fromNBT(keyNbt); + + customData.set(dataKey, dataValue); + } + } } public static void loadEnchantments(NBTList enchantments, EnchantmentSetter setter) { @@ -226,7 +250,7 @@ public final class NBTUtils { } } - public static void saveDataIntoNBT(ItemStack itemStack, NBTCompound itemNBT) { + public static void saveDataIntoNBT(@NotNull ItemStack itemStack, @NotNull NBTCompound itemNBT) { // Unbreakable if (itemStack.isUnbreakable()) { itemNBT.setInt("Unbreakable", 1); @@ -317,11 +341,86 @@ public final class NBTUtils { // End custom model data // Start custom meta - final ItemMeta itemMeta = itemStack.getItemMeta(); - if (itemMeta != null) { - itemMeta.write(itemNBT); + { + final ItemMeta itemMeta = itemStack.getItemMeta(); + if (itemMeta != null) { + itemMeta.write(itemNBT); + } } // End custom meta + + // Start NbtData data + { + final Data data = itemStack.getData(); + if (data instanceof NbtDataImpl) { + NbtDataImpl nbtData = (NbtDataImpl) data; + nbtData.writeToNbt(itemNBT); + } + } + // End NbtData + } + + @Nullable + public static NBT toNBT(@NotNull Object value, @NotNull Class type, boolean supportDataType) { + type = PrimitiveConversion.getObjectClass(type); + if (type.equals(Boolean.class)) { + // No boolean type in NBT + return new NBTByte((byte) (((boolean) value) ? 1 : 0)); + } else if (type.equals(Byte.class)) { + return new NBTByte((byte) value); + } else if (type.equals(Character.class)) { + // No char type in NBT + return new NBTShort((short) value); + } else if (type.equals(Short.class)) { + return new NBTShort((short) value); + } else if (type.equals(Integer.class)) { + return new NBTInt((int) value); + } else if (type.equals(Long.class)) { + return new NBTLong((long) value); + } else if (type.equals(Float.class)) { + return new NBTFloat((float) value); + } else if (type.equals(Double.class)) { + return new NBTDouble((double) value); + } else if (type.equals(String.class)) { + return new NBTString((String) value); + } else if (type.equals(Byte[].class)) { + return new NBTByteArray((byte[]) value); + } else if (type.equals(Integer[].class)) { + return new NBTIntArray((int[]) value); + } else if (type.equals(Long[].class)) { + return new NBTLongArray((long[]) value); + } else { + if (supportDataType) { + // Custom NBT type, try to encode using the data manager + DataType dataType = MinecraftServer.getDataManager().getDataType(type); + Check.notNull(dataType, "The type '" + type + "' is not registered in DataManager and not a primitive type."); + + BinaryWriter writer = new BinaryWriter(); + dataType.encode(writer, value); + + final byte[] encodedValue = writer.toByteArray(); + + return new NBTByteArray(encodedValue); + } else { + return null; + } + } + } + + public static Object fromNBT(@NotNull NBT nbt) { + if (nbt instanceof NBTNumber) { + return ((NBTNumber) nbt).getValue(); + } else if (nbt instanceof NBTString) { + return ((NBTString) nbt).getValue(); + } else if (nbt instanceof NBTByteArray) { + return ((NBTByteArray) nbt).getValue(); + } else if (nbt instanceof NBTIntArray) { + return ((NBTIntArray) nbt).getValue(); + } else if (nbt instanceof NBTLongArray) { + return ((NBTLongArray) nbt).getValue(); + } + + throw new UnsupportedOperationException("NBT type " + nbt.getClass() + " is not handled properly."); } @FunctionalInterface diff --git a/src/main/java/net/minestom/server/utils/PrimitiveConversion.java b/src/main/java/net/minestom/server/utils/PrimitiveConversion.java index 5eebc1414..8bf2595c5 100644 --- a/src/main/java/net/minestom/server/utils/PrimitiveConversion.java +++ b/src/main/java/net/minestom/server/utils/PrimitiveConversion.java @@ -1,25 +1,44 @@ package net.minestom.server.utils; +import java.util.HashMap; +import java.util.Map; + public class PrimitiveConversion { + private static Map primitiveToBoxedTypeMap = new HashMap<>(); + + static { + // Primitive + primitiveToBoxedTypeMap.put(boolean.class, Boolean.class); + primitiveToBoxedTypeMap.put(byte.class, Byte.class); + primitiveToBoxedTypeMap.put(char.class, Character.class); + primitiveToBoxedTypeMap.put(short.class, Short.class); + primitiveToBoxedTypeMap.put(int.class, Integer.class); + primitiveToBoxedTypeMap.put(long.class, Long.class); + primitiveToBoxedTypeMap.put(float.class, Float.class); + primitiveToBoxedTypeMap.put(double.class, Double.class); + + // Primitive one dimension array + primitiveToBoxedTypeMap.put(boolean[].class, Boolean[].class); + primitiveToBoxedTypeMap.put(byte[].class, Byte[].class); + primitiveToBoxedTypeMap.put(char[].class, Character[].class); + primitiveToBoxedTypeMap.put(short[].class, Short[].class); + primitiveToBoxedTypeMap.put(int[].class, Integer[].class); + primitiveToBoxedTypeMap.put(long[].class, Long[].class); + primitiveToBoxedTypeMap.put(float[].class, Float[].class); + primitiveToBoxedTypeMap.put(double[].class, Double[].class); + } + + /** + * Converts primitive types to their boxed version. + *

+ * Used to avoid needing to double-check everything + * + * @param clazz the class to convert + * @return the boxed class type of the primitive one, {@code clazz} otherwise + */ public static Class getObjectClass(Class clazz) { - if (clazz == boolean.class) - return Boolean.class; - if (clazz == byte.class) - return Byte.class; - if (clazz == char.class) - return Character.class; - if (clazz == short.class) - return Short.class; - if (clazz == int.class) - return Integer.class; - if (clazz == long.class) - return Long.class; - if (clazz == float.class) - return Float.class; - if (clazz == double.class) - return Double.class; - return clazz; + return primitiveToBoxedTypeMap.getOrDefault(clazz, clazz); } public static String getObjectClassString(String clazz) { diff --git a/src/main/java/net/minestom/server/utils/validate/Check.java b/src/main/java/net/minestom/server/utils/validate/Check.java index 2ce0a0c1e..3050b447b 100644 --- a/src/main/java/net/minestom/server/utils/validate/Check.java +++ b/src/main/java/net/minestom/server/utils/validate/Check.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; /** - * Convenient class to check for common exceptions types. + * Convenient class to check for common exceptions. */ public final class Check { From a574c1fe2a53dc474f94b159a22d60b1df51b49e Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 7 Nov 2020 22:58:19 +0100 Subject: [PATCH 35/55] Close the command scanner once stopped --- src/main/java/net/minestom/server/command/CommandManager.java | 1 + src/main/java/net/minestom/server/data/DataImpl.java | 2 +- src/main/java/net/minestom/server/entity/PlayerSkin.java | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 45213edd7..bbd4debd3 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -61,6 +61,7 @@ public final class CommandManager { execute(consoleSender, command); } } + scanner.close(); }, "ConsoleCommand-Thread"); consoleThread.setDaemon(true); consoleThread.start(); diff --git a/src/main/java/net/minestom/server/data/DataImpl.java b/src/main/java/net/minestom/server/data/DataImpl.java index 0c1a373cb..d3dc75a78 100644 --- a/src/main/java/net/minestom/server/data/DataImpl.java +++ b/src/main/java/net/minestom/server/data/DataImpl.java @@ -21,7 +21,7 @@ public class DataImpl implements Data { protected final ConcurrentHashMap dataType = new ConcurrentHashMap<>(); @Override - public void set(@NotNull String key, @Nullable T value, @Nullable Class type) { + public synchronized void set(@NotNull String key, @Nullable T value, @Nullable Class type) { if (value != null) { this.data.put(key, value); this.dataType.put(key, type); diff --git a/src/main/java/net/minestom/server/entity/PlayerSkin.java b/src/main/java/net/minestom/server/entity/PlayerSkin.java index f563812a8..8c11364e1 100644 --- a/src/main/java/net/minestom/server/entity/PlayerSkin.java +++ b/src/main/java/net/minestom/server/entity/PlayerSkin.java @@ -86,9 +86,11 @@ public class PlayerSkin { final String url = "https://api.mojang.com/users/profiles/minecraft/" + username; try { + // Retrieve the mojang uuid from the name final String response = URLUtils.getText(url); final JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); final String uuid = jsonObject.get("id").getAsString(); + // Retrieve the skin data from the mojang uuid return fromUuid(uuid); } catch (IOException e) { e.printStackTrace(); From 682c6608570ec5a6283204843c4ab9644c99a952 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 8 Nov 2020 15:57:00 +0100 Subject: [PATCH 36/55] Fixed javadoc --- src/main/java/net/minestom/server/data/DataImpl.java | 2 +- src/main/java/net/minestom/server/entity/Player.java | 2 +- src/main/java/net/minestom/server/instance/Instance.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/data/DataImpl.java b/src/main/java/net/minestom/server/data/DataImpl.java index d3dc75a78..3b11cf01a 100644 --- a/src/main/java/net/minestom/server/data/DataImpl.java +++ b/src/main/java/net/minestom/server/data/DataImpl.java @@ -15,7 +15,7 @@ public class DataImpl implements Data { protected final ConcurrentHashMap data = new ConcurrentHashMap<>(); /** - * Data key -> Class + * Data key = Class * Used to know the type of an element of this data object (for serialization purpose) */ protected final ConcurrentHashMap dataType = new ConcurrentHashMap<>(); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 0a574039e..8d1a46fe9 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1446,7 +1446,7 @@ public class Player extends LivingEntity implements CommandSender { chunk.removeViewer(this); } - // Not sure what it does... + // Update client render distance updateViewPosition(newChunk); // Load new chunks diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 486bfb06a..4483ac977 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -165,7 +165,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta *

* WARNING: during unloading, all entities other than {@link Player} will be removed. *

- * For {@link InstanceContainer} it is done during {@link InstanceContainer#tick(long)} + * For {@link InstanceContainer} it is done during the next {@link InstanceContainer#tick(long)}. * * @param chunk the chunk to unload */ From 11fc1e39bc33db73e8422a2c07c2609d010653c5 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 8 Nov 2020 17:39:03 +0100 Subject: [PATCH 37/55] Cleanup InstanceContainer#copy --- .../server/instance/InstanceContainer.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index dfc12eaf8..0bfa83d68 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -491,7 +491,7 @@ public class InstanceContainer extends Instance { } @Override - public void saveChunksToStorage(Runnable callback) { + public void saveChunksToStorage(@Nullable Runnable callback) { this.chunkLoader.saveChunks(chunks.values(), callback); } @@ -630,15 +630,14 @@ public class InstanceContainer extends Instance { copiedInstance.srcInstance = this; copiedInstance.lastBlockChangeTime = lastBlockChangeTime; - ConcurrentHashMap copiedChunks = copiedInstance.chunks; - for (Map.Entry entry : chunks.entrySet()) { - final long index = entry.getKey(); - final Chunk chunk = entry.getValue(); + for (Chunk chunk : chunks.values()) { + final int chunkX = chunk.getChunkX(); + final int chunkZ = chunk.getChunkZ(); - final Chunk copiedChunk = chunk.copy(chunk.getChunkX(), chunk.getChunkZ()); + final Chunk copiedChunk = chunk.copy(chunkX, chunkZ); - copiedChunks.put(index, copiedChunk); - UPDATE_MANAGER.signalChunkLoad(copiedInstance, chunk.getChunkX(), chunk.getChunkZ()); + copiedInstance.cacheChunk(copiedChunk); + UPDATE_MANAGER.signalChunkLoad(copiedInstance, chunkX, chunkZ); } return copiedInstance; From 4412bea7b83bc0f2f80b906cdc32c3c2a11b5b2c Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 8 Nov 2020 20:13:24 +0100 Subject: [PATCH 38/55] NBTUtils comments and contributors link in README.md --- .github/README.md | 1 + .../net/minestom/server/utils/NBTUtils.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/README.md b/.github/README.md index 2b3f4101c..29c15ae2d 100644 --- a/.github/README.md +++ b/.github/README.md @@ -84,6 +84,7 @@ It is a field where Minecraft evolved a lot, inventories are now used a lot as c Commands are the simplest way of communication between clients and server. Since 1.13 Minecraft has incorporated a new library denominated "Brigadier", we then integrated an API meant to use the full potential of args types. # Credits +* The [contributors](https://github.com/Minestom/Minestom/graphs/contributors) of the project * [The Minecraft Coalition](https://wiki.vg/) and [`#mcdevs`](https://github.com/mcdevs) - protocol and file formats research. * [The Minecraft Wiki](https://minecraft.gamepedia.com/Minecraft_Wiki) for all their useful info diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index 4de564447..05246b0e9 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -360,6 +360,14 @@ public final class NBTUtils { // End NbtData } + /** + * Converts an object into its {@link NBT} equivalent. + * + * @param value + * @param type + * @param supportDataType + * @return + */ @Nullable public static NBT toNBT(@NotNull Object value, @NotNull Class type, boolean supportDataType) { type = PrimitiveConversion.getObjectClass(type); @@ -407,6 +415,15 @@ public final class NBTUtils { } } + /** + * Converts a nbt object to its raw value. + *

+ * Currently support number, string, byte/int/long array. + * + * @param nbt the nbt tag to convert + * @return the value representation of a tag + * @throws UnsupportedOperationException if the tag type is not supported + */ public static Object fromNBT(@NotNull NBT nbt) { if (nbt instanceof NBTNumber) { return ((NBTNumber) nbt).getValue(); From f9035bf94930ecf1cec014b47f6ed36a51ae1bf9 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 8 Nov 2020 22:40:50 +0100 Subject: [PATCH 39/55] Param comments for NBTUtils#toNBT --- src/main/java/net/minestom/server/utils/NBTUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index 05246b0e9..e6d6cb9c5 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -363,10 +363,10 @@ public final class NBTUtils { /** * Converts an object into its {@link NBT} equivalent. * - * @param value - * @param type - * @param supportDataType - * @return + * @param value the value to convert + * @param type the type of the value, used to know which {@link DataType} to use if {@code value} is not a primitive type + * @param supportDataType true to allow using a {@link DataType} to encode {@code value} into a byte array if not a primitive type + * @return the converted value, null if {@code type} is not a primitive type and {@code supportDataType} is false */ @Nullable public static NBT toNBT(@NotNull Object value, @NotNull Class type, boolean supportDataType) { From e583f21b747d2f79b4cbe59e8d5f3b7d2f4c1762 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 9 Nov 2020 03:21:18 +0100 Subject: [PATCH 40/55] Fixed client crashing when pinging with an older client --- .../java/net/minestom/server/command/CommandManager.java | 5 ++--- .../server/command/builder/arguments/ArgumentWord.java | 6 ++++-- .../network/packet/client/status/StatusRequestPacket.java | 2 +- src/main/java/net/minestom/server/utils/NBTUtils.java | 3 +++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index bbd4debd3..03faa36fa 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -441,9 +441,8 @@ public final class CommandManager { List nodes = new ArrayList<>(); // You can uncomment this to test any brigadier parser on the client - /*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable); - testNode.parser = "minecraft:entity"; - testNode.properties = packetWriter -> packetWriter.writeByte((byte) 0x0); + /*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable, false); + testNode.parser = "minecraft:item_predicate"; if (true) { return nodes; diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java index 5a28ec3e2..7fe30106e 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentWord.java @@ -8,7 +8,7 @@ import java.util.Arrays; /** * Represents a single word in the command. *

- * You can specify the only correct words with {@link #from(String...)}. + * You can specify the valid words with {@link #from(String...)} (do not abuse it or the client will not be able to join). *

* Example: hey */ @@ -25,6 +25,8 @@ public class ArgumentWord extends Argument { /** * Used to force the use of a few precise words instead of complete freedom. + *

+ * WARNING: having an array too long would result in a packet too big or the client being stuck during login. * * @param restrictions the accepted words * @return 'this' @@ -73,7 +75,7 @@ public class ArgumentWord extends Argument { } /** - * Gets all the word restrictions + * Gets all the word restrictions. * * @return the word restrictions, can be null */ diff --git a/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java b/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java index 3c3f56494..d25de4161 100644 --- a/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/status/StatusRequestPacket.java @@ -22,7 +22,7 @@ public class StatusRequestPacket implements ClientPreplayPacket { responseData.setMaxPlayer(0); responseData.setOnline(0); responseData.setDescription("Minestom Server"); - responseData.setFavicon("data:image/png;base64,"); + responseData.setFavicon(""); if (consumer != null) consumer.accept(connection, responseData); diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index e6d6cb9c5..58facffe0 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -362,6 +362,9 @@ public final class NBTUtils { /** * Converts an object into its {@link NBT} equivalent. + *

+ * If {@code type} is not a primitive type or primitive array and {@code supportDataType} is true, + * the data will be encoded with the appropriate {@link DataType} into a byte array. * * @param value the value to convert * @param type the type of the value, used to know which {@link DataType} to use if {@code value} is not a primitive type From 826533e5ee4d7e93ceac2d3cf7ef7939a67220c4 Mon Sep 17 00:00:00 2001 From: TheMode Date: Mon, 9 Nov 2020 18:08:26 +0100 Subject: [PATCH 41/55] Added some dots Signed-off-by: TheMode --- src/main/java/net/minestom/server/instance/Instance.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 4483ac977..055b86e5b 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -1038,7 +1038,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta } /** - * Gets the registered {@link ExplosionSupplier}, or null if none was provided + * Gets the registered {@link ExplosionSupplier}, or null if none was provided. * * @return the instance explosion supplier, null if none was provided */ @@ -1048,7 +1048,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta } /** - * Registers the {@link ExplosionSupplier} to use in this instance + * Registers the {@link ExplosionSupplier} to use in this instance. * * @param supplier the explosion supplier */ @@ -1057,9 +1057,9 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta } /** - * Gets the instance space + * Gets the instance space. *

- * Used by the pathfinder for entities + * Used by the pathfinder for entities. * * @return the instance space */ From 247a328a097c5afd3e8ace961c52c6a619f22cd3 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 9 Nov 2020 18:29:30 +0100 Subject: [PATCH 42/55] Annotations for the advancement API --- .../server/advancements/Advancement.java | 29 +++++++++++++------ .../advancements/AdvancementManager.java | 10 +++++-- .../net/minestom/server/bossbar/BossBar.java | 5 ++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minestom/server/advancements/Advancement.java b/src/main/java/net/minestom/server/advancements/Advancement.java index c1b77d17b..9b169cd6a 100644 --- a/src/main/java/net/minestom/server/advancements/Advancement.java +++ b/src/main/java/net/minestom/server/advancements/Advancement.java @@ -6,6 +6,8 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.AdvancementsPacket; import net.minestom.server.network.player.PlayerConnection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Date; @@ -39,8 +41,8 @@ public class Advancement { // Packet private AdvancementsPacket.Criteria criteria; - public Advancement(ColoredText title, ColoredText description, - ItemStack icon, FrameType frameType, + public Advancement(@NotNull ColoredText title, ColoredText description, + @NotNull ItemStack icon, @NotNull FrameType frameType, float x, float y) { this.title = title; this.description = description; @@ -50,8 +52,8 @@ public class Advancement { this.y = y; } - public Advancement(ColoredText title, ColoredText description, - Material icon, FrameType frameType, + public Advancement(@NotNull ColoredText title, @NotNull ColoredText description, + @NotNull Material icon, @NotNull FrameType frameType, float x, float y) { this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y); } @@ -80,13 +82,14 @@ public class Advancement { /** * Gets the advancement tab linked to this advancement. * - * @return the {@link AdvancementTab} linked to this advancement + * @return the {@link AdvancementTab} linked to this advancement, null if not linked to anything yet */ + @Nullable public AdvancementTab getTab() { return tab; } - protected void setTab(AdvancementTab tab) { + protected void setTab(@NotNull AdvancementTab tab) { this.tab = tab; } @@ -95,6 +98,7 @@ public class Advancement { * * @return the advancement title */ + @NotNull public ColoredText getTitle() { return title; } @@ -104,7 +108,7 @@ public class Advancement { * * @param title the new title */ - public void setTitle(ColoredText title) { + public void setTitle(@NotNull ColoredText title) { this.title = title; update(); } @@ -114,6 +118,7 @@ public class Advancement { * * @return the description title */ + @NotNull public ColoredText getDescription() { return description; } @@ -123,7 +128,7 @@ public class Advancement { * * @param description the new description */ - public void setDescription(ColoredText description) { + public void setDescription(@NotNull ColoredText description) { this.description = description; update(); } @@ -133,6 +138,7 @@ public class Advancement { * * @return the advancement icon */ + @NotNull public ItemStack getIcon() { return icon; } @@ -142,7 +148,7 @@ public class Advancement { * * @param icon the new advancement icon */ - public void setIcon(ItemStack icon) { + public void setIcon(@NotNull ItemStack icon) { this.icon = icon; update(); } @@ -182,6 +188,7 @@ public class Advancement { * * @return this advancement frame type */ + @NotNull public FrameType getFrameType() { return frameType; } @@ -271,6 +278,7 @@ public class Advancement { * * @return the advancement parent, null for {@link AdvancementRoot} */ + @Nullable protected Advancement getParent() { return parent; } @@ -279,6 +287,7 @@ public class Advancement { this.parent = parent; } + @NotNull protected AdvancementsPacket.ProgressMapping toProgressMapping() { AdvancementsPacket.ProgressMapping progressMapping = new AdvancementsPacket.ProgressMapping(); { @@ -291,6 +300,7 @@ public class Advancement { return progressMapping; } + @NotNull protected AdvancementsPacket.DisplayData toDisplayData() { AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); displayData.x = x; @@ -311,6 +321,7 @@ public class Advancement { * * @return the mapping of this advancement */ + @NotNull protected AdvancementsPacket.AdvancementMapping toMapping() { AdvancementsPacket.AdvancementMapping mapping = new AdvancementsPacket.AdvancementMapping(); { diff --git a/src/main/java/net/minestom/server/advancements/AdvancementManager.java b/src/main/java/net/minestom/server/advancements/AdvancementManager.java index 508c908ad..25e6c49a5 100644 --- a/src/main/java/net/minestom/server/advancements/AdvancementManager.java +++ b/src/main/java/net/minestom/server/advancements/AdvancementManager.java @@ -1,6 +1,8 @@ package net.minestom.server.advancements; import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Map; @@ -13,6 +15,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AdvancementManager { + // root identifier TO its advancement tab private final Map advancementTabMap = new ConcurrentHashMap<>(); /** @@ -23,7 +26,8 @@ public class AdvancementManager { * @return the newly created {@link AdvancementTab} * @throws IllegalStateException if a tab with the identifier {@code rootIdentifier} already exists */ - public AdvancementTab createTab(String rootIdentifier, AdvancementRoot root) { + @NotNull + public AdvancementTab createTab(@NotNull String rootIdentifier, @NotNull AdvancementRoot root) { Check.stateCondition(advancementTabMap.containsKey(rootIdentifier), "A tab with the identifier '" + rootIdentifier + "' already exists"); final AdvancementTab advancementTab = new AdvancementTab(rootIdentifier, root); @@ -37,7 +41,8 @@ public class AdvancementManager { * @param rootIdentifier the root identifier of the tab * @return the {@link AdvancementTab} associated with the identifier, null if not any */ - public AdvancementTab getTab(String rootIdentifier) { + @Nullable + public AdvancementTab getTab(@NotNull String rootIdentifier) { return advancementTabMap.get(rootIdentifier); } @@ -46,6 +51,7 @@ public class AdvancementManager { * * @return the collection containing all created {@link AdvancementTab} */ + @NotNull public Collection getTabs() { return advancementTabMap.values(); } diff --git a/src/main/java/net/minestom/server/bossbar/BossBar.java b/src/main/java/net/minestom/server/bossbar/BossBar.java index f7d6e6231..c2dcd3bf2 100644 --- a/src/main/java/net/minestom/server/bossbar/BossBar.java +++ b/src/main/java/net/minestom/server/bossbar/BossBar.java @@ -40,7 +40,7 @@ public class BossBar implements Viewable { * @param color the boss bar color * @param division the boss bar division */ - public BossBar(ColoredText title, @NotNull BarColor color, @NotNull BarDivision division) { + public BossBar(@NotNull ColoredText title, @NotNull BarColor color, @NotNull BarDivision division) { this.title = title; this.color = color; this.division = division; @@ -96,6 +96,7 @@ public class BossBar implements Viewable { * * @return the current title of the bossbar */ + @NotNull public ColoredText getTitle() { return title; } @@ -105,7 +106,7 @@ public class BossBar implements Viewable { * * @param title the new title of the bossbar */ - public void setTitle(ColoredText title) { + public void setTitle(@NotNull ColoredText title) { this.title = title; updateTitle(); } From 7db94f3a651f9c773b6d6656f64df86888505be9 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 9 Nov 2020 23:48:34 +0100 Subject: [PATCH 43/55] Support for velocity modern forwarding --- .../net/minestom/server/MinecraftServer.java | 15 +++- .../advancements/AdvancementManager.java | 2 +- .../server/advancements/AdvancementRoot.java | 14 ++-- .../net/minestom/server/entity/Player.java | 1 - .../server/extras/velocity/VelocityProxy.java | 78 +++++++++++++++++++ .../server/network/ConnectionManager.java | 16 ++++ .../network/netty/channel/ClientChannel.java | 1 + .../handler/ClientLoginPacketsHandler.java | 2 + .../client/handshake/HandshakePacket.java | 4 +- .../login/EncryptionResponsePacket.java | 20 ++--- .../login/LoginPluginResponsePacket.java | 69 ++++++++++++++++ .../packet/client/login/LoginStartPacket.java | 61 +++++++++++---- .../packet/server/ServerPacketIdentifier.java | 6 ++ .../server/login/EncryptionRequestPacket.java | 7 +- .../packet/server/login/LoginDisconnect.java | 25 ------ .../server/login/LoginDisconnectPacket.java | 31 ++++++++ .../login/LoginPluginRequestPacket.java | 27 +++++++ .../server/login/LoginSuccessPacket.java | 3 +- .../server/login/SetCompressionPacket.java | 3 +- .../{login => play}/JoinGamePacket.java | 2 +- .../server/play/PluginMessagePacket.java | 1 + .../network/player/NettyPlayerConnection.java | 74 ++++++++++++++++++ .../network/player/PlayerConnection.java | 13 +--- .../server/utils/binary/BinaryWriter.java | 2 + src/test/java/demo/Main.java | 4 + src/test/java/demo/PlayerInit.java | 6 ++ 26 files changed, 404 insertions(+), 83 deletions(-) create mode 100644 src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java create mode 100644 src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java delete mode 100644 src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnect.java create mode 100644 src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java create mode 100644 src/main/java/net/minestom/server/network/packet/server/login/LoginPluginRequestPacket.java rename src/main/java/net/minestom/server/network/packet/server/{login => play}/JoinGamePacket.java (97%) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 68c6a7dd1..311de281f 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -54,6 +54,7 @@ import net.minestom.server.utils.validate.Check; import net.minestom.server.world.Difficulty; import net.minestom.server.world.DimensionTypeManager; import net.minestom.server.world.biomes.BiomeManager; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -227,15 +228,18 @@ public class MinecraftServer { * * @return the server brand name */ + @NotNull public static String getBrandName() { return brandName; } /** - * Changes the server brand name, update the name to all connected players. + * Changes the server brand name and send the change to all connected players. * * @param brandName the server brand name + * @throws NullPointerException if {@code brandName} is null */ + @NotNull public static void setBrandName(String brandName) { Check.notNull(brandName, "The brand name cannot be null"); MinecraftServer.brandName = brandName; @@ -267,6 +271,7 @@ public class MinecraftServer { * * @return the server difficulty */ + @NotNull public static Difficulty getDifficulty() { return difficulty; } @@ -276,7 +281,9 @@ public class MinecraftServer { * * @param difficulty the new server difficulty */ - public static void setDifficulty(Difficulty difficulty) { + @NotNull + public static void setDifficulty(@NotNull Difficulty difficulty) { + Check.notNull(difficulty, "The server difficulty cannot be null."); MinecraftServer.difficulty = difficulty; // The difficulty packet @@ -603,7 +610,7 @@ public class MinecraftServer { * @param responseDataConsumer the response data consumer, can be null * @throws IllegalStateException if called before {@link #init()} or if the server is already running */ - public void start(String address, int port, ResponseDataConsumer responseDataConsumer) { + public void start(@NotNull String address, int port, @Nullable ResponseDataConsumer responseDataConsumer) { Check.stateCondition(!initialized, "#start can only be called after #init"); Check.stateCondition(started, "The server is already started"); @@ -632,7 +639,7 @@ public class MinecraftServer { * @param port the server port * @see #start(String, int, ResponseDataConsumer) */ - public void start(String address, int port) { + public void start(@NotNull String address, int port) { start(address, port, null); } diff --git a/src/main/java/net/minestom/server/advancements/AdvancementManager.java b/src/main/java/net/minestom/server/advancements/AdvancementManager.java index 25e6c49a5..2ce596795 100644 --- a/src/main/java/net/minestom/server/advancements/AdvancementManager.java +++ b/src/main/java/net/minestom/server/advancements/AdvancementManager.java @@ -15,7 +15,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AdvancementManager { - // root identifier TO its advancement tab + // root identifier = its advancement tab private final Map advancementTabMap = new ConcurrentHashMap<>(); /** diff --git a/src/main/java/net/minestom/server/advancements/AdvancementRoot.java b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java index 3850be9e9..2867ddc99 100644 --- a/src/main/java/net/minestom/server/advancements/AdvancementRoot.java +++ b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java @@ -3,6 +3,8 @@ package net.minestom.server.advancements; import net.minestom.server.chat.ColoredText; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents an {@link Advancement} which is the root of an {@link AdvancementTab}. @@ -12,18 +14,18 @@ import net.minestom.server.item.Material; */ public class AdvancementRoot extends Advancement { - public AdvancementRoot(ColoredText title, ColoredText description, - ItemStack icon, FrameType frameType, + public AdvancementRoot(@NotNull ColoredText title, @NotNull ColoredText description, + @NotNull ItemStack icon, @NotNull FrameType frameType, float x, float y, - String background) { + @Nullable String background) { super(title, description, icon, frameType, x, y); setBackground(background); } - public AdvancementRoot(ColoredText title, ColoredText description, - Material icon, FrameType frameType, + public AdvancementRoot(@NotNull ColoredText title, @NotNull ColoredText description, + @NotNull Material icon, FrameType frameType, float x, float y, - String background) { + @Nullable String background) { super(title, description, icon, frameType, x, y); setBackground(background); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 8d1a46fe9..9936148f6 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -34,7 +34,6 @@ import net.minestom.server.network.PlayerProvider; import net.minestom.server.network.packet.client.ClientPlayPacket; import net.minestom.server.network.packet.client.play.ClientChatMessagePacket; import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.packet.server.login.JoinGamePacket; import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; diff --git a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java new file mode 100644 index 000000000..08e9cb4ec --- /dev/null +++ b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java @@ -0,0 +1,78 @@ +package net.minestom.server.extras.velocity; + +import net.minestom.server.utils.binary.BinaryReader; +import org.jetbrains.annotations.NotNull; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class VelocityProxy { + + public static final String PLAYER_INFO_CHANNEL = "velocity:player_info"; + + private static boolean enabled; + private static byte[] secret; + + /** + * Enables velocity modern forwarding. + * + * @param secret the forwarding secret, + * be sure to do not hardcode it in your code but to retrieve it from a file or anywhere else safe + */ + public static void enable(@NotNull String secret) { + VelocityProxy.enabled = true; + VelocityProxy.secret = secret.getBytes(); + } + + /** + * Gets if velocity modern forwarding is enabled. + * + * @return true if velocity modern forwarding is enabled + */ + public static boolean isEnabled() { + return enabled; + } + + public static boolean checkIntegrity(@NotNull BinaryReader reader) { + + if (!enabled) { + return false; + } + + final byte[] signature = reader.readBytes(32); + + final byte[] data = reader.getRemainingBytes(); + + try { + final Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(secret, "HmacSHA256")); + final byte[] mySignature = mac.doFinal(data); + if (!MessageDigest.isEqual(signature, mySignature)) { + return false; + } + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + + /*int version = buf.readVarInt(); + if (version != SUPPORTED_FORWARDING_VERSION) { + throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + SUPPORTED_FORWARDING_VERSION); + }*/ + + return true; + } + + private static void readProperties(final BinaryReader reader) { + final int properties = reader.readVarInt(); + for (int i1 = 0; i1 < properties; i1++) { + final String name = reader.readSizedString(); + final String value = reader.readSizedString(); + final String signature = reader.readBoolean() ? reader.readSizedString() : null; + System.out.println("test: " + name + " " + value + " " + signature); + } + } + +} diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 9aeae34bc..e18558b01 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -5,6 +5,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.entity.fakeplayer.FakePlayer; import net.minestom.server.listener.manager.PacketConsumer; import net.minestom.server.network.packet.client.login.LoginStartPacket; +import net.minestom.server.network.packet.server.login.LoginSuccessPacket; import net.minestom.server.network.packet.server.play.ChatMessagePacket; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.callback.validator.PlayerValidator; @@ -269,4 +270,19 @@ public final class ConnectionManager { this.players.remove(player); this.connectionPlayerMap.remove(connection); } + + /** + * Sends a {@link LoginSuccessPacket} and change the connection state to {@link ConnectionState#PLAY}. + * + * @param connection the player connection + * @param uuid the uuid of the player + * @param username the username of the player + */ + public void startPlayState(@NotNull PlayerConnection connection, @NotNull UUID uuid, @NotNull String username) { + LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket(uuid, username); + connection.sendPacket(loginSuccessPacket); + + connection.setConnectionState(ConnectionState.PLAY); + createPlayer(uuid, username, connection); + } } 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 810cc7a6c..4b8a51f12 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 @@ -61,6 +61,7 @@ public class ClientChannel extends SimpleChannelInboundHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { log.info(cause.getMessage()); + cause.printStackTrace(); ctx.close(); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/handler/ClientLoginPacketsHandler.java b/src/main/java/net/minestom/server/network/packet/client/handler/ClientLoginPacketsHandler.java index c5f36e1f8..122026d74 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handler/ClientLoginPacketsHandler.java +++ b/src/main/java/net/minestom/server/network/packet/client/handler/ClientLoginPacketsHandler.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.client.handler; import net.minestom.server.network.packet.client.login.EncryptionResponsePacket; +import net.minestom.server.network.packet.client.login.LoginPluginResponsePacket; import net.minestom.server.network.packet.client.login.LoginStartPacket; public class ClientLoginPacketsHandler extends ClientPacketsHandler { @@ -8,6 +9,7 @@ public class ClientLoginPacketsHandler extends ClientPacketsHandler { public ClientLoginPacketsHandler() { register(0, LoginStartPacket::new); register(1, EncryptionResponsePacket::new); + register(2, LoginPluginResponsePacket::new); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java index 9b240405d..9658a9af1 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java @@ -5,7 +5,7 @@ import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.client.ClientPreplayPacket; -import net.minestom.server.network.packet.server.login.LoginDisconnect; +import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; @@ -47,7 +47,7 @@ public class HandshakePacket implements ClientPreplayPacket { } } else { // Incorrect client version - connection.sendPacket(new LoginDisconnect(INVALID_VERSION_TEXT.toString())); + connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT.toString())); connection.disconnect(); } break; diff --git a/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java index 084dbdf51..dbbe5ccc8 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java @@ -5,9 +5,7 @@ import com.mojang.authlib.exceptions.AuthenticationUnavailableException; import net.minestom.server.MinecraftServer; import net.minestom.server.data.type.array.ByteArrayData; import net.minestom.server.extras.mojangAuth.MojangCrypt; -import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.client.ClientPreplayPacket; -import net.minestom.server.network.packet.server.login.LoginSuccessPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; @@ -32,17 +30,18 @@ public class EncryptionResponsePacket implements ClientPreplayPacket { if (!(connection instanceof NettyPlayerConnection)) { return; } + final NettyPlayerConnection nettyConnection = (NettyPlayerConnection) connection; new Thread(THREAD_NAME + " #" + UNIQUE_THREAD_ID.incrementAndGet()) { public void run() { try { - if (!Arrays.equals(connection.getNonce(), getNonce())) { - MinecraftServer.getLOGGER().error(connection.getLoginUsername() + " tried to login with an invalid nonce!"); + final String loginUsername = nettyConnection.getLoginUsername(); + if (!Arrays.equals(nettyConnection.getNonce(), getNonce())) { + MinecraftServer.getLOGGER().error(loginUsername + " tried to login with an invalid nonce!"); return; } - if (!connection.getLoginUsername().isEmpty()) { - final NettyPlayerConnection nettyConnection = (NettyPlayerConnection) connection; + if (!loginUsername.isEmpty()) { final byte[] digestedData = MojangCrypt.digestData("", MinecraftServer.getKeyPair().getPublic(), getSecretKey()); @@ -54,7 +53,7 @@ public class EncryptionResponsePacket implements ClientPreplayPacket { } final String string3 = new BigInteger(digestedData).toString(16); - final GameProfile gameProfile = MinecraftServer.getSessionService().hasJoinedServer(new GameProfile(null, connection.getLoginUsername()), string3); + final GameProfile gameProfile = MinecraftServer.getSessionService().hasJoinedServer(new GameProfile(null, loginUsername), string3); nettyConnection.setEncryptionKey(getSecretKey()); final int threshold = MinecraftServer.getCompressionThreshold(); @@ -62,11 +61,8 @@ public class EncryptionResponsePacket implements ClientPreplayPacket { nettyConnection.enableCompression(threshold); } - LoginSuccessPacket loginSuccessPacket = new LoginSuccessPacket(gameProfile.getId(), gameProfile.getName()); - connection.sendPacket(loginSuccessPacket); - MinecraftServer.getLOGGER().info("UUID of player {} is {}", connection.getLoginUsername(), gameProfile.getId()); - connection.setConnectionState(ConnectionState.PLAY); - CONNECTION_MANAGER.createPlayer(gameProfile.getId(), gameProfile.getName(), connection); + MinecraftServer.getLOGGER().info("UUID of player {} is {}", loginUsername, gameProfile.getId()); + CONNECTION_MANAGER.startPlayState(connection, gameProfile.getId(), gameProfile.getName()); } } catch (AuthenticationUnavailableException e) { e.printStackTrace(); diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java new file mode 100644 index 000000000..9378c86a1 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java @@ -0,0 +1,69 @@ +package net.minestom.server.network.packet.client.login; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.chat.ChatColor; +import net.minestom.server.chat.ColoredText; +import net.minestom.server.extras.velocity.VelocityProxy; +import net.minestom.server.network.ConnectionManager; +import net.minestom.server.network.packet.client.ClientPreplayPacket; +import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; +import net.minestom.server.network.player.NettyPlayerConnection; +import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.utils.binary.BinaryReader; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class LoginPluginResponsePacket implements ClientPreplayPacket { + + private final static ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); + + public static final ColoredText INVALID_PROXY_RESPONSE = ColoredText.of(ChatColor.RED, "Invalid proxy response!"); + + public int messageId; + public boolean successful; + public byte[] data; + + @Override + public void process(@NotNull PlayerConnection connection) { + + // Proxy support + if (connection instanceof NettyPlayerConnection) { + final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) connection; + final String channel = nettyPlayerConnection.getPluginRequestChannel(messageId); + + if (channel != null) { + boolean success = false; + + // Velocity + if (VelocityProxy.isEnabled() && channel.equals(VelocityProxy.PLAYER_INFO_CHANNEL)) { + if (data != null) { + BinaryReader reader = new BinaryReader(data); + success = VelocityProxy.checkIntegrity(reader); + } + } + + if (success) { + // Proxy usage always mean that the server is in offline mode + final String username = nettyPlayerConnection.getLoginUsername(); + final UUID playerUuid = CONNECTION_MANAGER.getPlayerConnectionUuid(connection, username); + + CONNECTION_MANAGER.startPlayState(connection, playerUuid, username); + } else { + LoginDisconnectPacket disconnectPacket = new LoginDisconnectPacket(INVALID_PROXY_RESPONSE); + nettyPlayerConnection.sendPacket(disconnectPacket); + } + + } + } + } + + @Override + public void read(@NotNull BinaryReader reader) { + this.messageId = reader.readVarInt(); + this.successful = reader.readBoolean(); + if (successful) { + this.data = reader.getRemainingBytes(); + } + } +} diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index 733815319..16b8c2827 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -4,39 +4,74 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; import net.minestom.server.extras.MojangAuth; +import net.minestom.server.extras.velocity.VelocityProxy; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.client.ClientPreplayPacket; import net.minestom.server.network.packet.server.login.EncryptionRequestPacket; -import net.minestom.server.network.packet.server.login.LoginDisconnect; -import net.minestom.server.network.packet.server.login.LoginSuccessPacket; +import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; +import net.minestom.server.network.packet.server.login.LoginPluginRequestPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; import org.jetbrains.annotations.NotNull; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; public class LoginStartPacket implements ClientPreplayPacket { - private static final String ALREADY_CONNECTED_JSON = - ColoredText.of(ChatColor.RED, "You are already on this server").toString(); + private static final ColoredText ALREADY_CONNECTED_JSON = ColoredText.of(ChatColor.RED, "You are already on this server"); public String username; @Override public void process(@NotNull PlayerConnection connection) { - if (MojangAuth.isUsingMojangAuth()) { + + // Cache the login username + if (connection instanceof NettyPlayerConnection) { + ((NettyPlayerConnection) connection).UNSAFE_setLoginUsername(username); + } + + // Proxy support (only for netty clients) + if (connection instanceof NettyPlayerConnection) { + final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) connection; + + { + // Velocity support + if (VelocityProxy.isEnabled()) { + + final int messageId = ThreadLocalRandom.current().nextInt(); + final String channel = VelocityProxy.PLAYER_INFO_CHANNEL; + + nettyPlayerConnection.addPluginRequestEntry(messageId, channel); + + LoginPluginRequestPacket loginPluginRequestPacket = new LoginPluginRequestPacket(); + loginPluginRequestPacket.messageId = messageId; + loginPluginRequestPacket.channel = channel; + loginPluginRequestPacket.data = null; + connection.sendPacket(loginPluginRequestPacket); + + return; + } + } + + } + + if (MojangAuth.isUsingMojangAuth() && connection instanceof NettyPlayerConnection) { + // Mojang auth if (CONNECTION_MANAGER.getPlayer(username) != null) { - connection.sendPacket(new LoginDisconnect(ALREADY_CONNECTED_JSON)); + connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED_JSON)); connection.disconnect(); return; } - connection.setConnectionState(ConnectionState.LOGIN); - connection.setLoginUsername(username); - EncryptionRequestPacket encryptionRequestPacket = new EncryptionRequestPacket(connection); - connection.sendPacket(encryptionRequestPacket); + final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) connection; + + nettyPlayerConnection.setConnectionState(ConnectionState.LOGIN); + EncryptionRequestPacket encryptionRequestPacket = new EncryptionRequestPacket(nettyPlayerConnection); + nettyPlayerConnection.sendPacket(encryptionRequestPacket); } else { + // Offline final UUID playerUuid = CONNECTION_MANAGER.getPlayerConnectionUuid(connection, username); final int threshold = MinecraftServer.getCompressionThreshold(); @@ -45,11 +80,7 @@ public class LoginStartPacket implements ClientPreplayPacket { ((NettyPlayerConnection) connection).enableCompression(threshold); } - LoginSuccessPacket successPacket = new LoginSuccessPacket(playerUuid, username); - connection.sendPacket(successPacket); - - connection.setConnectionState(ConnectionState.PLAY); - CONNECTION_MANAGER.createPlayer(playerUuid, username, connection); + CONNECTION_MANAGER.startPlayState(connection, playerUuid, username); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java index 3971b8224..3b70103fc 100644 --- a/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java +++ b/src/main/java/net/minestom/server/network/packet/server/ServerPacketIdentifier.java @@ -2,6 +2,12 @@ package net.minestom.server.network.packet.server; public class ServerPacketIdentifier { + public static final int LOGIN_DISCONNECT = 0x00; + public static final int LOGIN_ENCRYPTION_REQUEST = 0x01; + public static final int LOGIN_SUCCESS = 0x02; + public static final int LOGIN_SET_COMPRESSION = 0x03; + public static final int LOGIN_PLUGIN_REQUEST = 0x04; + public static final int SPAWN_ENTITY = 0x00; public static final int SPAWN_EXPERIENCE_ORB = 0x01; public static final int SPAWN_LIVING_ENTITY = 0x02; diff --git a/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java index f3d5da982..0054054fe 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/EncryptionRequestPacket.java @@ -3,7 +3,8 @@ package net.minestom.server.network.packet.server.login; import net.minestom.server.MinecraftServer; import net.minestom.server.data.type.array.ByteArrayData; import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,7 @@ public class EncryptionRequestPacket implements ServerPacket { public byte[] publicKey; public byte[] nonce = new byte[4]; - public EncryptionRequestPacket(PlayerConnection connection) { + public EncryptionRequestPacket(NettyPlayerConnection connection) { ThreadLocalRandom.current().nextBytes(nonce); connection.setNonce(nonce); } @@ -29,6 +30,6 @@ public class EncryptionRequestPacket implements ServerPacket { @Override public int getId() { - return 0x01; + return ServerPacketIdentifier.LOGIN_ENCRYPTION_REQUEST; } } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnect.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnect.java deleted file mode 100644 index c1e5d3272..000000000 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnect.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.minestom.server.network.packet.server.login; - -import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.utils.binary.BinaryWriter; -import org.jetbrains.annotations.NotNull; - -public class LoginDisconnect implements ServerPacket { - - private String kickMessage; - - public LoginDisconnect(String kickMessage) { - this.kickMessage = kickMessage; - } - - @Override - public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(kickMessage); - } - - @Override - public int getId() { - return 0x00; - } - -} diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java new file mode 100644 index 000000000..1038eb6a7 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java @@ -0,0 +1,31 @@ +package net.minestom.server.network.packet.server.login; + +import net.minestom.server.chat.JsonMessage; +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; + +public class LoginDisconnectPacket implements ServerPacket { + + private String kickMessage; // JSON text + + public LoginDisconnectPacket(@NotNull String kickMessage) { + this.kickMessage = kickMessage; + } + + public LoginDisconnectPacket(@NotNull JsonMessage jsonKickMessage) { + this(jsonKickMessage.toString()); + } + + @Override + public void write(@NotNull BinaryWriter writer) { + writer.writeSizedString(kickMessage); + } + + @Override + public int getId() { + return ServerPacketIdentifier.LOGIN_DISCONNECT; + } + +} diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginPluginRequestPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginPluginRequestPacket.java new file mode 100644 index 000000000..d2ae02ed6 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginPluginRequestPacket.java @@ -0,0 +1,27 @@ +package net.minestom.server.network.packet.server.login; + +import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; + +public class LoginPluginRequestPacket implements ServerPacket { + + public int messageId; + public String channel; + public byte[] data; + + @Override + public void write(@NotNull BinaryWriter writer) { + writer.writeVarInt(messageId); + writer.writeSizedString(channel); + if (data != null && data.length > 0) { + writer.writeBytes(data); + } + } + + @Override + public int getId() { + return ServerPacketIdentifier.LOGIN_PLUGIN_REQUEST; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java index b48b936cc..81b57ba35 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginSuccessPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.server.login; import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; @@ -24,6 +25,6 @@ public class LoginSuccessPacket implements ServerPacket { @Override public int getId() { - return 0x02; + return ServerPacketIdentifier.LOGIN_SUCCESS; } } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/SetCompressionPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/SetCompressionPacket.java index 88fb49cb9..152950c65 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/SetCompressionPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/SetCompressionPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.server.login; import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; @@ -19,6 +20,6 @@ public class SetCompressionPacket implements ServerPacket { @Override public int getId() { - return 0x03; + return ServerPacketIdentifier.LOGIN_SET_COMPRESSION; } } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java similarity index 97% rename from src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java rename to src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java index 46191b208..eb481c51a 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/JoinGamePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/JoinGamePacket.java @@ -1,4 +1,4 @@ -package net.minestom.server.network.packet.server.login; +package net.minestom.server.network.packet.server.play; import net.minestom.server.MinecraftServer; import net.minestom.server.entity.GameMode; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PluginMessagePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PluginMessagePacket.java index 2d1af7f14..4b41e7dfb 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PluginMessagePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PluginMessagePacket.java @@ -29,6 +29,7 @@ public class PluginMessagePacket implements ServerPacket { * * @return the current brand name packet */ + @NotNull public static PluginMessagePacket getBrandPacket() { PluginMessagePacket brandMessage = new PluginMessagePacket(); brandMessage.channel = "minecraft:brand"; 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 c13ec2b42..d9d09d1d7 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -4,9 +4,11 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.socket.SocketChannel; import lombok.Getter; +import lombok.Setter; import net.minestom.server.extras.mojangAuth.Decrypter; import net.minestom.server.extras.mojangAuth.Encrypter; import net.minestom.server.extras.mojangAuth.MojangCrypt; +import net.minestom.server.network.ConnectionState; 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; @@ -16,6 +18,8 @@ import org.jetbrains.annotations.Nullable; import javax.crypto.SecretKey; import java.net.SocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Represents a networking connection with Netty. @@ -30,9 +34,19 @@ public class NettyPlayerConnection extends PlayerConnection { @Getter private boolean compressed = false; + //Could be null. Only used for Mojang Auth + @Getter + @Setter + private byte[] nonce = new byte[4]; + + private String loginUsername; private String serverAddress; private int serverPort; + // Used for the login plugin request packet, to retrive the channel from a message id, + // cleared once the player enters the play state + private Map pluginRequestMap = new ConcurrentHashMap<>(); + public NettyPlayerConnection(@NotNull SocketChannel channel) { super(); this.channel = channel; @@ -114,6 +128,27 @@ public class NettyPlayerConnection extends PlayerConnection { return channel; } + /** + * Retrieves the username received from the client during connection. + *

+ * This value has not been checked and could be anything. + * + * @return the username given by the client, unchecked + */ + @Nullable + public String getLoginUsername() { + return loginUsername; + } + + /** + * Sets the internal login username field + * + * @param loginUsername the new login username field + */ + public void UNSAFE_setLoginUsername(@NotNull String loginUsername) { + this.loginUsername = loginUsername; + } + /** * Gets the server address that the client used to connect. *

@@ -137,6 +172,45 @@ public class NettyPlayerConnection extends PlayerConnection { return serverPort; } + /** + * Adds an entry to the plugin request map. + *

+ * Only working if {@link #getConnectionState()} is {@link net.minestom.server.network.ConnectionState#LOGIN}. + * + * @param messageId the message id + * @param channel the packet channel + * @throws IllegalStateException if a messageId with the value {@code messageId} already exists for this connection + */ + public void addPluginRequestEntry(int messageId, @NotNull String channel) { + if (!getConnectionState().equals(ConnectionState.LOGIN)) { + return; + } + Check.stateCondition(pluginRequestMap.containsKey(messageId), "You cannot have two messageId with the same value"); + this.pluginRequestMap.put(messageId, channel); + } + + /** + * Gets a request channel from a message id, previously cached using {@link #addPluginRequestEntry(int, String)}. + *

+ * Be aware that the internal map is cleared once the player enters the play state. + * + * @param messageId the message id + * @return the channel linked to the message id, null if not found + */ + @Nullable + public String getPluginRequestChannel(int messageId) { + return pluginRequestMap.get(messageId); + } + + @Override + public void setConnectionState(@NotNull ConnectionState connectionState) { + super.setConnectionState(connectionState); + // Clear the plugin request map (since it is not used anymore) + if (connectionState.equals(ConnectionState.PLAY)) { + this.pluginRequestMap.clear(); + } + } + /** * Used in {@link net.minestom.server.network.packet.client.handshake.HandshakePacket} to change the internal fields. * 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 37db193de..6a9c41acb 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -2,14 +2,13 @@ package net.minestom.server.network.player; 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; import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.packet.server.login.LoginDisconnect; +import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; import net.minestom.server.network.packet.server.play.DisconnectPacket; import org.jetbrains.annotations.NotNull; @@ -23,14 +22,6 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class PlayerConnection { private Player player; - //Could be null. Only used for Mojang Auth - @Getter - @Setter - private String loginUsername; - //Could be null. Only used for Mojang Auth - @Getter - @Setter - private byte[] nonce = new byte[4]; private ConnectionState connectionState; private boolean online; @@ -64,7 +55,7 @@ public abstract class PlayerConnection { if (count > MinecraftServer.getRateLimit()) { // Sent too many packets if (connectionState == ConnectionState.LOGIN) { - sendPacket(new LoginDisconnect("Too Many Packets")); + sendPacket(new LoginDisconnectPacket("Too Many Packets")); } else { DisconnectPacket disconnectPacket = new DisconnectPacket(); disconnectPacket.message = rateLimitKickMessage; diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java index 09a992418..0169af600 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java @@ -186,6 +186,8 @@ public class BinaryWriter extends OutputStream { /** * Writes a byte array. + *

+ * WARNING: it doesn't write the length of {@code bytes}. * * @param bytes the byte array to write */ diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index ba8a5f9d5..9b106397a 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -3,6 +3,7 @@ package demo; import demo.blocks.BurningTorchBlock; import demo.blocks.StoneBlock; import demo.blocks.UpdatableBlockDemo; +import demo.commands.GamemodeCommand; import demo.commands.TestCommand; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; @@ -28,6 +29,7 @@ public class Main { CommandManager commandManager = MinecraftServer.getCommandManager(); commandManager.register(new TestCommand()); + commandManager.register(new GamemodeCommand()); /*commandManager.register(new EntitySelectorCommand()); commandManager.register(new HealthCommand()); commandManager.register(new SimpleCommand()); @@ -48,6 +50,8 @@ public class Main { PlayerInit.init(); + //VelocityProxy.enable("rBeJJ79W4MVU"); + //MojangAuth.init(); minecraftServer.start("0.0.0.0", 25565, PlayerInit.getResponseDataConsumer()); diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 4705aecbd..2dfa2c6e7 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -5,6 +5,8 @@ import demo.generator.NoiseTestGenerator; import net.minestom.server.MinecraftServer; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.chat.ColoredText; +import net.minestom.server.data.Data; +import net.minestom.server.data.NbtDataImpl; import net.minestom.server.entity.*; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.type.monster.EntityZombie; @@ -172,7 +174,11 @@ public class PlayerInit { } ItemStack itemStack = new ItemStack(Material.DIAMOND_PICKAXE, (byte) 64); + Data data = new NbtDataImpl(); + data.set("test", 51); + itemStack.setData(data); player.getInventory().addItemStack(itemStack); + System.out.println("test " + data.get("test")); //player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte)64)); }); From c6c8f6b9d6a19507ec5b533cbd06e6c27298139e Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 00:13:57 +0100 Subject: [PATCH 44/55] Usage comments for VelocityProxy --- .../net/minestom/server/extras/MojangAuth.java | 4 +++- .../server/extras/velocity/VelocityProxy.java | 18 ++++++------------ .../packet/client/login/LoginStartPacket.java | 1 + 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minestom/server/extras/MojangAuth.java b/src/main/java/net/minestom/server/extras/MojangAuth.java index 7e5a616f1..8d7bbfa39 100644 --- a/src/main/java/net/minestom/server/extras/MojangAuth.java +++ b/src/main/java/net/minestom/server/extras/MojangAuth.java @@ -9,7 +9,9 @@ public class MojangAuth { private static boolean usingMojangAuth = false; /** - * Enable mojang authentication on the server. + * Enables mojang authentication on the server. + *

+ * Be aware that enabling a proxy will make Mojang authentication ignored. */ public static void init() { if (MinecraftServer.getNettyServer().getAddress() == null) { diff --git a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java index 08e9cb4ec..0510d3a21 100644 --- a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java +++ b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java @@ -9,7 +9,12 @@ import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -public class VelocityProxy { +/** + * Support for Velocity proxy. + *

+ * Can be enabled by simply calling {@link #enable(String)}. + */ +public final class VelocityProxy { public static final String PLAYER_INFO_CHANNEL = "velocity:player_info"; @@ -37,7 +42,6 @@ public class VelocityProxy { } public static boolean checkIntegrity(@NotNull BinaryReader reader) { - if (!enabled) { return false; } @@ -65,14 +69,4 @@ public class VelocityProxy { return true; } - private static void readProperties(final BinaryReader reader) { - final int properties = reader.readVarInt(); - for (int i1 = 0; i1 < properties; i1++) { - final String name = reader.readSizedString(); - final String value = reader.readSizedString(); - final String signature = reader.readBoolean() ? reader.readSizedString() : null; - System.out.println("test: " + name + " " + value + " " + signature); - } - } - } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index 16b8c2827..faa9c1620 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -43,6 +43,7 @@ public class LoginStartPacket implements ClientPreplayPacket { final int messageId = ThreadLocalRandom.current().nextInt(); final String channel = VelocityProxy.PLAYER_INFO_CHANNEL; + // Important to retrieve the channel in the response packet nettyPlayerConnection.addPluginRequestEntry(messageId, channel); LoginPluginRequestPacket loginPluginRequestPacket = new LoginPluginRequestPacket(); From 68bb479f4a8aa1842477a10f81f45394c681490e Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 07:42:11 +0100 Subject: [PATCH 45/55] Cleanup --- .../net/minestom/server/MinecraftServer.java | 4 +-- .../minestom/server/extras/MojangAuth.java | 2 +- .../server/extras/PlacementRules.java | 2 +- .../server/extras/mojangAuth/MojangCrypt.java | 2 +- .../server/network/PacketProcessor.java | 22 +++++++-------- .../network/packet/client/ClientPacket.java | 10 +++---- .../client/handler/ClientPacketsHandler.java | 28 ++++++++++++++++--- .../packet/client/login/LoginStartPacket.java | 2 +- .../network/player/NettyPlayerConnection.java | 2 +- 9 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 311de281f..2fe64734a 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -70,7 +70,7 @@ import java.util.Collection; * The server needs to be initialized with {@link #init()} and started with {@link #start(String, int)}. * You should register all of your dimensions, biomes, commands, events, etc... in-between. */ -public class MinecraftServer { +public final class MinecraftServer { @Getter private final static Logger LOGGER = LoggerFactory.getLogger(MinecraftServer.class); @@ -289,7 +289,7 @@ public class MinecraftServer { // The difficulty packet ServerDifficultyPacket serverDifficultyPacket = new ServerDifficultyPacket(); serverDifficultyPacket.difficulty = difficulty; - serverDifficultyPacket.locked = true; // Can only be modified on singleplayer + serverDifficultyPacket.locked = true; // Can only be modified on single-player // Send the packet to all online players PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), serverDifficultyPacket); } diff --git a/src/main/java/net/minestom/server/extras/MojangAuth.java b/src/main/java/net/minestom/server/extras/MojangAuth.java index 8d7bbfa39..08ff99972 100644 --- a/src/main/java/net/minestom/server/extras/MojangAuth.java +++ b/src/main/java/net/minestom/server/extras/MojangAuth.java @@ -3,7 +3,7 @@ package net.minestom.server.extras; import lombok.Getter; import net.minestom.server.MinecraftServer; -public class MojangAuth { +public final class MojangAuth { @Getter private static boolean usingMojangAuth = false; diff --git a/src/main/java/net/minestom/server/extras/PlacementRules.java b/src/main/java/net/minestom/server/extras/PlacementRules.java index e2014517c..bb6d92905 100644 --- a/src/main/java/net/minestom/server/extras/PlacementRules.java +++ b/src/main/java/net/minestom/server/extras/PlacementRules.java @@ -7,7 +7,7 @@ import net.minestom.server.instance.block.rule.vanilla.AxisPlacementRule; import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule; import net.minestom.server.instance.block.rule.vanilla.WallPlacementRule; -public class PlacementRules { +public final class PlacementRules { public static void init() { BlockManager blockManager = MinecraftServer.getBlockManager(); diff --git a/src/main/java/net/minestom/server/extras/mojangAuth/MojangCrypt.java b/src/main/java/net/minestom/server/extras/mojangAuth/MojangCrypt.java index 1f23e18b8..c0f08a2f3 100644 --- a/src/main/java/net/minestom/server/extras/mojangAuth/MojangCrypt.java +++ b/src/main/java/net/minestom/server/extras/mojangAuth/MojangCrypt.java @@ -10,7 +10,7 @@ import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.*; -public class MojangCrypt { +public final class MojangCrypt { private static final Logger LOGGER = LogManager.getLogger(); @Nullable diff --git a/src/main/java/net/minestom/server/network/PacketProcessor.java b/src/main/java/net/minestom/server/network/PacketProcessor.java index 21a919ab9..df8d60e9b 100644 --- a/src/main/java/net/minestom/server/network/PacketProcessor.java +++ b/src/main/java/net/minestom/server/network/PacketProcessor.java @@ -14,31 +14,28 @@ import net.minestom.server.network.packet.client.handshake.HandshakePacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; +import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class PacketProcessor { +public final class PacketProcessor { private final Map connectionPlayerConnectionMap = new ConcurrentHashMap<>(); - // Protocols + // Protocols state private final ClientStatusPacketsHandler statusPacketsHandler; private final ClientLoginPacketsHandler loginPacketsHandler; private final ClientPlayPacketsHandler playPacketsHandler; public PacketProcessor() { - this.statusPacketsHandler = new ClientStatusPacketsHandler(); this.loginPacketsHandler = new ClientLoginPacketsHandler(); this.playPacketsHandler = new ClientPlayPacketsHandler(); } - private List printBlackList = Arrays.asList(17, 18, 19); - public void process(ChannelHandlerContext channel, InboundPacket packet) { + // Create the netty player connection object if not existing PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent( channel, c -> new NettyPlayerConnection((SocketChannel) channel.channel()) ); @@ -48,10 +45,6 @@ public class PacketProcessor { final ConnectionState connectionState = playerConnection.getConnectionState(); - //if (!printBlackList.contains(id)) { - //System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id) + " State: " + connectionState); - //} - BinaryReader binaryReader = new BinaryReader(packet.body); if (connectionState == ConnectionState.UNKNOWN) { @@ -84,6 +77,13 @@ public class PacketProcessor { } } + /** + * Retrieves a player connection from its channel. + * + * @param channel the connection channel + * @return the connection of this channel, null if not found + */ + @Nullable public PlayerConnection getPlayerConnection(ChannelHandlerContext channel) { return connectionPlayerConnectionMap.get(channel); } diff --git a/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java b/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java index 62564b4f4..43bcdd328 100644 --- a/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/ClientPacket.java @@ -1,10 +1,10 @@ package net.minestom.server.network.packet.client; -import net.minestom.server.utils.binary.BinaryReader; -import org.jetbrains.annotations.NotNull; +import net.minestom.server.utils.binary.Readable; -public interface ClientPacket { - - void read(@NotNull BinaryReader reader); +/** + * Represents a packet received from a client. + */ +public interface ClientPacket extends Readable { } diff --git a/src/main/java/net/minestom/server/network/packet/client/handler/ClientPacketsHandler.java b/src/main/java/net/minestom/server/network/packet/client/handler/ClientPacketsHandler.java index 2b6650afd..214f18487 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handler/ClientPacketsHandler.java +++ b/src/main/java/net/minestom/server/network/packet/client/handler/ClientPacketsHandler.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.client.handler; import net.minestom.server.network.packet.client.ClientPacket; +import org.jetbrains.annotations.NotNull; import java.util.function.Supplier; @@ -9,17 +10,30 @@ public class ClientPacketsHandler { // Max packet id private static final int SIZE = 0x30; - private final Supplier[] supplierAccesses = new Supplier[SIZE]; + private final ClientPacketSupplier[] supplierAccesses = new ClientPacketSupplier[SIZE]; - public void register(int id, Supplier packetSupplier) { - supplierAccesses[id] = packetSupplier; + /** + * Registers a client packet which can be retrieved later using {@link #getPacketInstance(int)}. + * + * @param id the packet id + * @param packetSupplier the supplier of the packet + */ + public void register(int id, @NotNull ClientPacketSupplier packetSupplier) { + this.supplierAccesses[id] = packetSupplier; } + /** + * Retrieves a {@link net.minestom.server.network.packet.client.ClientPlayPacket} from its id. + * + * @param id the packet id + * @return the associated client packet + * @throws IllegalStateException if {@code id} is not a valid packet id, or unregistered + */ public ClientPacket getPacketInstance(int id) { if (id > SIZE) throw new IllegalStateException("Packet ID 0x" + Integer.toHexString(id) + " has been tried to be parsed, debug needed"); - Supplier supplier = supplierAccesses[id]; + ClientPacketSupplier supplier = supplierAccesses[id]; if (supplierAccesses[id] == null) throw new IllegalStateException("Packet id 0x" + Integer.toHexString(id) + " isn't registered!"); @@ -28,4 +42,10 @@ public class ClientPacketsHandler { return supplier.get(); } + /** + * Convenient interface to supply a {@link ClientPacket}. + */ + protected interface ClientPacketSupplier extends Supplier { + } + } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index faa9c1620..f31071c44 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -43,7 +43,7 @@ public class LoginStartPacket implements ClientPreplayPacket { final int messageId = ThreadLocalRandom.current().nextInt(); final String channel = VelocityProxy.PLAYER_INFO_CHANNEL; - // Important to retrieve the channel in the response packet + // Important in order to retrieve the channel in the response packet nettyPlayerConnection.addPluginRequestEntry(messageId, channel); LoginPluginRequestPacket loginPluginRequestPacket = new LoginPluginRequestPacket(); 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 d9d09d1d7..d2e0bd24c 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -43,7 +43,7 @@ public class NettyPlayerConnection extends PlayerConnection { private String serverAddress; private int serverPort; - // Used for the login plugin request packet, to retrive the channel from a message id, + // Used for the login plugin request packet, to retrieve the channel from a message id, // cleared once the player enters the play state private Map pluginRequestMap = new ConcurrentHashMap<>(); From 3ddca82aaa5ae134f72cb492fa7359c5c1c9b483 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 08:01:27 +0100 Subject: [PATCH 46/55] Cleanup 2 --- .../server/benchmark/BenchmarkManager.java | 7 +++-- .../net/minestom/server/bossbar/BarColor.java | 2 +- .../minestom/server/bossbar/BarDivision.java | 2 +- .../minestom/server/chat/ChatHoverEvent.java | 4 +-- .../net/minestom/server/chat/JsonMessage.java | 2 +- .../server/collision/BoundingBox.java | 11 +++---- .../server/collision/CollisionUtils.java | 2 +- .../server/command/builder/Command.java | 3 +- .../arguments/ArgumentDynamicStringArray.java | 3 +- .../arguments/ArgumentDynamicWord.java | 3 +- .../server/extras/velocity/VelocityProxy.java | 2 +- .../minestom/server/instance/Instance.java | 4 +-- .../inventory/type/FurnaceInventory.java | 2 +- .../network/player/NettyPlayerConnection.java | 2 +- .../minestom/server/utils/NamespaceID.java | 11 +++---- .../server/world/DimensionTypeManager.java | 29 ++++++++++--------- 16 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java index ddb824b32..269f1ff85 100644 --- a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java +++ b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java @@ -7,6 +7,7 @@ import net.minestom.server.chat.ChatColor; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; @@ -24,6 +25,8 @@ import static net.minestom.server.MinecraftServer.*; *

* Needs to be enabled with {@link #enable(UpdateOption)}. Memory can then be accessed with {@link #getUsedMemory()} * and the CPUs usage with {@link #getResultMap()} or {@link #getCpuMonitoringMessage()}. + *

+ * Be aware that this is not the most accurate method, you should use a proper java profiler depending on your needs. */ public final class BenchmarkManager { @@ -53,7 +56,7 @@ public final class BenchmarkManager { private long time; - public void enable(UpdateOption updateOption) { + public void enable(@NotNull UpdateOption updateOption) { Check.stateCondition(enabled, "A benchmark is already running, please disable it first."); time = updateOption.getTimeUnit().toMilliseconds(updateOption.getValue()); @@ -84,7 +87,7 @@ public final class BenchmarkManager { this.enabled = false; } - public void addThreadMonitor(String threadName) { + public void addThreadMonitor(@NotNull String threadName) { THREADS.add(threadName); } diff --git a/src/main/java/net/minestom/server/bossbar/BarColor.java b/src/main/java/net/minestom/server/bossbar/BarColor.java index 63ccd523c..79c997ae5 100644 --- a/src/main/java/net/minestom/server/bossbar/BarColor.java +++ b/src/main/java/net/minestom/server/bossbar/BarColor.java @@ -1,7 +1,7 @@ package net.minestom.server.bossbar; /** - * Represents the displayed color of a {@link BossBar} + * Represents the displayed color of a {@link BossBar}. */ public enum BarColor { PINK, diff --git a/src/main/java/net/minestom/server/bossbar/BarDivision.java b/src/main/java/net/minestom/server/bossbar/BarDivision.java index 113da4d39..317aed640 100644 --- a/src/main/java/net/minestom/server/bossbar/BarDivision.java +++ b/src/main/java/net/minestom/server/bossbar/BarDivision.java @@ -1,7 +1,7 @@ package net.minestom.server.bossbar; /** - * Used to define the number of segments on a {@link BossBar} + * Used to define the number of segments on a {@link BossBar}. */ public enum BarDivision { SOLID, diff --git a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java index be292addf..cf66a0d96 100644 --- a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java +++ b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java @@ -13,8 +13,8 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; public class ChatHoverEvent { private final String action; - private String value; - private JsonObject valueObject; + private final String value; + private final JsonObject valueObject; private final boolean isJson; private ChatHoverEvent(@NotNull String action, @NotNull String value) { diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index d9881ea59..08729948b 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -58,7 +58,7 @@ public abstract class JsonMessage { public static class RawJsonMessage extends JsonMessage { - private JsonObject jsonObject; + private final JsonObject jsonObject; public RawJsonMessage(@NotNull JsonObject jsonObject) { this.jsonObject = jsonObject; diff --git a/src/main/java/net/minestom/server/collision/BoundingBox.java b/src/main/java/net/minestom/server/collision/BoundingBox.java index ebc527cc4..821338d3c 100644 --- a/src/main/java/net/minestom/server/collision/BoundingBox.java +++ b/src/main/java/net/minestom/server/collision/BoundingBox.java @@ -61,28 +61,25 @@ public class BoundingBox { final float offsetX = 1; final float x = blockPosition.getX(); - final float minX = x; final float maxX = x + offsetX; - final boolean checkX = getMinX() < maxX && getMaxX() > minX; + final boolean checkX = getMinX() < maxX && getMaxX() > x; if (!checkX) return false; final float y = blockPosition.getY(); - final float minY = y; final float maxY = y + 0.99999f; - final boolean checkY = getMinY() < maxY && getMaxY() > minY; + final boolean checkY = getMinY() < maxY && getMaxY() > y; if (!checkY) return false; final float offsetZ = 1; final float z = blockPosition.getZ(); - final float minZ = z; final float maxZ = z + offsetZ; - final boolean checkZ = getMinZ() < maxZ && getMaxZ() > minZ; - return checkZ; + // Z check + return getMinZ() < maxZ && getMaxZ() > z; } public boolean intersect(float x, float y, float z) { diff --git a/src/main/java/net/minestom/server/collision/CollisionUtils.java b/src/main/java/net/minestom/server/collision/CollisionUtils.java index ef7746af2..b4780accb 100644 --- a/src/main/java/net/minestom/server/collision/CollisionUtils.java +++ b/src/main/java/net/minestom/server/collision/CollisionUtils.java @@ -105,7 +105,7 @@ public class CollisionUtils { if (!collisionFound) { Vector direction = new Vector(); direction.copy(axis); - collisionFound |= !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions); + collisionFound = !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions); } // find the corner which moved the least diff --git a/src/main/java/net/minestom/server/command/builder/Command.java b/src/main/java/net/minestom/server/command/builder/Command.java index 811d7d6ae..6302a0b05 100644 --- a/src/main/java/net/minestom/server/command/builder/Command.java +++ b/src/main/java/net/minestom/server/command/builder/Command.java @@ -163,9 +163,10 @@ public class Command { } /** - * Sets the default {@link CommandExecutor} (which is called when there is no argument). + * Sets the default {@link CommandExecutor}. * * @param executor the new default executor, null to remove it + * @see #getDefaultExecutor() */ public void setDefaultExecutor(@Nullable CommandExecutor executor) { this.defaultExecutor = executor; diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java index 71689b35e..23128e3c0 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicStringArray.java @@ -35,8 +35,7 @@ public class ArgumentDynamicStringArray extends Argument { public int getConditionResult(@NotNull String[] value) { // true if 'value' is valid based on the dynamic restriction - final boolean restrictionCheck = dynamicRestriction != null ? - dynamicRestriction.isValid(value) : true; + final boolean restrictionCheck = dynamicRestriction == null || dynamicRestriction.isValid(value); if (!restrictionCheck) { return RESTRICTION_ERROR; diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java index df259bebe..331ca68b6 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentDynamicWord.java @@ -37,8 +37,7 @@ public class ArgumentDynamicWord extends Argument { public int getConditionResult(@NotNull String value) { // true if 'value' is valid based on the dynamic restriction - final boolean restrictionCheck = dynamicRestriction != null ? - dynamicRestriction.isValid(value) : true; + final boolean restrictionCheck = dynamicRestriction == null || dynamicRestriction.isValid(value); if (!restrictionCheck) { return RESTRICTION_ERROR; diff --git a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java index 0510d3a21..f479ac110 100644 --- a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java +++ b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java @@ -10,7 +10,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** - * Support for Velocity proxy. + * Support for Velocity modern forwarding. *

* Can be enabled by simply calling {@link #enable(String)}. */ diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 055b86e5b..8b22da131 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -661,8 +661,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta * Sends a {@link BlockActionPacket} for all the viewers of the specific position. * * @param blockPosition the block position - * @param actionId - * @param actionParam + * @param actionId the action id, depends on the block + * @param actionParam the action parameter, depends on the block * @see BlockActionPacket for the action id & param */ public void sendBlockAction(@NotNull BlockPosition blockPosition, byte actionId, byte actionParam) { diff --git a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java index 48bd187a5..6943fb8ce 100644 --- a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java @@ -27,7 +27,7 @@ public class FurnaceInventory extends Inventory { /** * Represents the amount of tick until the fire icon come empty. * - * @param remainingFuelTick + * @param remainingFuelTick the amount of tick until the fire icon is empty */ public void setRemainingFuelTick(short remainingFuelTick) { this.remainingFuelTick = remainingFuelTick; 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 d2e0bd24c..c360bf800 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -45,7 +45,7 @@ public class NettyPlayerConnection extends PlayerConnection { // Used for the login plugin request packet, to retrieve the channel from a message id, // cleared once the player enters the play state - private Map pluginRequestMap = new ConcurrentHashMap<>(); + private final Map pluginRequestMap = new ConcurrentHashMap<>(); public NettyPlayerConnection(@NotNull SocketChannel channel) { super(); diff --git a/src/main/java/net/minestom/server/utils/NamespaceID.java b/src/main/java/net/minestom/server/utils/NamespaceID.java index 527b3b884..9c22c76b2 100644 --- a/src/main/java/net/minestom/server/utils/NamespaceID.java +++ b/src/main/java/net/minestom/server/utils/NamespaceID.java @@ -22,10 +22,11 @@ public class NamespaceID implements CharSequence { * Extracts the domain from the namespace ID. "minecraft:stone" would return "minecraft". * If no ':' character is found, "minecraft" is returned. * - * @param namespaceID + * @param namespaceID the namespace id to get the domain from * @return the domain of the namespace ID */ - public static String getDomain(String namespaceID) { + @NotNull + public static String getDomain(@NotNull String namespaceID) { final int index = namespaceID.indexOf(':'); if (index < 0) return "minecraft"; @@ -37,10 +38,10 @@ public class NamespaceID implements CharSequence { * Extracts the path from the namespace ID. "minecraft:blocks/stone" would return "blocks/stone". * If no ':' character is found, the

namespaceID
is returned. * - * @param namespaceID + * @param namespaceID the namespace id to get the path from * @return the path of the namespace ID */ - public static String getPath(String namespaceID) { + public static String getPath(@NotNull String namespaceID) { final int index = namespaceID.indexOf(':'); if (index < 0) return namespaceID; @@ -61,7 +62,7 @@ public class NamespaceID implements CharSequence { return from(getDomain(id), getPath(id)); } - private NamespaceID(String path) { + private NamespaceID(@NotNull String path) { final int index = path.indexOf(':'); if (index < 0) { this.domain = "minecraft"; diff --git a/src/main/java/net/minestom/server/world/DimensionTypeManager.java b/src/main/java/net/minestom/server/world/DimensionTypeManager.java index f45dc2900..ad184317d 100644 --- a/src/main/java/net/minestom/server/world/DimensionTypeManager.java +++ b/src/main/java/net/minestom/server/world/DimensionTypeManager.java @@ -1,5 +1,6 @@ package net.minestom.server.world; +import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTList; import org.jglrxavpok.hephaistos.nbt.NBTTypes; @@ -9,45 +10,47 @@ import java.util.LinkedList; import java.util.List; /** - * Allows servers to register custom dimensions. Also used during player joining to send the list of all existing dimensions. - * + * Allows servers to register custom dimensions. Also used during player login to send the list of all existing dimensions. + *

* Contains {@link DimensionType#OVERWORLD} by default but can be removed. */ -public class DimensionTypeManager { +public final class DimensionTypeManager { - private List dimensionTypes = new LinkedList<>(); + private final List dimensionTypes = new LinkedList<>(); public DimensionTypeManager() { addDimension(DimensionType.OVERWORLD); } /** - * Add a new dimension type. This does NOT send the new list to players. - * @param dimensionType + * Adds a new dimension type. This does NOT send the new list to players. + * + * @param dimensionType the dimension to add */ - public void addDimension(DimensionType dimensionType) { + public void addDimension(@NotNull DimensionType dimensionType) { dimensionTypes.add(dimensionType); } /** * Removes a dimension type. This does NOT send the new list to players. - * @param dimensionType + * + * @param dimensionType the dimension to remove * @return if the dimension type was removed, false if it was not present before */ - public boolean removeDimension(DimensionType dimensionType) { + public boolean removeDimension(@NotNull DimensionType dimensionType) { return dimensionTypes.remove(dimensionType); } /** - * Returns an immutable copy of the dimension types already registered - * @return + * Returns an immutable copy of the dimension types already registered. + * + * @return an unmodifiable {@link List} containing all the added dimensions */ + @NotNull public List unmodifiableList() { return Collections.unmodifiableList(dimensionTypes); } - - public NBTCompound toNBT() { NBTCompound dimensions = new NBTCompound(); dimensions.setString("type", "minecraft:dimension_type"); From f2e52ff463b0e8461e9fd7263d13374b5fc0c7ee Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 18:13:24 +0100 Subject: [PATCH 47/55] Send an EntityMovementPacket every tick if the player did not move since the last one --- .../server/entity/EntityCreature.java | 8 +- .../net/minestom/server/entity/Player.java | 117 ++++++++++-------- ...yPacket.java => EntityMovementPacket.java} | 2 +- 3 files changed, 68 insertions(+), 59 deletions(-) rename src/main/java/net/minestom/server/network/packet/server/play/{EntityPacket.java => EntityMovementPacket.java} (89%) diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index c80c6e246..8e1229281 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -14,7 +14,7 @@ import net.minestom.server.instance.Instance; import net.minestom.server.instance.WorldBorder; import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.play.EntityEquipmentPacket; -import net.minestom.server.network.packet.server.play.EntityPacket; +import net.minestom.server.network.packet.server.play.EntityMovementPacket; import net.minestom.server.network.packet.server.play.SpawnLivingEntityPacket; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.Position; @@ -176,8 +176,8 @@ public abstract class EntityCreature extends LivingEntity { final PlayerConnection playerConnection = player.getPlayerConnection(); - EntityPacket entityPacket = new EntityPacket(); - entityPacket.entityId = getEntityId(); + EntityMovementPacket entityMovementPacket = new EntityMovementPacket(); + entityMovementPacket.entityId = getEntityId(); SpawnLivingEntityPacket spawnLivingEntityPacket = new SpawnLivingEntityPacket(); spawnLivingEntityPacket.entityId = getEntityId(); @@ -186,7 +186,7 @@ public abstract class EntityCreature extends LivingEntity { spawnLivingEntityPacket.position = getPosition(); spawnLivingEntityPacket.headPitch = 0; - playerConnection.sendPacket(entityPacket); + playerConnection.sendPacket(entityMovementPacket); playerConnection.sendPacket(spawnLivingEntityPacket); playerConnection.sendPacket(getVelocityPacket()); playerConnection.sendPacket(getMetadataPacket()); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 9936148f6..4a6d4b9c2 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -398,63 +398,72 @@ public class Player extends LivingEntity implements CommandSender { // Multiplayer sync final boolean positionChanged = position.getX() != lastX || position.getY() != lastY || position.getZ() != lastZ; final boolean viewChanged = position.getYaw() != lastYaw || position.getPitch() != lastPitch; - if (!getViewers().isEmpty() && (positionChanged || viewChanged)) { - ServerPacket updatePacket; - ServerPacket optionalUpdatePacket = null; - if (positionChanged && viewChanged) { - EntityPositionAndRotationPacket entityPositionAndRotationPacket = new EntityPositionAndRotationPacket(); - entityPositionAndRotationPacket.entityId = getEntityId(); - entityPositionAndRotationPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); - entityPositionAndRotationPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); - entityPositionAndRotationPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); - entityPositionAndRotationPacket.yaw = position.getYaw(); - entityPositionAndRotationPacket.pitch = position.getPitch(); - entityPositionAndRotationPacket.onGround = onGround; + if (!viewers.isEmpty()) { + if (positionChanged || viewChanged) { + // Player moved since last time + + ServerPacket updatePacket; + ServerPacket optionalUpdatePacket = null; + if (positionChanged && viewChanged) { + EntityPositionAndRotationPacket entityPositionAndRotationPacket = new EntityPositionAndRotationPacket(); + entityPositionAndRotationPacket.entityId = getEntityId(); + entityPositionAndRotationPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); + entityPositionAndRotationPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); + entityPositionAndRotationPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); + entityPositionAndRotationPacket.yaw = position.getYaw(); + entityPositionAndRotationPacket.pitch = position.getPitch(); + entityPositionAndRotationPacket.onGround = onGround; + + lastX = position.getX(); + lastY = position.getY(); + lastZ = position.getZ(); + lastYaw = position.getYaw(); + lastPitch = position.getPitch(); + updatePacket = entityPositionAndRotationPacket; + } else if (positionChanged) { + EntityPositionPacket entityPositionPacket = new EntityPositionPacket(); + entityPositionPacket.entityId = getEntityId(); + entityPositionPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); + entityPositionPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); + entityPositionPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); + entityPositionPacket.onGround = onGround; + lastX = position.getX(); + lastY = position.getY(); + lastZ = position.getZ(); + updatePacket = entityPositionPacket; + } else { + // View changed + EntityRotationPacket entityRotationPacket = new EntityRotationPacket(); + entityRotationPacket.entityId = getEntityId(); + entityRotationPacket.yaw = position.getYaw(); + entityRotationPacket.pitch = position.getPitch(); + entityRotationPacket.onGround = onGround; + + lastYaw = position.getYaw(); + lastPitch = position.getPitch(); + updatePacket = entityRotationPacket; + } + + if (viewChanged) { + EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket(); + entityHeadLookPacket.entityId = getEntityId(); + entityHeadLookPacket.yaw = position.getYaw(); + optionalUpdatePacket = entityHeadLookPacket; + } + + // Send the update packet + if (optionalUpdatePacket != null) { + sendPacketsToViewers(updatePacket, optionalUpdatePacket); + } else { + sendPacketToViewers(updatePacket); + } - lastX = position.getX(); - lastY = position.getY(); - lastZ = position.getZ(); - lastYaw = position.getYaw(); - lastPitch = position.getPitch(); - updatePacket = entityPositionAndRotationPacket; - } else if (positionChanged) { - EntityPositionPacket entityPositionPacket = new EntityPositionPacket(); - entityPositionPacket.entityId = getEntityId(); - entityPositionPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); - entityPositionPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); - entityPositionPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); - entityPositionPacket.onGround = onGround; - lastX = position.getX(); - lastY = position.getY(); - lastZ = position.getZ(); - updatePacket = entityPositionPacket; } else { - // View changed - EntityRotationPacket entityRotationPacket = new EntityRotationPacket(); - entityRotationPacket.entityId = getEntityId(); - entityRotationPacket.yaw = position.getYaw(); - entityRotationPacket.pitch = position.getPitch(); - entityRotationPacket.onGround = onGround; - - lastYaw = position.getYaw(); - lastPitch = position.getPitch(); - updatePacket = entityRotationPacket; + // Player did not move since last time + EntityMovementPacket entityMovementPacket = new EntityMovementPacket(); + entityMovementPacket.entityId = getEntityId(); + sendPacketToViewers(entityMovementPacket); } - - if (viewChanged) { - EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket(); - entityHeadLookPacket.entityId = getEntityId(); - entityHeadLookPacket.yaw = position.getYaw(); - optionalUpdatePacket = entityHeadLookPacket; - } - - // Send the update packet - if (optionalUpdatePacket != null) { - sendPacketsToViewers(updatePacket, optionalUpdatePacket); - } else { - sendPacketToViewers(updatePacket); - } - } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntityPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntityMovementPacket.java similarity index 89% rename from src/main/java/net/minestom/server/network/packet/server/play/EntityPacket.java rename to src/main/java/net/minestom/server/network/packet/server/play/EntityMovementPacket.java index 6c4d076ee..1e70ab86a 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntityPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntityMovementPacket.java @@ -5,7 +5,7 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class EntityPacket implements ServerPacket { +public class EntityMovementPacket implements ServerPacket { public int entityId; From bcee5424dc628e18550e78e1fd1a32cbbf42fd00 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 21:14:24 +0100 Subject: [PATCH 48/55] Fixed copied packets and compression using velocity forwarding --- .../server/extras/velocity/VelocityProxy.java | 18 ++++++++----- .../login/EncryptionResponsePacket.java | 5 ---- .../login/LoginPluginResponsePacket.java | 14 ++++++++++ .../packet/client/login/LoginStartPacket.java | 17 ++++++------ .../network/player/NettyPlayerConnection.java | 26 ++++++++++++++----- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java index f479ac110..daeb72f07 100644 --- a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java +++ b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java @@ -1,10 +1,13 @@ package net.minestom.server.extras.velocity; +import com.google.common.net.InetAddresses; +import io.netty.buffer.ByteBuf; import net.minestom.server.utils.binary.BinaryReader; import org.jetbrains.annotations.NotNull; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import java.net.InetAddress; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -17,6 +20,7 @@ import java.security.NoSuchAlgorithmException; public final class VelocityProxy { public static final String PLAYER_INFO_CHANNEL = "velocity:player_info"; + private static final int SUPPORTED_FORWARDING_VERSION = 1; private static boolean enabled; private static byte[] secret; @@ -48,7 +52,9 @@ public final class VelocityProxy { final byte[] signature = reader.readBytes(32); - final byte[] data = reader.getRemainingBytes(); + ByteBuf buf = reader.getBuffer(); + final byte[] data = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), data); try { final Mac mac = Mac.getInstance("HmacSHA256"); @@ -61,12 +67,12 @@ public final class VelocityProxy { throw new AssertionError(e); } - /*int version = buf.readVarInt(); - if (version != SUPPORTED_FORWARDING_VERSION) { - throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + SUPPORTED_FORWARDING_VERSION); - }*/ + final int version = reader.readVarInt(); + return version == SUPPORTED_FORWARDING_VERSION; + } - return true; + public static InetAddress readAddress(@NotNull BinaryReader reader) { + return InetAddresses.forString(reader.readSizedString()); } } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java index dbbe5ccc8..8255c8a43 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/EncryptionResponsePacket.java @@ -55,11 +55,6 @@ public class EncryptionResponsePacket implements ClientPreplayPacket { final String string3 = new BigInteger(digestedData).toString(16); final GameProfile gameProfile = MinecraftServer.getSessionService().hasJoinedServer(new GameProfile(null, loginUsername), string3); nettyConnection.setEncryptionKey(getSecretKey()); - final int threshold = MinecraftServer.getCompressionThreshold(); - - if (threshold > 0) { - nettyConnection.enableCompression(threshold); - } MinecraftServer.getLOGGER().info("UUID of player {} is {}", loginUsername, gameProfile.getId()); CONNECTION_MANAGER.startPlayState(connection, gameProfile.getId(), gameProfile.getName()); diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java index 9378c86a1..79e54277a 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java @@ -12,6 +12,9 @@ import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; import org.jetbrains.annotations.NotNull; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.UUID; public class LoginPluginResponsePacket implements ClientPreplayPacket { @@ -34,16 +37,27 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket { if (channel != null) { boolean success = false; + SocketAddress socketAddress = null; // Velocity if (VelocityProxy.isEnabled() && channel.equals(VelocityProxy.PLAYER_INFO_CHANNEL)) { if (data != null) { BinaryReader reader = new BinaryReader(data); success = VelocityProxy.checkIntegrity(reader); + if (success) { + // Get the real connection address + final InetAddress address = VelocityProxy.readAddress(reader); + final int port = ((java.net.InetSocketAddress) connection.getRemoteAddress()).getPort(); + socketAddress = new InetSocketAddress(address, port); + } } } if (success) { + if (socketAddress != null) { + nettyPlayerConnection.setRemoteAddress(socketAddress); + } + // Proxy usage always mean that the server is in offline mode final String username = nettyPlayerConnection.getLoginUsername(); final UUID playerUuid = CONNECTION_MANAGER.getPlayerConnectionUuid(connection, username); diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index f31071c44..52c150317 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -27,9 +27,16 @@ public class LoginStartPacket implements ClientPreplayPacket { @Override public void process(@NotNull PlayerConnection connection) { - // Cache the login username + // Cache the login username and start compression if enabled if (connection instanceof NettyPlayerConnection) { - ((NettyPlayerConnection) connection).UNSAFE_setLoginUsername(username); + NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) connection; + nettyPlayerConnection.UNSAFE_setLoginUsername(username); + + // Compression + final int threshold = MinecraftServer.getCompressionThreshold(); + if (threshold > 0) { + nettyPlayerConnection.enableCompression(threshold); + } } // Proxy support (only for netty clients) @@ -75,12 +82,6 @@ public class LoginStartPacket implements ClientPreplayPacket { // Offline final UUID playerUuid = CONNECTION_MANAGER.getPlayerConnectionUuid(connection, username); - final int threshold = MinecraftServer.getCompressionThreshold(); - - if (threshold > 0 && connection instanceof NettyPlayerConnection) { - ((NettyPlayerConnection) connection).enableCompression(threshold); - } - CONNECTION_MANAGER.startPlayState(connection, playerUuid, username); } } 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 c360bf800..cebb7fbab 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -29,6 +29,8 @@ import java.util.concurrent.ConcurrentHashMap; public class NettyPlayerConnection extends PlayerConnection { private final SocketChannel channel; + + private SocketAddress remoteAddress; @Getter private boolean encrypted = false; @Getter @@ -50,6 +52,7 @@ public class NettyPlayerConnection extends PlayerConnection { public NettyPlayerConnection(@NotNull SocketChannel channel) { super(); this.channel = channel; + this.remoteAddress = channel.remoteAddress(); } /** @@ -80,25 +83,25 @@ public class NettyPlayerConnection extends PlayerConnection { @Override public void sendPacket(@NotNull ByteBuf buffer, boolean copy) { - if ((encrypted || compressed) && copy) { + if (copy) { buffer = buffer.copy(); buffer.retain(); channel.writeAndFlush(buffer); buffer.release(); } else { - getChannel().writeAndFlush(buffer); + channel.writeAndFlush(buffer); } } @Override public void writePacket(@NotNull ByteBuf buffer, boolean copy) { - if ((encrypted || compressed) && copy) { + if (copy) { buffer = buffer.copy(); buffer.retain(); channel.write(buffer); buffer.release(); } else { - getChannel().write(buffer); + channel.write(buffer); } } @@ -115,7 +118,18 @@ public class NettyPlayerConnection extends PlayerConnection { @NotNull @Override public SocketAddress getRemoteAddress() { - return getChannel().remoteAddress(); + return remoteAddress; + } + + /** + * Changes the internal remote address field. + *

+ * Mostly unsafe, used internally when interacting with a proxy. + * + * @param remoteAddress the new connection remote address + */ + public void setRemoteAddress(@NotNull SocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; } @Override @@ -141,7 +155,7 @@ public class NettyPlayerConnection extends PlayerConnection { } /** - * Sets the internal login username field + * Sets the internal login username field. * * @param loginUsername the new login username field */ From bbf9f92b52b08b939f7a1728b6466dfc856ba020 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 21:38:08 +0100 Subject: [PATCH 49/55] Added bungee forwarding support --- .../server/extras/bungee/BungeeCordProxy.java | 27 ++++++++++++++++++ .../server/network/PacketProcessor.java | 26 +++++++++++++---- .../client/handshake/HandshakePacket.java | 28 +++++++++++++++++++ src/test/java/demo/Main.java | 4 ++- src/test/java/demo/PlayerInit.java | 6 ---- src/test/java/demo/commands/TestCommand.java | 6 ++-- 6 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 src/main/java/net/minestom/server/extras/bungee/BungeeCordProxy.java diff --git a/src/main/java/net/minestom/server/extras/bungee/BungeeCordProxy.java b/src/main/java/net/minestom/server/extras/bungee/BungeeCordProxy.java new file mode 100644 index 000000000..9886e1f75 --- /dev/null +++ b/src/main/java/net/minestom/server/extras/bungee/BungeeCordProxy.java @@ -0,0 +1,27 @@ +package net.minestom.server.extras.bungee; + +/** + * BungeeCord forwarding support. This does not count as a security feature and you will still be required to manage your firewall. + *

+ * Please consider using {@link net.minestom.server.extras.velocity.VelocityProxy} instead. + */ +public final class BungeeCordProxy { + + private static boolean enabled; + + /** + * Enables bungee IP forwarding. + */ + public static void enable() { + BungeeCordProxy.enabled = true; + } + + /** + * Gets if bungee IP forwarding is enabled. + * + * @return true if forwarding is enabled + */ + public static boolean isEnabled() { + return enabled; + } +} diff --git a/src/main/java/net/minestom/server/network/PacketProcessor.java b/src/main/java/net/minestom/server/network/PacketProcessor.java index df8d60e9b..77d4d6556 100644 --- a/src/main/java/net/minestom/server/network/PacketProcessor.java +++ b/src/main/java/net/minestom/server/network/PacketProcessor.java @@ -14,13 +14,19 @@ import net.minestom.server.network.packet.client.handshake.HandshakePacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; +import net.minestom.server.utils.binary.Readable; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public final class PacketProcessor { + private final static Logger LOGGER = LoggerFactory.getLogger(PacketProcessor.class); + private final Map connectionPlayerConnectionMap = new ConcurrentHashMap<>(); // Protocols state @@ -34,7 +40,7 @@ public final class PacketProcessor { this.playPacketsHandler = new ClientPlayPacketsHandler(); } - public void process(ChannelHandlerContext channel, InboundPacket packet) { + public void process(@NotNull ChannelHandlerContext channel, @NotNull InboundPacket packet) { // Create the netty player connection object if not existing PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent( channel, c -> new NettyPlayerConnection((SocketChannel) channel.channel()) @@ -51,7 +57,7 @@ public final class PacketProcessor { // Should be handshake packet if (packet.packetId == 0) { HandshakePacket handshakePacket = new HandshakePacket(); - handshakePacket.read(binaryReader); + safeRead(playerConnection, handshakePacket, binaryReader); handshakePacket.process(playerConnection); } return; @@ -61,17 +67,17 @@ public final class PacketProcessor { case PLAY: final Player player = playerConnection.getPlayer(); ClientPlayPacket playPacket = (ClientPlayPacket) playPacketsHandler.getPacketInstance(packet.packetId); - playPacket.read(binaryReader); + safeRead(playerConnection, playPacket, binaryReader); player.addPacketToQueue(playPacket); break; case LOGIN: final ClientPreplayPacket loginPacket = (ClientPreplayPacket) loginPacketsHandler.getPacketInstance(packet.packetId); - loginPacket.read(binaryReader); + safeRead(playerConnection, loginPacket, binaryReader); loginPacket.process(playerConnection); break; case STATUS: final ClientPreplayPacket statusPacket = (ClientPreplayPacket) statusPacketsHandler.getPacketInstance(packet.packetId); - statusPacket.read(binaryReader); + safeRead(playerConnection, statusPacket, binaryReader); statusPacket.process(playerConnection); break; } @@ -91,4 +97,14 @@ public final class PacketProcessor { public void removePlayerConnection(ChannelHandlerContext channel) { connectionPlayerConnectionMap.remove(channel); } + + private void safeRead(@NotNull PlayerConnection connection, @NotNull Readable readable, @NotNull BinaryReader reader) { + try { + readable.read(reader); + } catch (Exception e) { + final Player player = connection.getPlayer(); + final String username = player != null ? player.getUsername() : "null"; + LOGGER.warn("Connection " + connection.getRemoteAddress() + " (" + username + ") sent an unexpected packet."); + } + } } diff --git a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java index 9658a9af1..8ccff15cd 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java @@ -3,6 +3,7 @@ package net.minestom.server.network.packet.client.handshake; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; +import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.client.ClientPreplayPacket; import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; @@ -11,6 +12,8 @@ import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryReader; import org.jetbrains.annotations.NotNull; +import java.net.SocketAddress; + public class HandshakePacket implements ClientPreplayPacket { /** @@ -18,6 +21,8 @@ public class HandshakePacket implements ClientPreplayPacket { */ private static final ColoredText INVALID_VERSION_TEXT = ColoredText.of(ChatColor.RED, "Invalid Version, please use " + MinecraftServer.VERSION_NAME); + private static final ColoredText INVALID_BUNGEE_FORWARDING = ColoredText.of(ChatColor.RED, "If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); + private int protocolVersion; private String serverAddress; private int serverPort; @@ -33,6 +38,29 @@ public class HandshakePacket implements ClientPreplayPacket { @Override public void process(@NotNull PlayerConnection connection) { + + if (BungeeCordProxy.isEnabled() && connection instanceof NettyPlayerConnection) { + NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) connection; + + if (serverAddress != null) { + final String[] split = serverAddress.split("\00"); + + if (split.length == 3 || split.length == 4) { + this.serverAddress = split[0]; + + final SocketAddress socketAddress = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) connection.getRemoteAddress()).getPort()); + nettyPlayerConnection.setRemoteAddress(socketAddress); + } else { + nettyPlayerConnection.sendPacket(new LoginDisconnectPacket(INVALID_BUNGEE_FORWARDING)); + nettyPlayerConnection.disconnect(); + return; + } + } else { + // Happen when a client ping the server, ignore + return; + } + } + switch (nextState) { case 1: connection.setConnectionState(ConnectionState.STATUS); diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index 9b106397a..1891e5b6e 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -7,6 +7,7 @@ import demo.commands.GamemodeCommand; import demo.commands.TestCommand; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; +import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule; import net.minestom.server.storage.StorageManager; @@ -51,8 +52,9 @@ public class Main { PlayerInit.init(); //VelocityProxy.enable("rBeJJ79W4MVU"); + BungeeCordProxy.enable(); - //MojangAuth.init(); + // MojangAuth.init(); minecraftServer.start("0.0.0.0", 25565, PlayerInit.getResponseDataConsumer()); } diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 2dfa2c6e7..4705aecbd 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -5,8 +5,6 @@ import demo.generator.NoiseTestGenerator; import net.minestom.server.MinecraftServer; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.chat.ColoredText; -import net.minestom.server.data.Data; -import net.minestom.server.data.NbtDataImpl; import net.minestom.server.entity.*; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.type.monster.EntityZombie; @@ -174,11 +172,7 @@ public class PlayerInit { } ItemStack itemStack = new ItemStack(Material.DIAMOND_PICKAXE, (byte) 64); - Data data = new NbtDataImpl(); - data.set("test", 51); - itemStack.setData(data); player.getInventory().addItemStack(itemStack); - System.out.println("test " + data.get("test")); //player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte)64)); }); diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index 456458317..83a5abd57 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -5,6 +5,7 @@ import net.minestom.server.command.builder.Arguments; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.item.ItemStack; public class TestCommand extends Command { @@ -17,7 +18,7 @@ public class TestCommand extends Command { //addSyntax(this::execute, dynamicWord); } - Argument test = ArgumentType.DynamicWord("testArg").fromRestrictions(value -> value.contains("a")); + Argument test = ArgumentType.ItemStack("item"); test.setCallback((source, value, error) -> { System.out.println("ERROR " + error); @@ -29,7 +30,8 @@ public class TestCommand extends Command { }); addSyntax((source, args) -> { - System.out.println("HEY IT WORKS"); + ItemStack itemStack = args.getItemStack("item"); + System.out.println("HEY IT WORKS "+itemStack.getMaterial()); }, test); } From ad357d70fc2a84c34b8e004cb04ad3eb0756cf93 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 23:16:35 +0100 Subject: [PATCH 50/55] Added ArgumentRelativeBlockPosition, ArgumentRelativeVec3 and ArgumentRelativeVec2 --- .../net/minestom/server/UpdateManager.java | 1 + .../server/command/CommandManager.java | 14 +++- .../server/command/builder/Arguments.java | 12 +++ .../builder/arguments/ArgumentType.java | 15 ++++ .../arguments/relative/ArgumentRelative.java | 23 ++++++ .../ArgumentRelativeBlockPosition.java | 76 +++++++++++++++++++ .../relative/ArgumentRelativeVec2.java | 71 +++++++++++++++++ .../relative/ArgumentRelativeVec3.java | 76 +++++++++++++++++++ .../server/network/PacketProcessor.java | 7 ++ .../utils/location/RelativeBlockPosition.java | 26 +++++++ .../utils/location/RelativeLocation.java | 21 +++++ .../server/utils/location/RelativeVec.java | 27 +++++++ src/test/java/demo/Main.java | 3 +- src/test/java/demo/commands/TestCommand.java | 8 +- 14 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java create mode 100644 src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java create mode 100644 src/main/java/net/minestom/server/utils/location/RelativeLocation.java create mode 100644 src/main/java/net/minestom/server/utils/location/RelativeVec.java diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index eb1842975..2a7751e56 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -40,6 +40,7 @@ public final class UpdateManager { private final ConcurrentLinkedQueue tickEndCallbacks = new ConcurrentLinkedQueue<>(); { + // DEFAULT THREAD PROVIDER //threadProvider = new PerInstanceThreadProvider(); threadProvider = new PerGroupChunkProvider(); } diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 03faa36fa..159a27131 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -15,6 +15,9 @@ import net.minestom.server.command.builder.arguments.number.ArgumentDouble; import net.minestom.server.command.builder.arguments.number.ArgumentFloat; import net.minestom.server.command.builder.arguments.number.ArgumentInteger; import net.minestom.server.command.builder.arguments.number.ArgumentNumber; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBlockPosition; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3; import net.minestom.server.command.builder.condition.CommandCondition; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerCommandEvent; @@ -442,7 +445,7 @@ public final class CommandManager { // You can uncomment this to test any brigadier parser on the client /*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable, false); - testNode.parser = "minecraft:item_predicate"; + testNode.parser = "minecraft:vec3"; if (true) { return nodes; @@ -595,6 +598,15 @@ public final class CommandManager { } else if (argument instanceof ArgumentNbtTag) { DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false); argumentNode.parser = "minecraft:nbt_tag"; + } else if (argument instanceof ArgumentRelativeBlockPosition) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false); + argumentNode.parser = "minecraft:block_pos"; + } else if (argument instanceof ArgumentRelativeVec3) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false); + argumentNode.parser = "minecraft:vec3"; + } else if (argument instanceof ArgumentRelativeVec2) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false); + argumentNode.parser = "minecraft:vec2"; } return nodes; diff --git a/src/main/java/net/minestom/server/command/builder/Arguments.java b/src/main/java/net/minestom/server/command/builder/Arguments.java index c9c86c1aa..bee9098d3 100644 --- a/src/main/java/net/minestom/server/command/builder/Arguments.java +++ b/src/main/java/net/minestom/server/command/builder/Arguments.java @@ -7,6 +7,8 @@ import net.minestom.server.item.Enchantment; import net.minestom.server.item.ItemStack; import net.minestom.server.particle.Particle; import net.minestom.server.potion.PotionEffect; +import net.minestom.server.utils.location.RelativeBlockPosition; +import net.minestom.server.utils.location.RelativeVec; import net.minestom.server.utils.math.FloatRange; import net.minestom.server.utils.math.IntRange; import net.minestom.server.utils.time.UpdateOption; @@ -126,6 +128,16 @@ public final class Arguments { return (NBT) getObject(id); } + @NotNull + public RelativeBlockPosition getRelativeBlockPosition(@NotNull String id) { + return (RelativeBlockPosition) getObject(id); + } + + @NotNull + public RelativeVec getRelativeVector(@NotNull String id) { + return (RelativeVec) getObject(id); + } + @NotNull public Object getObject(@NotNull String id) { return args.computeIfAbsent(id, s -> { diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 494340390..ff46ff741 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -9,6 +9,9 @@ import net.minestom.server.command.builder.arguments.number.ArgumentDouble; import net.minestom.server.command.builder.arguments.number.ArgumentFloat; import net.minestom.server.command.builder.arguments.number.ArgumentInteger; import net.minestom.server.command.builder.arguments.number.ArgumentLong; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBlockPosition; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3; import org.jetbrains.annotations.NotNull; /** @@ -109,4 +112,16 @@ public class ArgumentType { return new ArgumentNbtTag(id); } + public static ArgumentRelativeBlockPosition RelativeBlockPosition(@NotNull String id) { + return new ArgumentRelativeBlockPosition(id); + } + + public static ArgumentRelativeVec3 RelativeVec3(@NotNull String id) { + return new ArgumentRelativeVec3(id); + } + + public static ArgumentRelativeVec2 RelativeVec2(@NotNull String id) { + return new ArgumentRelativeVec2(id); + } + } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java new file mode 100644 index 000000000..4529f3221 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java @@ -0,0 +1,23 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.command.builder.arguments.Argument; +import org.jetbrains.annotations.NotNull; + +public abstract class ArgumentRelative extends Argument { + + public static final String RELATIVE_CHAR = "~"; + + public static final int INVALID_NUMBER_COUNT_ERROR = 1; + public static final int INVALID_NUMBER_ERROR = 2; + + private final int numberCount; + + public ArgumentRelative(@NotNull String id, int numberCount) { + super(id, true); + this.numberCount = numberCount; + } + + public int getNumberCount() { + return numberCount; + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java new file mode 100644 index 000000000..7afafe65a --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java @@ -0,0 +1,76 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.utils.BlockPosition; +import net.minestom.server.utils.location.RelativeBlockPosition; +import org.jetbrains.annotations.NotNull; + +public class ArgumentRelativeBlockPosition extends ArgumentRelative { + + public ArgumentRelativeBlockPosition(@NotNull String id) { + super(id, 3); + } + + @Override + public int getCorrectionResult(@NotNull String value) { + final String[] split = value.split(" "); + + // Check if the value has enough element to be correct + if (split.length != getNumberCount()) { + return INVALID_NUMBER_COUNT_ERROR; + } + + // Check if each element is correct + for (String element : split) { + if (!element.equals(RELATIVE_CHAR)) { + try { + // Will throw the exception if not an integer + Integer.parseInt(element); + } catch (NumberFormatException e) { + return INVALID_NUMBER_ERROR; + } + } + } + + return SUCCESS; + } + + @NotNull + @Override + public RelativeBlockPosition parse(@NotNull String value) { + final String[] split = value.split(" "); + + BlockPosition blockPosition = new BlockPosition(0, 0, 0); + boolean relativeX = false; + boolean relativeY = false; + boolean relativeZ = false; + + for (int i = 0; i < split.length; i++) { + final String element = split[i]; + if (element.equals(RELATIVE_CHAR)) { + if (i == 0) { + relativeX = true; + } else if (i == 1) { + relativeY = true; + } else if (i == 2) { + relativeZ = true; + } + } else { + final int number = Integer.parseInt(element); + if (i == 0) { + blockPosition.setX(number); + } else if (i == 1) { + blockPosition.setY(number); + } else if (i == 2) { + blockPosition.setZ(number); + } + } + } + + return new RelativeBlockPosition(blockPosition, relativeX, relativeY, relativeZ); + } + + @Override + public int getConditionResult(@NotNull RelativeBlockPosition value) { + return SUCCESS; + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java new file mode 100644 index 000000000..5d54fa909 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java @@ -0,0 +1,71 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.utils.Vector; +import net.minestom.server.utils.location.RelativeVec; +import org.jetbrains.annotations.NotNull; + +public class ArgumentRelativeVec2 extends ArgumentRelative { + + public ArgumentRelativeVec2(@NotNull String id) { + super(id, 2); + } + + @Override + public int getCorrectionResult(@NotNull String value) { + final String[] split = value.split(" "); + + // Check if the value has enough element to be correct + if (split.length != getNumberCount()) { + return INVALID_NUMBER_COUNT_ERROR; + } + + // Check if each element is correct + for (String element : split) { + if (!element.equals(RELATIVE_CHAR)) { + try { + // Will throw the exception if not a float + Float.parseFloat(element); + } catch (NumberFormatException e) { + return INVALID_NUMBER_ERROR; + } + } + } + + return SUCCESS; + } + + @NotNull + @Override + public RelativeVec parse(@NotNull String value) { + final String[] split = value.split(" "); + + Vector vector = new Vector(); + boolean relativeX = false; + boolean relativeZ = false; + + for (int i = 0; i < split.length; i++) { + final String element = split[i]; + if (element.equals(RELATIVE_CHAR)) { + if (i == 0) { + relativeX = true; + } else if (i == 1) { + relativeZ = true; + } + } else { + final float number = Float.parseFloat(element); + if (i == 0) { + vector.setX(number); + } else if (i == 1) { + vector.setZ(number); + } + } + } + + return new RelativeVec(vector, relativeX, false, relativeZ); + } + + @Override + public int getConditionResult(@NotNull RelativeVec value) { + return SUCCESS; + } +} \ No newline at end of file diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java new file mode 100644 index 000000000..bc14279d1 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java @@ -0,0 +1,76 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.utils.Vector; +import net.minestom.server.utils.location.RelativeVec; +import org.jetbrains.annotations.NotNull; + +public class ArgumentRelativeVec3 extends ArgumentRelative { + + public ArgumentRelativeVec3(@NotNull String id) { + super(id, 3); + } + + @Override + public int getCorrectionResult(@NotNull String value) { + final String[] split = value.split(" "); + + // Check if the value has enough element to be correct + if (split.length != getNumberCount()) { + return INVALID_NUMBER_COUNT_ERROR; + } + + // Check if each element is correct + for (String element : split) { + if (!element.equals(RELATIVE_CHAR)) { + try { + // Will throw the exception if not a float + Float.parseFloat(element); + } catch (NumberFormatException e) { + return INVALID_NUMBER_ERROR; + } + } + } + + return SUCCESS; + } + + @NotNull + @Override + public RelativeVec parse(@NotNull String value) { + final String[] split = value.split(" "); + + Vector vector = new Vector(); + boolean relativeX = false; + boolean relativeY = false; + boolean relativeZ = false; + + for (int i = 0; i < split.length; i++) { + final String element = split[i]; + if (element.equals(RELATIVE_CHAR)) { + if (i == 0) { + relativeX = true; + } else if (i == 1) { + relativeY = true; + } else if (i == 2) { + relativeZ = true; + } + } else { + final float number = Float.parseFloat(element); + if (i == 0) { + vector.setX(number); + } else if (i == 1) { + vector.setY(number); + } else if (i == 2) { + vector.setZ(number); + } + } + } + + return new RelativeVec(vector, relativeX, relativeY, relativeZ); + } + + @Override + public int getConditionResult(@NotNull RelativeVec value) { + return SUCCESS; + } +} diff --git a/src/main/java/net/minestom/server/network/PacketProcessor.java b/src/main/java/net/minestom/server/network/PacketProcessor.java index 77d4d6556..f5825e2d1 100644 --- a/src/main/java/net/minestom/server/network/PacketProcessor.java +++ b/src/main/java/net/minestom/server/network/PacketProcessor.java @@ -98,6 +98,13 @@ public final class PacketProcessor { connectionPlayerConnectionMap.remove(channel); } + /** + * Calls {@link Readable#read(BinaryReader)} and catch all the exceptions to be printed using the packet processor logger. + * + * @param connection the connection who sent the packet + * @param readable the readable interface + * @param reader the buffer containing the packet + */ private void safeRead(@NotNull PlayerConnection connection, @NotNull Readable readable, @NotNull BinaryReader reader) { try { readable.read(reader); diff --git a/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java new file mode 100644 index 000000000..d0440fe4c --- /dev/null +++ b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java @@ -0,0 +1,26 @@ +package net.minestom.server.utils.location; + +import net.minestom.server.entity.Entity; +import net.minestom.server.utils.BlockPosition; +import net.minestom.server.utils.Position; + +public class RelativeBlockPosition extends RelativeLocation { + + public RelativeBlockPosition(BlockPosition location, boolean relativeX, boolean relativeY, boolean relativeZ) { + super(location, relativeX, relativeY, relativeZ); + } + + @Override + public BlockPosition fromRelativePosition(Entity entity) { + if (!relativeX && !relativeY && !relativeZ) { + return location.copy(); + } + final Position entityPosition = entity.getPosition(); + + final int x = relativeX ? (int) entityPosition.getX() : location.getX(); + final int y = relativeY ? (int) entityPosition.getY() : location.getY(); + final int z = relativeZ ? (int) entityPosition.getZ() : location.getZ(); + + return new BlockPosition(x, y, z); + } +} diff --git a/src/main/java/net/minestom/server/utils/location/RelativeLocation.java b/src/main/java/net/minestom/server/utils/location/RelativeLocation.java new file mode 100644 index 000000000..0d53460b7 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/location/RelativeLocation.java @@ -0,0 +1,21 @@ +package net.minestom.server.utils.location; + +import net.minestom.server.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class RelativeLocation { + + protected T location; + protected boolean relativeX, relativeY, relativeZ; + + public RelativeLocation(@NotNull T location, boolean relativeX, boolean relativeY, boolean relativeZ) { + this.location = location; + this.relativeX = relativeX; + this.relativeY = relativeY; + this.relativeZ = relativeZ; + } + + public abstract T fromRelativePosition(@Nullable Entity entity); + +} diff --git a/src/main/java/net/minestom/server/utils/location/RelativeVec.java b/src/main/java/net/minestom/server/utils/location/RelativeVec.java new file mode 100644 index 000000000..b5247276a --- /dev/null +++ b/src/main/java/net/minestom/server/utils/location/RelativeVec.java @@ -0,0 +1,27 @@ +package net.minestom.server.utils.location; + +import net.minestom.server.entity.Entity; +import net.minestom.server.utils.Position; +import net.minestom.server.utils.Vector; +import org.jetbrains.annotations.Nullable; + +public class RelativeVec extends RelativeLocation { + + public RelativeVec(Vector location, boolean relativeX, boolean relativeY, boolean relativeZ) { + super(location, relativeX, relativeY, relativeZ); + } + + @Override + public Vector fromRelativePosition(@Nullable Entity entity) { + if (!relativeX && !relativeY && !relativeZ) { + return location.copy(); + } + final Position entityPosition = entity.getPosition(); + + final float x = relativeX ? (int) entityPosition.getX() : location.getX(); + final float y = relativeY ? (int) entityPosition.getY() : location.getY(); + final float z = relativeZ ? (int) entityPosition.getZ() : location.getZ(); + + return new Vector(x, y, z); + } +} diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index 1891e5b6e..1ab916818 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -7,7 +7,6 @@ import demo.commands.GamemodeCommand; import demo.commands.TestCommand; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; -import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule; import net.minestom.server.storage.StorageManager; @@ -52,7 +51,7 @@ public class Main { PlayerInit.init(); //VelocityProxy.enable("rBeJJ79W4MVU"); - BungeeCordProxy.enable(); + //BungeeCordProxy.enable(); // MojangAuth.init(); diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index 83a5abd57..e99ff2fa9 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -5,7 +5,7 @@ import net.minestom.server.command.builder.Arguments; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.arguments.ArgumentType; -import net.minestom.server.item.ItemStack; +import net.minestom.server.utils.location.RelativeVec; public class TestCommand extends Command { @@ -18,7 +18,7 @@ public class TestCommand extends Command { //addSyntax(this::execute, dynamicWord); } - Argument test = ArgumentType.ItemStack("item"); + Argument test = ArgumentType.RelativeVec2("pos"); test.setCallback((source, value, error) -> { System.out.println("ERROR " + error); @@ -30,8 +30,8 @@ public class TestCommand extends Command { }); addSyntax((source, args) -> { - ItemStack itemStack = args.getItemStack("item"); - System.out.println("HEY IT WORKS "+itemStack.getMaterial()); + RelativeVec location = args.getRelativeVector("pos"); + System.out.println("IT WORKS " + location.fromRelativePosition(source.asPlayer())); }, test); } From 78bf5dda05acf052f179c9889856c7667e9ac2de Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 10 Nov 2020 23:31:35 +0100 Subject: [PATCH 51/55] Comments for the relative location arguments --- .../arguments/relative/ArgumentRelative.java | 5 +++ .../ArgumentRelativeBlockPosition.java | 5 +++ .../relative/ArgumentRelativeVec2.java | 5 +++ .../relative/ArgumentRelativeVec3.java | 5 +++ .../utils/location/RelativeBlockPosition.java | 5 +++ .../utils/location/RelativeLocation.java | 37 +++++++++++++++++++ .../server/utils/location/RelativeVec.java | 5 +++ 7 files changed, 67 insertions(+) diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java index 4529f3221..bb0d60df3 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java @@ -17,6 +17,11 @@ public abstract class ArgumentRelative extends Argument { this.numberCount = numberCount; } + /** + * Gets the amount of numbers that this relative location needs. + * + * @return the amount of coordinate required + */ public int getNumberCount() { return numberCount; } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java index 7afafe65a..56fb9d2b1 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java @@ -4,6 +4,11 @@ import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.location.RelativeBlockPosition; import org.jetbrains.annotations.NotNull; +/** + * Represents a {@link BlockPosition} with 3 integer numbers (x;y;z) which can take relative coordinates. + *

+ * Example: 5 ~ -3 + */ public class ArgumentRelativeBlockPosition extends ArgumentRelative { public ArgumentRelativeBlockPosition(@NotNull String id) { diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java index 5d54fa909..9333e2290 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java @@ -4,6 +4,11 @@ import net.minestom.server.utils.Vector; import net.minestom.server.utils.location.RelativeVec; import org.jetbrains.annotations.NotNull; +/** + * Represents a {@link Vector} with 2 floating numbers (x;z) which can take relative coordinates. + *

+ * Example: -1.2 ~ + */ public class ArgumentRelativeVec2 extends ArgumentRelative { public ArgumentRelativeVec2(@NotNull String id) { diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java index bc14279d1..f00fb7305 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java @@ -4,6 +4,11 @@ import net.minestom.server.utils.Vector; import net.minestom.server.utils.location.RelativeVec; import org.jetbrains.annotations.NotNull; +/** + * Represents a {@link Vector} with 3 floating numbers (x;y;z) which can take relative coordinates. + *

+ * Example: -1.2 ~ 5 + */ public class ArgumentRelativeVec3 extends ArgumentRelative { public ArgumentRelativeVec3(@NotNull String id) { diff --git a/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java index d0440fe4c..9fb2a6ac8 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java @@ -4,6 +4,11 @@ import net.minestom.server.entity.Entity; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; +/** + * Represents a relative {@link BlockPosition}. + * + * @see RelativeLocation + */ public class RelativeBlockPosition extends RelativeLocation { public RelativeBlockPosition(BlockPosition location, boolean relativeX, boolean relativeY, boolean relativeZ) { diff --git a/src/main/java/net/minestom/server/utils/location/RelativeLocation.java b/src/main/java/net/minestom/server/utils/location/RelativeLocation.java index 0d53460b7..af5959f13 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeLocation.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeLocation.java @@ -4,6 +4,11 @@ import net.minestom.server.entity.Entity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * Represents a location which can have fields relative to an {@link Entity} position. + * + * @param the location type + */ public abstract class RelativeLocation { protected T location; @@ -16,6 +21,38 @@ public abstract class RelativeLocation { this.relativeZ = relativeZ; } + /** + * Gets the location based on the relative fields and {@code entity}. + * + * @param entity the entity to get the relative position from + * @return the location + */ public abstract T fromRelativePosition(@Nullable Entity entity); + /** + * Gets if the 'x' field is relative. + * + * @return true if the 'x' field is relative + */ + public boolean isRelativeX() { + return relativeX; + } + + /** + * Gets if the 'y' field is relative. + * + * @return true if the 'y' field is relative + */ + public boolean isRelativeY() { + return relativeY; + } + + /** + * Gets if the 'z' field is relative. + * + * @return true if the 'z' field is relative + */ + public boolean isRelativeZ() { + return relativeZ; + } } diff --git a/src/main/java/net/minestom/server/utils/location/RelativeVec.java b/src/main/java/net/minestom/server/utils/location/RelativeVec.java index b5247276a..9ac0f24aa 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeVec.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeVec.java @@ -5,6 +5,11 @@ import net.minestom.server.utils.Position; import net.minestom.server.utils.Vector; import org.jetbrains.annotations.Nullable; +/** + * Represents a relative {@link Vector}. + * + * @see RelativeLocation + */ public class RelativeVec extends RelativeLocation { public RelativeVec(Vector location, boolean relativeX, boolean relativeY, boolean relativeZ) { From 99d27b78c42886ff09062d317e0506245828c9b2 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 11 Nov 2020 00:29:06 +0100 Subject: [PATCH 52/55] Fixed NbtDataImpl not working in creative mode --- src/main/java/net/minestom/server/data/NbtDataImpl.java | 3 ++- src/main/java/net/minestom/server/utils/NBTUtils.java | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/data/NbtDataImpl.java b/src/main/java/net/minestom/server/data/NbtDataImpl.java index fb479fda8..5e84b5eba 100644 --- a/src/main/java/net/minestom/server/data/NbtDataImpl.java +++ b/src/main/java/net/minestom/server/data/NbtDataImpl.java @@ -45,7 +45,8 @@ public class NbtDataImpl extends DataImpl { Check.notNull(nbt, "The type '" + type + "' is not supported within NbtDataImpl, if you wish to use a custom type you can encode the value into a byte array using a DataType"); - nbtCompound.set(KEY_PREFIX + key, nbt); + final String finalKey = KEY_PREFIX + key; + nbtCompound.set(finalKey, nbt); } } diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index 58facffe0..bdd736808 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -188,21 +188,21 @@ public final class NBTUtils { // Meta specific field final ItemMeta itemMeta = item.getItemMeta(); - if (itemMeta == null) - return; - itemMeta.read(nbt); + if (itemMeta != null) { + itemMeta.read(nbt); + } NbtDataImpl customData = null; for (String key : nbt.getKeys()) { if (key.startsWith(NbtDataImpl.KEY_PREFIX)) { if (customData == null) { customData = new NbtDataImpl(); + item.setData(customData); } final NBT keyNbt = nbt.get(key); final String dataKey = key.replaceFirst(NbtDataImpl.KEY_PREFIX, ""); final Object dataValue = fromNBT(keyNbt); - customData.set(dataKey, dataValue); } } From 6cbe656b150522f250969926100341a12c06e094 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 11 Nov 2020 02:11:28 +0100 Subject: [PATCH 53/55] Added "semi-relative" location argument support --- .../ArgumentRelativeBlockPosition.java | 28 +++++++++++++++++-- .../relative/ArgumentRelativeVec2.java | 25 +++++++++++++++-- .../relative/ArgumentRelativeVec3.java | 28 +++++++++++++++++-- .../utils/location/RelativeBlockPosition.java | 6 ++-- .../server/utils/location/RelativeVec.java | 6 ++-- src/test/java/demo/PlayerInit.java | 4 +++ 6 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java index 56fb9d2b1..3e5e6c564 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java @@ -26,13 +26,23 @@ public class ArgumentRelativeBlockPosition extends ArgumentRelative RELATIVE_CHAR.length()) { + try { + final String potentialNumber = element.substring(1); + // Will throw the exception if not an integer + Integer.parseInt(potentialNumber); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + return INVALID_NUMBER_ERROR; + } + } } } @@ -51,7 +61,8 @@ public class ArgumentRelativeBlockPosition extends ArgumentRelative { // Check if each element is correct for (String element : split) { - if (!element.equals(RELATIVE_CHAR)) { + if (!element.startsWith(RELATIVE_CHAR)) { try { // Will throw the exception if not a float Float.parseFloat(element); } catch (NumberFormatException e) { return INVALID_NUMBER_ERROR; } + } else { + if (element.length() > RELATIVE_CHAR.length()) { + try { + final String potentialNumber = element.substring(1); + // Will throw the exception if not a float + Float.parseFloat(potentialNumber); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + return INVALID_NUMBER_ERROR; + } + } } } @@ -50,12 +60,23 @@ public class ArgumentRelativeVec2 extends ArgumentRelative { for (int i = 0; i < split.length; i++) { final String element = split[i]; - if (element.equals(RELATIVE_CHAR)) { + if (element.startsWith(RELATIVE_CHAR)) { if (i == 0) { relativeX = true; } else if (i == 1) { relativeZ = true; } + + if (element.length() != RELATIVE_CHAR.length()) { + final String potentialNumber = element.substring(1); + final float number = Float.parseFloat(potentialNumber); + if (i == 0) { + vector.setX(number); + } else if (i == 1) { + vector.setZ(number); + } + } + } else { final float number = Float.parseFloat(element); if (i == 0) { diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java index f00fb7305..6e37fb4ce 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java @@ -26,13 +26,23 @@ public class ArgumentRelativeVec3 extends ArgumentRelative { // Check if each element is correct for (String element : split) { - if (!element.equals(RELATIVE_CHAR)) { + if (!element.startsWith(RELATIVE_CHAR)) { try { // Will throw the exception if not a float Float.parseFloat(element); } catch (NumberFormatException e) { return INVALID_NUMBER_ERROR; } + } else { + if (element.length() > RELATIVE_CHAR.length()) { + try { + final String potentialNumber = element.substring(1); + // Will throw the exception if not a float + Float.parseFloat(potentialNumber); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + return INVALID_NUMBER_ERROR; + } + } } } @@ -51,7 +61,8 @@ public class ArgumentRelativeVec3 extends ArgumentRelative { for (int i = 0; i < split.length; i++) { final String element = split[i]; - if (element.equals(RELATIVE_CHAR)) { + if (element.startsWith(RELATIVE_CHAR)) { + if (i == 0) { relativeX = true; } else if (i == 1) { @@ -59,6 +70,19 @@ public class ArgumentRelativeVec3 extends ArgumentRelative { } else if (i == 2) { relativeZ = true; } + + if (element.length() != RELATIVE_CHAR.length()) { + final String potentialNumber = element.substring(1); + final float number = Float.parseFloat(potentialNumber); + if (i == 0) { + vector.setX(number); + } else if (i == 1) { + vector.setY(number); + } else if (i == 2) { + vector.setZ(number); + } + } + } else { final float number = Float.parseFloat(element); if (i == 0) { diff --git a/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java index 9fb2a6ac8..585246b8e 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeBlockPosition.java @@ -22,9 +22,9 @@ public class RelativeBlockPosition extends RelativeLocation { } final Position entityPosition = entity.getPosition(); - final int x = relativeX ? (int) entityPosition.getX() : location.getX(); - final int y = relativeY ? (int) entityPosition.getY() : location.getY(); - final int z = relativeZ ? (int) entityPosition.getZ() : location.getZ(); + final int x = location.getX() + (relativeX ? (int) entityPosition.getX() : 0); + final int y = location.getY() + (relativeY ? (int) entityPosition.getY() : 0); + final int z = location.getZ() + (relativeZ ? (int) entityPosition.getZ() : 0); return new BlockPosition(x, y, z); } diff --git a/src/main/java/net/minestom/server/utils/location/RelativeVec.java b/src/main/java/net/minestom/server/utils/location/RelativeVec.java index 9ac0f24aa..67992a9f3 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeVec.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeVec.java @@ -23,9 +23,9 @@ public class RelativeVec extends RelativeLocation { } final Position entityPosition = entity.getPosition(); - final float x = relativeX ? (int) entityPosition.getX() : location.getX(); - final float y = relativeY ? (int) entityPosition.getY() : location.getY(); - final float z = relativeZ ? (int) entityPosition.getZ() : location.getZ(); + final float x = location.getX() + (relativeX ? entityPosition.getX() : 0); + final float y = location.getY() + (relativeY ? entityPosition.getY() : 0); + final float z = location.getZ() + (relativeZ ? entityPosition.getZ() : 0); return new Vector(x, y, z); } diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 4705aecbd..dce14fc71 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -5,6 +5,7 @@ import demo.generator.NoiseTestGenerator; import net.minestom.server.MinecraftServer; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.chat.ColoredText; +import net.minestom.server.data.NbtDataImpl; import net.minestom.server.entity.*; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.entity.type.monster.EntityZombie; @@ -172,6 +173,9 @@ public class PlayerInit { } ItemStack itemStack = new ItemStack(Material.DIAMOND_PICKAXE, (byte) 64); + NbtDataImpl data = new NbtDataImpl(); + data.set("testc",2); + itemStack.setData(data); player.getInventory().addItemStack(itemStack); //player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte)64)); From d520a0ebc4a31e6d8dfffba4d6bc403795dc2d6d Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 11 Nov 2020 03:31:15 +0100 Subject: [PATCH 54/55] Created ArgumentRelativeVec to prevent code duplication --- .../relative/ArgumentRelativeVec.java | 54 +++++++++++++++++++ .../relative/ArgumentRelativeVec2.java | 40 +------------- .../relative/ArgumentRelativeVec3.java | 41 +------------- 3 files changed, 56 insertions(+), 79 deletions(-) create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java new file mode 100644 index 000000000..59ac25447 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java @@ -0,0 +1,54 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.utils.location.RelativeVec; +import org.jetbrains.annotations.NotNull; + +/** + * Common super class for {@link ArgumentRelativeVec2} and {@link ArgumentRelativeVec3}. + */ +public abstract class ArgumentRelativeVec extends ArgumentRelative { + + public ArgumentRelativeVec(@NotNull String id, int numberCount) { + super(id, numberCount); + } + + @Override + public int getCorrectionResult(@NotNull String value) { + final String[] split = value.split(" "); + + // Check if the value has enough element to be correct + if (split.length != getNumberCount()) { + return INVALID_NUMBER_COUNT_ERROR; + } + + // Check if each element is correct + for (String element : split) { + if (!element.startsWith(RELATIVE_CHAR)) { + try { + // Will throw the exception if not a float + Float.parseFloat(element); + } catch (NumberFormatException e) { + return INVALID_NUMBER_ERROR; + } + } else { + if (element.length() > RELATIVE_CHAR.length()) { + try { + final String potentialNumber = element.substring(1); + // Will throw the exception if not a float + Float.parseFloat(potentialNumber); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + return INVALID_NUMBER_ERROR; + } + } + } + } + + return SUCCESS; + } + + @Override + public int getConditionResult(@NotNull RelativeVec value) { + return SUCCESS; + } + +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java index 5a5d22cb6..54c2b2a16 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java @@ -9,46 +9,12 @@ import org.jetbrains.annotations.NotNull; *

* Example: -1.2 ~ */ -public class ArgumentRelativeVec2 extends ArgumentRelative { +public class ArgumentRelativeVec2 extends ArgumentRelativeVec { public ArgumentRelativeVec2(@NotNull String id) { super(id, 2); } - @Override - public int getCorrectionResult(@NotNull String value) { - final String[] split = value.split(" "); - - // Check if the value has enough element to be correct - if (split.length != getNumberCount()) { - return INVALID_NUMBER_COUNT_ERROR; - } - - // Check if each element is correct - for (String element : split) { - if (!element.startsWith(RELATIVE_CHAR)) { - try { - // Will throw the exception if not a float - Float.parseFloat(element); - } catch (NumberFormatException e) { - return INVALID_NUMBER_ERROR; - } - } else { - if (element.length() > RELATIVE_CHAR.length()) { - try { - final String potentialNumber = element.substring(1); - // Will throw the exception if not a float - Float.parseFloat(potentialNumber); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - return INVALID_NUMBER_ERROR; - } - } - } - } - - return SUCCESS; - } - @NotNull @Override public RelativeVec parse(@NotNull String value) { @@ -90,8 +56,4 @@ public class ArgumentRelativeVec2 extends ArgumentRelative { return new RelativeVec(vector, relativeX, false, relativeZ); } - @Override - public int getConditionResult(@NotNull RelativeVec value) { - return SUCCESS; - } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java index 6e37fb4ce..f11985d41 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java @@ -9,46 +9,12 @@ import org.jetbrains.annotations.NotNull; *

* Example: -1.2 ~ 5 */ -public class ArgumentRelativeVec3 extends ArgumentRelative { +public class ArgumentRelativeVec3 extends ArgumentRelativeVec { public ArgumentRelativeVec3(@NotNull String id) { super(id, 3); } - @Override - public int getCorrectionResult(@NotNull String value) { - final String[] split = value.split(" "); - - // Check if the value has enough element to be correct - if (split.length != getNumberCount()) { - return INVALID_NUMBER_COUNT_ERROR; - } - - // Check if each element is correct - for (String element : split) { - if (!element.startsWith(RELATIVE_CHAR)) { - try { - // Will throw the exception if not a float - Float.parseFloat(element); - } catch (NumberFormatException e) { - return INVALID_NUMBER_ERROR; - } - } else { - if (element.length() > RELATIVE_CHAR.length()) { - try { - final String potentialNumber = element.substring(1); - // Will throw the exception if not a float - Float.parseFloat(potentialNumber); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - return INVALID_NUMBER_ERROR; - } - } - } - } - - return SUCCESS; - } - @NotNull @Override public RelativeVec parse(@NotNull String value) { @@ -97,9 +63,4 @@ public class ArgumentRelativeVec3 extends ArgumentRelative { return new RelativeVec(vector, relativeX, relativeY, relativeZ); } - - @Override - public int getConditionResult(@NotNull RelativeVec value) { - return SUCCESS; - } } From dde343001023fb5489c4930abbcfcd98b80db5f6 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 11 Nov 2020 04:24:09 +0100 Subject: [PATCH 55/55] ArgumentRelative comment --- .../command/builder/arguments/relative/ArgumentRelative.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java index bb0d60df3..2ab9471e0 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java @@ -3,6 +3,11 @@ package net.minestom.server.command.builder.arguments.relative; import net.minestom.server.command.builder.arguments.Argument; import org.jetbrains.annotations.NotNull; +/** + * Common interface for all the relative location arguments. + * + * @param the relative location type + */ public abstract class ArgumentRelative extends Argument { public static final String RELATIVE_CHAR = "~";