diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b9715dadb..054b0ce92 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ #### **Did you find a bug?** * Open a new GitHub issue if it's not already reported. -* Use the relevant bug report template to create the issue. +* Explain it clearly, with steps (or code) to reproduce it. #### **Did you write some code that fixes a bug?** * Open a new GitHub pull-request with the commits if it hasn't already been proposed. diff --git a/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md deleted file mode 100644 index 9c4f21d6c..000000000 --- a/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Bug report -labels: Bug -about: Use this to report unexpected behavior (bugs and errors). ---- -### What is the current behavior? - - -### What is the expected behavior? - - -### What steps will reproduce the problem? - - -### What operating system and java version is being used? - - -### If there is an exception, use pastebin/hastebin (NB: no expiry date!) to send the stacktrace - - -### Additional information / Possible thoughts on why this bug is happening - diff --git a/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.md deleted file mode 100644 index 1a3588f66..000000000 --- a/.github/ISSUE_TEMPLATE/FEATURE_TEMPLATE.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature Request -labels: Enhancement -about: Use this to request an addition (feature). ---- -### What would you like added/changed? - - -### Why do you think this is a good addition/alteration? - - -### Why do you want this to be added? - - -### Additional Information diff --git a/.github/ISSUE_TEMPLATE/QUESTION_TEMPLATE.md b/.github/ISSUE_TEMPLATE/QUESTION_TEMPLATE.md deleted file mode 100644 index ef4781501..000000000 --- a/.github/ISSUE_TEMPLATE/QUESTION_TEMPLATE.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Question -labels: Question -about: Use this to ask a question. ---- -### Your Question: - diff --git a/build.gradle b/build.gradle index a8588881c..9c3200bcb 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,18 @@ allprojects { options { destinationDir(file("docs")) addBooleanOption('html5', true) + links "https://jd.adventure.kyori.net/api/$adventureVersion/" + links "https://docs.oracle.com/en/java/javase/11/docs/api/" + } + + // see https://stackoverflow.com/a/56641766 + doLast { + // Append the fix to the file + def searchScript = new File(destinationDir.getAbsolutePath() + '/search.js') + searchScript.append '\n\n' + + 'getURLPrefix = function(ui) {\n' + + ' return \'\';\n' + + '};\n' } } diff --git a/src/main/java/net/minestom/server/bossbar/BarColor.java b/src/main/java/net/minestom/server/bossbar/BarColor.java index 9173782ea..0467e4235 100644 --- a/src/main/java/net/minestom/server/bossbar/BarColor.java +++ b/src/main/java/net/minestom/server/bossbar/BarColor.java @@ -2,7 +2,7 @@ package net.minestom.server.bossbar; /** * Represents the displayed color of a {@link BossBar}. - * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar.Color} */ @Deprecated public enum BarColor { diff --git a/src/main/java/net/minestom/server/bossbar/BarDivision.java b/src/main/java/net/minestom/server/bossbar/BarDivision.java index 54f9fe79f..07bd95a1f 100644 --- a/src/main/java/net/minestom/server/bossbar/BarDivision.java +++ b/src/main/java/net/minestom/server/bossbar/BarDivision.java @@ -3,7 +3,7 @@ package net.minestom.server.bossbar; /** * Used to define the number of segments on a {@link BossBar}. * - * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar.Overlay} */ @Deprecated public enum BarDivision { diff --git a/src/main/java/net/minestom/server/bossbar/BossBar.java b/src/main/java/net/minestom/server/bossbar/BossBar.java index b2a043989..c64b2265a 100644 --- a/src/main/java/net/minestom/server/bossbar/BossBar.java +++ b/src/main/java/net/minestom/server/bossbar/BossBar.java @@ -20,7 +20,7 @@ import java.util.concurrent.CopyOnWriteArraySet; *

