Merge branch 'master' into new-position-api

This commit is contained in:
TheMode 2021-07-04 17:40:18 +02:00
commit 1a55644c5e
50 changed files with 724 additions and 289 deletions

View File

@ -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) {

View File

@ -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)
*/

View File

@ -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;

View File

@ -20,6 +20,8 @@ public interface Acquirable<T> {
* Gets all the {@link Entity entities} being ticked in the current thread.
* <p>
* Useful when you want to ensure that no acquisition is ever done.
* <p>
* Be aware that the entity stream is only updated at the beginning of the thread tick.
*
* @return the entities ticked in the current thread
*/

View File

@ -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;

View File

@ -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).
* <p>
* Example: 50d, 25s, 75t
*/
public class ArgumentTime extends Argument<UpdateOption> {
public class ArgumentTime extends Argument<Duration> {
public static final int INVALID_TIME_FORMAT = -2;
public static final int NO_NUMBER = -3;
@ -28,12 +30,12 @@ public class ArgumentTime extends Argument<UpdateOption> {
@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<UpdateOption> {
} 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<UpdateOption> {
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);
}

View File

@ -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<EntityEvent>, 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<EntityEvent>, Da
* Sets the entity in fire visually.
* <p>
* 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<EntityEvent>, 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<EntityEvent>, 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<EntityEvent>, Da
return HoverEvent.showEntity(ShowEntity.of(this.entityType, this.uuid));
}
private UpdateOption getSynchronizationCooldown() {
private Duration getSynchronizationCooldown() {
return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN);
}

View File

@ -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();

View File

@ -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();
}
/**

View File

@ -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();
}
/**

View File

@ -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<Player> 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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -42,12 +42,16 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
};
public final Set<String> 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");

View File

@ -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;

View File

@ -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
* <p>
* 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)}.

View File

@ -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;

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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<Pot
for (NBTCompound potionCompound : customEffectList) {
final byte id = potionCompound.getAsByte("Id");
final byte amplifier = potionCompound.getAsByte("Amplifier");
final int duration = potionCompound.containsKey("Duration") ? potionCompound.getNumber("Duration").intValue() : (int) TimeUnit.SECOND.toMilliseconds(30);
final int duration = potionCompound.containsKey("Duration") ? potionCompound.getNumber("Duration").intValue() : (int) Duration.ofSeconds(30).toMillis();
final boolean ambient = potionCompound.containsKey("Ambient") ? potionCompound.getAsByte("Ambient") == 1 : false;
final boolean showParticles = potionCompound.containsKey("ShowParticles") ? potionCompound.getAsByte("ShowParticles") == 1 : true;
final boolean showIcon = potionCompound.containsKey("ShowIcon") ? potionCompound.getAsByte("ShowIcon") == 1 : true;

View File

@ -6,6 +6,7 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.metadata.other.ArmorStandMeta;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.PlayerBlockInteractEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
@ -132,6 +133,14 @@ public class BlockPlacementListener {
entity.getEntityType() == EntityType.ITEM)
continue;
// Marker Armor Stands should not prevent block placement
if(entity.getEntityMeta() instanceof ArmorStandMeta) {
ArmorStandMeta armorStandMeta = (ArmorStandMeta) entity.getEntityMeta();
if(armorStandMeta.isMarker()) {
continue;
}
}
intersect = entity.getBoundingBox().intersect(blockPosition);
if (intersect)
break;

View File

@ -7,13 +7,13 @@ import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.time.UpdateOption;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -25,7 +25,7 @@ import static net.minestom.server.MinecraftServer.*;
/**
* Small monitoring tools that can be used to check the current memory usage and Minestom threads CPU usage.
* <p>
* 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()}.
* <p>
* 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, () -> {

View File

@ -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 {

View File

@ -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<Entity> updatableEntities = ConcurrentHashMap.newKeySet();
private final Set<Entity> 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();
}
/**

View File

@ -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<BatchRunnable, TickContext> 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;
}
}
}

View File

@ -20,8 +20,8 @@ import java.util.stream.Collectors;
* An object which manages all the {@link Task}'s.
* <p>
* {@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()}.
* <p>
* Shutdown tasks are built with {@link #buildShutdownTask(Runnable)} and are executed, as the name implies, when the server stops.

View File

@ -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.
* <p>
* 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;
}

View File

@ -327,8 +327,11 @@ public class Vector implements PublicCloneable<Vector> {
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<Vector> {
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<Vector> {
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;
}

View File

@ -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);
}
}

View File

@ -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 extends Temporal> 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);
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}

View File

@ -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));

View File

@ -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();
}

View File

@ -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

View File

@ -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