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.
*