diff --git a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java index 1ab0676f7..92dc949f0 100644 --- a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java +++ b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java @@ -4,7 +4,6 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.map.Framebuffer; import net.minestom.server.map.MapColors; import net.minestom.server.timer.Task; -import net.minestom.server.utils.time.TimeUnit; import org.lwjgl.BufferUtils; import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFWErrorCallback; @@ -12,6 +11,8 @@ import org.lwjgl.opengl.GL; import org.lwjgl.system.MemoryStack; import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; @@ -71,7 +72,11 @@ public abstract class GLFWCapableBuffer { GL.createCapabilities(); } - public Task setupRenderLoop(long period, TimeUnit unit, Runnable rendering) { + public Task setupRenderLoop(long period, TemporalUnit unit, Runnable rendering) { + return setupRenderLoop(Duration.of(period, unit), rendering); + } + + public Task setupRenderLoop(Duration period, Runnable rendering) { return MinecraftServer.getSchedulerManager() .buildTask(new Runnable() { private boolean first = true; @@ -85,7 +90,7 @@ public abstract class GLFWCapableBuffer { render(rendering); } }) - .repeat(period, unit) + .repeat(period) .schedule(); } @@ -97,7 +102,7 @@ public abstract class GLFWCapableBuffer { /** * Called in render after glFlush to read the pixel buffer contents and convert it to map colors. - * Only call if you do not use {@link #render(Runnable)} nor {@link #setupRenderLoop(long, TimeUnit, Runnable)} + * Only call if you do not use {@link #render(Runnable)} nor {@link #setupRenderLoop} */ public void prepareMapColors() { if(onlyMapColors) { diff --git a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWFramebuffer.java b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWFramebuffer.java index c02a5aff4..c34476c64 100644 --- a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWFramebuffer.java +++ b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWFramebuffer.java @@ -1,7 +1,6 @@ package net.minestom.server.map.framebuffers; import net.minestom.server.map.Framebuffer; -import net.minestom.server.utils.time.TimeUnit; import org.lwjgl.BufferUtils; import java.nio.ByteBuffer; @@ -22,7 +21,7 @@ import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_API; * to automatically render to the offscreen buffer on a specialized thread. * * GLFWFramebuffer does not provide guarantee that the result of {@link #toMapColors()} is synchronized with rendering, but - * it will be updated after each frame rendered through {@link #render(Runnable)} or {@link #setupRenderLoop(long, TimeUnit, Runnable)}. + * it will be updated after each frame rendered through {@link #render(Runnable)} or {@link #setupRenderLoop(long, java.time.temporal.TemporalUnit, Runnable)}. * * This framebuffer is meant to render to a single map (ie it is only compatible with 128x128 rendering) */ diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index 419d0d8eb..afe43c11d 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -108,16 +108,8 @@ public final class UpdateManager { // Tick all instances MinecraftServer.getInstanceManager().getInstances().forEach(instance -> instance.tick(tickStart)); - // Tick all chunks (and entities inside) - final CountDownLatch countDownLatch = threadProvider.update(tickStart); - - // Wait tick end - try { - countDownLatch.await(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + this.threadProvider.updateAndAwait(tickStart); // Clear removed entities & update threads long tickTime = System.currentTimeMillis() - tickStart; diff --git a/src/main/java/net/minestom/server/acquirable/Acquirable.java b/src/main/java/net/minestom/server/acquirable/Acquirable.java index b1f290e85..54a93350c 100644 --- a/src/main/java/net/minestom/server/acquirable/Acquirable.java +++ b/src/main/java/net/minestom/server/acquirable/Acquirable.java @@ -20,6 +20,8 @@ public interface Acquirable { * Gets all the {@link Entity entities} being ticked in the current thread. *

* Useful when you want to ensure that no acquisition is ever done. + *

+ * Be aware that the entity stream is only updated at the beginning of the thread tick. * * @return the entities ticked in the current thread */ diff --git a/src/main/java/net/minestom/server/adventure/MinestomAdventure.java b/src/main/java/net/minestom/server/adventure/MinestomAdventure.java index e2429639c..8e9bf2ae9 100644 --- a/src/main/java/net/minestom/server/adventure/MinestomAdventure.java +++ b/src/main/java/net/minestom/server/adventure/MinestomAdventure.java @@ -26,7 +26,7 @@ public final class MinestomAdventure { /** * If components should be automatically translated in outgoing packets. */ - public static final boolean AUTOMATIC_COMPONENT_TRANSLATION = false; + public static boolean AUTOMATIC_COMPONENT_TRANSLATION = false; static final Localizable NULL_LOCALIZABLE = () -> null; diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTime.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTime.java index 37c80edc6..57c2330ac 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTime.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTime.java @@ -7,15 +7,17 @@ import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; +import java.time.Duration; +import java.time.temporal.TemporalUnit; + /** * Represents an argument giving a time (day/second/tick). *

* Example: 50d, 25s, 75t */ -public class ArgumentTime extends Argument { +public class ArgumentTime extends Argument { public static final int INVALID_TIME_FORMAT = -2; public static final int NO_NUMBER = -3; @@ -28,12 +30,12 @@ public class ArgumentTime extends Argument { @NotNull @Override - public UpdateOption parse(@NotNull String input) throws ArgumentSyntaxException { + public Duration parse(@NotNull String input) throws ArgumentSyntaxException { final char lastChar = input.charAt(input.length() - 1); - TimeUnit timeUnit; + TemporalUnit timeUnit; if (Character.isDigit(lastChar)) - timeUnit = TimeUnit.TICK; + timeUnit = TimeUnit.SERVER_TICK; else if (SUFFIXES.contains(lastChar)) { input = input.substring(0, input.length() - 1); @@ -42,7 +44,7 @@ public class ArgumentTime extends Argument { } else if (lastChar == 's') { timeUnit = TimeUnit.SECOND; } else if (lastChar == 't') { - timeUnit = TimeUnit.TICK; + timeUnit = TimeUnit.SERVER_TICK; } else { throw new ArgumentSyntaxException("Time needs to have the unit d, s, t, or none", input, NO_NUMBER); } @@ -52,7 +54,7 @@ public class ArgumentTime extends Argument { try { // Check if value is a number final int time = Integer.parseInt(input); - return new UpdateOption(time, timeUnit); + return Duration.of(time, timeUnit); } catch (NumberFormatException e) { throw new ArgumentSyntaxException("Time needs to be a number", input, NO_NUMBER); } diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 3878aa7f9..8512a9221 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -46,13 +46,14 @@ import net.minestom.server.utils.entity.EntityUtils; import net.minestom.server.utils.player.PlayerUtils; 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.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -127,8 +128,8 @@ public class Entity implements Viewable, Tickable, EventHandler, Da protected EntityType entityType; // UNSAFE to change, modify at your own risk // Network synchronization, send the absolute position of the entity each X milliseconds - private static final UpdateOption SYNCHRONIZATION_COOLDOWN = new UpdateOption(1, TimeUnit.MINUTE); - private UpdateOption customSynchronizationCooldown; + private static final Duration SYNCHRONIZATION_COOLDOWN = Duration.of(1, TimeUnit.MINUTE); + private Duration customSynchronizationCooldown; private long lastAbsoluteSynchronizationTime; // Events @@ -1142,7 +1143,7 @@ public class Entity implements Viewable, Tickable, EventHandler, Da * Sets the entity in fire visually. *

* WARNING: if you want to apply damage or specify a duration, - * see {@link LivingEntity#setFireForDuration(int, TimeUnit)}. + * see {@link LivingEntity#setFireForDuration(int, TemporalUnit)}. * * @param fire should the entity be set in fire */ @@ -1528,18 +1529,28 @@ public class Entity implements Viewable, Tickable, EventHandler, Da * * @param delay the time before removing the entity, * 0 to cancel the removing - * @param timeUnit the unit of the delay + * @param temporalUnit the unit of the delay */ - public void scheduleRemove(long delay, @NotNull TimeUnit timeUnit) { - if (delay == 0) { // Cancel the scheduled remove - this.scheduledRemoveTime = 0; - return; - } - this.scheduledRemoveTime = System.currentTimeMillis() + timeUnit.toMilliseconds(delay); + public void scheduleRemove(long delay, @NotNull TemporalUnit temporalUnit) { + scheduleRemove(Duration.of(delay, temporalUnit)); } /** - * Gets if the entity removal has been scheduled with {@link #scheduleRemove(long, TimeUnit)}. + * Triggers {@link #remove()} after the specified time. + * + * @param delay the time before removing the entity, + * 0 to cancel the removing + */ + public void scheduleRemove(Duration delay) { + if (delay.isZero()) { // Cancel the scheduled remove + this.scheduledRemoveTime = 0; + return; + } + this.scheduledRemoveTime = System.currentTimeMillis() + delay.toMillis(); + } + + /** + * Gets if the entity removal has been scheduled with {@link #scheduleRemove(Duration)}. * * @return true if the entity removal has been scheduled */ @@ -1609,8 +1620,20 @@ public class Entity implements Viewable, Tickable, EventHandler, Da * Set custom cooldown for position synchronization. * * @param cooldown custom cooldown for position synchronization. + * @deprecated Replaced by {@link #setCustomSynchronizationCooldown(Duration)} */ - public void setCustomSynchronizationCooldown(@Nullable UpdateOption cooldown) { + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public void setCustomSynchronizationCooldown(@Nullable net.minestom.server.utils.time.UpdateOption cooldown) { + setCustomSynchronizationCooldown(cooldown != null ? Duration.ofMillis(cooldown.toMilliseconds()) : null); + } + + /** + * Set custom cooldown for position synchronization. + * + * @param cooldown custom cooldown for position synchronization. + */ + public void setCustomSynchronizationCooldown(@Nullable Duration cooldown) { this.customSynchronizationCooldown = cooldown; } @@ -1619,7 +1642,7 @@ public class Entity implements Viewable, Tickable, EventHandler, Da return HoverEvent.showEntity(ShowEntity.of(this.entityType, this.uuid)); } - private UpdateOption getSynchronizationCooldown() { + private Duration getSynchronizationCooldown() { return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN); } diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index e7f7e18be..c8166c737 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -14,6 +14,7 @@ import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.Duration; import java.util.Collection; import java.util.Set; import java.util.UUID; @@ -80,7 +81,7 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent if (removalAnimationDelay > 0) { // Needed for proper death animation (wait for it to finish before destroying the entity) - scheduleRemove(removalAnimationDelay, TimeUnit.MILLISECOND); + scheduleRemove(Duration.of(removalAnimationDelay, TimeUnit.MILLISECOND)); } else { // Instant removal without animation playback remove(); diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index 275348262..aae2eaebb 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -10,10 +10,11 @@ import net.minestom.server.item.StackingRule; import net.minestom.server.utils.Position; 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; import org.jetbrains.annotations.Nullable; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.Set; /** @@ -24,7 +25,7 @@ public class ItemEntity extends Entity { /** * Used to slow down the merge check delay */ - private static UpdateOption mergeUpdateOption = new UpdateOption(10, TimeUnit.TICK); + private static Duration mergeDelay = Duration.of(10, TimeUnit.SERVER_TICK); /** * The last time that this item has checked his neighbors for merge @@ -60,8 +61,8 @@ public class ItemEntity extends Entity { * @return the merge update option */ @Nullable - public static UpdateOption getMergeUpdateOption() { - return mergeUpdateOption; + public static Duration getMergeDelay() { + return mergeDelay; } /** @@ -69,15 +70,29 @@ public class ItemEntity extends Entity { * Can be set to null to entirely remove the delay. * * @param mergeUpdateOption the new merge update option + * + * @deprecated Replaced by {@link #setMergeDelay(Duration)} */ - public static void setMergeUpdateOption(@Nullable UpdateOption mergeUpdateOption) { - ItemEntity.mergeUpdateOption = mergeUpdateOption; + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public static void setMergeUpdateOption(@Nullable net.minestom.server.utils.time.UpdateOption mergeUpdateOption) { + setMergeDelay(mergeUpdateOption != null ? mergeUpdateOption.toDuration() : null); + } + + /** + * Changes the merge delay. + * Can be set to null to entirely remove the delay. + * + * @param delay the new merge delay + */ + public static void setMergeDelay(@Nullable Duration delay) { + ItemEntity.mergeDelay = delay; } @Override public void update(long time) { if (isMergeable() && isPickable() && - (mergeUpdateOption == null || !Cooldown.hasCooldown(time, lastMergeCheck, mergeUpdateOption))) { + (mergeDelay == null || !Cooldown.hasCooldown(time, lastMergeCheck, mergeDelay))) { this.lastMergeCheck = time; final Chunk chunk = instance.getChunkAt(getPosition()); @@ -213,7 +228,7 @@ public class ItemEntity extends Entity { } /** - * Gets the pickup delay in milliseconds, defined by {@link #setPickupDelay(long, TimeUnit)}. + * Gets the pickup delay in milliseconds, defined by {@link #setPickupDelay(Duration)}. * * @return the pickup delay */ @@ -225,10 +240,19 @@ public class ItemEntity extends Entity { * Sets the pickup delay of the ItemEntity. * * @param delay the pickup delay - * @param timeUnit the unit of the delay + * @param temporalUnit the unit of the delay */ - public void setPickupDelay(long delay, @NotNull TimeUnit timeUnit) { - this.pickupDelay = timeUnit.toMilliseconds(delay); + public void setPickupDelay(long delay, @NotNull TemporalUnit temporalUnit) { + setPickupDelay(Duration.of(delay, temporalUnit)); + } + + /** + * Sets the pickup delay of the ItemEntity. + * + * @param delay the pickup delay + */ + public void setPickupDelay(Duration delay) { + this.pickupDelay = delay.toMillis(); } /** diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index f1fd8642e..28657961c 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -31,10 +31,11 @@ import net.minestom.server.utils.Vector; import net.minestom.server.utils.block.BlockIterator; 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; import org.jetbrains.annotations.Nullable; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -42,7 +43,7 @@ public class LivingEntity extends Entity implements EquipmentHandler { // ItemStack pickup protected boolean canPickupItem; - protected Cooldown itemPickupCooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + protected Cooldown itemPickupCooldown = new Cooldown(Duration.of(5, TimeUnit.SERVER_TICK)); protected boolean isDead; @@ -316,21 +317,31 @@ public class LivingEntity extends Entity implements EquipmentHandler { * @param duration duration in ticks of the effect */ public void setFireForDuration(int duration) { - setFireForDuration(duration, TimeUnit.TICK); + setFireForDuration(duration, TimeUnit.SERVER_TICK); } /** * Sets fire to this entity for a given duration. * * @param duration duration of the effect - * @param unit unit used to express the duration + * @param temporalUnit unit used to express the duration * @see #setOnFire(boolean) if you want it to be permanent without any event callback */ - public void setFireForDuration(int duration, TimeUnit unit) { - EntityFireEvent entityFireEvent = new EntityFireEvent(this, duration, unit); + public void setFireForDuration(int duration, TemporalUnit temporalUnit) { + setFireForDuration(Duration.of(duration, temporalUnit)); + } + + /** + * Sets fire to this entity for a given duration. + * + * @param duration duration of the effect + * @see #setOnFire(boolean) if you want it to be permanent without any event callback + */ + public void setFireForDuration(Duration duration) { + EntityFireEvent entityFireEvent = new EntityFireEvent(this, duration); // Do not start fire event if the fire needs to be removed (< 0 duration) - if (duration > 0) { + if (duration.toMillis() > 0) { EventDispatcher.callCancellable(entityFireEvent, () -> { final long fireTime = entityFireEvent.getFireTime(TimeUnit.MILLISECOND); setOnFire(true); @@ -659,7 +670,7 @@ public class LivingEntity extends Entity implements EquipmentHandler { * Gets the time in ms between two fire damage applications. * * @return the time in ms - * @see #setFireDamagePeriod(long, TimeUnit) + * @see #setFireDamagePeriod(Duration) */ public long getFireDamagePeriod() { return fireDamagePeriod; @@ -669,11 +680,19 @@ public class LivingEntity extends Entity implements EquipmentHandler { * Changes the delay between two fire damage applications. * * @param fireDamagePeriod the delay - * @param timeUnit the time unit + * @param temporalUnit the time unit */ - public void setFireDamagePeriod(long fireDamagePeriod, @NotNull TimeUnit timeUnit) { - fireDamagePeriod = timeUnit.toMilliseconds(fireDamagePeriod); - this.fireDamagePeriod = fireDamagePeriod; + public void setFireDamagePeriod(long fireDamagePeriod, @NotNull TemporalUnit temporalUnit) { + setFireDamagePeriod(Duration.of(fireDamagePeriod, temporalUnit)); + } + + /** + * Changes the delay between two fire damage applications. + * + * @param fireDamagePeriod the delay + */ + public void setFireDamagePeriod(Duration fireDamagePeriod) { + this.fireDamagePeriod = fireDamagePeriod.toMillis(); } /** diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 0b875b1fd..ccd89ab08 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -76,7 +76,6 @@ import net.minestom.server.utils.instance.InstanceUtils; import net.minestom.server.utils.inventory.PlayerInventoryUtils; 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 net.minestom.server.world.DimensionType; import org.jetbrains.annotations.ApiStatus; @@ -84,6 +83,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -157,7 +157,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private final Set targetBreakers = Collections.singleton(this); // Experience orb pickup - protected Cooldown experiencePickupCooldown = new Cooldown(new UpdateOption(10, TimeUnit.TICK)); + protected Cooldown experiencePickupCooldown = new Cooldown(Duration.of(10, TimeUnit.SERVER_TICK)); private BelowNameTag belowNameTag; 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 88dcfd66e..df309307d 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 @@ -9,10 +9,11 @@ import net.minestom.server.entity.EntityProjectile; import net.minestom.server.utils.Position; 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; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.function.Function; /** @@ -20,16 +21,14 @@ import java.util.function.Function; */ public class CombinedAttackGoal extends GoalSelector { - private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private final Cooldown cooldown = new Cooldown(Duration.of(5, TimeUnit.SERVER_TICK)); private final int meleeRangeSquared; - private final int meleeDelay; - private final TimeUnit meleeTimeUnit; + private final Duration meleeDelay; private final int rangedRangeSquared; private final double rangedPower; private final double rangedSpread; - private final int rangedDelay; - private final TimeUnit rangedTimeUnit; + private final Duration rangedDelay; private final int desirableRangeSquared; private final boolean comeClose; @@ -52,7 +51,7 @@ public class CombinedAttackGoal extends GoalSelector { */ public CombinedAttackGoal(@NotNull EntityCreature entityCreature, int meleeRange, int rangedRange, double rangedPower, double rangedSpread, - int delay, TimeUnit timeUnit, + int delay, TemporalUnit timeUnit, int desirableRange, boolean comeClose) { this( entityCreature, @@ -62,6 +61,28 @@ public class CombinedAttackGoal extends GoalSelector { ); } + /** + * @param entityCreature the entity to add the goal to. + * @param meleeRange the allowed range the entity can hit others in melee. + * @param rangedRange the allowed range the entity can shoot others. + * @param rangedPower shot power (1 for normal). + * @param rangedSpread shot spread (0 for best accuracy). + * @param delay the delay between any attacks. + * @param desirableRange the desirable range: the entity will try to stay no further than this distance. + * @param comeClose if entity should go as close as possible to the target whether target is not in line of sight for a ranged attack. + */ + public CombinedAttackGoal(@NotNull EntityCreature entityCreature, + int meleeRange, int rangedRange, double rangedPower, double rangedSpread, + Duration delay, + int desirableRange, boolean comeClose) { + this( + entityCreature, + meleeRange, delay, + rangedRange, rangedPower, rangedSpread, delay, + desirableRange, comeClose + ); + } + /** * @param entityCreature the entity to add the goal to. * @param meleeRange the allowed range the entity can hit others in melee. @@ -76,18 +97,35 @@ public class CombinedAttackGoal extends GoalSelector { * @param comeClose if entity should go as close as possible to the target whether target is not in line of sight for a ranged attack. */ public CombinedAttackGoal(@NotNull EntityCreature entityCreature, - int meleeRange, int meleeDelay, TimeUnit meleeTimeUnit, - int rangedRange, double rangedPower, double rangedSpread, int rangedDelay, TimeUnit rangedTimeUnit, + int meleeRange, int meleeDelay, TemporalUnit meleeTimeUnit, + int rangedRange, double rangedPower, double rangedSpread, int rangedDelay, TemporalUnit rangedTimeUnit, + int desirableRange, boolean comeClose) { + this(entityCreature, meleeRange, Duration.of(meleeDelay, meleeTimeUnit), rangedRange, rangedPower, rangedSpread, + Duration.of(rangedDelay, rangedTimeUnit), desirableRange, comeClose); + } + + /** + * @param entityCreature the entity to add the goal to. + * @param meleeRange the allowed range the entity can hit others in melee. + * @param meleeDelay the delay between melee attacks. + * @param rangedRange the allowed range the entity can shoot others. + * @param rangedPower shot power (1 for normal). + * @param rangedSpread shot spread (0 for best accuracy). + * @param rangedDelay the delay between ranged attacks. + * @param desirableRange the desirable range: the entity will try to stay no further than this distance. + * @param comeClose if entity should go as close as possible to the target whether target is not in line of sight for a ranged attack. + */ + public CombinedAttackGoal(@NotNull EntityCreature entityCreature, + int meleeRange, Duration meleeDelay, + int rangedRange, double rangedPower, double rangedSpread, Duration rangedDelay, int desirableRange, boolean comeClose) { super(entityCreature); this.meleeRangeSquared = meleeRange * meleeRange; this.meleeDelay = meleeDelay; - this.meleeTimeUnit = meleeTimeUnit; this.rangedRangeSquared = rangedRange * rangedRange; this.rangedPower = rangedPower; this.rangedSpread = rangedSpread; this.rangedDelay = rangedDelay; - this.rangedTimeUnit = rangedTimeUnit; this.desirableRangeSquared = desirableRange * desirableRange; this.comeClose = comeClose; Check.argCondition(desirableRange > rangedRange, "Desirable range can not exceed ranged range!"); @@ -129,12 +167,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 (!Cooldown.hasCooldown(time, this.lastAttack, this.meleeTimeUnit, this.meleeDelay)) { + if (!Cooldown.hasCooldown(time, this.lastAttack, this.meleeDelay)) { this.entityCreature.attack(target, true); this.lastAttack = time; } } else if (distanceSquared <= this.rangedRangeSquared) { - if (!Cooldown.hasCooldown(time, this.lastAttack, this.rangedTimeUnit, this.rangedDelay)) { + if (!Cooldown.hasCooldown(time, this.lastAttack, 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); diff --git a/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java b/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java index 4dbe90ecd..47a895279 100644 --- a/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java +++ b/src/main/java/net/minestom/server/entity/ai/goal/FollowTargetGoal.java @@ -6,12 +6,13 @@ import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.Position; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; +import java.time.Duration; + public class FollowTargetGoal extends GoalSelector { - private final UpdateOption pathUpdateOption; + private final Duration pathDuration; private long lastUpdateTime = 0; private boolean forceEnd = false; private Position lastTargetPos; @@ -21,10 +22,24 @@ public class FollowTargetGoal extends GoalSelector { * * @param entityCreature the entity * @param pathUpdateOption the time between each path update (to check if the target moved) + * + * @deprecated Replaced by {@link #FollowTargetGoal(EntityCreature, Duration)} */ - public FollowTargetGoal(@NotNull EntityCreature entityCreature, @NotNull UpdateOption pathUpdateOption) { + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public FollowTargetGoal(@NotNull EntityCreature entityCreature, @NotNull net.minestom.server.utils.time.UpdateOption pathUpdateOption) { + this(entityCreature, pathUpdateOption.toDuration()); + } + + /** + * Creates a follow target goal object. + * + * @param entityCreature the entity + * @param pathDuration the time between each path update (to check if the target moved) + */ + public FollowTargetGoal(@NotNull EntityCreature entityCreature, @NotNull Duration pathDuration) { super(entityCreature); - this.pathUpdateOption = pathUpdateOption; + this.pathDuration = pathDuration; } @Override @@ -64,8 +79,8 @@ public class FollowTargetGoal extends GoalSelector { @Override public void tick(long time) { if (forceEnd || - pathUpdateOption.getValue() == 0 || - pathUpdateOption.getTimeUnit().toMilliseconds(pathUpdateOption.getValue()) + lastUpdateTime > time) { + pathDuration.isZero() || + pathDuration.toMillis() + lastUpdateTime > time) { return; } Position targetPos = entityCreature.getTarget() != null ? entityCreature.getTarget().getPosition() : null; 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 dec9fc85b..761d8ecb1 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 @@ -8,21 +8,22 @@ import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.utils.Position; 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; +import java.time.Duration; +import java.time.temporal.TemporalUnit; + /** * Attacks the entity's target ({@link EntityCreature#getTarget()}) OR the closest entity * which can be targeted with the entity {@link TargetSelector}. */ public class MeleeAttackGoal extends GoalSelector { - private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private final Cooldown cooldown = new Cooldown(Duration.of(5, TimeUnit.SERVER_TICK)); private long lastHit; private final double range; - private final int delay; - private final TimeUnit timeUnit; + private final Duration delay; private boolean stop; private Entity cachedTarget; @@ -33,11 +34,19 @@ public class MeleeAttackGoal extends GoalSelector { * @param delay the delay between each attacks * @param timeUnit the unit of the delay */ - public MeleeAttackGoal(@NotNull EntityCreature entityCreature, double range, int delay, @NotNull TimeUnit timeUnit) { + public MeleeAttackGoal(@NotNull EntityCreature entityCreature, double range, int delay, @NotNull TemporalUnit timeUnit) { + this(entityCreature, range, Duration.of(delay, timeUnit)); + } + + /** + * @param entityCreature the entity to add the goal to + * @param range the allowed range the entity can attack others. + * @param delay the delay between each attacks + */ + public MeleeAttackGoal(@NotNull EntityCreature entityCreature, double range, Duration delay) { super(entityCreature); this.range = range; this.delay = delay; - this.timeUnit = timeUnit; } public @NotNull Cooldown getCooldown() { @@ -72,7 +81,7 @@ public class MeleeAttackGoal extends GoalSelector { // Attack the target entity if (entityCreature.getDistance(target) <= range) { - if (!Cooldown.hasCooldown(time, lastHit, timeUnit, delay)) { + if (!Cooldown.hasCooldown(time, lastHit, delay)) { entityCreature.attack(target, true); this.lastHit = time; } 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 4332ac8bd..e0af845e8 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 @@ -9,19 +9,19 @@ import net.minestom.server.entity.EntityProjectile; import net.minestom.server.utils.Position; 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; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.function.Function; public class RangedAttackGoal extends GoalSelector { - private final Cooldown cooldown = new Cooldown(new UpdateOption(5, TimeUnit.TICK)); + private final Cooldown cooldown = new Cooldown(Duration.of(5, TimeUnit.SERVER_TICK)); private long lastShot; - private final int delay; - private final TimeUnit timeUnit; + private final Duration delay; private final int attackRangeSquared; private final int desirableRangeSquared; private final boolean comeClose; @@ -43,10 +43,22 @@ public class RangedAttackGoal extends GoalSelector { * @param power shot power (1 for normal). * @param timeUnit the unit of the delay. */ - public RangedAttackGoal(@NotNull EntityCreature entityCreature, int delay, int attackRange, int desirableRange, boolean comeClose, double power, double spread, @NotNull TimeUnit timeUnit) { + public RangedAttackGoal(@NotNull EntityCreature entityCreature, int delay, int attackRange, int desirableRange, boolean comeClose, double power, double spread, @NotNull TemporalUnit timeUnit) { + this(entityCreature, Duration.of(delay, timeUnit), attackRange, desirableRange, comeClose, power, spread); + } + + /** + * @param entityCreature the entity to add the goal to. + * @param delay the delay between each shots. + * @param attackRange the allowed range the entity can shoot others. + * @param desirableRange the desirable range: the entity will try to stay no further than this distance. + * @param comeClose whether entity should go as close as possible to the target whether target is not in line of sight. + * @param spread shot spread (0 for best accuracy). + * @param power shot power (1 for normal). + */ + public RangedAttackGoal(@NotNull EntityCreature entityCreature, Duration delay, int attackRange, int desirableRange, boolean comeClose, double power, double spread) { super(entityCreature); this.delay = delay; - this.timeUnit = timeUnit; this.attackRangeSquared = attackRange * attackRange; this.desirableRangeSquared = desirableRange * desirableRange; this.comeClose = comeClose; @@ -90,7 +102,7 @@ public class RangedAttackGoal extends GoalSelector { double distanceSquared = this.entityCreature.getDistanceSquared(target); boolean comeClose = false; if (distanceSquared <= this.attackRangeSquared) { - if (!Cooldown.hasCooldown(time, this.lastShot, this.timeUnit, this.delay)) { + if (!Cooldown.hasCooldown(time, this.lastShot, this.delay)) { if (this.entityCreature.hasLineOfSight(target)) { Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D); diff --git a/src/main/java/net/minestom/server/entity/fakeplayer/FakePlayer.java b/src/main/java/net/minestom/server/entity/fakeplayer/FakePlayer.java index 52acf40c6..ad4584280 100644 --- a/src/main/java/net/minestom/server/entity/fakeplayer/FakePlayer.java +++ b/src/main/java/net/minestom/server/entity/fakeplayer/FakePlayer.java @@ -151,7 +151,7 @@ public class FakePlayer extends Player implements NavigableEntity { private void handleTabList(PlayerConnection connection) { if (!option.isInTabList()) { // Remove from tab-list - MinecraftServer.getSchedulerManager().buildTask(() -> connection.sendPacket(getRemovePlayerToList())).delay(20, TimeUnit.TICK).schedule(); + MinecraftServer.getSchedulerManager().buildTask(() -> connection.sendPacket(getRemovePlayerToList())).delay(20, TimeUnit.SERVER_TICK).schedule(); } } } diff --git a/src/main/java/net/minestom/server/entity/hologram/Hologram.java b/src/main/java/net/minestom/server/entity/hologram/Hologram.java index fc18fa61d..0c680ff95 100644 --- a/src/main/java/net/minestom/server/entity/hologram/Hologram.java +++ b/src/main/java/net/minestom/server/entity/hologram/Hologram.java @@ -20,8 +20,10 @@ import java.util.Set; public class Hologram implements Viewable { private static final float OFFSET_Y = -0.9875f; + private static final float MARKER_OFFSET_Y = -0.40625f; private final Entity entity; + private final float yOffset; private Position position; private Component text; @@ -75,13 +77,31 @@ public class Hologram implements Viewable { * @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}. */ public Hologram(Instance instance, Position spawnPosition, Component text, boolean autoViewable) { + this(instance, spawnPosition, text, autoViewable, false); + } + + /** + * Constructs a new {@link Hologram} with the given parameters. + * + * @param instance The instance where the hologram should be spawned. + * @param spawnPosition The spawn position of this hologram. + * @param text The text of this hologram. + * @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}. + */ + public Hologram(Instance instance, Position spawnPosition, Component text, boolean autoViewable, boolean marker) { this.entity = new Entity(EntityType.ARMOR_STAND); ArmorStandMeta armorStandMeta = (ArmorStandMeta) entity.getEntityMeta(); armorStandMeta.setNotifyAboutChanges(false); - armorStandMeta.setSmall(true); + if(marker) { + this.yOffset = MARKER_OFFSET_Y; + armorStandMeta.setMarker(true); + } else { + this.yOffset = OFFSET_Y; + armorStandMeta.setSmall(true); + } armorStandMeta.setHasNoGravity(true); armorStandMeta.setCustomName(Component.empty()); armorStandMeta.setCustomNameVisible(true); @@ -89,7 +109,7 @@ public class Hologram implements Viewable { armorStandMeta.setNotifyAboutChanges(true); - this.entity.setInstance(instance, spawnPosition.clone().add(0, OFFSET_Y, 0)); + this.entity.setInstance(instance, spawnPosition.clone().add(0, this.yOffset, 0)); this.entity.setAutoViewable(autoViewable); this.position = spawnPosition; @@ -112,7 +132,7 @@ public class Hologram implements Viewable { */ public void setPosition(Position position) { checkRemoved(); - position.add(0, OFFSET_Y, 0); + position.add(0, this.yOffset, 0); this.position = position; this.entity.teleport(position); } diff --git a/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java b/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java index 5ffdad699..3eac073d4 100644 --- a/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java +++ b/src/main/java/net/minestom/server/event/entity/EntityFireEvent.java @@ -3,37 +3,37 @@ package net.minestom.server.event.entity; import net.minestom.server.entity.Entity; import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.event.trait.EntityEvent; -import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; +import java.time.Duration; +import java.time.temporal.TemporalUnit; + public class EntityFireEvent implements EntityEvent, CancellableEvent { private final Entity entity; - private int duration; - private TimeUnit timeUnit; + private Duration duration; private boolean cancelled; - public EntityFireEvent(Entity entity, int duration, TimeUnit timeUnit) { + public EntityFireEvent(Entity entity, int duration, TemporalUnit temporalUnit) { + this(entity, Duration.of(duration, temporalUnit)); + } + + public EntityFireEvent(Entity entity, Duration duration) { this.entity = entity; - setFireTime(duration, timeUnit); + setFireTime(duration); } - public long getFireTime(TimeUnit timeUnit) { - switch (timeUnit) { - case TICK: - return duration; - case MILLISECOND: - return timeUnit.toMilliseconds(duration); - default: - // Unexpected - return -1; - } + public long getFireTime(TemporalUnit temporalUnit) { + return duration.toNanos() / temporalUnit.getDuration().toNanos(); } - public void setFireTime(int duration, TimeUnit timeUnit) { + public void setFireTime(int duration, TemporalUnit temporalUnit) { + setFireTime(Duration.of(duration, temporalUnit)); + } + + public void setFireTime(Duration duration) { this.duration = duration; - this.timeUnit = timeUnit; } @Override diff --git a/src/main/java/net/minestom/server/event/server/ClientPingServerEvent.java b/src/main/java/net/minestom/server/event/server/ClientPingServerEvent.java index f1cddd191..b6df76970 100644 --- a/src/main/java/net/minestom/server/event/server/ClientPingServerEvent.java +++ b/src/main/java/net/minestom/server/event/server/ClientPingServerEvent.java @@ -3,9 +3,10 @@ package net.minestom.server.event.server; import net.minestom.server.event.trait.CancellableEvent; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; +import java.time.Duration; + /** * Called when a {@link PlayerConnection} sends a ping packet, @@ -14,13 +15,13 @@ import org.jetbrains.annotations.NotNull; * @see ServerListPingEvent */ public class ClientPingServerEvent implements CancellableEvent { - private static final UpdateOption DEFAULT_DELAY = new UpdateOption(0, TimeUnit.MILLISECOND); + private static final Duration DEFAULT_DELAY = Duration.of(0, TimeUnit.MILLISECOND); private final PlayerConnection connection; private long payload; private boolean cancelled = false; - private UpdateOption delay; + private Duration delay; /** * Creates a new client ping server event with 0 delay @@ -40,13 +41,24 @@ public class ClientPingServerEvent implements CancellableEvent { * @param connection the player connection * @param payload the payload the client sent */ - public ClientPingServerEvent(@NotNull PlayerConnection connection, long payload, UpdateOption delay) { + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public ClientPingServerEvent(@NotNull PlayerConnection connection, long payload, net.minestom.server.utils.time.UpdateOption delay) { + this(connection, payload, delay.toDuration()); + } + + /** + * Creates a new client ping server event with 0 delay + * + * @param connection the player connection + * @param payload the payload the client sent + */ + public ClientPingServerEvent(@NotNull PlayerConnection connection, long payload, Duration delay) { this.connection = connection; this.payload = payload; this.delay = delay; } - /** * PlayerConnection of received packet. Note that the player has not joined the server * at this time. @@ -82,7 +94,7 @@ public class ClientPingServerEvent implements CancellableEvent { * * @return the delay */ - public @NotNull UpdateOption getDelay() { + public @NotNull Duration getDelay() { return delay; } @@ -90,9 +102,35 @@ public class ClientPingServerEvent implements CancellableEvent { * Adds to the delay until minestom will send the ping response packet. * * @param delay the delay + * + * @deprecated Replaced by {@link #addDelay(Duration)} */ - public void addDelay(@NotNull UpdateOption delay) { - this.delay = new UpdateOption(this.delay.toMilliseconds() + delay.toMilliseconds(), TimeUnit.MILLISECOND); + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public void addDelay(@NotNull net.minestom.server.utils.time.UpdateOption delay) { + addDelay(delay.toDuration()); + } + + /** + * Adds to the delay until minestom will send the ping response packet. + * + * @param delay the delay + */ + public void addDelay(@NotNull Duration delay) { + this.delay = this.delay.plus(delay); + } + + /** + * Sets the delay until minestom will send the ping response packet. + * + * @param delay the delay + * + * @deprecated Replaced by {@link #setDelay(Duration)} + */ + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public void setDelay(@NotNull net.minestom.server.utils.time.UpdateOption delay) { + setDelay(delay.toDuration()); } /** @@ -100,7 +138,7 @@ public class ClientPingServerEvent implements CancellableEvent { * * @param delay the delay */ - public void setDelay(@NotNull UpdateOption delay) { + public void setDelay(@NotNull Duration delay) { this.delay = delay; } diff --git a/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java b/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java index 85a713ea8..350b05b83 100644 --- a/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java +++ b/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java @@ -83,7 +83,7 @@ public class OpenToLAN { eventCooldown = new Cooldown(config.delayBetweenEvent); task = MinecraftServer.getSchedulerManager().buildTask(OpenToLAN::ping) - .repeat(config.delayBetweenPings.getValue(), config.delayBetweenPings.getTimeUnit()) + .repeat(config.delayBetweenPings) .schedule(); return true; } diff --git a/src/main/java/net/minestom/server/extras/lan/OpenToLANConfig.java b/src/main/java/net/minestom/server/extras/lan/OpenToLANConfig.java index 5290567e0..61a5e5d6a 100644 --- a/src/main/java/net/minestom/server/extras/lan/OpenToLANConfig.java +++ b/src/main/java/net/minestom/server/extras/lan/OpenToLANConfig.java @@ -2,10 +2,10 @@ package net.minestom.server.extras.lan; import net.minestom.server.event.server.ServerListPingEvent; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.util.Objects; /** @@ -15,7 +15,7 @@ import java.util.Objects; */ public class OpenToLANConfig { int port; - UpdateOption delayBetweenPings, delayBetweenEvent; + Duration delayBetweenPings, delayBetweenEvent; /** * Creates a new config with the port set to random and the delay between pings set @@ -23,8 +23,8 @@ public class OpenToLANConfig { */ public OpenToLANConfig() { this.port = 0; - this.delayBetweenPings = new UpdateOption(1500, TimeUnit.MILLISECOND); - this.delayBetweenEvent = new UpdateOption(30, TimeUnit.SECOND); + this.delayBetweenPings = Duration.of(1500, TimeUnit.MILLISECOND); + this.delayBetweenEvent = Duration.of(30, TimeUnit.SECOND); } /** @@ -46,7 +46,7 @@ public class OpenToLANConfig { * @return {@code this}, for chaining */ @Contract("_ -> this") - public @NotNull OpenToLANConfig pingDelay(@NotNull UpdateOption delay) { + public @NotNull OpenToLANConfig pingDelay(@NotNull Duration delay) { this.delayBetweenPings = Objects.requireNonNull(delay, "delay"); return this; } @@ -58,7 +58,7 @@ public class OpenToLANConfig { * @return {@code this}, for chaining */ @Contract("_ -> this") - public @NotNull OpenToLANConfig eventCallDelay(@NotNull UpdateOption delay) { + public @NotNull OpenToLANConfig eventCallDelay(@NotNull Duration delay) { this.delayBetweenEvent = Objects.requireNonNull(delay, "delay"); return this; } diff --git a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java index 98c1d56e8..b7dd8288d 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java @@ -42,12 +42,16 @@ public class MinestomRootClassLoader extends HierarchyClassLoader { }; public final Set protectedPackages = new HashSet<>() { { - add("com.google"); + add("com.google.j2objc"); + add("com.google.common"); // guava + add("com.google.errorprone"); + add("com.google.gson"); add("com.mojang"); add("org.objectweb.asm"); add("org.slf4j"); - add("org.apache"); - add("org.spongepowered"); + add("org.apache.logging"); + add("org.spongepowered.asm"); // Mixin + add("org.spongepowered.tools"); // Mixin add("net.minestom.server.extras.selfmodification"); add("org.jboss.shrinkwrap.resolver"); add("kotlin"); diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 00f4a14c2..831f46b7d 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -20,13 +20,13 @@ 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.Cooldown; -import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import net.minestom.server.world.biomes.Biome; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.SoftReference; +import java.time.Duration; import java.util.Set; /** @@ -143,10 +143,10 @@ public class DynamicChunk extends Chunk { final CustomBlock customBlock = getCustomBlock(index); // Update cooldown - final UpdateOption updateOption = customBlock.getUpdateOption(); - if (updateOption != null) { + final Duration updateFrequency = customBlock.getUpdateFrequency(); + if (updateFrequency != null) { final long lastUpdate = updatableBlocksLastUpdate.get(index); - final boolean hasCooldown = Cooldown.hasCooldown(time, lastUpdate, updateOption); + final boolean hasCooldown = Cooldown.hasCooldown(time, lastUpdate, updateFrequency); 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 cae571ff2..32534ccf4 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -39,7 +39,6 @@ import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.entity.EntityUtils; 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 net.minestom.server.world.DimensionType; import org.jetbrains.annotations.ApiStatus; @@ -47,6 +46,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -80,7 +81,7 @@ public abstract class Instance implements BlockModifier, Tickable, TagHandler, P // The time of the instance private long time; private int timeRate = 1; - private UpdateOption timeUpdate = new UpdateOption(1, TimeUnit.SECOND); + private Duration timeUpdate = Duration.of(1, TimeUnit.SECOND); private long lastTimeUpdate; // Field for tick events @@ -416,7 +417,7 @@ public abstract class Instance implements BlockModifier, Tickable, TagHandler, P * @return the client update rate for time related packet */ @Nullable - public UpdateOption getTimeUpdate() { + public Duration getTimeUpdate() { return timeUpdate; } @@ -428,7 +429,21 @@ public abstract class Instance implements BlockModifier, Tickable, TagHandler, P * * @param timeUpdate the new update rate concerning time */ - public void setTimeUpdate(@Nullable UpdateOption timeUpdate) { + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + public void setTimeUpdate(@Nullable net.minestom.server.utils.time.UpdateOption timeUpdate) { + setTimeUpdate(timeUpdate != null ? timeUpdate.toDuration() : null); + } + + /** + * Changes the rate at which the client is updated about the time + *

+ * Setting it to null means that the client will never know about time change + * (but will still change server-side) + * + * @param timeUpdate the new update rate concerning time + */ + public void setTimeUpdate(@Nullable Duration timeUpdate) { this.timeUpdate = timeUpdate; } @@ -1020,7 +1035,7 @@ public abstract class Instance implements BlockModifier, Tickable, TagHandler, P * @param unit in what unit is the time expressed * @param position the location of the block to update */ - public abstract void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position); + public abstract void scheduleUpdate(int time, @NotNull TemporalUnit unit, @NotNull BlockPosition position); /** * Performs a single tick in the instance, including scheduled tasks from {@link #scheduleNextTick(Consumer)}. diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index d8a7d5fe2..ffc209330 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -24,13 +24,13 @@ import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkSupplier; import net.minestom.server.utils.chunk.ChunkUtils; -import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.validate.Check; import net.minestom.server.world.DimensionType; import net.minestom.server.world.biomes.Biome; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.temporal.TemporalUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -754,7 +754,7 @@ public class InstanceContainer extends Instance { } @Override - public void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position) { + public void scheduleUpdate(int time, @NotNull TemporalUnit unit, @NotNull BlockPosition position) { final CustomBlock toUpdate = getCustomBlock(position); if (toUpdate == null) { return; diff --git a/src/main/java/net/minestom/server/instance/SharedInstance.java b/src/main/java/net/minestom/server/instance/SharedInstance.java index 19644b480..14a5c9710 100644 --- a/src/main/java/net/minestom/server/instance/SharedInstance.java +++ b/src/main/java/net/minestom/server/instance/SharedInstance.java @@ -6,10 +6,10 @@ import net.minestom.server.storage.StorageLocation; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; import net.minestom.server.utils.chunk.ChunkCallback; -import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.temporal.TemporalUnit; import java.util.Collection; import java.util.UUID; @@ -133,7 +133,7 @@ public class SharedInstance extends Instance { } @Override - public void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position) { + public void scheduleUpdate(int time, @NotNull TemporalUnit unit, @NotNull BlockPosition position) { this.instanceContainer.scheduleUpdate(time, unit, position); } diff --git a/src/main/java/net/minestom/server/instance/block/CustomBlock.java b/src/main/java/net/minestom/server/instance/block/CustomBlock.java index da9df7f7d..ff6a6aaa7 100644 --- a/src/main/java/net/minestom/server/instance/block/CustomBlock.java +++ b/src/main/java/net/minestom/server/instance/block/CustomBlock.java @@ -14,12 +14,12 @@ import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.network.packet.server.play.BlockBreakAnimationPacket; import net.minestom.server.utils.BlockPosition; -import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import java.time.Duration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -67,12 +67,12 @@ public abstract class CustomBlock { } /** - * Calling delay depends on {@link #getUpdateOption()} which should be overridden. + * Calling delay depends on {@link #getUpdateFrequency()} which should be overridden. * * @param instance the instance of the block * @param blockPosition the position of the block * @param data the data associated with the block - * @throws UnsupportedOperationException if {@link #getUpdateOption()} + * @throws UnsupportedOperationException if {@link #getUpdateFrequency()} * is not null but the update method is not overridden */ public void update(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data) { @@ -89,7 +89,7 @@ public abstract class CustomBlock { * @return the update option of the block, null if not any */ @Nullable - public UpdateOption getUpdateOption() { + public Duration getUpdateFrequency() { return null; } @@ -172,14 +172,14 @@ public abstract class CustomBlock { /** * Gets if this {@link CustomBlock} requires any tick update. * - * @return true if {@link #getUpdateOption()} is not null and the value is positive + * @return true if {@link #getUpdateFrequency()} is not null and the value is positive */ public boolean hasUpdate() { - final UpdateOption updateOption = getUpdateOption(); - if (updateOption == null) + final Duration updateFrequency = getUpdateFrequency(); + if (updateFrequency == null) return false; - return updateOption.getValue() > 0; + return !updateFrequency.isNegative() && !updateFrequency.isZero(); } diff --git a/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java b/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java index fe0dc537d..8a7c9f2c1 100644 --- a/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/AnvilInventory.java @@ -1,14 +1,20 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; public class AnvilInventory extends Inventory { private short repairCost; - public AnvilInventory(String title) { + public AnvilInventory(@NotNull Component title) { + super(InventoryType.ANVIL, title); + } + + public AnvilInventory(@NotNull String title) { super(InventoryType.ANVIL, title); } diff --git a/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java b/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java index e63cc9466..0c84abbcd 100644 --- a/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/BeaconInventory.java @@ -1,9 +1,11 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; import net.minestom.server.potion.PotionEffect; +import org.jetbrains.annotations.NotNull; public class BeaconInventory extends Inventory { @@ -11,7 +13,11 @@ public class BeaconInventory extends Inventory { private PotionEffect firstPotionEffect; private PotionEffect secondPotionEffect; - public BeaconInventory(String title) { + public BeaconInventory(@NotNull Component title) { + super(InventoryType.BEACON, title); + } + + public BeaconInventory(@NotNull String title) { super(InventoryType.BEACON, title); } diff --git a/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java b/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java index 6177f183a..ed0bc7d78 100644 --- a/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/BrewingStandInventory.java @@ -1,15 +1,21 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; public class BrewingStandInventory extends Inventory { private short brewTime; private short fuelTime; - public BrewingStandInventory(String title) { + public BrewingStandInventory(@NotNull Component title) { + super(InventoryType.BREWING_STAND, title); + } + + public BrewingStandInventory(@NotNull String title) { super(InventoryType.BREWING_STAND, title); } diff --git a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java index 7b5ef2aab..349af72c3 100644 --- a/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/EnchantmentTableInventory.java @@ -1,9 +1,11 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; import net.minestom.server.item.Enchantment; +import org.jetbrains.annotations.NotNull; public class EnchantmentTableInventory extends Inventory { @@ -12,7 +14,11 @@ public class EnchantmentTableInventory extends Inventory { private final short[] enchantmentShown = new short[EnchantmentSlot.values().length]; private final short[] enchantmentLevel = new short[EnchantmentSlot.values().length]; - public EnchantmentTableInventory(String title) { + public EnchantmentTableInventory(@NotNull Component title) { + super(InventoryType.ENCHANTMENT, title); + } + + public EnchantmentTableInventory(@NotNull String title) { super(InventoryType.ENCHANTMENT, title); } diff --git a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java index 6943fb8ce..9e357da99 100644 --- a/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/FurnaceInventory.java @@ -1,8 +1,10 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryProperty; import net.minestom.server.inventory.InventoryType; +import org.jetbrains.annotations.NotNull; public class FurnaceInventory extends Inventory { @@ -11,7 +13,11 @@ public class FurnaceInventory extends Inventory { private short progressArrow; private short maximumProgress; - public FurnaceInventory(String title) { + public FurnaceInventory(@NotNull Component title) { + super(InventoryType.FURNACE, title); + } + + public FurnaceInventory(@NotNull String title) { super(InventoryType.FURNACE, title); } diff --git a/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java b/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java index e7a521376..5db943fd4 100644 --- a/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java +++ b/src/main/java/net/minestom/server/inventory/type/VillagerInventory.java @@ -1,5 +1,6 @@ package net.minestom.server.inventory.type; +import net.kyori.adventure.text.Component; import net.minestom.server.entity.Player; import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; @@ -11,7 +12,12 @@ public class VillagerInventory extends Inventory { protected TradeListPacket tradeListPacket; - public VillagerInventory(String title) { + public VillagerInventory(@NotNull Component title) { + super(InventoryType.MERCHANT, title); + setupPacket(); + } + + public VillagerInventory(@NotNull String title) { super(InventoryType.MERCHANT, title); setupPacket(); } diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java index e7ee9ad7d..db2255560 100644 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java @@ -6,13 +6,13 @@ import net.minestom.server.item.ItemMetaBuilder; import net.minestom.server.potion.CustomPotionEffect; import net.minestom.server.potion.PotionType; import net.minestom.server.registry.Registries; -import net.minestom.server.utils.time.TimeUnit; 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; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -98,7 +98,7 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider - * Needs to be enabled with {@link #enable(UpdateOption)}. Memory can then be accessed with {@link #getUsedMemory()} + * Needs to be enabled with {@link #enable(Duration)}. Memory can then be accessed with {@link #getUsedMemory()} * and the CPUs usage with {@link #getResultMap()} or {@link #getCpuMonitoringMessage()}. *

* Be aware that this is not the most accurate method, you should use a proper java profiler depending on your needs. @@ -57,10 +57,10 @@ public final class BenchmarkManager { private long time; - public void enable(@NotNull UpdateOption updateOption) { + public void enable(@NotNull Duration duration) { Check.stateCondition(enabled, "A benchmark is already running, please disable it first."); - time = updateOption.getTimeUnit().toMilliseconds(updateOption.getValue()); + time = duration.toMillis(); final Thread thread = new Thread(null, () -> { diff --git a/src/main/java/net/minestom/server/network/packet/client/status/PingPacket.java b/src/main/java/net/minestom/server/network/packet/client/status/PingPacket.java index b5b1dd779..5e336a12f 100644 --- a/src/main/java/net/minestom/server/network/packet/client/status/PingPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/status/PingPacket.java @@ -25,7 +25,7 @@ public class PingPacket implements ClientPreplayPacket { if (clientPingEvent.isCancelled()) { connection.disconnect(); } else { - if (clientPingEvent.getDelay().toMilliseconds() == 0) { + if (clientPingEvent.getDelay().isZero()) { connection.sendPacket(new PongPacket(clientPingEvent.getPayload())); connection.disconnect(); } else { diff --git a/src/main/java/net/minestom/server/thread/ThreadProvider.java b/src/main/java/net/minestom/server/thread/ThreadProvider.java index c864ee001..666d4b04c 100644 --- a/src/main/java/net/minestom/server/thread/ThreadProvider.java +++ b/src/main/java/net/minestom/server/thread/ThreadProvider.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Phaser; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; @@ -30,6 +30,8 @@ public abstract class ThreadProvider { private final Set updatableEntities = ConcurrentHashMap.newKeySet(); private final Set removedEntities = ConcurrentHashMap.newKeySet(); + private final Phaser phaser = new Phaser(1); + public ThreadProvider(int threadCount) { this.threads = new ArrayList<>(threadCount); @@ -112,24 +114,23 @@ public abstract class ThreadProvider { * * @param time the tick time in milliseconds */ - public synchronized @NotNull CountDownLatch update(long time) { - CountDownLatch countDownLatch = new CountDownLatch(threads.size()); - for (TickThread thread : threads) { + public void updateAndAwait(long time) { + for (var entry : threadChunkMap.entrySet()) { + final TickThread thread = entry.getKey(); + final var chunkEntries = entry.getValue(); + if (chunkEntries == null || chunkEntries.isEmpty()) { + // Nothing to tick + continue; + } // Execute tick - thread.runnable.startTick(countDownLatch, () -> { - final var chunkEntries = threadChunkMap.get(thread); - if (chunkEntries == null || chunkEntries.isEmpty()) { - // Nothing to tick - Acquirable.refreshEntries(Collections.emptySet()); - return; - } - + this.phaser.register(); + thread.runnable.startTick(phaser, () -> { Acquirable.refreshEntries(chunkEntries); final ReentrantLock lock = thread.getLock(); lock.lock(); chunkEntries.forEach(chunkEntry -> { - Chunk chunk = chunkEntry.chunk; + final Chunk chunk = chunkEntry.chunk; if (!ChunkUtils.isLoaded(chunk)) return; chunk.tick(time); @@ -138,18 +139,18 @@ public abstract class ThreadProvider { for (Entity entity : entities) { if (lock.hasQueuedThreads()) { lock.unlock(); - // #acquire callbacks should be called here + // #acquire() callbacks should be called here lock.lock(); } entity.tick(time); } } }); - Acquirable.refreshEntries(Collections.emptySet()); lock.unlock(); + // #acquire() callbacks }); } - return countDownLatch; + this.phaser.arriveAndAwaitAdvance(); } /** diff --git a/src/main/java/net/minestom/server/thread/TickThread.java b/src/main/java/net/minestom/server/thread/TickThread.java index 0b4faab59..426dd58fb 100644 --- a/src/main/java/net/minestom/server/thread/TickThread.java +++ b/src/main/java/net/minestom/server/thread/TickThread.java @@ -4,7 +4,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Phaser; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; @@ -44,7 +44,6 @@ public class TickThread extends Thread { } protected static class BatchRunnable implements Runnable { - private static final AtomicReferenceFieldUpdater CONTEXT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(BatchRunnable.class, TickContext.class, "tickContext"); @@ -57,25 +56,20 @@ public class TickThread extends Thread { public void run() { Check.notNull(tickThread, "The linked BatchThread cannot be null!"); while (!stop) { - LockSupport.park(tickThread); - if (stop) - break; - TickContext localContext = tickContext; + final TickContext localContext = tickContext; // The context is necessary to control the tick rates - if (localContext == null) { - continue; + if (localContext != null) { + // Execute tick + CONTEXT_UPDATER.compareAndSet(this, localContext, null); + localContext.runnable.run(); + localContext.phaser.arriveAndDeregister(); } - - // Execute tick - localContext.runnable.run(); - - localContext.countDownLatch.countDown(); - CONTEXT_UPDATER.compareAndSet(this, localContext, null); + LockSupport.park(this); } } - protected void startTick(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) { - this.tickContext = new TickContext(countDownLatch, runnable); + protected void startTick(@NotNull Phaser phaser, @NotNull Runnable runnable) { + this.tickContext = new TickContext(phaser, runnable); LockSupport.unpark(tickThread); } @@ -85,13 +79,12 @@ public class TickThread extends Thread { } private static class TickContext { - private final CountDownLatch countDownLatch; + private final Phaser phaser; private final Runnable runnable; - private TickContext(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) { - this.countDownLatch = countDownLatch; + private TickContext(@NotNull Phaser phaser, @NotNull Runnable runnable) { + this.phaser = phaser; this.runnable = runnable; } } - } diff --git a/src/main/java/net/minestom/server/timer/SchedulerManager.java b/src/main/java/net/minestom/server/timer/SchedulerManager.java index 4c126be8d..bddfd29b8 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerManager.java +++ b/src/main/java/net/minestom/server/timer/SchedulerManager.java @@ -20,8 +20,8 @@ import java.util.stream.Collectors; * An object which manages all the {@link Task}'s. *

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

* Shutdown tasks are built with {@link #buildShutdownTask(Runnable)} and are executed, as the name implies, when the server stops. diff --git a/src/main/java/net/minestom/server/timer/TaskBuilder.java b/src/main/java/net/minestom/server/timer/TaskBuilder.java index bb7a03121..cf3af3b57 100644 --- a/src/main/java/net/minestom/server/timer/TaskBuilder.java +++ b/src/main/java/net/minestom/server/timer/TaskBuilder.java @@ -3,13 +3,15 @@ package net.minestom.server.timer; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minestom.server.extras.selfmodification.MinestomRootClassLoader; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; +import java.time.Duration; +import java.time.temporal.TemporalUnit; + /** * A builder which represents a fluent Object to schedule tasks. *

- * You can specify a delay with {@link #delay(long, TimeUnit)} or {@link #repeat(long, TimeUnit)} + * You can specify a delay with {@link #delay(long, TemporalUnit)} or {@link #repeat(long, TemporalUnit)} * and then schedule the {@link Task} with {@link #schedule()}. */ public class TaskBuilder { @@ -68,9 +70,8 @@ public class TaskBuilder { * @return this builder, for chaining */ @NotNull - public TaskBuilder delay(long time, @NotNull TimeUnit unit) { - this.delay = unit.toMilliseconds(time); - return this; + public TaskBuilder delay(long time, @NotNull TemporalUnit unit) { + return delay(Duration.of(time, unit)); } /** @@ -78,10 +79,25 @@ public class TaskBuilder { * * @param updateOption the UpdateOption for this builder. * @return this builder, for chaining + * + * @deprecated Replaced by {@link #delay(Duration)} + */ + @SuppressWarnings("removal") + @NotNull + @Deprecated(forRemoval = true) + public TaskBuilder delay(net.minestom.server.utils.time.UpdateOption updateOption) { + return delay(updateOption.toDuration()); + } + + /** + * Specifies that the {@link Task} should delay its execution by the specified amount of time. + * + * @param duration the Duration for this builder. + * @return this builder, for chaining */ @NotNull - public TaskBuilder delay(UpdateOption updateOption) { - this.delay = updateOption.toMilliseconds(); + public TaskBuilder delay(Duration duration) { + this.delay = duration.toMillis(); return this; } @@ -89,13 +105,12 @@ public class TaskBuilder { * Specifies that the {@link Task} should continue to run after waiting for the specified value until it is terminated. * * @param time The time until the repetition - * @param unit The {@link TimeUnit} for {@code time} + * @param unit The {@link TemporalUnit} for {@code time} * @return this builder, for chaining */ @NotNull - public TaskBuilder repeat(long time, @NotNull TimeUnit unit) { - this.repeat = unit.toMilliseconds(time); - return this; + public TaskBuilder repeat(long time, @NotNull TemporalUnit unit) { + return repeat(Duration.of(time, unit)); } /** @@ -103,10 +118,25 @@ public class TaskBuilder { * * @param updateOption the UpdateOption for this builder. * @return this builder, for chaining + * + * @deprecated Replaced by {@link #repeat(Duration)} + */ + @SuppressWarnings("removal") + @NotNull + @Deprecated(forRemoval = true) + public TaskBuilder repeat(net.minestom.server.utils.time.UpdateOption updateOption) { + return repeat(updateOption.toDuration()); + } + + /** + * Specifies that the {@link Task} should continue to run after waiting for the specified value until it is terminated. + * + * @param duration the Duration for this builder. + * @return this builder, for chaining */ @NotNull - public TaskBuilder repeat(UpdateOption updateOption) { - this.repeat = updateOption.toMilliseconds(); + public TaskBuilder repeat(Duration duration) { + this.repeat = duration.toMillis(); return this; } diff --git a/src/main/java/net/minestom/server/utils/Vector.java b/src/main/java/net/minestom/server/utils/Vector.java index 1833b8a7b..2357e0e92 100644 --- a/src/main/java/net/minestom/server/utils/Vector.java +++ b/src/main/java/net/minestom/server/utils/Vector.java @@ -327,8 +327,11 @@ public class Vector implements PublicCloneable { double angleCos = Math.cos(angle); double angleSin = Math.sin(angle); - this.y = angleCos * getY() - angleSin * getZ(); - this.z = angleSin * getY() + angleCos * getZ(); + double oldY = getY(); + double oldZ = getZ(); + + this.y = angleCos * oldY - angleSin * oldZ; + this.z = angleSin * oldY + angleCos * oldZ; return this; } @@ -349,8 +352,11 @@ public class Vector implements PublicCloneable { double angleCos = Math.cos(angle); double angleSin = Math.sin(angle); - this.x = angleCos * getX() + angleSin * getZ(); - this.z = -angleSin * getX() + angleCos * getZ(); + double oldX = getX(); + double oldZ = getZ(); + + this.x = angleCos * oldX + angleSin * oldZ; + this.z = -angleSin * oldX + angleCos * oldZ; return this; } @@ -371,8 +377,11 @@ public class Vector implements PublicCloneable { double angleCos = Math.cos(angle); double angleSin = Math.sin(angle); - this.x = angleCos * getX() - angleSin * getY(); - this.y = angleSin * getX() + angleCos * getY(); + double oldX = getX(); + double oldY = getY(); + + this.x = angleCos * oldX - angleSin * oldY; + this.y = angleSin * oldX + angleCos * oldY; return this; } diff --git a/src/main/java/net/minestom/server/utils/time/Cooldown.java b/src/main/java/net/minestom/server/utils/time/Cooldown.java index 1110da38b..0ee9efd9b 100644 --- a/src/main/java/net/minestom/server/utils/time/Cooldown.java +++ b/src/main/java/net/minestom/server/utils/time/Cooldown.java @@ -2,18 +2,30 @@ package net.minestom.server.utils.time; import org.jetbrains.annotations.NotNull; +import java.time.Duration; +import java.time.temporal.TemporalUnit; + public final class Cooldown { - private UpdateOption updateOption; + private final Duration duration; private long lastUpdate; + /** + * @deprecated Replaced by {@link #Cooldown(Duration)} + */ + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) public Cooldown(@NotNull UpdateOption updateOption) { - this.updateOption = updateOption; + this(updateOption.toDuration()); + } + + public Cooldown(Duration duration) { + this.duration = duration; this.lastUpdate = System.currentTimeMillis(); } - public UpdateOption getUpdateOption() { - return this.updateOption; + public Duration getDuration() { + return this.duration; } public void refreshLastUpdate(long lastUpdate) { @@ -21,7 +33,7 @@ public final class Cooldown { } public boolean isReady(long time) { - return !hasCooldown(time, lastUpdate, updateOption.getTimeUnit(), updateOption.getValue()); + return !hasCooldown(time, lastUpdate, duration); } /** @@ -29,13 +41,12 @@ public final class Cooldown { * * @param currentTime the current time in milliseconds * @param lastUpdate the last update in milliseconds - * @param timeUnit the time unit of the cooldown + * @param temporalUnit the time unit of the cooldown * @param cooldown the value of the cooldown * @return true if the cooldown is in progress, false otherwise */ - public static boolean hasCooldown(long currentTime, long lastUpdate, @NotNull TimeUnit timeUnit, long cooldown) { - final long cooldownMs = timeUnit.toMilliseconds(cooldown); - return currentTime - lastUpdate < cooldownMs; + public static boolean hasCooldown(long currentTime, long lastUpdate, @NotNull TemporalUnit temporalUnit, long cooldown) { + return hasCooldown(currentTime, lastUpdate, Duration.of(cooldown, temporalUnit)); } /** @@ -45,20 +56,37 @@ public final class Cooldown { * @param lastUpdate the last update in milliseconds * @param updateOption the cooldown * @return true if the cooldown is in progress, false otherwise + * + * @deprecated Replaced by {@link #hasCooldown(long, long, Duration)} */ + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) public static boolean hasCooldown(long currentTime, long lastUpdate, @NotNull UpdateOption updateOption) { - return hasCooldown(currentTime, lastUpdate, updateOption.getTimeUnit(), updateOption.getValue()); + return hasCooldown(currentTime, lastUpdate, updateOption.toDuration()); + } + + /** + * Gets if something is in cooldown based on the current time. + * + * @param currentTime the current time in milliseconds + * @param lastUpdate the last update in milliseconds + * @param duration the cooldown + * @return true if the cooldown is in progress, false otherwise + */ + public static boolean hasCooldown(long currentTime, long lastUpdate, @NotNull Duration duration) { + final long cooldownMs = duration.toMillis(); + return currentTime - lastUpdate < cooldownMs; } /** * Gets if something is in cooldown based on the current time ({@link System#currentTimeMillis()}). * * @param lastUpdate the last update in milliseconds - * @param timeUnit the time unit of the cooldown + * @param temporalUnit the time unit of the cooldown * @param cooldown the value of the cooldown * @return true if the cooldown is in progress, false otherwise */ - public static boolean hasCooldown(long lastUpdate, @NotNull TimeUnit timeUnit, int cooldown) { - return hasCooldown(System.currentTimeMillis(), lastUpdate, timeUnit, cooldown); + public static boolean hasCooldown(long lastUpdate, @NotNull TemporalUnit temporalUnit, int cooldown) { + return hasCooldown(System.currentTimeMillis(), lastUpdate, temporalUnit, cooldown); } } diff --git a/src/main/java/net/minestom/server/utils/time/Tick.java b/src/main/java/net/minestom/server/utils/time/Tick.java new file mode 100644 index 000000000..e749e2018 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/time/Tick.java @@ -0,0 +1,112 @@ +package net.minestom.server.utils.time; + +import net.minestom.server.MinecraftServer; + +import java.time.Duration; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalUnit; + +/** + * A TemporalUnit that represents one tick. + */ +public final class Tick implements TemporalUnit { + /** + * A TemporalUnit representing the server tick. This is defined using + * {@link MinecraftServer#TICK_MS}. + */ + public static Tick SERVER_TICKS = new Tick(MinecraftServer.TICK_MS); + + /** + * A TemporalUnit representing the client tick. This is always equal to 50ms. + */ + public static Tick CLIENT_TICKS = new Tick(50); + + private final long milliseconds; + private final int tps; + + /** + * Creates a new tick. + * + * @param length the length of the tick in milliseconds + */ + private Tick(long length) { + if (length <= 0) { + throw new IllegalArgumentException("length cannot be negative"); + } + + this.milliseconds = length; + this.tps = Math.toIntExact(Duration.ofSeconds(1).dividedBy(Duration.ofMillis(this.milliseconds))); + } + + /** + * Creates a duration from an amount of ticks. + * + * @param ticks the amount of ticks + * @return the duration + */ + public static Duration server(long ticks) { + return Duration.of(ticks, SERVER_TICKS); + } + + /** + * Creates a duration from an amount of client-side ticks. + * + * @param ticks the amount of ticks + * @return the duration + */ + public static Duration client(long ticks) { + return Duration.of(ticks, CLIENT_TICKS); + } + + /** + * Gets the number of whole ticks that occur in the provided duration. Note that this + * method returns an {@code int} as this is the unit that Minecraft stores ticks in. + * + * @param duration the duration + * @return the number of whole ticks in this duration + * @throws ArithmeticException if the duration is zero or an overflow occurs + */ + public int fromDuration(Duration duration) { + return Math.toIntExact(duration.dividedBy(this.getDuration())); + } + + /** + * Gets the whole number of these ticks that occur in one second. + * + * @return the number + */ + public int getTicksPerSecond() { + return this.tps; + } + + @Override + public Duration getDuration() { + return Duration.ofMillis(this.milliseconds); + } + + @Override + public boolean isDurationEstimated() { + return false; + } + + @Override + public boolean isDateBased() { + return false; + } + + @Override + public boolean isTimeBased() { + return true; + } + + @SuppressWarnings("unchecked") // following ChronoUnit#addTo + @Override + public R addTo(R temporal, long amount) { + return (R) temporal.plus(amount, this); + } + + @Override + public long between(Temporal start, Temporal end) { + return start.until(end, this); + } +} \ No newline at end of file diff --git a/src/main/java/net/minestom/server/utils/time/TimeUnit.java b/src/main/java/net/minestom/server/utils/time/TimeUnit.java index 0b9805391..cda2f73a2 100644 --- a/src/main/java/net/minestom/server/utils/time/TimeUnit.java +++ b/src/main/java/net/minestom/server/utils/time/TimeUnit.java @@ -1,34 +1,22 @@ package net.minestom.server.utils.time; -import net.minestom.server.MinecraftServer; - -public enum TimeUnit { - - TICK, DAY, HOUR, MINUTE, SECOND, MILLISECOND; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +public class TimeUnit { + public static final TemporalUnit DAY = ChronoUnit.DAYS; + public static final TemporalUnit HOUR = ChronoUnit.HOURS; + public static final TemporalUnit MINUTE = ChronoUnit.MINUTES; + public static final TemporalUnit SECOND = ChronoUnit.SECONDS; + public static final TemporalUnit MILLISECOND = ChronoUnit.MILLIS; + public static final TemporalUnit SERVER_TICK = Tick.SERVER_TICKS; + public static final TemporalUnit CLIENT_TICK = Tick.CLIENT_TICKS; /** - * Converts a value and its unit to milliseconds. - * - * @param value the time value - * @return the converted milliseconds based on the time value and the unit + * @deprecated Please use either {@link #SERVER_TICK} or {@link #CLIENT_TICK} */ - public long toMilliseconds(long value) { - switch (this) { - case TICK: - return MinecraftServer.TICK_MS * value; - case DAY: - return value * 86_400_000; - case HOUR: - return value * 3_600_000; - case MINUTE: - return value * 60_000; - case SECOND: - return value * 1000; - case MILLISECOND: - return value; - default: - return -1; // Unexpected - } - } + @Deprecated(forRemoval = true) + public static final TemporalUnit TICK = CLIENT_TICK; + private TimeUnit() { + } } diff --git a/src/main/java/net/minestom/server/utils/time/UpdateOption.java b/src/main/java/net/minestom/server/utils/time/UpdateOption.java index 7f186bc73..cc9faa457 100644 --- a/src/main/java/net/minestom/server/utils/time/UpdateOption.java +++ b/src/main/java/net/minestom/server/utils/time/UpdateOption.java @@ -2,16 +2,22 @@ package net.minestom.server.utils.time; import org.jetbrains.annotations.NotNull; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.Objects; +/** + * @deprecated Replaced by {@link java.time.Duration} + */ +@Deprecated(forRemoval = true) public class UpdateOption { private final long value; - private final TimeUnit timeUnit; + private final TemporalUnit temporalUnit; - public UpdateOption(long value, @NotNull TimeUnit timeUnit) { + public UpdateOption(long value, @NotNull TemporalUnit temporalUnit) { this.value = value; - this.timeUnit = timeUnit; + this.temporalUnit = temporalUnit; } public long getValue() { @@ -19,13 +25,13 @@ public class UpdateOption { } @NotNull - public TimeUnit getTimeUnit() { - return timeUnit; + public TemporalUnit getTemporalUnit() { + return temporalUnit; } @Override public int hashCode() { - return Objects.hash(value, timeUnit); + return Objects.hash(value, temporalUnit); } @Override @@ -33,7 +39,7 @@ public class UpdateOption { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UpdateOption updateOption = (UpdateOption) o; - return Objects.equals(value, updateOption.value) && Objects.equals(timeUnit, updateOption.timeUnit); + return Objects.equals(value, updateOption.value) && Objects.equals(temporalUnit, updateOption.temporalUnit); } /** @@ -42,6 +48,10 @@ public class UpdateOption { * @return the converted milliseconds based on the time value and the unit */ public long toMilliseconds() { - return timeUnit.toMilliseconds(value); + return toDuration().toMillis(); + } + + public Duration toDuration() { + return Duration.of(value, temporalUnit); } } diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index c3cec28cc..8347e5018 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -22,7 +22,8 @@ import net.minestom.server.storage.StorageManager; import net.minestom.server.storage.systems.FileStorageSystem; import net.minestom.server.utils.identity.NamedAndIdentified; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; + +import java.time.Duration; public class Main { @@ -62,7 +63,7 @@ public class Main { StorageManager storageManager = MinecraftServer.getStorageManager(); storageManager.defineDefaultStorageSystem(FileStorageSystem::new); - MinecraftServer.getBenchmarkManager().enable(new UpdateOption(10 * 1000, TimeUnit.MILLISECOND)); + MinecraftServer.getBenchmarkManager().enable(Duration.of(10 * 1000, TimeUnit.MILLISECOND)); MinecraftServer.getSchedulerManager().buildShutdownTask(() -> System.out.println("Good night")).schedule(); @@ -110,7 +111,7 @@ public class Main { //MojangAuth.init(); // useful for testing - we don't need to worry about event calls so just set this to a long time - OpenToLAN.open(new OpenToLANConfig().eventCallDelay(new UpdateOption(1, TimeUnit.DAY))); + OpenToLAN.open(new OpenToLANConfig().eventCallDelay(Duration.of(1, TimeUnit.DAY))); minecraftServer.start("0.0.0.0", 25565); //Runtime.getRuntime().addShutdownHook(new Thread(MinecraftServer::stopCleanly)); diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index b4e15b9ba..615139f85 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -40,6 +40,7 @@ import net.minestom.server.utils.incubator.Vec; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.world.DimensionType; +import java.time.Duration; import java.util.Collection; import java.util.Random; import java.util.Set; @@ -82,7 +83,7 @@ public class PlayerInit { Position position = player.getPosition().clone().add(0, 1.5f, 0); ItemEntity itemEntity = new ItemEntity(droppedItem, position); - itemEntity.setPickupDelay(500, TimeUnit.MILLISECOND); + itemEntity.setPickupDelay(Duration.of(500, TimeUnit.MILLISECOND)); itemEntity.setInstance(player.getInstance()); Vector velocity = player.getPosition().clone().getDirection().multiply(6); itemEntity.setVelocity(velocity); @@ -188,7 +189,7 @@ public class PlayerInit { .append(Component.text("ACQ TIME: " + MathUtils.round(tickMonitor.getAcquisitionTime(), 2) + "ms")); final Component footer = benchmarkManager.getCpuMonitoringMessage(); Audiences.players().sendPlayerListHeaderAndFooter(header, footer); - }).repeat(10, TimeUnit.TICK).schedule(); + }).repeat(10, TimeUnit.SERVER_TICK).schedule(); } diff --git a/src/test/java/demo/blocks/CustomBlockSample.java b/src/test/java/demo/blocks/CustomBlockSample.java index 03c9388fd..336efcc8d 100644 --- a/src/test/java/demo/blocks/CustomBlockSample.java +++ b/src/test/java/demo/blocks/CustomBlockSample.java @@ -7,15 +7,15 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.Duration; import java.util.Set; public class CustomBlockSample extends CustomBlock { - private static final UpdateOption UPDATE_OPTION = new UpdateOption(3, TimeUnit.TICK); + private static final Duration DURATION = Duration.of(3, TimeUnit.SERVER_TICK); public CustomBlockSample() { super(Block.GOLD_BLOCK, "custom_block"); @@ -45,10 +45,9 @@ public class CustomBlockSample extends CustomBlock { //instance.refreshBlockStateId(blockPosition, (short) (blockId+1)); } - @Nullable @Override - public UpdateOption getUpdateOption() { - return UPDATE_OPTION; + public Duration getUpdateFrequency() { + return DURATION; } @Override diff --git a/src/test/java/demo/blocks/UpdatableBlockDemo.java b/src/test/java/demo/blocks/UpdatableBlockDemo.java index 4bf1a2c43..454f7f38f 100644 --- a/src/test/java/demo/blocks/UpdatableBlockDemo.java +++ b/src/test/java/demo/blocks/UpdatableBlockDemo.java @@ -7,14 +7,14 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.time.TimeUnit; -import net.minestom.server.utils.time.UpdateOption; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.util.Set; public class UpdatableBlockDemo extends CustomBlock { - private static final UpdateOption UPDATE_OPTION = new UpdateOption(20, TimeUnit.TICK); + private static final Duration DURATION = Duration.of(20, TimeUnit.SERVER_TICK); public UpdatableBlockDemo() { super(Block.DIRT, "updatable"); @@ -41,8 +41,8 @@ public class UpdatableBlockDemo extends CustomBlock { } @Override - public UpdateOption getUpdateOption() { - return UPDATE_OPTION; + public Duration getUpdateFrequency() { + return DURATION; } @Override