diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index c30df5db7..03b84a319 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -99,22 +99,17 @@ public final class CommandManager { * @return the execution result */ public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String command) { - // Command event if (sender instanceof Player) { - Player player = (Player) sender; - + final Player player = (Player) sender; PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command); EventDispatcher.call(playerCommandEvent); - if (playerCommandEvent.isCancelled()) return CommandResult.of(CommandResult.Type.CANCELLED, command); - command = playerCommandEvent.getCommand(); } - // Process the command - final var result = dispatcher.execute(sender, command); + final CommandResult result = dispatcher.execute(sender, command); if (result.getType() == CommandResult.Type.UNKNOWN) { if (unknownCommandCallback != null) { this.unknownCommandCallback.apply(sender, command); 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 efbd1595c..1d35d7f66 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java +++ b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java @@ -65,8 +65,7 @@ public class CommandDispatcher { this.cache.invalidateAll(); } - @NotNull - public Set getCommands() { + public @NotNull Set getCommands() { return Collections.unmodifiableSet(commands); } @@ -76,8 +75,7 @@ public class CommandDispatcher { * @param commandName the command name * @return the {@link Command} associated with the name, null if not any */ - @Nullable - public Command findCommand(@NotNull String commandName) { + public @Nullable Command findCommand(@NotNull String commandName) { commandName = commandName.toLowerCase(); return commandMap.getOrDefault(commandName, null); } @@ -89,8 +87,7 @@ public class CommandDispatcher { * @param commandString the command with the argument(s) * @return the command result */ - @NotNull - public CommandResult execute(@NotNull CommandSender source, @NotNull String commandString) { + public @NotNull CommandResult execute(@NotNull CommandSender source, @NotNull String commandString) { CommandResult commandResult = parse(commandString); ParsedCommand parsedCommand = commandResult.parsedCommand; if (parsedCommand != null) { @@ -105,10 +102,8 @@ public class CommandDispatcher { * @param commandString the command (containing the command name and the args if any) * @return the parsing result */ - @NotNull - public CommandResult parse(@NotNull String commandString) { + public @NotNull CommandResult parse(@NotNull String commandString) { commandString = commandString.trim(); - // Verify if the result is cached { final CommandResult cachedResult = cache.getIfPresent(commandString); @@ -134,18 +129,15 @@ public class CommandDispatcher { findParsedCommand(command, commandName, commandQueryResult.args, commandString, result); // Cache result - { - this.cache.put(commandString, result); - } + this.cache.put(commandString, result); return result; } - @Nullable - private ParsedCommand findParsedCommand(@NotNull Command command, - @NotNull String commandName, @NotNull String[] args, - @NotNull String commandString, - @NotNull CommandResult result) { + private @Nullable ParsedCommand findParsedCommand(@NotNull Command command, + @NotNull String commandName, @NotNull String[] args, + @NotNull String commandString, + @NotNull CommandResult result) { final boolean hasArgument = args.length > 0; // Search for subcommand @@ -162,41 +154,37 @@ public class CommandDispatcher { final String input = commandName + StringUtils.SPACE + String.join(StringUtils.SPACE, args); - ParsedCommand parsedCommand = new ParsedCommand(); parsedCommand.command = command; parsedCommand.commandString = commandString; // The default executor should be used if no argument is provided - { - if (!hasArgument) { - Optional optionalSyntax = command.getSyntaxes() - .stream() - .filter(syntax -> syntax.getArguments().length == 0) - .findFirst(); + if (!hasArgument) { + Optional optionalSyntax = command.getSyntaxes() + .stream() + .filter(syntax -> syntax.getArguments().length == 0) + .findFirst(); - if (optionalSyntax.isPresent()) { - // Empty syntax found - final CommandSyntax syntax = optionalSyntax.get(); + if (optionalSyntax.isPresent()) { + // Empty syntax found + final CommandSyntax syntax = optionalSyntax.get(); + parsedCommand.syntax = syntax; + parsedCommand.executor = syntax.getExecutor(); + parsedCommand.context = new CommandContext(input); - parsedCommand.syntax = syntax; - parsedCommand.executor = syntax.getExecutor(); + result.type = CommandResult.Type.SUCCESS; + result.parsedCommand = parsedCommand; + return parsedCommand; + } else { + // No empty syntax, use default executor if any + final CommandExecutor defaultExecutor = command.getDefaultExecutor(); + if (defaultExecutor != null) { + parsedCommand.executor = defaultExecutor; parsedCommand.context = new CommandContext(input); result.type = CommandResult.Type.SUCCESS; result.parsedCommand = parsedCommand; return parsedCommand; - } else { - // No empty syntax, use default executor if any - final CommandExecutor defaultExecutor = command.getDefaultExecutor(); - if (defaultExecutor != null) { - parsedCommand.executor = defaultExecutor; - parsedCommand.context = new CommandContext(input); - - result.type = CommandResult.Type.SUCCESS; - result.parsedCommand = parsedCommand; - return parsedCommand; - } } } } @@ -207,7 +195,6 @@ public class CommandDispatcher { final Collection syntaxes = command.getSyntaxes(); // Contains all the fully validated syntaxes (we later find the one with the most amount of arguments) List validSyntaxes = new ArrayList<>(syntaxes.size()); - // Contains all the syntaxes that are not fully correct, used to later, retrieve the "most correct syntax" // Number of correct argument - The data about the failing argument Int2ObjectRBTreeMap syntaxesSuggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder()); @@ -233,29 +220,25 @@ public class CommandDispatcher { result.parsedCommand = parsedCommand; return parsedCommand; } - } // No all-correct syntax, find the closest one to use the argument callback - { - // Get closest valid syntax - if (!syntaxesSuggestions.isEmpty()) { - final int max = syntaxesSuggestions.firstIntKey(); // number of correct arguments in the most correct syntax - final CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max); - final CommandSyntax syntax = suggestionHolder.syntax; - final ArgumentSyntaxException argumentSyntaxException = suggestionHolder.argumentSyntaxException; - final int argIndex = suggestionHolder.argIndex; + if (!syntaxesSuggestions.isEmpty()) { + final int max = syntaxesSuggestions.firstIntKey(); // number of correct arguments in the most correct syntax + final CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max); + final CommandSyntax syntax = suggestionHolder.syntax; + final ArgumentSyntaxException argumentSyntaxException = suggestionHolder.argumentSyntaxException; + final int argIndex = suggestionHolder.argIndex; - // Found the closest syntax with at least 1 correct argument - final Argument argument = syntax.getArguments()[argIndex]; - if (argument.hasErrorCallback()) { - parsedCommand.callback = argument.getCallback(); - parsedCommand.argumentSyntaxException = argumentSyntaxException; + // Found the closest syntax with at least 1 correct argument + final Argument argument = syntax.getArguments()[argIndex]; + if (argument.hasErrorCallback() && argumentSyntaxException != null) { + parsedCommand.callback = argument.getCallback(); + parsedCommand.argumentSyntaxException = argumentSyntaxException; - result.type = CommandResult.Type.INVALID_SYNTAX; - result.parsedCommand = parsedCommand; - return parsedCommand; - } + result.type = CommandResult.Type.INVALID_SYNTAX; + result.parsedCommand = parsedCommand; + return parsedCommand; } } diff --git a/src/main/java/net/minestom/server/coordinate/Pos.java b/src/main/java/net/minestom/server/coordinate/Pos.java index bba299e2f..c1083eff5 100644 --- a/src/main/java/net/minestom/server/coordinate/Pos.java +++ b/src/main/java/net/minestom/server/coordinate/Pos.java @@ -23,7 +23,7 @@ public final class Pos implements Point { this.x = x; this.y = y; this.z = z; - this.yaw = yaw; + this.yaw = fixYaw(yaw); this.pitch = pitch; } @@ -108,7 +108,7 @@ public final class Pos implements Point { @Contract(pure = true) public @NotNull Pos withYaw(@NotNull DoubleUnaryOperator operator) { - return new Pos(x, y, z, (float) operator.applyAsDouble(yaw), pitch); + return withYaw((float) operator.applyAsDouble(yaw)); } @Contract(pure = true) @@ -118,7 +118,7 @@ public final class Pos implements Point { @Contract(pure = true) public @NotNull Pos withPitch(@NotNull DoubleUnaryOperator operator) { - return new Pos(x, y, z, yaw, (float) operator.applyAsDouble(pitch)); + return withPitch((float) operator.applyAsDouble(pitch)); } /** @@ -325,4 +325,21 @@ public final class Pos implements Point { public interface Operator { @NotNull Pos apply(double x, double y, double z, float yaw, float pitch); } + + /** + * Fixes a yaw value that is not between -180.0F and 180.0F + * So for example -1355.0F becomes 85.0F and 225.0F becomes -135.0F + * + * @param yaw The possible "wrong" yaw + * @return a fixed yaw + */ + private static float fixYaw(float yaw) { + yaw = yaw % 360; + if (yaw < -180.0F) { + yaw += 360.0F; + } else if (yaw > 180.0F) { + yaw -= 360.0F; + } + return yaw; + } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 0517c1504..21e533eb3 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -318,7 +318,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, if (expandedBoundingBox.intersect(itemBoundingBox)) { if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled()) continue; - PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(experienceOrb); + PickupExperienceEvent pickupExperienceEvent = new PickupExperienceEvent(this, experienceOrb); EventDispatcher.callCancellable(pickupExperienceEvent, () -> { short experienceCount = pickupExperienceEvent.getExperienceCount(); // TODO give to player entity.remove(); diff --git a/src/main/java/net/minestom/server/entity/ai/goal/RandomStrollGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/RandomStrollGoal.java index b934ec882..f05a749c3 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/RandomStrollGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/RandomStrollGoal.java @@ -6,8 +6,8 @@ import net.minestom.server.entity.ai.GoalSelector; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Random; public class RandomStrollGoal extends GoalSelector { @@ -15,6 +15,7 @@ public class RandomStrollGoal extends GoalSelector { private final int radius; private final List closePositions; + private final Random random = new Random(); private long lastStroll; @@ -31,8 +32,11 @@ public class RandomStrollGoal extends GoalSelector { @Override public void start() { - Collections.shuffle(closePositions); - for (var position : closePositions) { + int remainingAttempt = closePositions.size(); + while (remainingAttempt-- > 0) { + final int index = random.nextInt(closePositions.size()); + final Vec position = closePositions.get(index); + final var target = entityCreature.getPosition().add(position); final boolean result = entityCreature.getNavigator().setPathTo(target); if (result) { diff --git a/src/main/java/net/minestom/server/event/item/PickupExperienceEvent.java b/src/main/java/net/minestom/server/event/item/PickupExperienceEvent.java index 832ee6f52..f6f391729 100644 --- a/src/main/java/net/minestom/server/event/item/PickupExperienceEvent.java +++ b/src/main/java/net/minestom/server/event/item/PickupExperienceEvent.java @@ -1,21 +1,30 @@ package net.minestom.server.event.item; import net.minestom.server.entity.ExperienceOrb; +import net.minestom.server.entity.Player; import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerEvent; import org.jetbrains.annotations.NotNull; -public class PickupExperienceEvent implements CancellableEvent { +public class PickupExperienceEvent implements CancellableEvent, PlayerEvent { + private final Player player; private final ExperienceOrb experienceOrb; private short experienceCount; private boolean cancelled; - public PickupExperienceEvent(@NotNull ExperienceOrb experienceOrb) { + public PickupExperienceEvent(@NotNull Player player, @NotNull ExperienceOrb experienceOrb) { + this.player = player; this.experienceOrb = experienceOrb; this.experienceCount = experienceOrb.getExperienceCount(); } + @Override + public @NotNull Player getPlayer() { + return player; + } + @NotNull public ExperienceOrb getExperienceOrb() { return experienceOrb; diff --git a/src/main/java/net/minestom/server/instance/AnvilLoader.java b/src/main/java/net/minestom/server/instance/AnvilLoader.java index 6a813afdb..2282cf57e 100644 --- a/src/main/java/net/minestom/server/instance/AnvilLoader.java +++ b/src/main/java/net/minestom/server/instance/AnvilLoader.java @@ -233,6 +233,7 @@ public class AnvilLoader implements IChunkLoader { try { LOGGER.debug("Attempt saving at {} {}", chunk.getChunkX(), chunk.getChunkZ()); mcaFile.writeColumn(column); + mcaFile.forget(column); } catch (IOException e) { LOGGER.error("Failed to save chunk " + chunkX + ", " + chunkZ, e); EXCEPTION_MANAGER.handleException(e); diff --git a/src/main/java/net/minestom/server/item/ItemHideFlag.java b/src/main/java/net/minestom/server/item/ItemHideFlag.java index 5d05cbb28..a90cf5972 100644 --- a/src/main/java/net/minestom/server/item/ItemHideFlag.java +++ b/src/main/java/net/minestom/server/item/ItemHideFlag.java @@ -4,18 +4,15 @@ package net.minestom.server.item; * Represents a hide flag which can be applied to an {@link ItemStack} using {@link ItemMetaBuilder#hideFlag(int)}. */ public enum ItemHideFlag { - HIDE_ENCHANTS(1), - HIDE_ATTRIBUTES(2), - HIDE_UNBREAKABLE(4), - HIDE_DESTROYS(8), - HIDE_PLACED_ON(16), - HIDE_POTION_EFFECTS(32); + HIDE_ENCHANTS, + HIDE_ATTRIBUTES, + HIDE_UNBREAKABLE, + HIDE_DESTROYS, + HIDE_PLACED_ON, + HIDE_POTION_EFFECTS, + HIDE_DYE; - private final int bitFieldPart; - - ItemHideFlag(int bit) { - this.bitFieldPart = bit; - } + private final int bitFieldPart = 1 << this.ordinal(); public int getBitFieldPart() { return bitFieldPart; diff --git a/src/main/java/net/minestom/server/monitoring/BenchmarkManager.java b/src/main/java/net/minestom/server/monitoring/BenchmarkManager.java index f7feb1eba..83a6360d3 100644 --- a/src/main/java/net/minestom/server/monitoring/BenchmarkManager.java +++ b/src/main/java/net/minestom/server/monitoring/BenchmarkManager.java @@ -31,8 +31,7 @@ import static net.minestom.server.MinecraftServer.*; * 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 { - - public static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); + private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); private static final List THREADS = new ArrayList<>(); static { @@ -46,12 +45,10 @@ public final class BenchmarkManager { private final Long2LongMap lastUserTimeMap = new Long2LongOpenHashMap(); private final Long2LongMap lastWaitedMap = new Long2LongOpenHashMap(); private final Long2LongMap lastBlockedMap = new Long2LongOpenHashMap(); - private final Map resultMap = new ConcurrentHashMap<>(); private boolean enabled = false; private volatile boolean stop = false; - private long time; public void enable(@NotNull Duration duration) { @@ -96,13 +93,11 @@ public final class BenchmarkManager { return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); } - @NotNull - public Map getResultMap() { + public @NotNull Map getResultMap() { return Collections.unmodifiableMap(resultMap); } - @NotNull - public Component getCpuMonitoringMessage() { + public @NotNull Component getCpuMonitoringMessage() { Check.stateCondition(!enabled, "CPU monitoring is only possible when the benchmark manager is enabled."); TextComponent.Builder benchmarkMessage = Component.text(); for (var resultEntry : resultMap.entrySet()) { @@ -121,23 +116,16 @@ public final class BenchmarkManager { benchmarkMessage.append(Component.text("% WAITED ", NamedTextColor.GREEN)); benchmarkMessage.append(Component.newline()); } - return benchmarkMessage.build(); } private void refreshData() { ThreadInfo[] threadInfo = THREAD_MX_BEAN.getThreadInfo(THREAD_MX_BEAN.getAllThreadIds()); for (ThreadInfo threadInfo2 : threadInfo) { + if (threadInfo2 == null) continue; // Can happen if the thread does not exist final String name = threadInfo2.getThreadName(); - boolean shouldBenchmark = false; - for (String thread : THREADS) { - if (name.startsWith(thread)) { - shouldBenchmark = true; - break; - } - } - if (!shouldBenchmark) - continue; + final boolean shouldBenchmark = THREADS.stream().anyMatch(name::startsWith); + if (!shouldBenchmark) continue; final long id = threadInfo2.getThreadId(); diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 24aaf4360..f9a49c2aa 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -349,11 +349,14 @@ public final class ConnectionManager { if (playerConnection instanceof PlayerSocketConnection) { final PlayerSocketConnection socketConnection = (PlayerSocketConnection) playerConnection; socketConnection.writeAndFlush(disconnectPacket); + playerConnection.disconnect(); try { socketConnection.getChannel().close(); } catch (IOException e) { e.printStackTrace(); } + } else { + player.remove(); } } this.players.clear(); diff --git a/src/main/java/net/minestom/server/network/socket/Server.java b/src/main/java/net/minestom/server/network/socket/Server.java index cbbd63359..8604c5d3d 100644 --- a/src/main/java/net/minestom/server/network/socket/Server.java +++ b/src/main/java/net/minestom/server/network/socket/Server.java @@ -17,7 +17,7 @@ public final class Server { public static final Logger LOGGER = LoggerFactory.getLogger(Server.class); public static final int WORKER_COUNT = Integer.getInteger("minestom.workers", Runtime.getRuntime().availableProcessors()); - public static final int SOCKET_BUFFER_SIZE = Integer.getInteger("minestom.buffer-size", 262_143); + public static final int SOCKET_BUFFER_SIZE = Integer.getInteger("minestom.buffer-size", 1_048_575); public static final int MAX_PACKET_SIZE = 2_097_151; // 3 bytes var-int public static final boolean NO_DELAY = true; 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 25495a138..ad3fb0ae7 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java @@ -235,6 +235,7 @@ public class BinaryWriter extends OutputStream { * @param bytes the byte array to write */ public void writeBytes(byte @NotNull [] bytes) { + if (bytes.length == 0) return; ensureSize(bytes.length); buffer.put(bytes); }