diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java index 0ec885b97d65..1dc68bb994ce 100644 --- a/paper-api/src/main/java/org/bukkit/Server.java +++ b/paper-api/src/main/java/org/bukkit/Server.java @@ -2761,4 +2761,36 @@ default boolean isOwnedByCurrentRegion(@NotNull org.bukkit.block.Block block) { */ void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value); // Paper end - API to check if the server is sleeping + + /** + * Retrieves the stopwatch associated with this key. + * + * @param key The key + * @return The stopwatch if found, null otherwise + */ + @Nullable Stopwatch getStopwatch(@NotNull NamespacedKey key); + + /** + * Get all stopwatches. + * + * @return all stopwatches + */ + @NotNull Set getStopwatches(); + + /** + * Creates a new stopwatch with the specified key. + * + * @param key The namespaced key to associate the stopwatch with + * @return A new stopwatch + */ + @NotNull Stopwatch addStopwatch(@NotNull NamespacedKey key); + + /** + * Remove a stopwatch. + * + * @param key The key associated with the stopwatch. + * @return true if the stopwatch was removed, false otherwise + */ + boolean removeStopwatch(@NotNull NamespacedKey key); + } diff --git a/paper-api/src/main/java/org/bukkit/Stopwatch.java b/paper-api/src/main/java/org/bukkit/Stopwatch.java new file mode 100644 index 000000000000..068bd67a4afd --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/Stopwatch.java @@ -0,0 +1,48 @@ +package org.bukkit; + +/** + * Represents a stopwatch that measures real-world elapsed time, independent of Minecraft server ticks.
+ * Stopwatches use Java Virtual Machine's high-resolution time source to measure time. It can be accessed with the + * {@link System#nanoTime()} method. + */ +public interface Stopwatch extends Keyed { + + /** + * Returns the value, in milliseconds, of the Java Virtual Machine's high-resolution time source as it was at the + * moment this object was created. + * + * @return Timestamp at creation + */ + long creationTime(); + + /** + * Retrieves the accumulated time in milliseconds. + * + * @return Accumulated time + */ + long accumulatedElapsedTime(); + + /** + * Calculates the elapsed time in milliseconds between the start of the stopwatch + * and the specified timestamp. + * + * @param time A value, in milliseconds, of the Java Virtual Machine's high-resolution time source + * @return The elapsed time in milliseconds since the stopwatch was started and the given timestamp. + */ + long elapsedMilliseconds(long time); + + /** + * Calculates the elapsed time in seconds between the start of the stopwatch + * and the specified timestamp. + * + * @param time A value, in milliseconds, of the Java Virtual Machine's high-resolution time source + * @return The elapsed time in seconds since the stopwatch was started and the given timestamp. + */ + double elapsedSeconds(long time); + + /** + * Restart the stopwatch. + */ + void restart(); + +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index b2961752b50e..2ce10fff7d41 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -77,6 +77,7 @@ import net.minecraft.util.GsonHelper; import net.minecraft.util.datafix.DataFixers; import net.minecraft.world.Difficulty; +import net.minecraft.world.Stopwatches; import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.village.VillageSiege; @@ -125,6 +126,7 @@ import org.bukkit.Server; import org.bukkit.ServerLinks; import org.bukkit.ServerTickManager; +import org.bukkit.Stopwatch; import org.bukkit.StructureType; import org.bukkit.UnsafeValues; import org.bukkit.Warning.WarningState; @@ -2964,4 +2966,43 @@ public boolean isPaused() { public void allowPausing(final Plugin plugin, final boolean value) { this.console.addPluginAllowingSleep(plugin.getName(), value); } + + @Override + public @Nullable Stopwatch getStopwatch(final @NotNull NamespacedKey key) { + final net.minecraft.world.Stopwatch stopwatch = this.getServer().getStopwatches() + .get(CraftNamespacedKey.toMinecraft(key)); + if (stopwatch == null) + return null; + return new CraftStopwatch( + stopwatch, + this.getServer(), + key + ); + } + + @Override + public @NotNull Set getStopwatches() { + final Stopwatches stopwatches = this.getServer().getStopwatches(); + return stopwatches.ids().stream().map(id -> new CraftStopwatch( + stopwatches.get(id), + this.getServer(), + CraftNamespacedKey.fromMinecraft(id) + )).collect(Collectors.toSet()); + } + + @Override + public @NotNull Stopwatch addStopwatch(final @NotNull NamespacedKey key) { + final Identifier id = CraftNamespacedKey.toMinecraft(key); + final Stopwatches stopwatches = this.getServer().getStopwatches(); + stopwatches.add( + id, + new net.minecraft.world.Stopwatch(Stopwatches.currentTime()) + ); + return new CraftStopwatch(stopwatches.get(id), this.getServer(), key); + } + + @Override + public boolean removeStopwatch(final @NotNull NamespacedKey key) { + return this.getServer().getStopwatches().remove(CraftNamespacedKey.toMinecraft(key)); + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStopwatch.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStopwatch.java new file mode 100644 index 000000000000..de7dc13c7216 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStopwatch.java @@ -0,0 +1,58 @@ +package org.bukkit.craftbukkit; + + +import net.minecraft.resources.Identifier; +import net.minecraft.world.Stopwatches; +import org.bukkit.NamespacedKey; +import org.bukkit.Stopwatch; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.jetbrains.annotations.NotNull; + +public class CraftStopwatch implements Stopwatch { + + private net.minecraft.world.Stopwatch handle; + private final net.minecraft.server.MinecraftServer server; + private final NamespacedKey key; + + public CraftStopwatch(net.minecraft.world.Stopwatch handle, net.minecraft.server.MinecraftServer server, NamespacedKey key) { + this.handle = handle; + this.server = server; + this.key = key; + } + + @Override + public long creationTime() { + return handle.creationTime(); + } + + @Override + public long accumulatedElapsedTime() { + return handle.accumulatedElapsedTime(); + } + + @Override + public long elapsedMilliseconds(final long time) { + return handle.elapsedMilliseconds(time); + } + + @Override + public double elapsedSeconds(final long time) { + return handle.elapsedSeconds(time); + } + + @Override + public void restart() { + final Stopwatches stopwatches = server.getStopwatches(); + final Identifier id = CraftNamespacedKey.toMinecraft(key); + if (!stopwatches.update(id, + stopwatch -> new net.minecraft.world.Stopwatch(Stopwatches.currentTime()))) { + throw new UnsupportedOperationException("The stopwatch could not be restarted, because it was removed!"); + } + this.handle = stopwatches.get(id); + } + + @Override + public @NotNull NamespacedKey getKey() { + return key; + } +}