* You can retrieve all the boss bars of a {@link Player} with {@link #getBossBars(Player)}. * - * @deprecated Use {@link net.kyori.adventure.audience.Audience#showBossBar(net.kyori.adventure.bossbar.BossBar)} + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} */ @Deprecated public class BossBar implements Viewable { diff --git a/src/main/java/net/minestom/server/chat/ChatClickEvent.java b/src/main/java/net/minestom/server/chat/ChatClickEvent.java index 331ed1d3c..a1d300334 100644 --- a/src/main/java/net/minestom/server/chat/ChatClickEvent.java +++ b/src/main/java/net/minestom/server/chat/ChatClickEvent.java @@ -1,9 +1,11 @@ package net.minestom.server.chat; +import net.kyori.adventure.text.event.ClickEvent; import org.jetbrains.annotations.NotNull; /** * Represents a click event for a specific portion of the message. + * @deprecated Use {@link ClickEvent} */ @Deprecated public class ChatClickEvent { diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index 1d54d8bdd..9451b22c2 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -4,24 +4,31 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectMap; import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.ComponentBuilder; +import net.kyori.adventure.text.format.*; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minestom.server.color.Color; +import net.minestom.server.color.DyeColor; import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Represents a color in a text. You can either use one of the pre-made colors * or make your own using RGB. {@link ChatColor#fromRGB(byte, byte, byte)}. *

* Immutable class. + * @deprecated For chat colors, use {@link TextColor} or {@link NamedTextColor}. For styles, use {@link TextDecoration}. + * For colors in other contexts, see {@link Color} or {@link DyeColor}. */ @Deprecated -public final class ChatColor { +public final class ChatColor implements StyleBuilderApplicable { // Special public static final ChatColor NO_COLOR = new ChatColor(); @@ -310,4 +317,26 @@ public final class ChatColor { return header + code + footer; } + + @Override + @Contract(mutates = "param") + public void styleApply(Style.@NotNull Builder style) { + if (this.isEmpty()) { + style.color(NamedTextColor.WHITE); + } else if (Objects.equals(this.codeName, "reset")) { + style.color(NamedTextColor.WHITE); + + for (TextDecoration value : TextDecoration.NAMES.values()) { + style.decoration(value, TextDecoration.State.FALSE); + } + } else if (this.isSpecial() && this.codeName != null) { + TextDecoration decoration = TextDecoration.NAMES.value(this.codeName); + + if (decoration != null) { + style.decorate(decoration); + } + } else { + style.color(TextColor.color(this.red, this.green, this.blue)); + } + } } diff --git a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java index cfcd5e9ae..162447c38 100644 --- a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java +++ b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java @@ -16,6 +16,7 @@ import java.util.UUID; /** * Represents a hover event for a specific portion of the message. + * @deprecated Use {@link HoverEvent} */ @Deprecated public class ChatHoverEvent { diff --git a/src/main/java/net/minestom/server/chat/ChatParser.java b/src/main/java/net/minestom/server/chat/ChatParser.java index 7628ff9c0..79c5bb148 100644 --- a/src/main/java/net/minestom/server/chat/ChatParser.java +++ b/src/main/java/net/minestom/server/chat/ChatParser.java @@ -2,10 +2,12 @@ package net.minestom.server.chat; import com.google.gson.*; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.jetbrains.annotations.NotNull; /** * Class used to convert JSON string to proper chat message representation. + * @deprecated Use {@link GsonComponentSerializer} */ @Deprecated public final class ChatParser { diff --git a/src/main/java/net/minestom/server/chat/ColoredText.java b/src/main/java/net/minestom/server/chat/ColoredText.java index 7739a8655..95c52c073 100644 --- a/src/main/java/net/minestom/server/chat/ColoredText.java +++ b/src/main/java/net/minestom/server/chat/ColoredText.java @@ -2,6 +2,8 @@ package net.minestom.server.chat; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -16,6 +18,7 @@ import java.util.regex.Pattern; *

* To create one, you simply call one of the static methods like {@link #of(ChatColor, String)}, * you can then continue to append text with {@link #append(ChatColor, String)}. + * @deprecated Use {@link Component#text(String, TextColor)} */ @Deprecated public class ColoredText extends JsonMessage { diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index af8f79262..2c1626210 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -16,6 +16,7 @@ import java.util.Objects; * Examples are {@link ColoredText} and {@link RichMessage}. * * @see Chat Format + * @deprecated Use {@link Component} */ @Deprecated public abstract class JsonMessage implements ComponentLike { diff --git a/src/main/java/net/minestom/server/chat/RichMessage.java b/src/main/java/net/minestom/server/chat/RichMessage.java index 52a70d0a0..58a671cbe 100644 --- a/src/main/java/net/minestom/server/chat/RichMessage.java +++ b/src/main/java/net/minestom/server/chat/RichMessage.java @@ -2,6 +2,7 @@ package net.minestom.server.chat; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.kyori.adventure.text.Component; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,6 +19,7 @@ import java.util.List; * You will need to call the static method to initialize the message {@link #of(ColoredText)}, * events can be assigned with {@link #setClickEvent(ChatClickEvent)} and {@link #setHoverEvent(ChatHoverEvent)} * and new text element can also be appended {@link #append(ColoredText)}. + * @deprecated Use {@link Component} */ @Deprecated public class RichMessage extends JsonMessage { diff --git a/src/main/java/net/minestom/server/chat/TranslatableText.java b/src/main/java/net/minestom/server/chat/TranslatableText.java index 62a88ad2d..ac6b7b357 100644 --- a/src/main/java/net/minestom/server/chat/TranslatableText.java +++ b/src/main/java/net/minestom/server/chat/TranslatableText.java @@ -1,10 +1,12 @@ package net.minestom.server.chat; +import net.kyori.adventure.text.TranslatableComponent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Represents a translatable component which can be used in {@link ColoredText}. + * @deprecated Use {@link TranslatableComponent} */ @Deprecated public class TranslatableText { diff --git a/src/main/java/net/minestom/server/command/CommandSender.java b/src/main/java/net/minestom/server/command/CommandSender.java index bd0e44727..627621c8c 100644 --- a/src/main/java/net/minestom/server/command/CommandSender.java +++ b/src/main/java/net/minestom/server/command/CommandSender.java @@ -18,21 +18,17 @@ public interface CommandSender extends PermissionHandler, Audience { * Sends a raw string message. * * @param message the message to send - * - * @deprecated Use {@link #sendMessage(Component)} */ - @Deprecated - default void sendMessage(@NotNull String message) { this.sendMessage(Component.text(message)); } + default void sendMessage(@NotNull String message) { + this.sendMessage(Component.text(message)); + } /** * Sends multiple raw string messages. * * @param messages the messages to send - * - * @deprecated Use {@link #sendMessage(Component)} */ - @Deprecated - default void sendMessage(@NotNull String[] messages) { + default void sendMessage(@NotNull String @NotNull[] messages) { for (String message : messages) { sendMessage(message); } @@ -57,7 +53,7 @@ public interface CommandSender extends PermissionHandler, Audience { * @return true if 'this' is a player, false otherwise */ default boolean isPlayer() { - return this instanceof Player; + return false; } /** @@ -66,7 +62,7 @@ public interface CommandSender extends PermissionHandler, Audience { * @return true if 'this' is the console, false otherwise */ default boolean isConsole() { - return this instanceof ConsoleSender; + return false; } /** @@ -77,7 +73,7 @@ public interface CommandSender extends PermissionHandler, Audience { * @see #isPlayer() */ default Player asPlayer() { - return (Player) this; + throw new ClassCastException("CommandSender is not a Player"); } /** @@ -88,6 +84,6 @@ public interface CommandSender extends PermissionHandler, Audience { * @see #isConsole() */ default ConsoleSender asConsole() { - return (ConsoleSender) this; + throw new ClassCastException("CommandSender is not the ConsoleSender"); } } diff --git a/src/main/java/net/minestom/server/command/ConsoleSender.java b/src/main/java/net/minestom/server/command/ConsoleSender.java index 62c6c9994..75bd72ad7 100644 --- a/src/main/java/net/minestom/server/command/ConsoleSender.java +++ b/src/main/java/net/minestom/server/command/ConsoleSender.java @@ -16,15 +16,20 @@ import java.util.concurrent.CopyOnWriteArraySet; * Represents the console when sending a command to the server. */ public class ConsoleSender implements CommandSender { - - private final static Logger LOGGER = LoggerFactory.getLogger(ConsoleSender.class); + private static final PlainComponentSerializer PLAIN_SERIALIZER = PlainComponentSerializer.plain(); + private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleSender.class); private final Set permissions = new CopyOnWriteArraySet<>(); + @Override + public void sendMessage(@NotNull String message) { + LOGGER.info(message); + } + @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { // we don't use the serializer here as we just need the plain text of the message - LOGGER.info(PlainComponentSerializer.plain().serialize(message)); + this.sendMessage(PLAIN_SERIALIZER.serialize(message)); } @NotNull @@ -32,4 +37,14 @@ public class ConsoleSender implements CommandSender { public Set getAllPermissions() { return permissions; } + + @Override + public boolean isConsole() { + return true; + } + + @Override + public ConsoleSender asConsole() { + return this; + } } diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 5b4260b4d..cf44f6850 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -37,7 +37,7 @@ import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.entity.EntityUtils; import net.minestom.server.utils.player.PlayerUtils; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; @@ -684,7 +684,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission } // Scheduled synchronization - if (!CooldownUtils.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) { + if (!Cooldown.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) { this.lastAbsoluteSynchronizationTime = time; sendSynchronization(); } diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index 869e5f863..8ee4b7ea6 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -6,7 +6,7 @@ import net.minestom.server.instance.Instance; import net.minestom.server.item.ItemStack; import net.minestom.server.item.StackingRule; import net.minestom.server.utils.Position; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; @@ -75,7 +75,7 @@ public class ItemEntity extends ObjectEntity { @Override public void update(long time) { if (isMergeable() && isPickable() && - (mergeUpdateOption == null || !CooldownUtils.hasCooldown(time, lastMergeCheck, mergeUpdateOption))) { + (mergeUpdateOption == null || !Cooldown.hasCooldown(time, lastMergeCheck, mergeUpdateOption))) { this.lastMergeCheck = time; final Chunk chunk = instance.getChunkAt(getPosition()); diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index bb8ea3f3f..1bbdfc619 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -25,7 +25,7 @@ import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; import net.minestom.server.utils.Vector; import net.minestom.server.utils.block.BlockIterator; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; @@ -39,8 +39,7 @@ public class LivingEntity extends Entity implements EquipmentHandler { // ItemStack pickup protected boolean canPickupItem; - protected UpdateOption itemPickupCooldown = new UpdateOption(5, TimeUnit.TICK); - private long lastItemPickupCheckTime; + protected Cooldown itemPickupCooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); protected boolean isDead; @@ -213,8 +212,8 @@ public class LivingEntity extends Entity implements EquipmentHandler { } // Items picking - if (canPickupItem() && !CooldownUtils.hasCooldown(time, lastItemPickupCheckTime, itemPickupCooldown)) { - this.lastItemPickupCheckTime = time; + if (canPickupItem() && itemPickupCooldown.isReady(time)) { + itemPickupCooldown.refreshLastUpdate(time); final Chunk chunk = getChunk(); // TODO check surrounding chunks final Set entities = instance.getChunkEntities(chunk); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 41f55a2d7..4d8cbee66 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -12,6 +12,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEvent.ShowEntity; import net.kyori.adventure.text.event.HoverEventSource; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; @@ -67,7 +68,7 @@ import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.entity.EntityUtils; import net.minestom.server.utils.instance.InstanceUtils; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; @@ -151,8 +152,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private float lastPlayerSyncYaw, lastPlayerSyncPitch; // Experience orb pickup - protected UpdateOption experiencePickupCooldown = new UpdateOption(10, TimeUnit.TICK); - private long lastExperiencePickupCheckTime; + protected Cooldown experiencePickupCooldown = new Cooldown(new UpdateOption(10, TimeUnit.TICK)); private BelowNameTag belowNameTag; @@ -373,9 +373,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable, } // Experience orb pickup - if (!CooldownUtils.hasCooldown(time, lastExperiencePickupCheckTime, experiencePickupCooldown)) { - this.lastExperiencePickupCheckTime = time; - + if (experiencePickupCooldown.isReady(time)) { + experiencePickupCooldown.refreshLastUpdate(time); final Chunk chunk = getChunk(); // TODO check surrounding chunks final Set entities = instance.getChunkEntities(chunk); for (Entity entity : entities) { @@ -806,7 +805,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, */ @Deprecated public void sendJsonMessage(@NotNull String json) { - this.sendMessage(json); + this.sendMessage(GsonComponentSerializer.gson().deserialize(json)); } @Override @@ -2644,6 +2643,16 @@ public class Player extends LivingEntity implements CommandSender, Localizable, this.identity = Identity.identity(uuid); } + @Override + public boolean isPlayer() { + return true; + } + + @Override + public Player asPlayer() { + return this; + } + /** * Represents the main or off hand of the player. */ diff --git a/src/main/java/net/minestom/server/entity/ai/goal/CombinedAttackGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/CombinedAttackGoal.java index 9ea4083d4..c1d26a8b9 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/CombinedAttackGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/CombinedAttackGoal.java @@ -7,8 +7,9 @@ import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.entity.type.projectile.EntityProjectile; import net.minestom.server.utils.Position; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; @@ -19,6 +20,8 @@ import java.util.function.Function; */ public class CombinedAttackGoal extends GoalSelector { + private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private final int meleeRangeSquared; private final int meleeDelay; private final TimeUnit meleeTimeUnit; @@ -90,6 +93,10 @@ public class CombinedAttackGoal extends GoalSelector { Check.argCondition(desirableRange > rangedRange, "Desirable range can not exceed ranged range!"); } + public Cooldown getCooldown() { + return this.cooldown; + } + public void setProjectileGenerator(Function projectileGenerator) { this.projectileGenerator = projectileGenerator; } @@ -122,12 +129,12 @@ public class CombinedAttackGoal extends GoalSelector { boolean comeClose = false; // First of all, checking if to perform melee or ranged attack depending on the distance to target. if (distanceSquared <= this.meleeRangeSquared) { - if (!CooldownUtils.hasCooldown(time, this.lastAttack, this.meleeTimeUnit, this.meleeDelay)) { + if (!Cooldown.hasCooldown(time, this.lastAttack, this.meleeTimeUnit, this.meleeDelay)) { this.entityCreature.attack(target, true); this.lastAttack = time; } } else if (distanceSquared <= this.rangedRangeSquared) { - if (!CooldownUtils.hasCooldown(time, this.lastAttack, this.rangedTimeUnit, this.rangedDelay)) { + if (!Cooldown.hasCooldown(time, this.lastAttack, this.rangedTimeUnit, this.rangedDelay)) { if (this.entityCreature.hasLineOfSight(target)) { // If target is on line of entity sight, ranged attack can be performed Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D); @@ -159,7 +166,10 @@ public class CombinedAttackGoal extends GoalSelector { // Otherwise going to the target. Position targetPosition = target.getPosition(); if (pathPosition == null || !pathPosition.isSimilar(targetPosition)) { - navigator.setPathTo(targetPosition); + if (this.cooldown.isReady(time)) { + this.cooldown.refreshLastUpdate(time); + navigator.setPathTo(targetPosition); + } } } diff --git a/src/main/java/net/minestom/server/entity/ai/goal/MeleeAttackGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/MeleeAttackGoal.java index 2c46ec060..a4e45c3e0 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/MeleeAttackGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/MeleeAttackGoal.java @@ -6,11 +6,10 @@ import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.utils.Position; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.validate.Check; +import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Attacks the entity's target ({@link EntityCreature#getTarget()}) OR the closest entity @@ -18,6 +17,8 @@ import org.jetbrains.annotations.Nullable; */ public class MeleeAttackGoal extends GoalSelector { + private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private long lastHit; private final int delay; private final TimeUnit timeUnit; @@ -39,6 +40,10 @@ public class MeleeAttackGoal extends GoalSelector { this.timeUnit = timeUnit; } + public Cooldown getCooldown() { + return this.cooldown; + } + @Override public boolean shouldStart() { this.cachedTarget = findTarget(); @@ -67,7 +72,7 @@ public class MeleeAttackGoal extends GoalSelector { // Attack the target entity if (entityCreature.getDistance(target) <= range) { - if (!CooldownUtils.hasCooldown(time, lastHit, timeUnit, delay)) { + if (!Cooldown.hasCooldown(time, lastHit, timeUnit, delay)) { entityCreature.attack(target, true); this.lastHit = time; } @@ -79,7 +84,10 @@ public class MeleeAttackGoal extends GoalSelector { final Position pathPosition = navigator.getPathPosition(); final Position targetPosition = target.getPosition(); if (pathPosition == null || !pathPosition.isSimilar(targetPosition)) { - navigator.setPathTo(targetPosition); + if (this.cooldown.isReady(time)) { + this.cooldown.refreshLastUpdate(time); + navigator.setPathTo(targetPosition); + } } } } diff --git a/src/main/java/net/minestom/server/entity/ai/goal/RangedAttackGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/RangedAttackGoal.java index c8b8c7dbf..56f85ed0e 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/RangedAttackGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/RangedAttackGoal.java @@ -7,8 +7,9 @@ import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.entity.type.projectile.EntityProjectile; import net.minestom.server.utils.Position; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; @@ -16,6 +17,8 @@ import java.util.function.Function; public class RangedAttackGoal extends GoalSelector { + private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private long lastShot; private final int delay; private final TimeUnit timeUnit; @@ -52,6 +55,10 @@ public class RangedAttackGoal extends GoalSelector { Check.argCondition(desirableRange > attackRange, "Desirable range can not exceed attack range!"); } + public Cooldown getCooldown() { + return this.cooldown; + } + public void setProjectileGenerator(Function projectileGenerator) { this.projectileGenerator = projectileGenerator; } @@ -83,7 +90,7 @@ public class RangedAttackGoal extends GoalSelector { double distanceSquared = this.entityCreature.getDistanceSquared(target); boolean comeClose = false; if (distanceSquared <= this.attackRangeSquared) { - if (!CooldownUtils.hasCooldown(time, this.lastShot, this.timeUnit, this.delay)) { + if (!Cooldown.hasCooldown(time, this.lastShot, this.timeUnit, this.delay)) { if (this.entityCreature.hasLineOfSight(target)) { Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D); @@ -111,7 +118,10 @@ public class RangedAttackGoal extends GoalSelector { } Position targetPosition = target.getPosition(); if (pathPosition == null || !pathPosition.isSimilar(targetPosition)) { - navigator.setPathTo(targetPosition); + if (this.cooldown.isReady(time)) { + this.cooldown.refreshLastUpdate(time); + navigator.setPathTo(targetPosition); + } } } diff --git a/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java b/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java index 7f824143b..875c97cc2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/other/FishingHookMeta.java @@ -3,12 +3,14 @@ package net.minestom.server.entity.metadata.other; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.entity.metadata.ObjectDataProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class FishingHookMeta extends EntityMeta { +public class FishingHookMeta extends EntityMeta implements ObjectDataProvider { private Entity hooked; + private Entity owner; public FishingHookMeta(@NotNull Entity entity, @NotNull Metadata metadata) { super(entity, metadata); @@ -33,4 +35,22 @@ public class FishingHookMeta extends EntityMeta { super.metadata.setIndex((byte) 8, Metadata.Boolean(value)); } + @Nullable + public Entity getOwnerEntity() { + return owner; + } + + public void setOwnerEntity(@Nullable Entity value) { + this.owner = value; + } + + @Override + public int getObjectData() { + return owner != null ? owner.getEntityId() : 0; + } + + @Override + public boolean requiresVelocityPacketAtSpawn() { + return false; + } } diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 74295d536..2de27ece5 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -77,7 +77,7 @@ public class ExtensionManager { /** * Loads all extensions in the extension folder into this server. *

- * + *

* Pipeline: *
* Finds all .jar files in the extensions folder. @@ -88,32 +88,32 @@ public class ExtensionManager { *
* Verifies that all properties of extension.json are correctly set. *

- * + *

* It then sorts all those jars by their load order (making sure that an extension's dependencies load before it) *
* Note: Cyclic dependencies will stop both extensions from being loaded. *

- * + *

* Afterwards, it loads all external dependencies and adds them to the extension's files *

- * + *

* Then removes any invalid extensions (Invalid being its Load Status isn't SUCCESS) *

- * + *

* After that, it set its classloaders so each extension is self-contained, *

- * + *

* Removes invalid extensions again, *

- * + *

* and loads all of those extensions into Minestom *
* (Extension fields are set via reflection after each extension is verified, then loaded.) *

- * + *

* If the extension successfully loads, add it to the global extension Map (Name to Extension) *

- * + *

* And finally make a scheduler to clean observers per extension. */ public void loadExtensions() { @@ -193,7 +193,6 @@ public class ExtensionManager { * Loads an extension into Minestom. * * @param discoveredExtension The extension. Make sure to verify its integrity, set its class loader, and its files. - * * @return An extension object made from this DiscoveredExtension */ @Nullable @@ -213,7 +212,8 @@ public class ExtensionManager { try { jarClass = Class.forName(mainClass, true, loader); } catch (ClassNotFoundException e) { - LOGGER.error("Could not find main class '{}' in extension '{}'.", mainClass, extensionName, e); + LOGGER.error("Could not find main class '{}' in extension '{}'. If it is, be sure to run your server using Bootstrap#bootstrap", + mainClass, extensionName, e); return null; } @@ -295,7 +295,7 @@ public class ExtensionManager { /** * Get all extensions from the extensions folder and make them discovered. - * + *

* It skims the extension folder, discovers and verifies each extension, and returns those created DiscoveredExtensions. * * @return A list of discovered extensions from this folder. @@ -354,7 +354,6 @@ public class ExtensionManager { * Grabs a discovered extension from a jar. * * @param file The jar to grab it from (a .jar is a formatted .zip file) - * * @return The created DiscoveredExtension. */ @Nullable @@ -391,7 +390,8 @@ public class ExtensionManager { extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); } - allExtensions: // go through all the discovered extensions and get their dependencies as extensions + allExtensions: + // go through all the discovered extensions and get their dependencies as extensions for (DiscoveredExtension discoveredExtension : discoveredExtensions) { List dependencies = new ArrayList<>(discoveredExtension.getDependencies().length); @@ -481,6 +481,7 @@ public class ExtensionManager { /** * Checks if this list of extensions are loaded + * * @param extensions The list of extensions to check against. * @return If all of these extensions are loaded. */ @@ -603,7 +604,7 @@ public class ExtensionManager { try { for (String codeModifierClass : extension.getCodeModifiers()) { boolean loaded = modifiableClassLoader.loadModifier(extension.files.toArray(new URL[0]), codeModifierClass); - if(!loaded) { + if (!loaded) { extension.addMissingCodeModifier(codeModifierClass); } } @@ -613,17 +614,17 @@ public class ExtensionManager { Mixins.addConfiguration(mixinConfigFile); LOGGER.info("Found mixin in extension {}: {}", extension.getName(), mixinConfigFile); } catch (ServiceNotAvailableError | MixinError | MixinException e) { - if(MinecraftServer.getExceptionManager() != null) { + if (MinecraftServer.getExceptionManager() != null) { MinecraftServer.getExceptionManager().handleException(e); } else { e.printStackTrace(); } - LOGGER.error("Could not load Mixin configuration: "+mixinConfigFile); + LOGGER.error("Could not load Mixin configuration: " + mixinConfigFile); extension.setFailedToLoadMixinFlag(); } } } catch (Exception e) { - if(MinecraftServer.getExceptionManager() != null) { + if (MinecraftServer.getExceptionManager() != null) { MinecraftServer.getExceptionManager().handleException(e); } else { e.printStackTrace(); diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 438f98292..c67369f60 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -19,7 +19,7 @@ import net.minestom.server.utils.block.CustomBlockUtils; import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import net.minestom.server.world.biomes.Biome; @@ -145,7 +145,7 @@ public class DynamicChunk extends Chunk { final UpdateOption updateOption = customBlock.getUpdateOption(); if (updateOption != null) { final long lastUpdate = updatableBlocksLastUpdate.get(index); - final boolean hasCooldown = CooldownUtils.hasCooldown(time, lastUpdate, updateOption); + final boolean hasCooldown = Cooldown.hasCooldown(time, lastUpdate, updateOption); if (hasCooldown) continue; diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 94b2efc8b..409a3b302 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -30,7 +30,7 @@ import net.minestom.server.utils.Position; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.entity.EntityUtils; -import net.minestom.server.utils.time.CooldownUtils; +import net.minestom.server.utils.time.Cooldown; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; @@ -1026,7 +1026,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta this.time += timeRate; // time needs to be send to players - if (timeUpdate != null && !CooldownUtils.hasCooldown(time, lastTimeUpdate, timeUpdate)) { + if (timeUpdate != null && !Cooldown.hasCooldown(time, lastTimeUpdate, timeUpdate)) { PacketUtils.sendGroupedPacket(getPlayers(), createTimePacket()); this.lastTimeUpdate = time; } diff --git a/src/main/java/net/minestom/server/instance/block/rule/vanilla/StairsPlacementRule.java b/src/main/java/net/minestom/server/instance/block/rule/vanilla/StairsPlacementRule.java new file mode 100644 index 000000000..efc86fb2a --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/rule/vanilla/StairsPlacementRule.java @@ -0,0 +1,166 @@ +package net.minestom.server.instance.block.rule.vanilla; + +import net.minestom.server.entity.Player; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.BlockAlternative; +import net.minestom.server.instance.block.BlockFace; +import net.minestom.server.instance.block.rule.BlockPlacementRule; +import net.minestom.server.utils.BlockPosition; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StairsPlacementRule extends BlockPlacementRule { + + public StairsPlacementRule(@NotNull Block block) { + super(block); + } + + @Override + public short blockUpdate(@NotNull Instance instance, @NotNull BlockPosition blockPosition, short currentStateID) { + return currentStateID; + } + + @Override + public short blockPlace(@NotNull Instance instance, @NotNull Block block, @NotNull BlockFace blockFace, @NotNull BlockPosition blockPosition, @NotNull Player player) { + Facing facing = this.getFacing(player); + Shape shape = this.getShape(instance, blockPosition, facing); + BlockFace half = BlockFace.BOTTOM; // waiting for new block faces to be implemented + boolean waterlogged = false; // waiting for water to be implemented + return block.withProperties( + "facing=" + facing.toString().toLowerCase(), + "half=" + half.toString().toLowerCase(), + "shape=" + shape.toString().toLowerCase(), + "waterlogged=" + waterlogged + ); + } + + private enum Shape { + + STRAIGHT, + OUTER_LEFT, + OUTER_RIGHT, + INNER_LEFT, + INNER_RIGHT; + } + + private enum Facing { + + NORTH( + new BlockPosition(0, 0, 1), + new BlockPosition(0, 0, -1) + ), + EAST( + new BlockPosition(-1, 0, 0), + new BlockPosition(1, 0, 0) + ), + SOUTH( + new BlockPosition(0, 0, -1), + new BlockPosition(0, 0, 1) + ), + WEST( + new BlockPosition(1, 0, 0), + new BlockPosition(-1, 0, 0) + ); + + private final BlockPosition front; + private final BlockPosition back; + + Facing(@NotNull BlockPosition front, @NotNull BlockPosition back) { + this.front = front; + this.back = back; + } + + @NotNull + public Pair<@Nullable Shape, @Nullable Facing> getFront(@NotNull Instance instance, @NotNull BlockPosition blockPosition) { + return this.getProperties(instance, blockPosition.clone().add(this.front)); + } + + @NotNull + public Pair<@Nullable Shape, @Nullable Facing> getBack(@NotNull Instance instance, @NotNull BlockPosition blockPosition) { + return this.getProperties(instance, blockPosition.clone().add(this.back)); + } + + @NotNull + private Pair<@Nullable Shape, @Nullable Facing> getProperties(@NotNull Instance instance, @NotNull BlockPosition blockPosition) { + Block block = instance.getBlock(blockPosition); + if (block == null) { + return Pair.of(null, null); + } + short stateId = instance.getBlockStateId(blockPosition); + BlockAlternative alternative = block.getAlternative(stateId); + try { + Shape shape = Shape.valueOf(alternative.getProperty("shape").toUpperCase()); + Facing facing = Facing.valueOf(alternative.getProperty("facing").toUpperCase()); + return Pair.of(shape, facing); + } catch (Exception ex) { + return Pair.of(null, null); + } + } + } + + @NotNull + private Shape getShape(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @NotNull Facing facing) { + Pair front = facing.getFront(instance, blockPosition); + Pair back = facing.getBack(instance, blockPosition); + Shape shape = this.getShapeFromSide(front, facing, Shape.INNER_RIGHT, Shape.INNER_LEFT); + if (shape == null) { + shape = this.getShapeFromSide(back, facing, Shape.OUTER_RIGHT, Shape.OUTER_LEFT); + } + return shape == null ? Shape.STRAIGHT : shape; + } + + @Nullable + private Shape getShapeFromSide(@NotNull Pair side, @NotNull Facing facing, @NotNull Shape right, @NotNull Shape left) { + if (side.getLeft() == null) { + return null; + } + Facing sideFacing = side.getRight(); + if (facing.equals(Facing.NORTH)) { + if (sideFacing.equals(Facing.EAST)) { + return right; + } else if (sideFacing.equals(Facing.WEST)) { + return left; + } + } else if (facing.equals(Facing.SOUTH)) { + if (sideFacing.equals(Facing.EAST)) { + return left; + } else if (sideFacing.equals(Facing.WEST)) { + return right; + } + } else if (facing.equals(Facing.EAST)) { + if (sideFacing.equals(Facing.SOUTH)) { + return right; + } else if (sideFacing.equals(Facing.NORTH)) { + return left; + } + } else if (facing.equals(Facing.WEST)) { + if (sideFacing.equals(Facing.SOUTH)) { + return left; + } else if (sideFacing.equals(Facing.NORTH)) { + return right; + } + } + return null; + } + + @NotNull + private Facing getFacing(@NotNull Player player) { + float degrees = (player.getPosition().getYaw() - 90) % 360; + if (degrees < 0) { + degrees += 360; + } + if (0 <= degrees && degrees < 45) { + return Facing.WEST; + } else if (45 <= degrees && degrees < 135) { + return Facing.NORTH; + } else if (135 <= degrees && degrees < 225) { + return Facing.EAST; + } else if (225 <= degrees && degrees < 315) { + return Facing.SOUTH; + } else { // 315 <= degrees && degrees < 360 + return Facing.WEST; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minestom/server/network/packet/client/handler/ClientPlayPacketsHandler.java b/src/main/java/net/minestom/server/network/packet/client/handler/ClientPlayPacketsHandler.java index 31cb179a8..f044e5a0a 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handler/ClientPlayPacketsHandler.java +++ b/src/main/java/net/minestom/server/network/packet/client/handler/ClientPlayPacketsHandler.java @@ -35,8 +35,8 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler { register(0x1B, ClientPlayerDiggingPacket::new); register(0x1C, ClientEntityActionPacket::new); register(0x1D, ClientSteerVehiclePacket::new); - register(0x1E, ClientRecipeBookData::new); - register(0x1F, ClientRecipeBookData::new); + register(0x1E, ClientSetRecipeBookStatePacket::new); + register(0x1F, ClientSetDisplayedRecipePacket::new); register(0x20, ClientNameItemPacket::new); diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientRecipeBookData.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientRecipeBookData.java deleted file mode 100644 index cc9698595..000000000 --- a/src/main/java/net/minestom/server/network/packet/client/play/ClientRecipeBookData.java +++ /dev/null @@ -1,67 +0,0 @@ -package net.minestom.server.network.packet.client.play; - -import net.minestom.server.network.packet.client.ClientPlayPacket; -import net.minestom.server.utils.binary.BinaryReader; -import net.minestom.server.utils.binary.BinaryWriter; -import org.jetbrains.annotations.NotNull; - -public class ClientRecipeBookData extends ClientPlayPacket { - - public int type; - - public String recipeId = ""; - - public boolean craftingRecipeBookOpen; - public boolean craftingRecipeFilterActive; - public boolean smeltingRecipeBookOpen; - public boolean smeltingRecipeFilterActive; - public boolean blastingRecipeBookOpen; - public boolean blastingRecipeFilterActive; - public boolean smokingRecipeBookOpen; - public boolean smokingRecipeFilterActive; - - @Override - public void read(@NotNull BinaryReader reader) { - this.type = reader.readVarInt(); - - switch (type) { - case 0: - this.recipeId = reader.readSizedString(256); - break; - case 1: - this.craftingRecipeBookOpen = reader.readBoolean(); - this.craftingRecipeFilterActive = reader.readBoolean(); - this.smeltingRecipeBookOpen = reader.readBoolean(); - this.smeltingRecipeFilterActive = reader.readBoolean(); - this.blastingRecipeBookOpen = reader.readBoolean(); - this.blastingRecipeFilterActive = reader.readBoolean(); - this.smokingRecipeBookOpen = reader.readBoolean(); - this.smokingRecipeFilterActive = reader.readBoolean(); - break; - } - } - - @Override - public void write(@NotNull BinaryWriter writer) { - writer.writeVarInt(type); - - switch (type) { - case 0: - if(recipeId.length() > 256) - throw new IllegalArgumentException("recipeId must be less than 256 bytes"); - writer.writeSizedString(recipeId); - break; - - case 1: - writer.writeBoolean(this.craftingRecipeBookOpen); - writer.writeBoolean(this.craftingRecipeFilterActive); - writer.writeBoolean(this.smeltingRecipeBookOpen); - writer.writeBoolean(this.smeltingRecipeFilterActive); - writer.writeBoolean(this.blastingRecipeBookOpen); - writer.writeBoolean(this.blastingRecipeFilterActive); - writer.writeBoolean(this.smokingRecipeBookOpen); - writer.writeBoolean(this.smokingRecipeFilterActive); - break; - } - } -} diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientSetDisplayedRecipePacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientSetDisplayedRecipePacket.java new file mode 100644 index 000000000..860a58078 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientSetDisplayedRecipePacket.java @@ -0,0 +1,21 @@ +package net.minestom.server.network.packet.client.play; + +import net.minestom.server.network.packet.client.ClientPlayPacket; +import net.minestom.server.utils.binary.BinaryReader; +import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; + +public class ClientSetDisplayedRecipePacket extends ClientPlayPacket { + + public String recipeId = ""; + + @Override + public void read(@NotNull BinaryReader reader) { + this.recipeId = reader.readSizedString(256); + } + + @Override + public void write(@NotNull BinaryWriter writer) { + writer.writeSizedString(recipeId); + } +} \ No newline at end of file diff --git a/src/main/java/net/minestom/server/network/packet/client/play/ClientSetRecipeBookStatePacket.java b/src/main/java/net/minestom/server/network/packet/client/play/ClientSetRecipeBookStatePacket.java new file mode 100644 index 000000000..17a1e9e64 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/client/play/ClientSetRecipeBookStatePacket.java @@ -0,0 +1,31 @@ +package net.minestom.server.network.packet.client.play; + +import net.minestom.server.network.packet.client.ClientPlayPacket; +import net.minestom.server.utils.binary.BinaryReader; +import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; + +public class ClientSetRecipeBookStatePacket extends ClientPlayPacket { + + public BookType type = BookType.CRAFTING; + public boolean bookOpen; + public boolean filterActive; + + @Override + public void read(@NotNull BinaryReader reader) { + this.type = BookType.values()[reader.readVarInt()]; + this.bookOpen = reader.readBoolean(); + this.filterActive = reader.readBoolean(); + } + + @Override + public void write(@NotNull BinaryWriter writer) { + writer.writeVarInt(type.ordinal()); + writer.writeBoolean(bookOpen); + writer.writeBoolean(filterActive); + } + + public enum BookType { + CRAFTING, FURNACE, BLAST_FURNACE, SMOKER + } +} 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 ba7d97f23..24da7a1f0 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 @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.client.status; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.network.packet.client.ClientPreplayPacket; import net.minestom.server.network.packet.server.handshake.ResponsePacket; @@ -18,11 +19,11 @@ public class StatusRequestPacket implements ClientPreplayPacket { ResponseData responseData = new ResponseData(); // Fill default params - responseData.setName(MinecraftServer.VERSION_NAME); + responseData.setVersion(MinecraftServer.VERSION_NAME); responseData.setProtocol(MinecraftServer.PROTOCOL_VERSION); responseData.setMaxPlayer(0); responseData.setOnline(0); - responseData.setDescription("Minestom Server"); + responseData.setDescription(Component.text("Minestom Server")); responseData.setFavicon(""); if (consumer != null) diff --git a/src/main/java/net/minestom/server/ping/ResponseData.java b/src/main/java/net/minestom/server/ping/ResponseData.java index 498c30ae8..8c5f43ed0 100644 --- a/src/main/java/net/minestom/server/ping/ResponseData.java +++ b/src/main/java/net/minestom/server/ping/ResponseData.java @@ -2,6 +2,10 @@ package net.minestom.server.ping; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.minestom.server.entity.Player; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -15,19 +19,12 @@ import java.util.UUID; * net.minestom.server.MinecraftServer#start(String, int, ResponseDataConsumer)}. */ public class ResponseData { - - private final JsonObject jsonObject; - - private final JsonObject versionObject; - private final JsonObject playersObject; - private final JsonArray sampleArray; - private final JsonObject descriptionObject; private final List pingPlayers; - private String name; + private String version; private int protocol; private int maxPlayer; private int online; - private String description; + private Component description; private String favicon; @@ -35,11 +32,6 @@ public class ResponseData { * Constructs a new {@link ResponseData}. */ public ResponseData() { - this.jsonObject = new JsonObject(); - this.versionObject = new JsonObject(); - this.playersObject = new JsonObject(); - this.sampleArray = new JsonArray(); - this.descriptionObject = new JsonObject(); this.pingPlayers = new ArrayList<>(); } @@ -47,9 +39,20 @@ public class ResponseData { * Sets the name for the response. * * @param name The name for the response data. + * @deprecated Use {@link #setVersion(String)} */ + @Deprecated public void setName(String name) { - this.name = name; + this.setVersion(name); + } + + /** + * Sets the version name for the response. + * + * @param version The version name for the response data. + */ + public void setVersion(String version) { + this.version = version; } /** @@ -79,6 +82,26 @@ public class ResponseData { this.online = online; } + /** + * Adds some players to the response. + * + * @param players the players + */ + public void addPlayer(Iterable players) { + for (Player player : players) { + this.addPlayer(player); + } + } + + /** + * Adds a player to the response. + * + * @param player the player + */ + public void addPlayer(Player player) { + this.addPlayer(player.getUsername(), player.getUuid()); + } + /** * Adds a player to the response. * @@ -104,8 +127,19 @@ public class ResponseData { * Sets the response description. * * @param description The description for the response data. + * @deprecated Use {@link #setDescription(Component)} */ + @Deprecated public void setDescription(String description) { + this.description = LegacyComponentSerializer.legacySection().deserialize(description); + } + + /** + * Sets the response description. + * + * @param description The description for the response data. + */ + public void setDescription(Component description) { this.description = description; } @@ -125,13 +159,19 @@ public class ResponseData { */ @NotNull public JsonObject build() { - versionObject.addProperty("name", name); - versionObject.addProperty("protocol", protocol); + // version + final JsonObject versionObject = new JsonObject(); + versionObject.addProperty("name", this.version); + versionObject.addProperty("protocol", this.protocol); - playersObject.addProperty("max", maxPlayer); - playersObject.addProperty("online", online); + // players info + final JsonObject playersObject = new JsonObject(); + playersObject.addProperty("max", this.maxPlayer); + playersObject.addProperty("online", this.online); - for (PingPlayer pingPlayer : pingPlayers) { + // individual players + final JsonArray sampleArray = new JsonArray(); + for (PingPlayer pingPlayer : this.pingPlayers) { JsonObject pingPlayerObject = new JsonObject(); pingPlayerObject.addProperty("name", pingPlayer.name); pingPlayerObject.addProperty("id", pingPlayer.uuid.toString()); @@ -139,12 +179,14 @@ public class ResponseData { } playersObject.add("sample", sampleArray); - descriptionObject.addProperty("text", description); + final JsonObject descriptionObject = GsonComponentSerializer.gson().serializer() + .toJsonTree(this.description).getAsJsonObject(); + final JsonObject jsonObject = new JsonObject(); jsonObject.add("version", versionObject); jsonObject.add("players", playersObject); jsonObject.add("description", descriptionObject); - jsonObject.addProperty("favicon", favicon); + jsonObject.addProperty("favicon", this.favicon); return jsonObject; } diff --git a/src/main/java/net/minestom/server/utils/BlockPosition.java b/src/main/java/net/minestom/server/utils/BlockPosition.java index 52a068edb..2028fe070 100644 --- a/src/main/java/net/minestom/server/utils/BlockPosition.java +++ b/src/main/java/net/minestom/server/utils/BlockPosition.java @@ -212,18 +212,6 @@ public class BlockPosition implements PublicCloneable { MathUtils.square(getZ() - blockPosition.getZ()); } - /** - * Copies this block position. - * - * @return the cloned block position - * @deprecated use {@link #clone()} - */ - @Deprecated - @NotNull - public BlockPosition copy() { - return clone(); - } - @NotNull @Override public BlockPosition clone() { diff --git a/src/main/java/net/minestom/server/utils/Position.java b/src/main/java/net/minestom/server/utils/Position.java index 6fe065e9f..238647e3a 100644 --- a/src/main/java/net/minestom/server/utils/Position.java +++ b/src/main/java/net/minestom/server/utils/Position.java @@ -212,14 +212,6 @@ public class Position implements PublicCloneable { } } - /** - * @deprecated Please use {@link #clone()} - */ - @Deprecated - public Position copy() { - return clone(); - } - /** * Gets if the two objects are position and have the same values. * diff --git a/src/main/java/net/minestom/server/utils/Vector.java b/src/main/java/net/minestom/server/utils/Vector.java index 6399b308a..c77d69219 100644 --- a/src/main/java/net/minestom/server/utils/Vector.java +++ b/src/main/java/net/minestom/server/utils/Vector.java @@ -476,15 +476,6 @@ public class Vector implements PublicCloneable { '}'; } - /** - * @deprecated use {@link #clone()} - */ - @Deprecated - @NotNull - public Vector copy() { - return clone(); - } - @NotNull @Override public Vector clone() { diff --git a/src/main/java/net/minestom/server/utils/time/CooldownUtils.java b/src/main/java/net/minestom/server/utils/time/Cooldown.java similarity index 75% rename from src/main/java/net/minestom/server/utils/time/CooldownUtils.java rename to src/main/java/net/minestom/server/utils/time/Cooldown.java index 8d9fc90fa..1110da38b 100644 --- a/src/main/java/net/minestom/server/utils/time/CooldownUtils.java +++ b/src/main/java/net/minestom/server/utils/time/Cooldown.java @@ -2,10 +2,26 @@ package net.minestom.server.utils.time; import org.jetbrains.annotations.NotNull; -public final class CooldownUtils { +public final class Cooldown { - private CooldownUtils() { + private UpdateOption updateOption; + private long lastUpdate; + public Cooldown(@NotNull UpdateOption updateOption) { + this.updateOption = updateOption; + this.lastUpdate = System.currentTimeMillis(); + } + + public UpdateOption getUpdateOption() { + return this.updateOption; + } + + public void refreshLastUpdate(long lastUpdate) { + this.lastUpdate = lastUpdate; + } + + public boolean isReady(long time) { + return !hasCooldown(time, lastUpdate, updateOption.getTimeUnit(), updateOption.getValue()); } /** diff --git a/src/main/java/net/minestom/server/world/DimensionTypeManager.java b/src/main/java/net/minestom/server/world/DimensionTypeManager.java index 82b27bdde..06f2a50ab 100644 --- a/src/main/java/net/minestom/server/world/DimensionTypeManager.java +++ b/src/main/java/net/minestom/server/world/DimensionTypeManager.java @@ -1,6 +1,8 @@ package net.minestom.server.world; +import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTList; import org.jglrxavpok.hephaistos.nbt.NBTTypes; @@ -43,6 +45,33 @@ public final class DimensionTypeManager { return dimensionTypes.remove(dimensionType); } + /** + * @param namespaceID The dimension name + * @return true if the dimension is registered + */ + public boolean isRegistered(@NotNull NamespaceID namespaceID) { + return isRegistered(getDimension(namespaceID)); + } + + /** + * @param dimensionType dimension to check if is registered + * @return true if the dimension is registered + */ + public boolean isRegistered(@Nullable DimensionType dimensionType) { + return dimensionType != null && dimensionTypes.contains(dimensionType) && dimensionType.isRegistered(); + } + + /** + * Return to a @{@link DimensionType} only if present and registered + * + * @param namespaceID The Dimension Name + * @return an a DimensionType if it present and registered + */ + @Nullable + public DimensionType getDimension(@NotNull NamespaceID namespaceID) { + return unmodifiableList().stream().filter(dimensionType -> dimensionType.getName().equals(namespaceID)).filter(DimensionType::isRegistered).findFirst().orElse(null); + } + /** * Returns an immutable copy of the dimension types already registered. *