This commit is contained in:
Jake Potrebic 2024-04-28 23:49:32 +05:00 committed by GitHub
commit a48fbe800b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 1567 additions and 0 deletions

View File

@ -0,0 +1,832 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 20 May 2021 01:10:54 -0700
Subject: [PATCH] Better Stats API
diff --git a/src/main/java/io/papermc/paper/event/player/PlayerRequestStatisticsEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerRequestStatisticsEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce1b6ae06c411f0e346e3ccf5760ec5aa86e43e3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/PlayerRequestStatisticsEvent.java
@@ -0,0 +1,56 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.statistic.Statistic;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called when the player requests their statistics.
+ */
+public class PlayerRequestStatisticsEvent extends PlayerEvent implements Cancellable {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final Object2IntMap<Statistic<?>> statisticMap;
+ private boolean cancelled;
+
+ public PlayerRequestStatisticsEvent(@NotNull Player who, @NotNull Object2IntMap<Statistic<?>> statisticMap) {
+ super(who);
+ this.statisticMap = statisticMap;
+ }
+
+ /**
+ * Gets the statistic map to be sent to the player.
+ *
+ * @return the mutable statistic map
+ */
+ @NotNull
+ public Object2IntMap<Statistic<?>> getStatisticMap() {
+ return statisticMap;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/statistic/CustomStatistic.java b/src/main/java/io/papermc/paper/statistic/CustomStatistic.java
new file mode 100644
index 0000000000000000000000000000000000000000..abf3a7c06c90ed399e8f6da7eedfb9e6d257e544
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/CustomStatistic.java
@@ -0,0 +1,102 @@
+package io.papermc.paper.statistic;
+
+import net.kyori.adventure.translation.Translatable;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Custom statistic types.
+ */
+@ApiStatus.NonExtendable
+public interface CustomStatistic extends Keyed, Translatable {
+
+ CustomStatistic LEAVE_GAME = get("leave_game");
+ CustomStatistic PLAY_TIME = get("play_time");
+ CustomStatistic TOTAL_WORLD_TIME = get("total_world_time");
+ CustomStatistic TIME_SINCE_DEATH = get("time_since_death");
+ CustomStatistic TIME_SINCE_REST = get("time_since_rest");
+ CustomStatistic SNEAK_TIME = get("sneak_time");
+ CustomStatistic WALK_ONE_CM = get("walk_one_cm");
+ CustomStatistic CROUCH_ONE_CM = get("crouch_one_cm");
+ CustomStatistic SPRINT_ONE_CM = get("sprint_one_cm");
+ CustomStatistic WALK_ON_WATER_ONE_CM = get("walk_on_water_one_cm");
+ CustomStatistic FALL_ONE_CM = get("fall_one_cm");
+ CustomStatistic CLIMB_ONE_CM = get("climb_one_cm");
+ CustomStatistic FLY_ONE_CM = get("fly_one_cm");
+ CustomStatistic WALK_UNDER_WATER_ONE_CM = get("walk_under_water_one_cm");
+ CustomStatistic MINECART_ONE_CM = get("minecart_one_cm");
+ CustomStatistic BOAT_ONE_CM = get("boat_one_cm");
+ CustomStatistic PIG_ONE_CM = get("pig_one_cm");
+ CustomStatistic HORSE_ONE_CM = get("horse_one_cm");
+ CustomStatistic AVIATE_ONE_CM = get("aviate_one_cm");
+ CustomStatistic SWIM_ONE_CM = get("swim_one_cm");
+ CustomStatistic STRIDER_ONE_CM = get("strider_one_cm");
+ CustomStatistic JUMP = get("jump");
+ CustomStatistic DROP = get("drop");
+ CustomStatistic DAMAGE_DEALT = get("damage_dealt");
+ CustomStatistic DAMAGE_DEALT_ABSORBED = get("damage_dealt_absorbed");
+ CustomStatistic DAMAGE_DEALT_RESISTED = get("damage_dealt_resisted");
+ CustomStatistic DAMAGE_TAKEN = get("damage_taken");
+ CustomStatistic DAMAGE_BLOCKED_BY_SHIELD = get("damage_blocked_by_shield");
+ CustomStatistic DAMAGE_ABSORBED = get("damage_absorbed");
+ CustomStatistic DAMAGE_RESISTED = get("damage_resisted");
+ CustomStatistic DEATHS = get("deaths");
+ CustomStatistic MOB_KILLS = get("mob_kills");
+ CustomStatistic ANIMALS_BRED = get("animals_bred");
+ CustomStatistic PLAYER_KILLS = get("player_kills");
+ CustomStatistic FISH_CAUGHT = get("fish_caught");
+ CustomStatistic TALKED_TO_VILLAGER = get("talked_to_villager");
+ CustomStatistic TRADED_WITH_VILLAGER = get("traded_with_villager");
+ CustomStatistic EAT_CAKE_SLICE = get("eat_cake_slice");
+ CustomStatistic FILL_CAULDRON = get("fill_cauldron");
+ CustomStatistic USE_CAULDRON = get("use_cauldron");
+ CustomStatistic CLEAN_ARMOR = get("clean_armor");
+ CustomStatistic CLEAN_BANNER = get("clean_banner");
+ CustomStatistic CLEAN_SHULKER_BOX = get("clean_shulker_box");
+ CustomStatistic INTERACT_WITH_BREWINGSTAND = get("interact_with_brewingstand");
+ CustomStatistic INTERACT_WITH_BEACON = get("interact_with_beacon");
+ CustomStatistic INSPECT_DROPPER = get("inspect_dropper");
+ CustomStatistic INSPECT_HOPPER = get("inspect_hopper");
+ CustomStatistic INSPECT_DISPENSER = get("inspect_dispenser");
+ CustomStatistic PLAY_NOTEBLOCK = get("play_noteblock");
+ CustomStatistic TUNE_NOTEBLOCK = get("tune_noteblock");
+ CustomStatistic POT_FLOWER = get("pot_flower");
+ CustomStatistic TRIGGER_TRAPPED_CHEST = get("trigger_trapped_chest");
+ CustomStatistic OPEN_ENDERCHEST = get("open_enderchest");
+ CustomStatistic ENCHANT_ITEM = get("enchant_item");
+ CustomStatistic PLAY_RECORD = get("play_record");
+ CustomStatistic INTERACT_WITH_FURNACE = get("interact_with_furnace");
+ CustomStatistic INTERACT_WITH_CRAFTING_TABLE = get("interact_with_crafting_table");
+ CustomStatistic OPEN_CHEST = get("open_chest");
+ CustomStatistic SLEEP_IN_BED = get("sleep_in_bed");
+ CustomStatistic OPEN_SHULKER_BOX = get("open_shulker_box");
+ CustomStatistic OPEN_BARREL = get("open_barrel");
+ CustomStatistic INTERACT_WITH_BLAST_FURNACE = get("interact_with_blast_furnace");
+ CustomStatistic INTERACT_WITH_SMOKER = get("interact_with_smoker");
+ CustomStatistic INTERACT_WITH_LECTERN = get("interact_with_lectern");
+ CustomStatistic INTERACT_WITH_CAMPFIRE = get("interact_with_campfire");
+ CustomStatistic INTERACT_WITH_CARTOGRAPHY_TABLE = get("interact_with_cartography_table");
+ CustomStatistic INTERACT_WITH_LOOM = get("interact_with_loom");
+ CustomStatistic INTERACT_WITH_STONECUTTER = get("interact_with_stonecutter");
+ CustomStatistic BELL_RING = get("bell_ring");
+ CustomStatistic RAID_TRIGGER = get("raid_trigger");
+ CustomStatistic RAID_WIN = get("raid_win");
+ CustomStatistic INTERACT_WITH_ANVIL = get("interact_with_anvil");
+ CustomStatistic INTERACT_WITH_GRINDSTONE = get("interact_with_grindstone");
+ CustomStatistic TARGET_HIT = get("target_hit");
+ CustomStatistic INTERACT_WITH_SMITHING_TABLE = get("interact_with_smithing_table");
+
+ private static CustomStatistic get(final String key) {
+ return Registry.CUSTOM_STATISTIC.get(NamespacedKey.minecraft(key));
+ }
+
+ /**
+ * Gets the actual statistic for this custom stat.
+ *
+ * @return the actual statistic
+ */
+ @NotNull Statistic<CustomStatistic> statistic();
+}
diff --git a/src/main/java/io/papermc/paper/statistic/Statistic.java b/src/main/java/io/papermc/paper/statistic/Statistic.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cf244593dceb8e457e7faab72b26f31b3f2cd28
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/Statistic.java
@@ -0,0 +1,40 @@
+package io.papermc.paper.statistic;
+
+import org.bukkit.Keyed;
+import org.bukkit.scoreboard.Criteria;
+import org.bukkit.scoreboard.RenderType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents an individual statistic
+ *
+ * @param <S> stat (one of {@link org.bukkit.entity.EntityType}, {@link org.bukkit.Material} or {@link CustomStatistic}).
+ */
+@ApiStatus.NonExtendable
+public interface Statistic<S extends Keyed> extends Criteria {
+
+ /**
+ * Gets the statistic.
+ *
+ * @return the stat
+ */
+ @NotNull S value();
+
+ /**
+ * Get the stat type.
+ *
+ * @return the stat type
+ */
+ @NotNull StatisticType<S> type();
+
+ @Override
+ default boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ default @NotNull RenderType getDefaultRenderType() {
+ return RenderType.INTEGER;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/statistic/StatisticType.java b/src/main/java/io/papermc/paper/statistic/StatisticType.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3ffd55c3cabe66a9a2e9a1872c25000c06f7042
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/StatisticType.java
@@ -0,0 +1,60 @@
+package io.papermc.paper.statistic;
+
+import java.util.HashMap;
+import java.util.Map;
+import net.kyori.adventure.translation.Translatable;
+import org.bukkit.Keyed;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.entity.EntityType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+@ApiStatus.NonExtendable
+public interface StatisticType<S extends Keyed> extends Keyed, Translatable {
+
+ Map<NamespacedKey, StatisticType<?>> STATISTIC_TYPE_MAP = new HashMap<>();
+
+ StatisticType<Material> BLOCK_MINED = get("mined");
+ StatisticType<Material> ITEM_CRAFTED = get("crafted");
+ StatisticType<Material> ITEM_USED = get("used");
+ StatisticType<Material> ITEM_BROKEN = get("broken");
+ StatisticType<Material> ITEM_PICKED_UP = get("picked_up");
+ StatisticType<Material> ITEM_DROPPED = get("dropped");
+ StatisticType<EntityType> ENTITY_KILLED = get("killed");
+ StatisticType<EntityType> ENTITY_KILLED_BY = get("killed_by");
+ StatisticType<CustomStatistic> CUSTOM = get("custom");
+
+ @SuppressWarnings("unchecked")
+ private static <S extends Keyed> StatisticType<S> get(final String key) {
+ return (StatisticType<S>) Registry.STATISTIC_TYPE.get(NamespacedKey.minecraft(key));
+ }
+
+ /**
+ * Creates or gets the statistic from this type for the specified value.
+ *
+ * @param value what you want the stat of
+ * @return the statistic for that thing
+ * @throws IllegalArgumentException if the thing is not valid for this {@link StatisticType}
+ */
+ @NotNull Statistic<S> of(@NotNull S value);
+
+ /**
+ * Gets the registry associated with this stat type.
+ *
+ * @return the registry
+ */
+ @NotNull Registry<S> registry();
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * {@link StatisticType#CUSTOM} does <b>NOT</b> have a
+ * translation key.
+ * @throws IllegalArgumentException if used with {@link StatisticType#CUSTOM}
+ * @see CustomStatistic#translationKey()
+ */
+ @Override
+ @NotNull String translationKey();
+}
diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java
index bce07d84cafca677bb6fad78c21b82097f06430c..f3b41836057dacea93c5b084f97c1587148cfacf 100644
--- a/src/main/java/org/bukkit/OfflinePlayer.java
+++ b/src/main/java/org/bukkit/OfflinePlayer.java
@@ -270,6 +270,79 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @return last seen time
*/
public long getLastSeen();
+
+ /**
+ * Decrements the given stat for this player.
+ * <p>
+ * This is equivalent to the following code: {@code decrementStatistic(Statistic, 1)}
+ *
+ * @param statistic the stat to decrement
+ * @throws IllegalArgumentException if the stat is invalid OR decreasing the stat value would put the stat below 0
+ */
+ default void decrementStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic) {
+ this.decrementStatistic(statistic, 1);
+ }
+
+ /**
+ * Increments the given stat for this player.
+ * <p>
+ * This is equivalent to the following code: {@code incrementStatistic(Statistic, 1)}
+ *
+ * @param statistic the stat to increment
+ * @throws IllegalArgumentException if the stat is invalid
+ */
+ default void incrementStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic) {
+ this.incrementStatistic(statistic, 1);
+ }
+
+ /**
+ * Decrements the given stat for this player.
+ *
+ * @param statistic the stat to decrement
+ * @param amount the value to decrement by
+ * @throws IllegalArgumentException if the stat is invalid, the amount is nonpositive, or the stat
+ * would have a negative value after decrementing it
+ */
+ default void decrementStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic, int amount) {
+ this.incrementStatistic(statistic, -amount);
+ }
+
+ /**
+ * Increments the given stat for this player.
+ *
+ * @param statistic the stat to increment
+ * @param amount the amount to increment by
+ * @throws IllegalArgumentException if the stat is invalid or the amount is nonpositive
+ */
+ public void incrementStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic, int amount);
+
+ /**
+ * Sets the given stat for this player.
+ *
+ * @param statistic the stat to set
+ * @param newAmount the value to set the stat to
+ * @throws IllegalArgumentException if the stat is invalid or the amount is negative
+ */
+ public void setStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic, int newAmount);
+
+ /**
+ * Gets the given stat for this player.
+ *
+ * @param statistic the stat to get
+ * @return the amount for the stat
+ * @throws IllegalArgumentException if the stat is invalid
+ */
+ public int getStatistic(@NotNull io.papermc.paper.statistic.Statistic<?> statistic);
+
+ /**
+ * Get the formatted value for this stat. This is how the stat might
+ * appear in the client's statistic window
+ *
+ * @param statistic the stat to get the formatted value for
+ * @return the formatted value
+ * @throws IllegalArgumentException if the stat is invalid
+ */
+ public @NotNull String getFormattedValue(@NotNull io.papermc.paper.statistic.Statistic<?> statistic);
// Paper end
/**
@@ -282,7 +355,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if statistic is null
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException;
/**
@@ -295,7 +370,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if statistic is null
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException;
/**
@@ -307,7 +384,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException;
/**
@@ -319,7 +398,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException;
/**
@@ -331,7 +412,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if newValue is negative
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #setStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void setStatistic(@NotNull Statistic statistic, int newValue) throws IllegalArgumentException;
/**
@@ -342,7 +425,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if statistic is null
* @throws IllegalArgumentException if the statistic requires an
* additional parameter
+ * @deprecated use {@link #getStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public int getStatistic(@NotNull Statistic statistic) throws IllegalArgumentException;
/**
@@ -357,7 +442,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if material is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException;
/**
@@ -372,7 +459,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if material is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException;
/**
@@ -385,7 +474,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if material is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #getStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public int getStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException;
/**
@@ -399,7 +490,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException;
/**
@@ -413,7 +506,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException;
/**
@@ -427,7 +522,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if newValue is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #setStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void setStatistic(@NotNull Statistic statistic, @NotNull Material material, int newValue) throws IllegalArgumentException;
/**
@@ -442,7 +539,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if entityType is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException;
/**
@@ -457,7 +556,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if entityType is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException;
/**
@@ -470,7 +571,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if entityType is null
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #getStatistic(io.papermc.paper.statistic.Statistic)}
*/
+ @Deprecated // Paper
public int getStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException;
/**
@@ -484,7 +587,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount) throws IllegalArgumentException;
/**
@@ -498,7 +603,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if amount is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount);
/**
@@ -512,7 +619,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
* @throws IllegalArgumentException if newValue is negative
* @throws IllegalArgumentException if the given parameter is not valid
* for the statistic
+ * @deprecated use {@link #setStatistic(io.papermc.paper.statistic.Statistic, int)}
*/
+ @Deprecated // Paper
public void setStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int newValue);
/**
diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
index 800d23bb249e19d5cf924e7ba36684068624da02..f13315d196f2fda14ddeb5590b875ef6b4be890c 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
@@ -173,7 +173,9 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
* Server statistics.
*
* @see Statistic
+ * @deprecated use {@link #CUSTOM_STATISTIC} and {@link #STATISTIC_TYPE}
*/
+ @Deprecated(forRemoval = true) // Paper
Registry<Statistic> STATISTIC = new SimpleRegistry<>(Statistic.class);
/**
* Server structures.
@@ -296,6 +298,20 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
return StreamSupport.stream(this.spliterator(), false);
}
};
+ /**
+ * Custom statistics
+ *
+ * @see io.papermc.paper.statistic.CustomStatistic
+ */
+ Registry<io.papermc.paper.statistic.CustomStatistic> CUSTOM_STATISTIC = java.util.Objects.requireNonNull(org.bukkit.Bukkit.getRegistry(io.papermc.paper.statistic.CustomStatistic.class));
+
+ /**
+ * Statistic types
+ *
+ * @see io.papermc.paper.statistic.StatisticType
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ Registry<io.papermc.paper.statistic.StatisticType<?>> STATISTIC_TYPE = java.util.Objects.requireNonNull(org.bukkit.Bukkit.getRegistry((Class<io.papermc.paper.statistic.StatisticType<?>>) (Class) io.papermc.paper.statistic.StatisticType.class));
// Paper end
/**
diff --git a/src/main/java/org/bukkit/Statistic.java b/src/main/java/org/bukkit/Statistic.java
index 4ce888688d04eb0c4b2261a6474df870e7d2bb00..c2fe6c8e518a8a1b85c2582ae3887e3e09eccb36 100644
--- a/src/main/java/org/bukkit/Statistic.java
+++ b/src/main/java/org/bukkit/Statistic.java
@@ -5,7 +5,9 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a countable statistic, which is tracked by the server.
+ * @deprecated use {@link io.papermc.paper.statistic.StatisticType} and {@link io.papermc.paper.statistic.Statistic}
*/
+@Deprecated(forRemoval = true) // Paper
public enum Statistic implements Keyed {
DAMAGE_DEALT,
DAMAGE_TAKEN,
@@ -152,7 +154,9 @@ public enum Statistic implements Keyed {
/**
* The type of statistic.
*
+ * @deprecated use {@link io.papermc.paper.statistic.StatisticType}
*/
+ @Deprecated(forRemoval = true) // Paper
public enum Type {
/**
* Statistics of this type do not require a qualifier.
@@ -174,4 +178,65 @@ public enum Statistic implements Keyed {
*/
ENTITY;
}
+ // Paper start - add legacy conversion methods
+ @Deprecated(forRemoval = true)
+ public static Statistic toLegacy(final io.papermc.paper.statistic.Statistic<?> stat) {
+ if (stat.type() == io.papermc.paper.statistic.StatisticType.CUSTOM && stat.value() instanceof final io.papermc.paper.statistic.CustomStatistic customStatistic) {
+ if (customStatistic == io.papermc.paper.statistic.CustomStatistic.PLAY_TIME) { // special case cause upstream is wrong
+ return org.bukkit.Statistic.PLAY_ONE_MINUTE;
+ } else {
+ return java.util.Objects.requireNonNull(org.bukkit.Registry.STATISTIC.get(customStatistic.getKey()), "Couldn't convert " + stat + " to a legacy stat");
+ }
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.BLOCK_MINED) {
+ return Statistic.MINE_BLOCK;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ITEM_BROKEN) {
+ return Statistic.BREAK_ITEM;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ITEM_CRAFTED) {
+ return Statistic.CRAFT_ITEM;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ITEM_DROPPED) {
+ return Statistic.DROP;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ITEM_USED) {
+ return Statistic.USE_ITEM;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ITEM_PICKED_UP) {
+ return Statistic.PICKUP;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ENTITY_KILLED) {
+ return Statistic.KILL_ENTITY;
+ } else if (stat.type() == io.papermc.paper.statistic.StatisticType.ENTITY_KILLED_BY) {
+ return Statistic.ENTITY_KILLED_BY;
+ }
+ throw new IllegalArgumentException("Couldn't convert " + stat + " to a legacy stat");
+ }
+
+ @Deprecated(forRemoval = true)
+ public io.papermc.paper.statistic.Statistic<?> toModern(@org.jetbrains.annotations.Nullable org.bukkit.entity.EntityType entityType, @org.jetbrains.annotations.Nullable Material material) {
+ com.google.common.base.Preconditions.checkArgument(entityType == null || material == null, "No stat has an entity type and material value at the same time");
+ com.google.common.base.Preconditions.checkArgument(this.type != Type.UNTYPED || (entityType == null && material == null), "no value needed for untyped stats");
+ com.google.common.base.Preconditions.checkArgument(this.type != Type.ENTITY || entityType != null);
+ com.google.common.base.Preconditions.checkArgument(this.type != Type.BLOCK || material != null && material.isBlock());
+ com.google.common.base.Preconditions.checkArgument(this.type != Type.ITEM || material != null && material.isItem());
+ return switch (this.type) {
+ case UNTYPED -> {
+ if (this == PLAY_ONE_MINUTE) { // special case cause upstream is wrong
+ yield io.papermc.paper.statistic.CustomStatistic.PLAY_TIME.statistic();
+ } else {
+ yield java.util.Objects.requireNonNull(Registry.CUSTOM_STATISTIC.get(this.key), "Couldn't convert " + this + " to a modern stat").statistic();
+ }
+ }
+ case BLOCK -> io.papermc.paper.statistic.StatisticType.BLOCK_MINED.of(material);
+ case ITEM -> switch (this) {
+ case DROP -> io.papermc.paper.statistic.StatisticType.ITEM_DROPPED.of(material);
+ case BREAK_ITEM -> io.papermc.paper.statistic.StatisticType.ITEM_BROKEN.of(material);
+ case CRAFT_ITEM -> io.papermc.paper.statistic.StatisticType.ITEM_CRAFTED.of(material);
+ case USE_ITEM -> io.papermc.paper.statistic.StatisticType.ITEM_USED.of(material);
+ case PICKUP -> io.papermc.paper.statistic.StatisticType.ITEM_PICKED_UP.of(material);
+ default -> throw new IllegalArgumentException("Couldn't convert " + this + ", mat: " + material + " to a modern stat");
+ };
+ case ENTITY -> switch (this) {
+ case KILL_ENTITY -> io.papermc.paper.statistic.StatisticType.ENTITY_KILLED.of(entityType);
+ case ENTITY_KILLED_BY -> io.papermc.paper.statistic.StatisticType.ENTITY_KILLED_BY.of(entityType);
+ default -> throw new IllegalArgumentException("Couldn't convert " + this + ", entity_type: " + entityType + " to a modern stat");
+ };
+ };
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java b/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java
index f971844bf490c7a7bfbe305d33df739ed2197a37..ef36f8f1fe2b793870c6ca82eaf48612deb1e59d 100644
--- a/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java
@@ -18,48 +18,75 @@ import org.jetbrains.annotations.Nullable;
*/
public class PlayerStatisticIncrementEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
- protected final Statistic statistic;
+ private final io.papermc.paper.statistic.Statistic<?> statistic; // Paper
private final int initialValue;
private final int newValue;
private boolean isCancelled = false;
+ @Deprecated(forRemoval = true) // Paper
private final EntityType entityType;
+ @Deprecated(forRemoval = true) // Paper
private final Material material;
+ @Deprecated(forRemoval = true) // Paper
public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue) {
super(player);
- this.statistic = statistic;
+ this.statistic = statistic.toModern(null, null); // Paper
this.initialValue = initialValue;
this.newValue = newValue;
this.entityType = null;
this.material = null;
}
+ @Deprecated(forRemoval = true) // Paper
public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue, @NotNull EntityType entityType) {
super(player);
- this.statistic = statistic;
+ this.statistic = statistic.toModern(entityType, null); // Paper
this.initialValue = initialValue;
this.newValue = newValue;
this.entityType = entityType;
this.material = null;
}
+ @Deprecated(forRemoval = true) // Paper
public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue, @NotNull Material material) {
super(player);
- this.statistic = statistic;
+ this.statistic = statistic.toModern(null, material); // Paper
this.initialValue = initialValue;
this.newValue = newValue;
this.entityType = null;
this.material = material;
}
+ // Paper start
+ @org.jetbrains.annotations.ApiStatus.Internal
+ public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull io.papermc.paper.statistic.Statistic<?> statistic, int initialValue, int newValue) {
+ super(player);
+ this.statistic = statistic;
+ this.initialValue = initialValue;
+ this.newValue = newValue;
+ this.entityType = statistic.value() instanceof EntityType entityType ? entityType : null;
+ this.material = statistic.value() instanceof Material material ? material : null;
+ }
+
+ /**
+ * Gets the statistic that is being incremented.
+ *
+ * @return the incremented statistic
+ */
+ public @NotNull io.papermc.paper.statistic.Statistic<?> getStat() {
+ return this.statistic;
+ }
+ // Paper end
/**
* Gets the statistic that is being incremented.
*
* @return the incremented statistic
+ * @deprecated use {@link #getStat()}
*/
@NotNull
+ @Deprecated(forRemoval = true) // Paper
public Statistic getStatistic() {
- return statistic;
+ return Statistic.toLegacy(this.statistic); // Paper
}
/**
@@ -85,8 +112,10 @@ public class PlayerStatisticIncrementEvent extends PlayerEvent implements Cancel
* entity statistic otherwise returns null.
*
* @return the EntityType of the statistic
+ * @deprecated use {@link #getStat()}
*/
@Nullable
+ @Deprecated(forRemoval = true) // Paper
public EntityType getEntityType() {
return entityType;
}
@@ -96,8 +125,10 @@ public class PlayerStatisticIncrementEvent extends PlayerEvent implements Cancel
* or item statistic otherwise returns null.
*
* @return the Material of the statistic
+ * @deprecated use {@link #getStat()}
*/
@Nullable
+ @Deprecated(forRemoval = true) // Paper
public Material getMaterial() {
return material;
}
diff --git a/src/main/java/org/bukkit/scoreboard/Criteria.java b/src/main/java/org/bukkit/scoreboard/Criteria.java
index 3bc3abaf093d13e22b6ac2ee59ab584c92b4666a..56cf36b696062490de2adb68c796c5bb74732b0c 100644
--- a/src/main/java/org/bukkit/scoreboard/Criteria.java
+++ b/src/main/java/org/bukkit/scoreboard/Criteria.java
@@ -12,8 +12,8 @@ import org.jetbrains.annotations.NotNull;
* Represents a scoreboard criteria, either custom or built-in to the Minecraft server, used to
* keep track of and manually or automatically change scores on a scoreboard.
* <p>
- * While this class outlines constants for standard criteria, see {@link #statistic(Statistic)}
- * (and its overloads) to create instances for statistically-backed criteria.
+ * While this class outlines constants for standard criteria, see {@link io.papermc.paper.statistic.Statistic}
+ * for statistically-backed criteria.
*/
public interface Criteria {
@@ -241,8 +241,10 @@ public interface Criteria {
* {@link Material#isBlock()} is false
* @throws IllegalArgumentException if {@link Statistic#getType()} is {@link Type#ITEM}, but
* {@link Material#isItem()} is false
+ * @deprecated use {@link io.papermc.paper.statistic.Statistic}
*/
@NotNull
+ @Deprecated // Paper
public static Criteria statistic(@NotNull Statistic statistic, @NotNull Material material) {
Preconditions.checkArgument(statistic != null, "statistic must not be null");
Preconditions.checkArgument(material != null, "material must not be null");
@@ -298,8 +300,10 @@ public interface Criteria {
* @param entityType the relevant entity type
* @return the criteria
* @throws IllegalArgumentException if {@link Statistic#getType()} is not {@link Type#ENTITY}
+ * @deprecated use {@link io.papermc.paper.statistic.Statistic}
*/
@NotNull
+ @Deprecated(forRemoval = true) // Paper
public static Criteria statistic(@NotNull Statistic statistic, @NotNull EntityType entityType) {
Preconditions.checkArgument(statistic != null, "statistic must not be null");
Preconditions.checkArgument(entityType != null, "entityType must not be null");
@@ -331,8 +335,10 @@ public interface Criteria {
*
* @param statistic the statistic for which to get a criteria
* @return the criteria
+ * @deprecated Use {@link io.papermc.paper.statistic.Statistic}
*/
@NotNull
+ @Deprecated(forRemoval = true) // Paper
public static Criteria statistic(@NotNull Statistic statistic) {
Preconditions.checkArgument(statistic != null, "statistic must not be null");
return Bukkit.getScoreboardCriteria(org.bukkit.Bukkit.getUnsafe().getStatisticCriteriaKey(statistic)); // Paper

View File

@ -0,0 +1,735 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 20 May 2021 01:10:15 -0700
Subject: [PATCH] Better Stats API
== AT ==
public net.minecraft.stats.StatType map
diff --git a/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java b/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java
new file mode 100644
index 0000000000000000000000000000000000000000..c70300fb84918739190325946f26fd5231f3c0e0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java
@@ -0,0 +1,36 @@
+package io.papermc.paper.statistic;
+
+import net.minecraft.resources.ResourceLocation;
+import org.bukkit.NamespacedKey;
+import org.jetbrains.annotations.NotNull;
+
+public class PaperCustomStatistic implements CustomStatistic {
+
+ private final NamespacedKey key;
+ private final ResourceLocation nmsValue;
+
+ public PaperCustomStatistic(NamespacedKey key, ResourceLocation nmsValue) {
+ this.key = key;
+ this.nmsValue = nmsValue;
+ }
+
+ @Override
+ public @NotNull NamespacedKey getKey() {
+ return this.key;
+ }
+
+ @Override
+ public @NotNull String translationKey() {
+ return "stat." + this.nmsValue.toString().replace(':', '.');
+ }
+
+ @Override
+ public @NotNull Statistic<CustomStatistic> statistic() {
+ return StatisticType.CUSTOM.of(this);
+ }
+
+ @Override
+ public @NotNull String toString() {
+ return this.getKey().asString();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/statistic/PaperStatistic.java b/src/main/java/io/papermc/paper/statistic/PaperStatistic.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e44ffc5788883f659505db54258ed2a50f2aa15
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/PaperStatistic.java
@@ -0,0 +1,39 @@
+package io.papermc.paper.statistic;
+
+import net.minecraft.stats.Stat;
+import org.bukkit.Keyed;
+import org.jetbrains.annotations.NotNull;
+
+public class PaperStatistic<S extends Keyed, M> implements Statistic<S> {
+
+ private final Stat<M> handle;
+ private final S value;
+ private final M nmsValue;
+ private final StatisticType<S> type;
+
+ PaperStatistic(Stat<M> handle, @NotNull S value, M nmsValue, @NotNull StatisticType<S> type) {
+ this.handle = handle;
+ this.value = value;
+ this.nmsValue = nmsValue;
+ this.type = type;
+ }
+
+ @Override
+ public @NotNull S value() {
+ return this.value;
+ }
+
+ @Override
+ public @NotNull StatisticType<S> type() {
+ return this.type;
+ }
+
+ @Override
+ public @NotNull String getName() {
+ return Stat.buildName(this.handle.getType(),this.nmsValue);
+ }
+
+ public Stat<M> getHandle() {
+ return this.handle;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java b/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java
new file mode 100644
index 0000000000000000000000000000000000000000..2af5e257795f9b27028f9906676a1c8b75749b27
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java
@@ -0,0 +1,113 @@
+package io.papermc.paper.statistic;
+
+import com.google.common.base.Preconditions;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.stats.Stat;
+import net.minecraft.stats.StatType;
+import net.minecraft.stats.Stats;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.level.block.Block;
+import org.bukkit.Keyed;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.craftbukkit.entity.CraftEntityType;
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.bukkit.entity.EntityType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PaperStatisticType<S extends Keyed, M> implements StatisticType<S> {
+
+ private final NamespacedKey key;
+ private final StatType<M> nmsType;
+ private final Registry<S> registry;
+ private final @Nullable Function<S, M> toNms;
+ private final Map<S, Statistic<S>> statisticMap;
+ private final Predicate<S> typeCheck;
+
+ @SuppressWarnings("unchecked")
+ public static <M> StatisticType<?> create(final NamespacedKey key, final StatType<M> type) {
+ if (type == Stats.BLOCK_MINED) {
+ return new PaperStatisticType<>(key, (StatType<Block>) type, Registry.MATERIAL, CraftMagicNumbers::getBlock, Material::isBlock);
+ } else if (type == Stats.ITEM_CRAFTED || type == Stats.ITEM_USED || type == Stats.ITEM_BROKEN || type == Stats.ITEM_PICKED_UP || type == Stats.ITEM_DROPPED) {
+ return new PaperStatisticType<>(key, (StatType<Item>) type, Registry.MATERIAL, CraftMagicNumbers::getItem, Material::isItem);
+ } else if (type == Stats.ENTITY_KILLED || type == Stats.ENTITY_KILLED_BY) {
+ return new PaperStatisticType<>(key, (StatType<net.minecraft.world.entity.EntityType<?>>) type, Registry.ENTITY_TYPE, CraftEntityType::bukkitToMinecraft, t -> t != EntityType.UNKNOWN);
+ } else if (type == Stats.CUSTOM) {
+ return new PaperStatisticType<>(key, (StatType<ResourceLocation>) type, Registry.CUSTOM_STATISTIC, null);
+ } else {
+ throw new IllegalArgumentException("Did not recognize " + BuiltInRegistries.STAT_TYPE.getKey(type) + " as a statistic type");
+ }
+ }
+
+ private PaperStatisticType(NamespacedKey key, StatType<M> nmsType, Registry<S> registry, @Nullable Function<S, M> toNms) {
+ this(key, nmsType, registry, toNms, s -> true);
+ }
+
+ private PaperStatisticType(NamespacedKey key, StatType<M> nmsType, Registry<S> registry, @Nullable Function<S, M> toNms, Predicate<S> typeCheck) {
+ this.key = key;
+ this.nmsType = nmsType;
+ this.registry = registry;
+ this.toNms = toNms;
+ this.statisticMap = new IdentityHashMap<>();
+ this.typeCheck = typeCheck;
+ STATISTIC_TYPE_MAP.put(this.key, this);
+ }
+
+ public Statistic<S> of(S value) {
+ if (!this.typeCheck.test(value)) {
+ throw new IllegalArgumentException(value + " is not valid for " + this.getKey());
+ }
+ if (this == StatisticType.CUSTOM) {
+ return Objects.requireNonNull(this.statisticMap.get(value), "This should never be null as all custom stats should be present in this map upon initialization");
+ }
+ return this.statisticMap.computeIfAbsent(value, newValue -> {
+ final M nmsValue = Objects.requireNonNull(this.toNms).apply(value);
+ final Stat<M> nmsStat = this.nmsType.get(nmsValue);
+ return new PaperStatistic<>(nmsStat, value, nmsValue, this);
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public S registerCustomStatistic(S stat) {
+ if (this != StatisticType.CUSTOM) {
+ throw new IllegalArgumentException("Must be the CUSTOM_STATS stat type");
+ }
+ PaperStatisticType<CustomStatistic, ResourceLocation> cast = (PaperStatisticType<CustomStatistic, ResourceLocation>) this;
+ final Stat<ResourceLocation> nmsStat = cast.nmsType.get(CraftNamespacedKey.toMinecraft(stat.getKey()));
+ this.statisticMap.put(stat, new PaperStatistic<>(nmsStat, stat, CraftNamespacedKey.toMinecraft(stat.getKey()), this));
+ return stat;
+ }
+
+ @Override
+ public Registry<S> registry() {
+ return this.registry;
+ }
+
+ @Override
+ public NamespacedKey getKey() {
+ return this.key;
+ }
+
+ @Override
+ public String translationKey() {
+ Preconditions.checkArgument(this != StatisticType.CUSTOM, "CUSTOM_STATS does not have a translation key");
+ return "stat_type." + this.getKey().toString().replace(':', '.');
+ }
+
+ @Override
+ public String toString() {
+ return this.key.toString();
+ }
+
+}
diff --git a/src/main/java/io/papermc/paper/statistic/PaperStatistics.java b/src/main/java/io/papermc/paper/statistic/PaperStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..57bb4a61807c566036b1a594957c6ef243b1ce46
--- /dev/null
+++ b/src/main/java/io/papermc/paper/statistic/PaperStatistics.java
@@ -0,0 +1,99 @@
+package io.papermc.paper.statistic;
+
+import com.google.common.base.Preconditions;
+import java.util.Objects;
+import java.util.Set;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.stats.ServerStatsCounter;
+import net.minecraft.stats.Stat;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.level.block.Block;
+import org.bukkit.Material;
+import org.bukkit.Registry;
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class PaperStatistics {
+
+ private PaperStatistics() {
+ }
+
+ public static final Set<CustomStatistic> IGNORED_STATS_FOR_EVENT = Set.of(
+ CustomStatistic.FALL_ONE_CM,
+ CustomStatistic.BOAT_ONE_CM,
+ CustomStatistic.CLIMB_ONE_CM,
+ CustomStatistic.WALK_ON_WATER_ONE_CM,
+ CustomStatistic.WALK_UNDER_WATER_ONE_CM,
+ CustomStatistic.FLY_ONE_CM,
+ CustomStatistic.HORSE_ONE_CM,
+ CustomStatistic.MINECART_ONE_CM,
+ CustomStatistic.PIG_ONE_CM,
+ CustomStatistic.PLAY_TIME,
+ CustomStatistic.SWIM_ONE_CM,
+ CustomStatistic.WALK_ONE_CM,
+ CustomStatistic.SPRINT_ONE_CM,
+ CustomStatistic.CROUCH_ONE_CM,
+ CustomStatistic.TIME_SINCE_DEATH,
+ CustomStatistic.SNEAK_TIME,
+ CustomStatistic.TOTAL_WORLD_TIME,
+ CustomStatistic.TIME_SINCE_REST,
+ CustomStatistic.AVIATE_ONE_CM,
+ CustomStatistic.STRIDER_ONE_CM
+ );
+
+
+ public static void changeStatistic(ServerStatsCounter manager, Statistic<?> statistic, int delta) {
+ if (delta == 0) return;
+ Preconditions.checkNotNull(statistic, "statistic cannot be null");
+ final Stat<?> stat = getNMSStatistic(statistic);
+ //noinspection ConstantConditions
+ manager.setValue(null, stat, manager.getValue(stat) + delta);
+ }
+
+ public static void setStatistic(ServerStatsCounter manager, Statistic<?> statistic, int newAmount) {
+ Preconditions.checkNotNull(statistic, "Statistic cannot be null");
+ Preconditions.checkArgument(newAmount >= 0, "New amount must be greater than or equal to 0");
+ //noinspection ConstantConditions
+ manager.setValue(null, getNMSStatistic(statistic), newAmount);
+ }
+
+ public static int getStatistic(ServerStatsCounter manager, Statistic<?> statistic) {
+ Preconditions.checkNotNull(statistic, "Statistic cannot be null");
+ return manager.getValue(getNMSStatistic(statistic));
+ }
+
+ public static String getFormattedValue(ServerStatsCounter manager, Statistic<?> statistic) {
+ final Stat<?> nmsStat = getNMSStatistic(statistic);
+ return nmsStat.format(manager.getValue(nmsStat));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Statistic<?> getPaperStatistic(Stat<?> nmsStat) {
+ final ResourceLocation statTypeKey = Preconditions.checkNotNull(BuiltInRegistries.STAT_TYPE.getKey(nmsStat.getType()), "Could not get the stat type resource location from " + nmsStat);
+ final @Nullable StatisticType<?> type = Registry.STATISTIC_TYPE.get(CraftNamespacedKey.fromMinecraft(statTypeKey));
+ final Statistic<?> paperStat;
+ if (type == StatisticType.BLOCK_MINED) {
+ paperStat = StatisticType.BLOCK_MINED.of(CraftMagicNumbers.getMaterial((Block) nmsStat.getValue()));
+ } else if (type == StatisticType.ITEM_CRAFTED || type == StatisticType.ITEM_USED || type == StatisticType.ITEM_BROKEN || type == StatisticType.ITEM_PICKED_UP || type == StatisticType.ITEM_DROPPED) {
+ paperStat = ((StatisticType<Material>) type).of(CraftMagicNumbers.getMaterial((Item) nmsStat.getValue()));
+ } else if (type == StatisticType.ENTITY_KILLED) {
+ paperStat = StatisticType.ENTITY_KILLED.of(CraftMagicNumbers.getEntityType((net.minecraft.world.entity.EntityType<?>) nmsStat.getValue()));
+ } else if (type == StatisticType.ENTITY_KILLED_BY) {
+ paperStat = StatisticType.ENTITY_KILLED_BY.of(CraftMagicNumbers.getEntityType((net.minecraft.world.entity.EntityType<?>) nmsStat.getValue()));
+ } else if (type == StatisticType.CUSTOM) {
+ paperStat = StatisticType.CUSTOM.of(Objects.requireNonNull(Registry.CUSTOM_STATISTIC.get(CraftNamespacedKey.fromMinecraft((ResourceLocation) nmsStat.getValue()))));
+ } else {
+ throw new IllegalArgumentException("Did not recognize " + type + " as a statistic type");
+ }
+ return Objects.requireNonNull(paperStat, "Couldn't convert " + nmsStat + " to a stat of type " + type);
+ }
+
+ public static Stat<?> getNMSStatistic(Statistic<?> paperStat) {
+ return ((PaperStatistic<?, ?>) paperStat).getHandle();
+ }
+}
diff --git a/src/main/java/net/minecraft/stats/ServerStatsCounter.java b/src/main/java/net/minecraft/stats/ServerStatsCounter.java
index 9bb8d4d7be6a937980aa653db82be084d066a563..4bc706cda11509edd0bc8da21718ba8bbfc41f9f 100644
--- a/src/main/java/net/minecraft/stats/ServerStatsCounter.java
+++ b/src/main/java/net/minecraft/stats/ServerStatsCounter.java
@@ -245,6 +245,20 @@ public class ServerStatsCounter extends StatsCounter {
object2intmap.put(statistic, this.getValue(statistic));
}
+ // Paper start
+ if (io.papermc.paper.event.player.PlayerRequestStatisticsEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ io.papermc.paper.event.player.PlayerRequestStatisticsEvent statEvent = new io.papermc.paper.event.player.PlayerRequestStatisticsEvent(
+ player.getBukkitEntity(),
+ object2intmap.object2IntEntrySet()
+ .stream()
+ .collect(Object2IntOpenHashMap::new, (map, entry) -> map.put(io.papermc.paper.statistic.PaperStatistics.getPaperStatistic(entry.getKey()), entry.getIntValue()), Object2IntOpenHashMap::putAll)
+ );
+ if (!statEvent.callEvent()) {
+ return;
+ }
+ object2intmap = statEvent.getStatisticMap().object2IntEntrySet().stream().collect(Object2IntOpenHashMap::new, (map, entry) -> map.put(io.papermc.paper.statistic.PaperStatistics.getNMSStatistic(entry.getKey()), entry.getIntValue()), Object2IntOpenHashMap::putAll);
+ }
+ // Paper end
player.connection.send(new ClientboundAwardStatsPacket(object2intmap));
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 2bbc39c257965ad91ee360cdfcd3538a0f041c7e..2a0e49d6fcf63df10b56482462dcb025276a7e3b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -376,6 +376,48 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
return this.server.getHandle().getPlayerStats(this.getUniqueId(), this.getName());
}
+ // Paper start
+ @Override
+ public void incrementStatistic(io.papermc.paper.statistic.Statistic<?> statistic, int amount) {
+ if (this.isOnline()) {
+ this.getPlayer().incrementStatistic(statistic, amount);
+ } else {
+ ServerStatsCounter manager = getStatisticManager();
+ io.papermc.paper.statistic.PaperStatistics.changeStatistic(manager, statistic, amount);
+ manager.save();
+ }
+
+ }
+
+ @Override
+ public void setStatistic(io.papermc.paper.statistic.Statistic<?> statistic, int newAmount) {
+ if (this.isOnline()) {
+ this.getPlayer().setStatistic(statistic, newAmount);
+ } else {
+ ServerStatsCounter manager = getStatisticManager();
+ io.papermc.paper.statistic.PaperStatistics.setStatistic(manager, statistic, newAmount);
+ manager.save();
+ }
+ }
+
+ @Override
+ public int getStatistic(io.papermc.paper.statistic.Statistic<?> statistic) {
+ if (isOnline()) {
+ return this.getPlayer().getStatistic(statistic);
+ } else {
+ return io.papermc.paper.statistic.PaperStatistics.getStatistic(getStatisticManager(), statistic);
+ }
+ }
+
+ @Override
+ public String getFormattedValue(io.papermc.paper.statistic.Statistic<?> statistic) {
+ if (this.isOnline()) {
+ return this.getPlayer().getFormattedValue(statistic);
+ } else {
+ return io.papermc.paper.statistic.PaperStatistics.getFormattedValue(getStatisticManager(), statistic);
+ }
+ }
+ // Paper end
@Override
public void incrementStatistic(Statistic statistic) {
if (this.isOnline()) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 253b4cf66e94faf0bc8861318ae7549f52cd29d1..d3dfdbb7d1bbfc0e2a77a598ba03b2538a3ba76f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -79,6 +79,15 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
}
// Paper end
+ // Paper start - better stats API
+ if (bukkitClass == io.papermc.paper.statistic.StatisticType.class) {
+ return new CraftRegistry<>(io.papermc.paper.statistic.StatisticType.class, BuiltInRegistries.STAT_TYPE, io.papermc.paper.statistic.PaperStatisticType::create);
+ }
+ if (bukkitClass == io.papermc.paper.statistic.CustomStatistic.class) {
+ return new CraftRegistry<>(io.papermc.paper.statistic.CustomStatistic.class, BuiltInRegistries.CUSTOM_STAT, io.papermc.paper.statistic.PaperCustomStatistic::new);
+ }
+ // Paper end - better stats API
+
return null;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java
index d17b9f62ea78f6328200f5478bfa6bc70af7bb2b..8f8046900424c3b2e3dfa36af61a03e2cb7afcf4 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java
@@ -18,6 +18,7 @@ import org.bukkit.craftbukkit.entity.CraftEntityType;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.EntityType;
+@Deprecated(forRemoval = true) // Paper
public enum CraftStatistic {
DAMAGE_DEALT(Stats.DAMAGE_DEALT),
DAMAGE_TAKEN(Stats.DAMAGE_TAKEN),
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 3be5e4df190bff0087c8450b16e4e37b07169040..9cbfccbc432894aba3701717873c7be802ff14e6 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1507,6 +1507,27 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return bukkitRecipeKeys.build();
}
+ // Paper start
+ @Override
+ public void incrementStatistic(io.papermc.paper.statistic.Statistic<?> statistic, int amount) {
+ io.papermc.paper.statistic.PaperStatistics.changeStatistic(this.getHandle().getStats(), statistic, amount);
+ }
+
+ @Override
+ public void setStatistic(io.papermc.paper.statistic.Statistic<?> statistic, int newAmount) {
+ io.papermc.paper.statistic.PaperStatistics.setStatistic(this.getHandle().getStats(), statistic, newAmount);
+ }
+
+ @Override
+ public int getStatistic(io.papermc.paper.statistic.Statistic<?> statistic) {
+ return io.papermc.paper.statistic.PaperStatistics.getStatistic(this.getHandle().getStats(), statistic);
+ }
+
+ @Override
+ public String getFormattedValue(io.papermc.paper.statistic.Statistic<?> statistic) {
+ return io.papermc.paper.statistic.PaperStatistics.getFormattedValue(this.getHandle().getStats(), statistic);
+ }
+ // Paper end
@Override
public void incrementStatistic(Statistic statistic) {
CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, this.getHandle());
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index f67ec3f5f4b7e2f678609f2387cc8afa2adce161..421cc3b6c20af9f0325495b690c0a19bae2dd682 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1738,45 +1738,14 @@ public class CraftEventFactory {
Player player = ((ServerPlayer) entityHuman).getBukkitEntity();
Event event;
if (true) {
- org.bukkit.Statistic stat = CraftStatistic.getBukkitStatistic(statistic);
- if (stat == null) {
- System.err.println("Unhandled statistic: " + statistic);
- return null;
- }
- switch (stat) {
- case FALL_ONE_CM:
- case BOAT_ONE_CM:
- case CLIMB_ONE_CM:
- case WALK_ON_WATER_ONE_CM:
- case WALK_UNDER_WATER_ONE_CM:
- case FLY_ONE_CM:
- case HORSE_ONE_CM:
- case MINECART_ONE_CM:
- case PIG_ONE_CM:
- case PLAY_ONE_MINUTE:
- case SWIM_ONE_CM:
- case WALK_ONE_CM:
- case SPRINT_ONE_CM:
- case CROUCH_ONE_CM:
- case TIME_SINCE_DEATH:
- case SNEAK_TIME:
- case TOTAL_WORLD_TIME:
- case TIME_SINCE_REST:
- case AVIATE_ONE_CM:
- case STRIDER_ONE_CM:
+ // Paper start - better stats api
+ io.papermc.paper.statistic.Statistic<?> stat = io.papermc.paper.statistic.PaperStatistics.getPaperStatistic(statistic);
+ if (stat.value() instanceof io.papermc.paper.statistic.CustomStatistic customStatistic && io.papermc.paper.statistic.PaperStatistics.IGNORED_STATS_FOR_EVENT.contains(customStatistic)) {
// Do not process event for these - too spammy
return null;
- default:
- }
- if (stat.getType() == Type.UNTYPED) {
- event = new PlayerStatisticIncrementEvent(player, stat, current, newValue);
- } else if (stat.getType() == Type.ENTITY) {
- EntityType entityType = CraftStatistic.getEntityTypeFromStatistic((net.minecraft.stats.Stat<net.minecraft.world.entity.EntityType<?>>) statistic);
- event = new PlayerStatisticIncrementEvent(player, stat, current, newValue, entityType);
- } else {
- Material material = CraftStatistic.getMaterialFromStatistic(statistic);
- event = new PlayerStatisticIncrementEvent(player, stat, current, newValue, material);
}
+ event = new PlayerStatisticIncrementEvent(player, stat, current, newValue);
+ // Paper end - better stats api
}
entityHuman.level().getCraftServer().getPluginManager().callEvent(event);
return (Cancellable) event;
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
index 8464531a4ee400834d25c23b1eb723f49be8689e..64e7970de5cfb56309c71ce0eab0de4a34c86d9a 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
@@ -8,21 +8,21 @@ import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.RenderType;
public final class CraftCriteria implements Criteria {
- static final Map<String, CraftCriteria> DEFAULTS;
+ static final Map<String, Criteria> DEFAULTS; // Paper - stats api
static final CraftCriteria DUMMY;
static {
- ImmutableMap.Builder<String, CraftCriteria> defaults = ImmutableMap.builder();
+ ImmutableMap.Builder<String, Criteria> defaults = ImmutableMap.builder(); // Paper - stats api
for (Map.Entry<String, ObjectiveCriteria> entry : ObjectiveCriteria.CRITERIA_CACHE.entrySet()) {
String name = entry.getKey();
ObjectiveCriteria criteria = entry.getValue();
- defaults.put(name, new CraftCriteria(criteria));
+ defaults.put(name, convertFromNms(criteria)); // Paper - stats api
}
DEFAULTS = defaults.build();
- DUMMY = DEFAULTS.get("dummy");
+ DUMMY = (CraftCriteria) DEFAULTS.get("dummy"); // Paper - stats api
}
final ObjectiveCriteria criteria;
@@ -53,17 +53,23 @@ public final class CraftCriteria implements Criteria {
return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()];
}
- static CraftCriteria getFromNMS(Objective objective) {
- return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper
+ static Criteria getFromNMS(Objective objective) { // Paper - stats api
+ return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> convertFromNms(objective.getCriteria())); // Paper
}
- public static CraftCriteria getFromBukkit(String name) {
- CraftCriteria criteria = CraftCriteria.DEFAULTS.get(name);
+ // Paper start - stats api
+ static Criteria convertFromNms(ObjectiveCriteria criteria) {
+ return criteria instanceof net.minecraft.stats.Stat<?> stat ? io.papermc.paper.statistic.PaperStatistics.getPaperStatistic(stat) : new CraftCriteria(criteria);
+ }
+ // Paper end
+
+ public static Criteria getFromBukkit(String name) { // Paper - stats api
+ Criteria criteria = CraftCriteria.DEFAULTS.get(name); // Paper - stats api
if (criteria != null) {
return criteria;
}
- return ObjectiveCriteria.byName(name).map(CraftCriteria::new).orElseGet(() -> new CraftCriteria(name));
+ return ObjectiveCriteria.byName(name).map(CraftCriteria::convertFromNms).orElseGet(() -> new CraftCriteria(name)); // Paper - stats api
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
index 3157f3d2f9ce7af4a763203672817a7f5c7bd4fb..8e3a907c8172340aa456e8898cb5025c29dd18eb 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
@@ -12,7 +12,7 @@ import org.bukkit.scoreboard.Score;
final class CraftObjective extends CraftScoreboardComponent implements Objective {
private final net.minecraft.world.scores.Objective objective;
- private final CraftCriteria criteria;
+ private final Criteria criteria; // Paper - stats api
CraftObjective(CraftScoreboard scoreboard, net.minecraft.world.scores.Objective objective) {
super(scoreboard);
@@ -65,7 +65,7 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective
public String getCriteria() {
this.checkState();
- return this.criteria.bukkitName;
+ return this.criteria.getName(); // Paper - stats api
}
@Override
@@ -79,7 +79,7 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective
public boolean isModifiable() {
this.checkState();
- return !this.criteria.criteria.isReadOnly();
+ return !this.criteria.isReadOnly(); // Paper - stats api
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
index bd8a5bb2b84daf013750aec9887dcb4b02b382ad..d4edd7f165c62b15e810ce4cc68b8bf424c108ba 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
@@ -54,12 +54,15 @@ public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard {
Preconditions.checkArgument(name.length() <= Short.MAX_VALUE, "The name '%s' is longer than the limit of 32767 characters (%s)", name, name.length());
Preconditions.checkArgument(this.board.getObjective(name) == null, "An objective of name '%s' already exists", name);
// Paper start - lazily track plugin scoreboards
- if (((CraftCriteria) criteria).criteria != net.minecraft.world.scores.criteria.ObjectiveCriteria.DUMMY && !this.registeredGlobally) {
+ // Paper start - stats API
+ java.util.Optional<net.minecraft.world.scores.criteria.ObjectiveCriteria> nmsCriteria = net.minecraft.world.scores.criteria.ObjectiveCriteria.byName(criteria.getName());
+ if (nmsCriteria.isPresent() && nmsCriteria.get() != net.minecraft.world.scores.criteria.ObjectiveCriteria.DUMMY && !this.registeredGlobally) {
+ // Paper end - stats API
net.minecraft.server.MinecraftServer.getServer().server.getScoreboardManager().registerScoreboardForVanilla(this);
this.registeredGlobally = true;
}
// Paper end
- net.minecraft.world.scores.Objective objective = this.board.addObjective(name, ((CraftCriteria) criteria).criteria, io.papermc.paper.adventure.PaperAdventure.asVanilla(displayName), CraftScoreboardTranslations.fromBukkitRender(renderType), true, null);
+ net.minecraft.world.scores.Objective objective = this.board.addObjective(name, nmsCriteria.orElse(net.minecraft.world.scores.criteria.ObjectiveCriteria.DUMMY), io.papermc.paper.adventure.PaperAdventure.asVanilla(displayName), CraftScoreboardTranslations.fromBukkitRender(renderType), true, null); // Paper - stats API
return new CraftObjective(this, objective);
}
// Paper end - Adventure
diff --git a/src/test/java/io/papermc/paper/statistic/PaperStatsTest.java b/src/test/java/io/papermc/paper/statistic/PaperStatsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3584ed5fba7cfe8b8c5c91fa28a59ed40ddbc539
--- /dev/null
+++ b/src/test/java/io/papermc/paper/statistic/PaperStatsTest.java
@@ -0,0 +1,62 @@
+package io.papermc.paper.statistic;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.stats.StatType;
+import org.bukkit.Material;
+import org.bukkit.Registry;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class PaperStatsTest extends AbstractTestingBase {
+
+ @Test
+ void testNMSCustomStatToPaperCustomStat() {
+ Set<ResourceLocation> missingKeys = new HashSet<>();
+ for (ResourceLocation minecraftKey : BuiltInRegistries.CUSTOM_STAT) {
+ if (Registry.CUSTOM_STATISTIC.get(CraftNamespacedKey.fromMinecraft(minecraftKey)) == null) {
+ missingKeys.add(minecraftKey);
+ }
+ }
+ StringBuilder sb = new StringBuilder("\n");
+ for (ResourceLocation missingKey : missingKeys) {
+ sb.append("public static final CustomStatistic ").append(missingKey.getPath().toUpperCase(Locale.ENGLISH)).append(" = create(\"").append(missingKey.getPath()).append("\");\n");
+ }
+ if (!missingKeys.isEmpty()) {
+ System.out.println(sb);
+ }
+ assertEquals(0, missingKeys.size(), "Some stats are missing paper counterparts: " + missingKeys);
+ }
+
+ @Test
+ void testPaperCustomStatToNMSCustomStat() {
+ Set<CustomStatistic> extraStats = new HashSet<>();
+ for (CustomStatistic paperCustomStat : Registry.CUSTOM_STATISTIC) {
+ ResourceLocation stat = BuiltInRegistries.CUSTOM_STAT.get(CraftNamespacedKey.toMinecraft(paperCustomStat.getKey()));
+ if (stat == null) {
+ extraStats.add(paperCustomStat);
+ }
+ }
+ assertEquals(0, extraStats.size(), "These stats do not have NMS counterparts: " + extraStats);
+ }
+
+ @Test
+ void checkAllStatTypes() {
+ for (StatType<?> stat : BuiltInRegistries.STAT_TYPE) {
+ assertNotNull(Registry.STATISTIC_TYPE.get(CraftNamespacedKey.fromMinecraft(BuiltInRegistries.STAT_TYPE.getResourceKey(stat).orElseThrow().location())), BuiltInRegistries.STAT_TYPE.getKey(stat) + " is missing its paper counterpart");
+ }
+ }
+
+ @Test
+ void testInvalidStat() {
+ assertThrows(IllegalArgumentException.class, () -> StatisticType.BLOCK_MINED.of(Material.DIAMOND_PICKAXE), "created a block mined stat for a pickaxe");
+ }
+}
diff --git a/src/test/java/io/papermc/paper/world/TranslationKeyTest.java b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
index dbd1dc4453bd26fb6116b62f6ccbf69e92e09fc4..6eca15dcde875fdee50256ecfe8eb9e2dd2fb326 100644
--- a/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
+++ b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
@@ -1,6 +1,7 @@
package io.papermc.paper.world;
import com.destroystokyo.paper.ClientOption;
+import io.papermc.paper.statistic.StatisticType;
import java.util.Map;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.contents.TranslatableContents;
@@ -31,6 +32,14 @@ public class TranslationKeyTest extends AbstractTestingBase {
}
}
+ @Test
+ public void testStatType() {
+ for (StatisticType<?> statisticType : org.bukkit.Registry.STATISTIC_TYPE) {
+ if (statisticType == StatisticType.CUSTOM) continue;
+ Assertions.assertEquals(((TranslatableContents) BuiltInRegistries.STAT_TYPE.getOptional(CraftNamespacedKey.toMinecraft(statisticType.getKey())).orElseThrow().getDisplayName().getContents()).getKey(), statisticType.translationKey(), "translation key mismatch for " + statisticType);
+ }
+ }
+
@Test
public void testDifficultyKeys() {
for (Difficulty bukkitDifficulty : Difficulty.values()) {
diff --git a/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java
index 0bc367b633a84fe00a168f40fd31061b5a0d9e35..87dc7bdcc0d0fa2f822cfe4455eb3e59d0a43df3 100644
--- a/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java
+++ b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java
@@ -11,6 +11,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.support.AbstractTestingBase;
import org.junit.jupiter.api.Test;
+@Deprecated(forRemoval = true) // Paper
public class StatisticsAndAchievementsTest extends AbstractTestingBase {
@Test