diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/CustomStatisticKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CustomStatisticKeys.java new file mode 100644 index 000000000000..5b1a9617d2a8 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CustomStatisticKeys.java @@ -0,0 +1,573 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.statistic.CustomStatistic; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#CUSTOM_STAT}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class CustomStatisticKeys { + /** + * {@code minecraft:animals_bred} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ANIMALS_BRED = create(key("animals_bred")); + + /** + * {@code minecraft:aviate_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey AVIATE_ONE_CM = create(key("aviate_one_cm")); + + /** + * {@code minecraft:bell_ring} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BELL_RING = create(key("bell_ring")); + + /** + * {@code minecraft:boat_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BOAT_ONE_CM = create(key("boat_one_cm")); + + /** + * {@code minecraft:clean_armor} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLEAN_ARMOR = create(key("clean_armor")); + + /** + * {@code minecraft:clean_banner} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLEAN_BANNER = create(key("clean_banner")); + + /** + * {@code minecraft:clean_shulker_box} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLEAN_SHULKER_BOX = create(key("clean_shulker_box")); + + /** + * {@code minecraft:climb_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLIMB_ONE_CM = create(key("climb_one_cm")); + + /** + * {@code minecraft:crouch_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CROUCH_ONE_CM = create(key("crouch_one_cm")); + + /** + * {@code minecraft:damage_absorbed} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_ABSORBED = create(key("damage_absorbed")); + + /** + * {@code minecraft:damage_blocked_by_shield} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_BLOCKED_BY_SHIELD = create(key("damage_blocked_by_shield")); + + /** + * {@code minecraft:damage_dealt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_DEALT = create(key("damage_dealt")); + + /** + * {@code minecraft:damage_dealt_absorbed} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_DEALT_ABSORBED = create(key("damage_dealt_absorbed")); + + /** + * {@code minecraft:damage_dealt_resisted} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_DEALT_RESISTED = create(key("damage_dealt_resisted")); + + /** + * {@code minecraft:damage_resisted} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_RESISTED = create(key("damage_resisted")); + + /** + * {@code minecraft:damage_taken} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DAMAGE_TAKEN = create(key("damage_taken")); + + /** + * {@code minecraft:deaths} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DEATHS = create(key("deaths")); + + /** + * {@code minecraft:drop} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DROP = create(key("drop")); + + /** + * {@code minecraft:eat_cake_slice} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey EAT_CAKE_SLICE = create(key("eat_cake_slice")); + + /** + * {@code minecraft:enchant_item} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENCHANT_ITEM = create(key("enchant_item")); + + /** + * {@code minecraft:fall_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey FALL_ONE_CM = create(key("fall_one_cm")); + + /** + * {@code minecraft:fill_cauldron} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey FILL_CAULDRON = create(key("fill_cauldron")); + + /** + * {@code minecraft:fish_caught} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey FISH_CAUGHT = create(key("fish_caught")); + + /** + * {@code minecraft:fly_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey FLY_ONE_CM = create(key("fly_one_cm")); + + /** + * {@code minecraft:happy_ghast_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey HAPPY_GHAST_ONE_CM = create(key("happy_ghast_one_cm")); + + /** + * {@code minecraft:horse_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey HORSE_ONE_CM = create(key("horse_one_cm")); + + /** + * {@code minecraft:inspect_dispenser} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INSPECT_DISPENSER = create(key("inspect_dispenser")); + + /** + * {@code minecraft:inspect_dropper} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INSPECT_DROPPER = create(key("inspect_dropper")); + + /** + * {@code minecraft:inspect_hopper} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INSPECT_HOPPER = create(key("inspect_hopper")); + + /** + * {@code minecraft:interact_with_anvil} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_ANVIL = create(key("interact_with_anvil")); + + /** + * {@code minecraft:interact_with_beacon} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_BEACON = create(key("interact_with_beacon")); + + /** + * {@code minecraft:interact_with_blast_furnace} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_BLAST_FURNACE = create(key("interact_with_blast_furnace")); + + /** + * {@code minecraft:interact_with_brewingstand} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_BREWINGSTAND = create(key("interact_with_brewingstand")); + + /** + * {@code minecraft:interact_with_campfire} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_CAMPFIRE = create(key("interact_with_campfire")); + + /** + * {@code minecraft:interact_with_cartography_table} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_CARTOGRAPHY_TABLE = create(key("interact_with_cartography_table")); + + /** + * {@code minecraft:interact_with_crafting_table} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_CRAFTING_TABLE = create(key("interact_with_crafting_table")); + + /** + * {@code minecraft:interact_with_furnace} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_FURNACE = create(key("interact_with_furnace")); + + /** + * {@code minecraft:interact_with_grindstone} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_GRINDSTONE = create(key("interact_with_grindstone")); + + /** + * {@code minecraft:interact_with_lectern} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_LECTERN = create(key("interact_with_lectern")); + + /** + * {@code minecraft:interact_with_loom} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_LOOM = create(key("interact_with_loom")); + + /** + * {@code minecraft:interact_with_smithing_table} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_SMITHING_TABLE = create(key("interact_with_smithing_table")); + + /** + * {@code minecraft:interact_with_smoker} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_SMOKER = create(key("interact_with_smoker")); + + /** + * {@code minecraft:interact_with_stonecutter} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey INTERACT_WITH_STONECUTTER = create(key("interact_with_stonecutter")); + + /** + * {@code minecraft:jump} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey JUMP = create(key("jump")); + + /** + * {@code minecraft:leave_game} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey LEAVE_GAME = create(key("leave_game")); + + /** + * {@code minecraft:minecart_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey MINECART_ONE_CM = create(key("minecart_one_cm")); + + /** + * {@code minecraft:mob_kills} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey MOB_KILLS = create(key("mob_kills")); + + /** + * {@code minecraft:nautilus_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey NAUTILUS_ONE_CM = create(key("nautilus_one_cm")); + + /** + * {@code minecraft:open_barrel} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OPEN_BARREL = create(key("open_barrel")); + + /** + * {@code minecraft:open_chest} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OPEN_CHEST = create(key("open_chest")); + + /** + * {@code minecraft:open_enderchest} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OPEN_ENDERCHEST = create(key("open_enderchest")); + + /** + * {@code minecraft:open_shulker_box} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OPEN_SHULKER_BOX = create(key("open_shulker_box")); + + /** + * {@code minecraft:pig_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PIG_ONE_CM = create(key("pig_one_cm")); + + /** + * {@code minecraft:play_noteblock} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PLAY_NOTEBLOCK = create(key("play_noteblock")); + + /** + * {@code minecraft:play_record} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PLAY_RECORD = create(key("play_record")); + + /** + * {@code minecraft:play_time} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PLAY_TIME = create(key("play_time")); + + /** + * {@code minecraft:player_kills} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PLAYER_KILLS = create(key("player_kills")); + + /** + * {@code minecraft:pot_flower} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey POT_FLOWER = create(key("pot_flower")); + + /** + * {@code minecraft:raid_trigger} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey RAID_TRIGGER = create(key("raid_trigger")); + + /** + * {@code minecraft:raid_win} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey RAID_WIN = create(key("raid_win")); + + /** + * {@code minecraft:sleep_in_bed} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SLEEP_IN_BED = create(key("sleep_in_bed")); + + /** + * {@code minecraft:sneak_time} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SNEAK_TIME = create(key("sneak_time")); + + /** + * {@code minecraft:sprint_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SPRINT_ONE_CM = create(key("sprint_one_cm")); + + /** + * {@code minecraft:strider_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey STRIDER_ONE_CM = create(key("strider_one_cm")); + + /** + * {@code minecraft:swim_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SWIM_ONE_CM = create(key("swim_one_cm")); + + /** + * {@code minecraft:talked_to_villager} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TALKED_TO_VILLAGER = create(key("talked_to_villager")); + + /** + * {@code minecraft:target_hit} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TARGET_HIT = create(key("target_hit")); + + /** + * {@code minecraft:time_since_death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TIME_SINCE_DEATH = create(key("time_since_death")); + + /** + * {@code minecraft:time_since_rest} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TIME_SINCE_REST = create(key("time_since_rest")); + + /** + * {@code minecraft:total_world_time} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TOTAL_WORLD_TIME = create(key("total_world_time")); + + /** + * {@code minecraft:traded_with_villager} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TRADED_WITH_VILLAGER = create(key("traded_with_villager")); + + /** + * {@code minecraft:trigger_trapped_chest} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TRIGGER_TRAPPED_CHEST = create(key("trigger_trapped_chest")); + + /** + * {@code minecraft:tune_noteblock} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey TUNE_NOTEBLOCK = create(key("tune_noteblock")); + + /** + * {@code minecraft:use_cauldron} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey USE_CAULDRON = create(key("use_cauldron")); + + /** + * {@code minecraft:walk_on_water_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey WALK_ON_WATER_ONE_CM = create(key("walk_on_water_one_cm")); + + /** + * {@code minecraft:walk_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey WALK_ONE_CM = create(key("walk_one_cm")); + + /** + * {@code minecraft:walk_under_water_one_cm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey WALK_UNDER_WATER_ONE_CM = create(key("walk_under_water_one_cm")); + + private CustomStatisticKeys() { + } + + private static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.CUSTOM_STAT, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/StatisticTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/StatisticTypeKeys.java new file mode 100644 index 000000000000..3b8276d50d1a --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/StatisticTypeKeys.java @@ -0,0 +1,97 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.statistic.StatisticType; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#STAT_TYPE}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class StatisticTypeKeys { + /** + * {@code minecraft:broken} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> BROKEN = create(key("broken")); + + /** + * {@code minecraft:crafted} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> CRAFTED = create(key("crafted")); + + /** + * {@code minecraft:custom} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> CUSTOM = create(key("custom")); + + /** + * {@code minecraft:dropped} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> DROPPED = create(key("dropped")); + + /** + * {@code minecraft:killed} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> KILLED = create(key("killed")); + + /** + * {@code minecraft:killed_by} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> KILLED_BY = create(key("killed_by")); + + /** + * {@code minecraft:mined} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> MINED = create(key("mined")); + + /** + * {@code minecraft:picked_up} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> PICKED_UP = create(key("picked_up")); + + /** + * {@code minecraft:used} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> USED = create(key("used")); + + private StatisticTypeKeys() { + } + + private static TypedKey> create(final Key key) { + return TypedKey.create(RegistryKey.STAT_TYPE, key); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java index 7031a6bc9e60..a76b09d1e93a 100644 --- a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java +++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java @@ -16,6 +16,7 @@ import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Pose; +import org.bukkit.scoreboard.Criteria; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jspecify.annotations.NullMarked; @@ -104,4 +105,12 @@ class Holder { GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass); Set validMannequinPoses(); + + /** + * Gets the criteria for the non-stat built-in scoreboard criteria. + * + * @param key the key + * @return the criteria + */ + Criteria getCriteria(String key); } diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerRequestStatisticsEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerRequestStatisticsEvent.java new file mode 100644 index 000000000000..e91503ef892c --- /dev/null +++ b/paper-api/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.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when the player requests their statistics. + */ +@NullMarked +public class PlayerRequestStatisticsEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Object2IntMap> statisticMap; + private boolean cancelled; + + @ApiStatus.Internal + public PlayerRequestStatisticsEvent(final Player player, final Object2IntMap> statisticMap) { + super(player); + this.statisticMap = statisticMap; + } + + /** + * Gets the statistic map to be sent to the player. + * + * @return the mutable statistic map + */ + public Object2IntMap> getStatisticMap() { + return this.statisticMap; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(final boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index dcee3d88f5b7..ef1f4190de0d 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -3,6 +3,8 @@ import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.StatisticType; import net.kyori.adventure.key.Key; import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; @@ -132,6 +134,16 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.GameRuleKeys */ RegistryKey> GAME_RULE = create("game_rule"); + /** + * Built-in registry for custom statistics. + * @see io.papermc.paper.registry.keys.CustomStatisticKeys + */ + RegistryKey CUSTOM_STAT = create("custom_stat"); + /** + * Built-in registry for statistic types. + * @see io.papermc.paper.registry.keys.StatisticTypeKeys + */ + RegistryKey> STAT_TYPE = create("stat_type"); /* ********************** * * Data-driven Registries * diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistic.java b/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistic.java new file mode 100644 index 000000000000..c4f9a0f39cd3 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistic.java @@ -0,0 +1,23 @@ +package io.papermc.paper.statistic; + +import net.kyori.adventure.translation.Translatable; +import org.bukkit.Keyed; +import org.jetbrains.annotations.ApiStatus; + +/** + * Represents a statistic of the type {@link StatisticTypes#CUSTOM}. + * + * @see CustomStatistics + */ +@ApiStatus.NonExtendable +public interface CustomStatistic extends Keyed, Translatable { + + /** + * Gets the statistic with the given custom stat. + * + * @return the statistic for the custom stat + */ + default Statistic stat() { + return StatisticTypes.CUSTOM.forValue(this); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistics.java b/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistics.java new file mode 100644 index 000000000000..dffa0b248405 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/CustomStatistics.java @@ -0,0 +1,174 @@ +package io.papermc.paper.statistic; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; +import org.bukkit.Registry; + +/** + * All custom statistics. + */ +public final class CustomStatistics { + + // Start generate - CustomStatistics + public static final CustomStatistic ANIMALS_BRED = get("animals_bred"); + + public static final CustomStatistic AVIATE_ONE_CM = get("aviate_one_cm"); + + public static final CustomStatistic BELL_RING = get("bell_ring"); + + public static final CustomStatistic BOAT_ONE_CM = get("boat_one_cm"); + + public static final CustomStatistic CLEAN_ARMOR = get("clean_armor"); + + public static final CustomStatistic CLEAN_BANNER = get("clean_banner"); + + public static final CustomStatistic CLEAN_SHULKER_BOX = get("clean_shulker_box"); + + public static final CustomStatistic CLIMB_ONE_CM = get("climb_one_cm"); + + public static final CustomStatistic CROUCH_ONE_CM = get("crouch_one_cm"); + + public static final CustomStatistic DAMAGE_ABSORBED = get("damage_absorbed"); + + public static final CustomStatistic DAMAGE_BLOCKED_BY_SHIELD = get("damage_blocked_by_shield"); + + public static final CustomStatistic DAMAGE_DEALT = get("damage_dealt"); + + public static final CustomStatistic DAMAGE_DEALT_ABSORBED = get("damage_dealt_absorbed"); + + public static final CustomStatistic DAMAGE_DEALT_RESISTED = get("damage_dealt_resisted"); + + public static final CustomStatistic DAMAGE_RESISTED = get("damage_resisted"); + + public static final CustomStatistic DAMAGE_TAKEN = get("damage_taken"); + + public static final CustomStatistic DEATHS = get("deaths"); + + public static final CustomStatistic DROP = get("drop"); + + public static final CustomStatistic EAT_CAKE_SLICE = get("eat_cake_slice"); + + public static final CustomStatistic ENCHANT_ITEM = get("enchant_item"); + + public static final CustomStatistic FALL_ONE_CM = get("fall_one_cm"); + + public static final CustomStatistic FILL_CAULDRON = get("fill_cauldron"); + + public static final CustomStatistic FISH_CAUGHT = get("fish_caught"); + + public static final CustomStatistic FLY_ONE_CM = get("fly_one_cm"); + + public static final CustomStatistic HAPPY_GHAST_ONE_CM = get("happy_ghast_one_cm"); + + public static final CustomStatistic HORSE_ONE_CM = get("horse_one_cm"); + + public static final CustomStatistic INSPECT_DISPENSER = get("inspect_dispenser"); + + public static final CustomStatistic INSPECT_DROPPER = get("inspect_dropper"); + + public static final CustomStatistic INSPECT_HOPPER = get("inspect_hopper"); + + public static final CustomStatistic INTERACT_WITH_ANVIL = get("interact_with_anvil"); + + public static final CustomStatistic INTERACT_WITH_BEACON = get("interact_with_beacon"); + + public static final CustomStatistic INTERACT_WITH_BLAST_FURNACE = get("interact_with_blast_furnace"); + + public static final CustomStatistic INTERACT_WITH_BREWINGSTAND = get("interact_with_brewingstand"); + + public static final CustomStatistic INTERACT_WITH_CAMPFIRE = get("interact_with_campfire"); + + public static final CustomStatistic INTERACT_WITH_CARTOGRAPHY_TABLE = get("interact_with_cartography_table"); + + public static final CustomStatistic INTERACT_WITH_CRAFTING_TABLE = get("interact_with_crafting_table"); + + public static final CustomStatistic INTERACT_WITH_FURNACE = get("interact_with_furnace"); + + public static final CustomStatistic INTERACT_WITH_GRINDSTONE = get("interact_with_grindstone"); + + public static final CustomStatistic INTERACT_WITH_LECTERN = get("interact_with_lectern"); + + public static final CustomStatistic INTERACT_WITH_LOOM = get("interact_with_loom"); + + public static final CustomStatistic INTERACT_WITH_SMITHING_TABLE = get("interact_with_smithing_table"); + + public static final CustomStatistic INTERACT_WITH_SMOKER = get("interact_with_smoker"); + + public static final CustomStatistic INTERACT_WITH_STONECUTTER = get("interact_with_stonecutter"); + + public static final CustomStatistic JUMP = get("jump"); + + public static final CustomStatistic LEAVE_GAME = get("leave_game"); + + public static final CustomStatistic MINECART_ONE_CM = get("minecart_one_cm"); + + public static final CustomStatistic MOB_KILLS = get("mob_kills"); + + public static final CustomStatistic NAUTILUS_ONE_CM = get("nautilus_one_cm"); + + public static final CustomStatistic OPEN_BARREL = get("open_barrel"); + + public static final CustomStatistic OPEN_CHEST = get("open_chest"); + + public static final CustomStatistic OPEN_ENDERCHEST = get("open_enderchest"); + + public static final CustomStatistic OPEN_SHULKER_BOX = get("open_shulker_box"); + + public static final CustomStatistic PIG_ONE_CM = get("pig_one_cm"); + + public static final CustomStatistic PLAY_NOTEBLOCK = get("play_noteblock"); + + public static final CustomStatistic PLAY_RECORD = get("play_record"); + + public static final CustomStatistic PLAY_TIME = get("play_time"); + + public static final CustomStatistic PLAYER_KILLS = get("player_kills"); + + public static final CustomStatistic POT_FLOWER = get("pot_flower"); + + public static final CustomStatistic RAID_TRIGGER = get("raid_trigger"); + + public static final CustomStatistic RAID_WIN = get("raid_win"); + + public static final CustomStatistic SLEEP_IN_BED = get("sleep_in_bed"); + + public static final CustomStatistic SNEAK_TIME = get("sneak_time"); + + public static final CustomStatistic SPRINT_ONE_CM = get("sprint_one_cm"); + + public static final CustomStatistic STRIDER_ONE_CM = get("strider_one_cm"); + + public static final CustomStatistic SWIM_ONE_CM = get("swim_one_cm"); + + public static final CustomStatistic TALKED_TO_VILLAGER = get("talked_to_villager"); + + public static final CustomStatistic TARGET_HIT = get("target_hit"); + + public static final CustomStatistic TIME_SINCE_DEATH = get("time_since_death"); + + public static final CustomStatistic TIME_SINCE_REST = get("time_since_rest"); + + public static final CustomStatistic TOTAL_WORLD_TIME = get("total_world_time"); + + public static final CustomStatistic TRADED_WITH_VILLAGER = get("traded_with_villager"); + + public static final CustomStatistic TRIGGER_TRAPPED_CHEST = get("trigger_trapped_chest"); + + public static final CustomStatistic TUNE_NOTEBLOCK = get("tune_noteblock"); + + public static final CustomStatistic USE_CAULDRON = get("use_cauldron"); + + public static final CustomStatistic WALK_ON_WATER_ONE_CM = get("walk_on_water_one_cm"); + + public static final CustomStatistic WALK_ONE_CM = get("walk_one_cm"); + + public static final CustomStatistic WALK_UNDER_WATER_ONE_CM = get("walk_under_water_one_cm"); + // End generate - CustomStatistics + + private static CustomStatistic get(final @KeyPattern.Value String key) { + return Registry.CUSTOM_STAT.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + + private CustomStatistics() { + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/Statistic.java b/paper-api/src/main/java/io/papermc/paper/statistic/Statistic.java new file mode 100644 index 000000000000..7f164a06af64 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/Statistic.java @@ -0,0 +1,28 @@ +package io.papermc.paper.statistic; + +import org.bukkit.scoreboard.Criteria; +import org.jetbrains.annotations.ApiStatus; + +/** + * Represents an individual statistic. Obtained via {@link StatisticType#forValue(Object)}. + * Can be used as a criteria for {@link org.bukkit.scoreboard.Scoreboard#registerNewObjective(String, Criteria, net.kyori.adventure.text.Component)} + * + * @param stat value type + */ +@ApiStatus.NonExtendable +public interface Statistic extends Criteria { + + /** + * Gets the owner of the statistic. + * + * @return the stat owner + */ + S owner(); + + /** + * Gets the stat type. + * + * @return the stat type + */ + StatisticType type(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/StatisticType.java b/paper-api/src/main/java/io/papermc/paper/statistic/StatisticType.java new file mode 100644 index 000000000000..3ca8aed87590 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/StatisticType.java @@ -0,0 +1,43 @@ +package io.papermc.paper.statistic; + +import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.translation.Translatable; +import org.bukkit.Keyed; +import org.jetbrains.annotations.ApiStatus; + +/** + * A type of statistic. + * + * @param the value type of this statistic type + * @see StatisticTypes + */ +@ApiStatus.NonExtendable +public interface StatisticType extends Keyed, Translatable { + + /** + * 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 type + */ + Statistic forValue(S value); + + /** + * Gets the registry key associated with this stat type. + * + * @return the registry + */ + RegistryKey registryKey(); + + /** + * {@inheritDoc} + *

+ * {@link StatisticTypes#CUSTOM} does NOT have a + * translation key. + * + * @throws IllegalArgumentException if used with {@link StatisticTypes#CUSTOM} + */ + @Override + String translationKey(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/StatisticTypes.java b/paper-api/src/main/java/io/papermc/paper/statistic/StatisticTypes.java new file mode 100644 index 000000000000..2bd525883386 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/StatisticTypes.java @@ -0,0 +1,42 @@ +package io.papermc.paper.statistic; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; +import org.bukkit.Registry; +import org.bukkit.block.BlockType; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemType; + +/** + * All statistic types. + */ +public final class StatisticTypes { + + // Start generate - StatisticTypes + public static final StatisticType BLOCK_MINED = get("mined"); + + public static final StatisticType CUSTOM = get("custom"); + + public static final StatisticType ENTITY_KILLED = get("killed"); + + public static final StatisticType ENTITY_KILLED_BY = get("killed_by"); + + public static final StatisticType ITEM_BROKEN = get("broken"); + + public static final StatisticType ITEM_CRAFTED = get("crafted"); + + public static final StatisticType ITEM_DROPPED = get("dropped"); + + public static final StatisticType ITEM_PICKED_UP = get("picked_up"); + + public static final StatisticType ITEM_USED = get("used"); + // End generate - StatisticTypes + + @SuppressWarnings("unchecked") + private static StatisticType get(final @KeyPattern.Value String key) { + return (StatisticType) Registry.STAT_TYPE.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + + private StatisticTypes() { + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/statistic/package-info.java b/paper-api/src/main/java/io/papermc/paper/statistic/package-info.java new file mode 100644 index 000000000000..71a531e5f103 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/statistic/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.statistic; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-api/src/main/java/org/bukkit/Bukkit.java b/paper-api/src/main/java/org/bukkit/Bukkit.java index 14d81c7b138e..e26c8cc98805 100644 --- a/paper-api/src/main/java/org/bukkit/Bukkit.java +++ b/paper-api/src/main/java/org/bukkit/Bukkit.java @@ -2208,8 +2208,10 @@ public static ScoreboardManager getScoreboardManager() { * @param name the criteria name * @return the criteria * @see Criteria Criteria for a list of constants + * @deprecated use constants in {@link Criteria} or {@link io.papermc.paper.statistic.Statistic} */ @NotNull + @Deprecated(since = "1.21.11", forRemoval = true) public static Criteria getScoreboardCriteria(@NotNull String name) { return server.getScoreboardCriteria(name); } diff --git a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java index 0530e8b4f8c8..7a36d8984277 100644 --- a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java +++ b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java @@ -1,5 +1,6 @@ package org.bukkit; +import com.google.common.base.Preconditions; import java.time.Duration; import java.time.Instant; import java.util.Date; @@ -12,6 +13,8 @@ import org.bukkit.entity.Player; import org.bukkit.permissions.ServerOperator; import org.bukkit.profile.PlayerProfile; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.index.qual.Positive; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -263,7 +266,6 @@ default Location getBedSpawnLocation() { return this.getRespawnLocation(); } - // Paper start /** * Gets the last date and time that this player logged into the server. *

@@ -286,7 +288,74 @@ default Location getBedSpawnLocation() { * @return last seen time */ public long getLastSeen(); - // Paper end + + /** + * Decrements the given stat for this player. + *

+ * This is equivalent to the following code: {@code decrementStatistic(Statistic, 1)} + * + * @param statistic the stat to decrement + * @throws IllegalArgumentException if the stat would have a negative value after decrementing it + */ + default void decrementStatistic(final io.papermc.paper.statistic.Statistic statistic) { + this.decrementStatistic(statistic, 1); + } + + /** + * Increments the given stat for this player. + *

+ * This is equivalent to the following code: {@code incrementStatistic(Statistic, 1)} + * + * @param statistic the stat to increment + */ + default void incrementStatistic(final 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 amount is negative or 0 + * @throws IllegalArgumentException if the stat would have a negative value after decrementing it + */ + void decrementStatistic(io.papermc.paper.statistic.Statistic statistic, @Positive int amount); + + /** + * Increments the given stat for this player. + * + * @param statistic the stat to increment + * @param amount the amount to increment by + * @throws IllegalArgumentException if amount is negative or 0 + */ + void incrementStatistic(io.papermc.paper.statistic.Statistic statistic, @Positive 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 amount is negative + */ + void setStatistic(io.papermc.paper.statistic.Statistic statistic, @NonNegative int newAmount); + + /** + * Gets the given stat for this player. + * + * @param statistic the stat to get + * @return the amount for the stat + */ + int getStatistic(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 + */ + String getFormattedValue(io.papermc.paper.statistic.Statistic statistic); /** * Gets the Location where the player will spawn at, {@code null} if they @@ -312,6 +381,7 @@ default Location getBedSpawnLocation() { */ @Nullable Location getRespawnLocation(boolean loadLocationAndValidate); + // /** * Increments the given statistic for this player. *

@@ -322,8 +392,12 @@ default Location getBedSpawnLocation() { * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if the statistic requires an * additional parameter + * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic)} */ - public void incrementStatistic(Statistic statistic) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic) throws IllegalArgumentException { + this.incrementStatistic(statistic, 1); + } /** * Decrements the given statistic for this player. @@ -335,8 +409,13 @@ default Location getBedSpawnLocation() { * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if the statistic requires an * additional parameter + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)} */ - public void decrementStatistic(Statistic statistic) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic) throws IllegalArgumentException { + this.decrementStatistic(statistic, 1); + } /** * Increments the given statistic for this player. @@ -344,11 +423,16 @@ default Location getBedSpawnLocation() { * @param statistic Statistic to increment * @param amount Amount to increment this statistic by * @throws IllegalArgumentException if statistic is null - * @throws IllegalArgumentException if amount is negative * @throws IllegalArgumentException if the statistic requires an * additional parameter + * @throws IllegalArgumentException if amount isn't positive + * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)} */ - public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + this.incrementStatistic(statistic.toModern(null, null), amount); + } /** * Decrements the given statistic for this player. @@ -356,11 +440,17 @@ default Location getBedSpawnLocation() { * @param statistic Statistic to decrement * @param amount Amount to decrement this statistic by * @throws IllegalArgumentException if statistic is null - * @throws IllegalArgumentException if amount is negative * @throws IllegalArgumentException if the statistic requires an * additional parameter - */ - public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException; + * @throws IllegalArgumentException if amount isn't positive + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)} + */ + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + this.decrementStatistic(statistic.toModern(null, null), amount); + } /** * Sets the given statistic for this player. @@ -371,8 +461,13 @@ default Location getBedSpawnLocation() { * @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)} */ - public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + this.setStatistic(statistic.toModern(null, null), newValue); + } /** * Gets the value of the given statistic for this player. @@ -382,8 +477,13 @@ default Location getBedSpawnLocation() { * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if the statistic requires an * additional parameter + * @deprecated use {@link #getStatistic(io.papermc.paper.statistic.Statistic)} */ - public int getStatistic(Statistic statistic) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default int getStatistic(Statistic statistic) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + return this.getStatistic(statistic.toModern(null, null)); + } /** * Increments the given statistic for this player for the given material. @@ -397,8 +497,14 @@ default Location getBedSpawnLocation() { * @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)} */ - public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + this.incrementStatistic(statistic.toModern(null, material)); + } /** * Decrements the given statistic for this player for the given material. @@ -412,8 +518,15 @@ default Location getBedSpawnLocation() { * @throws IllegalArgumentException if material is null * @throws IllegalArgumentException if the given parameter is not valid * for the statistic - */ - public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)} + */ + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + this.decrementStatistic(statistic.toModern(null, material)); + } /** * Gets the value of the given statistic for this player. @@ -425,8 +538,14 @@ default Location getBedSpawnLocation() { * @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)} */ - public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + return this.getStatistic(statistic.toModern(null, material)); + } /** * Increments the given statistic for this player for the given material. @@ -436,11 +555,17 @@ default Location getBedSpawnLocation() { * @param amount Amount to increment this statistic by * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if material is null - * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if amount isn't positive * @throws IllegalArgumentException if the given parameter is not valid * for the statistic + * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)} */ - public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + this.incrementStatistic(statistic.toModern(null, material), amount); + } /** * Decrements the given statistic for this player for the given material. @@ -450,11 +575,18 @@ default Location getBedSpawnLocation() { * @param amount Amount to decrement this statistic by * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if material is null - * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if amount isn't positive * @throws IllegalArgumentException if the given parameter is not valid * for the statistic - */ - public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException; + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)} + */ + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + this.decrementStatistic(statistic.toModern(null, material), amount); + } /** * Sets the given statistic for this player for the given material. @@ -467,8 +599,14 @@ default Location getBedSpawnLocation() { * @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)} */ - public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(material != null, "Material cannot be null"); + this.setStatistic(statistic.toModern(null, material), newValue); + } /** * Increments the given statistic for this player for the given entity. @@ -482,8 +620,14 @@ default Location getBedSpawnLocation() { * @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)} */ - public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + this.incrementStatistic(statistic.toModern(entityType, null)); + } /** * Decrements the given statistic for this player for the given entity. @@ -497,8 +641,15 @@ default Location getBedSpawnLocation() { * @throws IllegalArgumentException if entityType is null * @throws IllegalArgumentException if the given parameter is not valid * for the statistic - */ - public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic)} + */ + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + this.decrementStatistic(statistic.toModern(entityType, null)); + } /** * Gets the value of the given statistic for this player. @@ -510,8 +661,14 @@ default Location getBedSpawnLocation() { * @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)} */ - public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + return this.getStatistic(statistic.toModern(entityType, null)); + } /** * Increments the given statistic for this player for the given entity. @@ -521,11 +678,17 @@ default Location getBedSpawnLocation() { * @param amount Amount to increment this statistic by * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if entityType is null - * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if amount isn't positive * @throws IllegalArgumentException if the given parameter is not valid * for the statistic + * @deprecated use {@link #incrementStatistic(io.papermc.paper.statistic.Statistic, int)} */ - public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) throws IllegalArgumentException; + @Deprecated(since = "1.21.11") + default void incrementStatistic(Statistic statistic, EntityType entityType, int amount) throws IllegalArgumentException { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + this.incrementStatistic(statistic.toModern(entityType, null), amount); + } /** * Decrements the given statistic for this player for the given entity. @@ -535,11 +698,18 @@ default Location getBedSpawnLocation() { * @param amount Amount to decrement this statistic by * @throws IllegalArgumentException if statistic is null * @throws IllegalArgumentException if entityType is null - * @throws IllegalArgumentException if amount is negative + * @throws IllegalArgumentException if amount isn't positive * @throws IllegalArgumentException if the given parameter is not valid * for the statistic - */ - public void decrementStatistic(Statistic statistic, EntityType entityType, int amount); + * @throws IllegalArgumentException if the statistic would have a negative value after decrementing it + * @deprecated use {@link #decrementStatistic(io.papermc.paper.statistic.Statistic, int)} + */ + @Deprecated(since = "1.21.11") + default void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + this.decrementStatistic(statistic.toModern(entityType, null), amount); + } /** * Sets the given statistic for this player for the given entity. @@ -552,8 +722,15 @@ default Location getBedSpawnLocation() { * @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)} */ - public void setStatistic(Statistic statistic, EntityType entityType, int newValue); + @Deprecated(since = "1.21.11") + default void setStatistic(Statistic statistic, EntityType entityType, int newValue) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); + this.setStatistic(statistic.toModern(entityType, null), newValue); + } + // /** * Gets the player's last death location. diff --git a/paper-api/src/main/java/org/bukkit/Registry.java b/paper-api/src/main/java/org/bukkit/Registry.java index 0859653089ac..54edb4378fd5 100644 --- a/paper-api/src/main/java/org/bukkit/Registry.java +++ b/paper-api/src/main/java/org/bukkit/Registry.java @@ -10,6 +10,8 @@ import io.papermc.paper.registry.TypedKey; import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.StatisticType; import java.util.Collection; import java.util.Iterator; import java.util.Locale; @@ -62,7 +64,7 @@ private static Registry registryFor(final RegistryKey re } @SuppressWarnings("removal") - @Deprecated(forRemoval = true, since = "1.21.4") + @Deprecated(since = "1.21.4", forRemoval = true) private static Registry legacyRegistryFor(final Class clazz) { return Objects.requireNonNull(RegistryAccess.registryAccess().getRegistry(clazz), () -> "No registry present for " + clazz.getSimpleName() + ". This is a bug."); } @@ -220,7 +222,9 @@ public Iterator iterator() { * Server statistics. * * @see Statistic + * @deprecated use {@link #CUSTOM_STAT} and {@link #STAT_TYPE} */ + @Deprecated(since = "1.21.11", forRemoval = true) Registry STATISTIC = new SimpleRegistry<>(Statistic.class); /** * Server structures. @@ -356,6 +360,20 @@ public int size() { */ Registry> GAME_RULE = registryFor(RegistryKey.GAME_RULE); + /** + * Custom statistics + * + * @see io.papermc.paper.statistic.CustomStatistic + */ + Registry CUSTOM_STAT = registryFor(RegistryKey.CUSTOM_STAT); + + /** + * Statistic types + * + * @see io.papermc.paper.statistic.StatisticType + */ + Registry> STAT_TYPE = registryFor(RegistryKey.STAT_TYPE); + // /** * @apiNote use {@link #MOB_EFFECT} instead diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java index 0ec885b97d65..8f1da8d48968 100644 --- a/paper-api/src/main/java/org/bukkit/Server.java +++ b/paper-api/src/main/java/org/bukkit/Server.java @@ -1978,8 +1978,10 @@ default int getAmbientSpawnLimit() { * @param name the criteria name * @return the criteria * @see Criteria Criteria for a list of constants + * @deprecated use constants in {@link Criteria} or {@link io.papermc.paper.statistic.Statistic} */ @NotNull + @Deprecated(since = "1.21.11", forRemoval = true) Criteria getScoreboardCriteria(@NotNull String name); /** diff --git a/paper-api/src/main/java/org/bukkit/Statistic.java b/paper-api/src/main/java/org/bukkit/Statistic.java index fc3d04da1453..4794dadd9c75 100644 --- a/paper-api/src/main/java/org/bukkit/Statistic.java +++ b/paper-api/src/main/java/org/bukkit/Statistic.java @@ -1,112 +1,126 @@ package org.bukkit; -import java.util.Locale; -import org.jetbrains.annotations.NotNull; +import com.google.common.base.Preconditions; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.StatisticType; +import io.papermc.paper.statistic.StatisticTypes; +import java.util.Objects; +import net.kyori.adventure.key.Key; +import org.bukkit.block.BlockType; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemType; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Represents a countable statistic, which is tracked by the server. + * + * @deprecated use {@link StatisticType} and {@link io.papermc.paper.statistic.Statistic} */ +@NullMarked +@Deprecated(since = "1.21.11") public enum Statistic implements Keyed { // Start generate - StatisticCustom - ANIMALS_BRED, - AVIATE_ONE_CM, - BELL_RING, - BOAT_ONE_CM, - ARMOR_CLEANED, - BANNER_CLEANED, - CLEAN_SHULKER_BOX, - CLIMB_ONE_CM, - CROUCH_ONE_CM, - DAMAGE_ABSORBED, - DAMAGE_BLOCKED_BY_SHIELD, - DAMAGE_DEALT, - DAMAGE_DEALT_ABSORBED, - DAMAGE_DEALT_RESISTED, - DAMAGE_RESISTED, - DAMAGE_TAKEN, - DEATHS, - DROP_COUNT, - CAKE_SLICES_EATEN, - ITEM_ENCHANTED, - FALL_ONE_CM, - CAULDRON_FILLED, - FISH_CAUGHT, - FLY_ONE_CM, - HAPPY_GHAST_ONE_CM, - HORSE_ONE_CM, - DISPENSER_INSPECTED, - DROPPER_INSPECTED, - HOPPER_INSPECTED, - INTERACT_WITH_ANVIL, - BEACON_INTERACTION, - INTERACT_WITH_BLAST_FURNACE, - BREWINGSTAND_INTERACTION, - INTERACT_WITH_CAMPFIRE, - INTERACT_WITH_CARTOGRAPHY_TABLE, - CRAFTING_TABLE_INTERACTION, - FURNACE_INTERACTION, - INTERACT_WITH_GRINDSTONE, - INTERACT_WITH_LECTERN, - INTERACT_WITH_LOOM, - INTERACT_WITH_SMITHING_TABLE, - INTERACT_WITH_SMOKER, - INTERACT_WITH_STONECUTTER, - JUMP, - LEAVE_GAME, - MINECART_ONE_CM, - MOB_KILLS, - NAUTILUS_ONE_CM, - OPEN_BARREL, - CHEST_OPENED, - ENDERCHEST_OPENED, - SHULKER_BOX_OPENED, - PIG_ONE_CM, - NOTEBLOCK_PLAYED, - RECORD_PLAYED, - PLAY_ONE_MINUTE, - PLAYER_KILLS, - FLOWER_POTTED, - RAID_TRIGGER, - RAID_WIN, - SLEEP_IN_BED, - SNEAK_TIME, - SPRINT_ONE_CM, - STRIDER_ONE_CM, - SWIM_ONE_CM, - TALKED_TO_VILLAGER, - TARGET_HIT, - TIME_SINCE_DEATH, - TIME_SINCE_REST, - TOTAL_WORLD_TIME, - TRADED_WITH_VILLAGER, - TRAPPED_CHEST_TRIGGERED, - NOTEBLOCK_TUNED, - CAULDRON_USED, - WALK_ON_WATER_ONE_CM, - WALK_ONE_CM, - WALK_UNDER_WATER_ONE_CM, + ANIMALS_BRED("animals_bred"), + AVIATE_ONE_CM("aviate_one_cm"), + BELL_RING("bell_ring"), + BOAT_ONE_CM("boat_one_cm"), + ARMOR_CLEANED("clean_armor"), + BANNER_CLEANED("clean_banner"), + CLEAN_SHULKER_BOX("clean_shulker_box"), + CLIMB_ONE_CM("climb_one_cm"), + CROUCH_ONE_CM("crouch_one_cm"), + DAMAGE_ABSORBED("damage_absorbed"), + DAMAGE_BLOCKED_BY_SHIELD("damage_blocked_by_shield"), + DAMAGE_DEALT("damage_dealt"), + DAMAGE_DEALT_ABSORBED("damage_dealt_absorbed"), + DAMAGE_DEALT_RESISTED("damage_dealt_resisted"), + DAMAGE_RESISTED("damage_resisted"), + DAMAGE_TAKEN("damage_taken"), + DEATHS("deaths"), + DROP_COUNT("drop"), + CAKE_SLICES_EATEN("eat_cake_slice"), + ITEM_ENCHANTED("enchant_item"), + FALL_ONE_CM("fall_one_cm"), + CAULDRON_FILLED("fill_cauldron"), + FISH_CAUGHT("fish_caught"), + FLY_ONE_CM("fly_one_cm"), + HAPPY_GHAST_ONE_CM("happy_ghast_one_cm"), + HORSE_ONE_CM("horse_one_cm"), + DISPENSER_INSPECTED("inspect_dispenser"), + DROPPER_INSPECTED("inspect_dropper"), + HOPPER_INSPECTED("inspect_hopper"), + INTERACT_WITH_ANVIL("interact_with_anvil"), + BEACON_INTERACTION("interact_with_beacon"), + INTERACT_WITH_BLAST_FURNACE("interact_with_blast_furnace"), + BREWINGSTAND_INTERACTION("interact_with_brewingstand"), + INTERACT_WITH_CAMPFIRE("interact_with_campfire"), + INTERACT_WITH_CARTOGRAPHY_TABLE("interact_with_cartography_table"), + CRAFTING_TABLE_INTERACTION("interact_with_crafting_table"), + FURNACE_INTERACTION("interact_with_furnace"), + INTERACT_WITH_GRINDSTONE("interact_with_grindstone"), + INTERACT_WITH_LECTERN("interact_with_lectern"), + INTERACT_WITH_LOOM("interact_with_loom"), + INTERACT_WITH_SMITHING_TABLE("interact_with_smithing_table"), + INTERACT_WITH_SMOKER("interact_with_smoker"), + INTERACT_WITH_STONECUTTER("interact_with_stonecutter"), + JUMP("jump"), + LEAVE_GAME("leave_game"), + MINECART_ONE_CM("minecart_one_cm"), + MOB_KILLS("mob_kills"), + NAUTILUS_ONE_CM("nautilus_one_cm"), + OPEN_BARREL("open_barrel"), + CHEST_OPENED("open_chest"), + ENDERCHEST_OPENED("open_enderchest"), + SHULKER_BOX_OPENED("open_shulker_box"), + PIG_ONE_CM("pig_one_cm"), + NOTEBLOCK_PLAYED("play_noteblock"), + RECORD_PLAYED("play_record"), + PLAY_ONE_MINUTE("play_time"), + PLAYER_KILLS("player_kills"), + FLOWER_POTTED("pot_flower"), + RAID_TRIGGER("raid_trigger"), + RAID_WIN("raid_win"), + SLEEP_IN_BED("sleep_in_bed"), + SNEAK_TIME("sneak_time"), + SPRINT_ONE_CM("sprint_one_cm"), + STRIDER_ONE_CM("strider_one_cm"), + SWIM_ONE_CM("swim_one_cm"), + TALKED_TO_VILLAGER("talked_to_villager"), + TARGET_HIT("target_hit"), + TIME_SINCE_DEATH("time_since_death"), + TIME_SINCE_REST("time_since_rest"), + TOTAL_WORLD_TIME("total_world_time"), + TRADED_WITH_VILLAGER("traded_with_villager"), + TRAPPED_CHEST_TRIGGERED("trigger_trapped_chest"), + NOTEBLOCK_TUNED("tune_noteblock"), + CAULDRON_USED("use_cauldron"), + WALK_ON_WATER_ONE_CM("walk_on_water_one_cm"), + WALK_ONE_CM("walk_one_cm"), + WALK_UNDER_WATER_ONE_CM("walk_under_water_one_cm"), // End generate - StatisticCustom // Start generate - StatisticType - BREAK_ITEM(Type.ITEM), - CRAFT_ITEM(Type.ITEM), - DROP(Type.ITEM), - KILL_ENTITY(Type.ENTITY), - ENTITY_KILLED_BY(Type.ENTITY), - MINE_BLOCK(Type.BLOCK), - PICKUP(Type.ITEM), - USE_ITEM(Type.ITEM); + BREAK_ITEM("broken", Type.ITEM), + CRAFT_ITEM("crafted", Type.ITEM), + DROP("dropped", Type.ITEM), + KILL_ENTITY("killed", Type.ENTITY), + ENTITY_KILLED_BY("killed_by", Type.ENTITY), + MINE_BLOCK("mined", Type.BLOCK), + PICKUP("picked_up", Type.ITEM), + USE_ITEM("used", Type.ITEM); // End generate - StatisticType private final Type type; private final NamespacedKey key; - private Statistic() { - this(Type.UNTYPED); + Statistic(final String key) { + this(key, Type.UNTYPED); } - private Statistic(/*@NotNull*/ Type type) { + Statistic(final/* @NotNull */String key, final/* @NotNull */Type type) { this.type = type; - this.key = NamespacedKey.minecraft(name().toLowerCase(Locale.ROOT)); + this.key = NamespacedKey.minecraft(key); } /** @@ -114,7 +128,6 @@ private Statistic(/*@NotNull*/ Type type) { * * @return the type of this statistic */ - @NotNull public Type getType() { return type; } @@ -146,7 +159,6 @@ public boolean isBlock() { return type == Type.BLOCK; } - @NotNull @Override public NamespacedKey getKey() { return key; @@ -155,7 +167,9 @@ public NamespacedKey getKey() { /** * The type of statistic. * + * @deprecated use {@link StatisticType} */ + @Deprecated(since = "1.21.11") public enum Type { /** * Statistics of this type do not require a qualifier. @@ -177,4 +191,36 @@ public enum Type { */ ENTITY; } + + @Deprecated + @ApiStatus.Internal + public static Statistic toLegacy(final io.papermc.paper.statistic.Statistic stat) { + Key key = stat.type().key(); + if (stat.type() == StatisticTypes.CUSTOM && stat.owner() instanceof final CustomStatistic customStatistic) { + key = customStatistic.getKey(); + } + return Objects.requireNonNull(Registry.STATISTIC.get(key), "Couldn't convert " + stat + " to a legacy stat"); + } + + @SuppressWarnings("unchecked") + @Deprecated + @ApiStatus.Internal + public io.papermc.paper.statistic.Statistic toModern(@Nullable EntityType entityType, @Nullable Material material) { + Preconditions.checkArgument(entityType == null || material == null, "No stat has an entity type and material value at the same time"); + Preconditions.checkArgument(this.type != Type.UNTYPED || (entityType == null && material == null), "no value needed for untyped stats"); + Preconditions.checkArgument(this.type != Type.ENTITY || (entityType != null && entityType != EntityType.UNKNOWN), "Must provide a valid entity type"); + Preconditions.checkArgument(this.type != Type.BLOCK || material != null && material.isBlock(), "Must provide a valid block material"); + Preconditions.checkArgument(this.type != Type.ITEM || material != null && material.isItem(), "Must provide a valid item material"); + if (this.type == Type.UNTYPED) { + return StatisticTypes.CUSTOM.forValue(Objects.requireNonNull(Registry.CUSTOM_STAT.get(this.key), "Couldn't convert " + this + " to a modern stat")); + } else { + StatisticType statType = Objects.requireNonNull(Registry.STAT_TYPE.get(this.key), "Couldn't convert " + this + " to a modern stat"); + return switch (this.type) { + case Type.BLOCK -> ((StatisticType) statType).forValue(Objects.requireNonNull(material.asBlockType())); + case Type.ITEM -> ((StatisticType) statType).forValue(Objects.requireNonNull(material.asItemType())); + case Type.ENTITY -> ((StatisticType) statType).forValue(Objects.requireNonNull(entityType)); + default -> throw new IllegalStateException("Unexpected value: " + this.type); + }; + } + } } diff --git a/paper-api/src/main/java/org/bukkit/UnsafeValues.java b/paper-api/src/main/java/org/bukkit/UnsafeValues.java index 82d8febbc565..4d90e75cc6c0 100644 --- a/paper-api/src/main/java/org/bukkit/UnsafeValues.java +++ b/paper-api/src/main/java/org/bukkit/UnsafeValues.java @@ -4,10 +4,10 @@ import io.papermc.paper.entity.EntitySerializationFlag; import io.papermc.paper.registry.RegistryKey; import net.kyori.adventure.text.event.HoverEvent; +import java.util.Map; import org.bukkit.advancement.Advancement; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; -import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; @@ -24,7 +24,6 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; /** * This interface provides value conversions that may be specific to a @@ -352,8 +351,6 @@ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { void setBiomeKey(RegionAccessor accessor, int x, int y, int z, NamespacedKey biomeKey); // Paper end - namespaced key biome methods - String getStatisticCriteriaKey(@NotNull org.bukkit.Statistic statistic); // Paper - fix custom stats criteria creation - // Paper start - spawn egg color visibility /** * Obtains the underlying color informating for a spawn egg of a given diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java index 5100c8dffe04..b3b50e607f69 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerStatisticIncrementEvent.java @@ -1,16 +1,16 @@ package org.bukkit.event.player; -import org.bukkit.Bukkit; +import io.papermc.paper.statistic.Statistic; import org.bukkit.Material; -import org.bukkit.Statistic; +import org.bukkit.block.BlockType; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; -import org.bukkit.material.MaterialData; +import org.bukkit.inventory.ItemType; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Called when a player statistic is incremented. @@ -18,56 +18,33 @@ * This event is not called for some high frequency statistics, e.g. movement * based statistics. */ +@NullMarked public class PlayerStatisticIncrementEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); - protected final Statistic statistic; + private final Statistic statistic; private final int initialValue; private final int newValue; - private final EntityType entityType; - private final Material material; + private final @Nullable EntityType entityType; + private final @Nullable Material material; private boolean cancelled; @ApiStatus.Internal - public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue) { + public PlayerStatisticIncrementEvent(final Player player, final Statistic statistic, final int initialValue, final int newValue) { super(player); this.statistic = statistic; this.initialValue = initialValue; this.newValue = newValue; - this.entityType = null; - this.material = null; - } - - @ApiStatus.Internal - public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue, @NotNull EntityType entityType) { - super(player); - this.statistic = statistic; - this.initialValue = initialValue; - this.newValue = newValue; - this.entityType = entityType; - this.material = null; - } - - @ApiStatus.Internal - public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic statistic, int initialValue, int newValue, @NotNull Material material) { - super(player); - this.statistic = statistic; - this.initialValue = initialValue; - this.newValue = newValue; - this.entityType = null; - if (material != null && material.isLegacy()) { - if (statistic.getType() == Statistic.Type.BLOCK) { - material = Bukkit.getUnsafe().fromLegacy(new MaterialData(material), false); - } else if (statistic.getType() == Statistic.Type.ITEM) { - material = Bukkit.getUnsafe().fromLegacy(new MaterialData(material), true); - } else { - // Theoretically, this should not happen, can probably print a warning, but for now it should be fine. - material = Bukkit.getUnsafe().fromLegacy(new MaterialData(material), false); - } + this.entityType = statistic.owner() instanceof final EntityType et ? et : null; + if (statistic.owner() instanceof final ItemType it) { + this.material = it.asMaterial(); + } else if (statistic.owner() instanceof final BlockType bt) { + this.material = bt.asMaterial(); + } else { + this.material = null; } - this.material = material; } /** @@ -75,11 +52,21 @@ public PlayerStatisticIncrementEvent(@NotNull Player player, @NotNull Statistic * * @return the incremented statistic */ - @NotNull - public Statistic getStatistic() { + public Statistic getStat() { return this.statistic; } + /** + * Gets the statistic that is being incremented. + * + * @return the incremented statistic + * @deprecated use {@link #getStat()} + */ + @Deprecated(since = "1.21.11") + public org.bukkit.Statistic getStatistic() { + return org.bukkit.Statistic.toLegacy(this.statistic); + } + /** * Gets the previous value of the statistic. * @@ -103,9 +90,10 @@ public int getNewValue() { * entity statistic otherwise returns {@code null}. * * @return the EntityType of the statistic + * @deprecated use {@link #getStat()} */ - @Nullable - public EntityType getEntityType() { + @Deprecated(since = "1.21.11") + public @Nullable EntityType getEntityType() { return this.entityType; } @@ -114,9 +102,10 @@ public EntityType getEntityType() { * or item statistic otherwise returns {@code null}. * * @return the Material of the statistic + * @deprecated use {@link #getStat()} */ - @Nullable - public Material getMaterial() { + @Deprecated(since = "1.21.11") + public @Nullable Material getMaterial() { return this.material; } @@ -126,17 +115,15 @@ public boolean isCancelled() { } @Override - public void setCancelled(boolean cancel) { + public void setCancelled(final boolean cancel) { this.cancelled = cancel; } - @NotNull @Override public HandlerList getHandlers() { return HANDLER_LIST; } - @NotNull public static HandlerList getHandlerList() { return HANDLER_LIST; } diff --git a/paper-api/src/main/java/org/bukkit/scoreboard/Criteria.java b/paper-api/src/main/java/org/bukkit/scoreboard/Criteria.java index 3bc3abaf093d..1ceefde26bed 100644 --- a/paper-api/src/main/java/org/bukkit/scoreboard/Criteria.java +++ b/paper-api/src/main/java/org/bukkit/scoreboard/Criteria.java @@ -1,204 +1,204 @@ package org.bukkit.scoreboard; import com.google.common.base.Preconditions; -import org.bukkit.Bukkit; +import io.papermc.paper.InternalAPIBridge; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.Statistic.Type; import org.bukkit.entity.EntityType; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NullMarked; /** * 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. *

- * 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. */ +@NullMarked public interface Criteria { /** * The dummy criteria. Not changed by the server. */ - public static final Criteria DUMMY = Bukkit.getScoreboardCriteria("dummy"); + Criteria DUMMY = InternalAPIBridge.get().getCriteria("dummy"); /** * The trigger criteria. Changed when a player runs the /trigger command for an objective. */ - public static final Criteria TRIGGER = Bukkit.getScoreboardCriteria("trigger"); + Criteria TRIGGER = InternalAPIBridge.get().getCriteria("trigger"); /** * Increments automatically when a player dies. */ - public static final Criteria DEATH_COUNT = Bukkit.getScoreboardCriteria("deathCount"); + Criteria DEATH_COUNT = InternalAPIBridge.get().getCriteria("deathCount"); /** * Increments automatically when a player kills another player. */ - public static final Criteria PLAYER_KILL_COUNT = Bukkit.getScoreboardCriteria("playerKillCount"); + Criteria PLAYER_KILL_COUNT = InternalAPIBridge.get().getCriteria("playerKillCount"); /** * Increments automatically when a player kills another living entity. */ - public static final Criteria TOTAL_KILL_COUNT = Bukkit.getScoreboardCriteria("totalKillCount"); + Criteria TOTAL_KILL_COUNT = InternalAPIBridge.get().getCriteria("totalKillCount"); /** * Mirrors the player's health points (0 for no health, 20 for maximum default health). */ - public static final Criteria HEALTH = Bukkit.getScoreboardCriteria("health"); + Criteria HEALTH = InternalAPIBridge.get().getCriteria("health"); /** * Mirrors the player's food points (0 for no food, 20 for maximum food). */ - public static final Criteria FOOD = Bukkit.getScoreboardCriteria("food"); + Criteria FOOD = InternalAPIBridge.get().getCriteria("food"); /** * Mirrors the player's air supply (0 for no air, 300 for maximum air). */ - public static final Criteria AIR = Bukkit.getScoreboardCriteria("air"); + Criteria AIR = InternalAPIBridge.get().getCriteria("air"); /** * Mirrors the player's armor points (0 for no armor, 20 for maximum armor). */ - public static final Criteria ARMOR = Bukkit.getScoreboardCriteria("armor"); + Criteria ARMOR = InternalAPIBridge.get().getCriteria("armor"); /** * Mirrors the player's experience points. */ - public static final Criteria XP = Bukkit.getScoreboardCriteria("xp"); + Criteria XP = InternalAPIBridge.get().getCriteria("xp"); /** * Mirrors the player's experience level. */ - public static final Criteria LEVEL = Bukkit.getScoreboardCriteria("level"); + Criteria LEVEL = InternalAPIBridge.get().getCriteria("level"); /** * Increments automatically when a player kills another player on the black team. */ - public static final Criteria TEAM_KILL_BLACK = Bukkit.getScoreboardCriteria("teamkill.black"); + Criteria TEAM_KILL_BLACK = InternalAPIBridge.get().getCriteria("teamkill.black"); /** * Increments automatically when a player kills another player on the dark blue team. */ - public static final Criteria TEAM_KILL_DARK_BLUE = Bukkit.getScoreboardCriteria("teamkill.dark_blue"); + Criteria TEAM_KILL_DARK_BLUE = InternalAPIBridge.get().getCriteria("teamkill.dark_blue"); /** * Increments automatically when a player kills another player on the dark green team. */ - public static final Criteria TEAM_KILL_DARK_GREEN = Bukkit.getScoreboardCriteria("teamkill.dark_green"); + Criteria TEAM_KILL_DARK_GREEN = InternalAPIBridge.get().getCriteria("teamkill.dark_green"); /** * Increments automatically when a player kills another player on the dark aqua team. */ - public static final Criteria TEAM_KILL_DARK_AQUA = Bukkit.getScoreboardCriteria("teamkill.dark_aqua"); + Criteria TEAM_KILL_DARK_AQUA = InternalAPIBridge.get().getCriteria("teamkill.dark_aqua"); /** * Increments automatically when a player kills another player on the dark red team. */ - public static final Criteria TEAM_KILL_DARK_RED = Bukkit.getScoreboardCriteria("teamkill.dark_red"); + Criteria TEAM_KILL_DARK_RED = InternalAPIBridge.get().getCriteria("teamkill.dark_red"); /** * Increments automatically when a player kills another player on the dark purple team. */ - public static final Criteria TEAM_KILL_DARK_PURPLE = Bukkit.getScoreboardCriteria("teamkill.dark_purple"); + Criteria TEAM_KILL_DARK_PURPLE = InternalAPIBridge.get().getCriteria("teamkill.dark_purple"); /** * Increments automatically when a player kills another player on the gold team. */ - public static final Criteria TEAM_KILL_GOLD = Bukkit.getScoreboardCriteria("teamkill.gold"); + Criteria TEAM_KILL_GOLD = InternalAPIBridge.get().getCriteria("teamkill.gold"); /** * Increments automatically when a player kills another player on the gray team. */ - public static final Criteria TEAM_KILL_GRAY = Bukkit.getScoreboardCriteria("teamkill.gray"); + Criteria TEAM_KILL_GRAY = InternalAPIBridge.get().getCriteria("teamkill.gray"); /** * Increments automatically when a player kills another player on the dark gray team. */ - public static final Criteria TEAM_KILL_DARK_GRAY = Bukkit.getScoreboardCriteria("teamkill.dark_gray"); + Criteria TEAM_KILL_DARK_GRAY = InternalAPIBridge.get().getCriteria("teamkill.dark_gray"); /** * Increments automatically when a player kills another player on the blue team. */ - public static final Criteria TEAM_KILL_BLUE = Bukkit.getScoreboardCriteria("teamkill.blue"); + Criteria TEAM_KILL_BLUE = InternalAPIBridge.get().getCriteria("teamkill.blue"); /** * Increments automatically when a player kills another player on the green team. */ - public static final Criteria TEAM_KILL_GREEN = Bukkit.getScoreboardCriteria("teamkill.green"); + Criteria TEAM_KILL_GREEN = InternalAPIBridge.get().getCriteria("teamkill.green"); /** * Increments automatically when a player kills another player on the aqua team. */ - public static final Criteria TEAM_KILL_AQUA = Bukkit.getScoreboardCriteria("teamkill.aqua"); + Criteria TEAM_KILL_AQUA = InternalAPIBridge.get().getCriteria("teamkill.aqua"); /** * Increments automatically when a player kills another player on the red team. */ - public static final Criteria TEAM_KILL_RED = Bukkit.getScoreboardCriteria("teamkill.red"); + Criteria TEAM_KILL_RED = InternalAPIBridge.get().getCriteria("teamkill.red"); /** * Increments automatically when a player kills another player on the light purple team. */ - public static final Criteria TEAM_KILL_LIGHT_PURPLE = Bukkit.getScoreboardCriteria("teamkill.light_purple"); + Criteria TEAM_KILL_LIGHT_PURPLE = InternalAPIBridge.get().getCriteria("teamkill.light_purple"); /** * Increments automatically when a player kills another player on the yellow team. */ - public static final Criteria TEAM_KILL_YELLOW = Bukkit.getScoreboardCriteria("teamkill.yellow"); + Criteria TEAM_KILL_YELLOW = InternalAPIBridge.get().getCriteria("teamkill.yellow"); /** * Increments automatically when a player kills another player on the white team. */ - public static final Criteria TEAM_KILL_WHITE = Bukkit.getScoreboardCriteria("teamkill.white"); + Criteria TEAM_KILL_WHITE = InternalAPIBridge.get().getCriteria("teamkill.white"); /** * Increments automatically when a player is killed by a player on the black team. */ - public static final Criteria KILLED_BY_TEAM_BLACK = Bukkit.getScoreboardCriteria("killedByTeam.black"); + Criteria KILLED_BY_TEAM_BLACK = InternalAPIBridge.get().getCriteria("killedByTeam.black"); /** * Increments automatically when a player is killed by a player on the dark blue team. */ - public static final Criteria KILLED_BY_TEAM_DARK_BLUE = Bukkit.getScoreboardCriteria("killedByTeam.dark_blue"); + Criteria KILLED_BY_TEAM_DARK_BLUE = InternalAPIBridge.get().getCriteria("killedByTeam.dark_blue"); /** * Increments automatically when a player is killed by a player on the dark green team. */ - public static final Criteria KILLED_BY_TEAM_DARK_GREEN = Bukkit.getScoreboardCriteria("killedByTeam.dark_green"); + Criteria KILLED_BY_TEAM_DARK_GREEN = InternalAPIBridge.get().getCriteria("killedByTeam.dark_green"); /** * Increments automatically when a player is killed by a player on the dark aqua team. */ - public static final Criteria KILLED_BY_TEAM_DARK_AQUA = Bukkit.getScoreboardCriteria("killedByTeam.dark_aqua"); + Criteria KILLED_BY_TEAM_DARK_AQUA = InternalAPIBridge.get().getCriteria("killedByTeam.dark_aqua"); /** * Increments automatically when a player is killed by a player on the dark red team. */ - public static final Criteria KILLED_BY_TEAM_DARK_RED = Bukkit.getScoreboardCriteria("killedByTeam.dark_red"); + Criteria KILLED_BY_TEAM_DARK_RED = InternalAPIBridge.get().getCriteria("killedByTeam.dark_red"); /** * Increments automatically when a player is killed by a player on the dark purple team. */ - public static final Criteria KILLED_BY_TEAM_DARK_PURPLE = Bukkit.getScoreboardCriteria("killedByTeam.dark_purple"); + Criteria KILLED_BY_TEAM_DARK_PURPLE = InternalAPIBridge.get().getCriteria("killedByTeam.dark_purple"); /** * Increments automatically when a player is killed by a player on the gold team. */ - public static final Criteria KILLED_BY_TEAM_GOLD = Bukkit.getScoreboardCriteria("killedByTeam.gold"); + Criteria KILLED_BY_TEAM_GOLD = InternalAPIBridge.get().getCriteria("killedByTeam.gold"); /** * Increments automatically when a player is killed by a player on the gray team. */ - public static final Criteria KILLED_BY_TEAM_GRAY = Bukkit.getScoreboardCriteria("killedByTeam.gray"); + Criteria KILLED_BY_TEAM_GRAY = InternalAPIBridge.get().getCriteria("killedByTeam.gray"); /** * Increments automatically when a player is killed by a player on the dark gray team. */ - public static final Criteria KILLED_BY_TEAM_DARK_GRAY = Bukkit.getScoreboardCriteria("killedByTeam.dark_gray"); + Criteria KILLED_BY_TEAM_DARK_GRAY = InternalAPIBridge.get().getCriteria("killedByTeam.dark_gray"); /** * Increments automatically when a player is killed by a player on the blue team. */ - public static final Criteria KILLED_BY_TEAM_BLUE = Bukkit.getScoreboardCriteria("killedByTeam.blue"); + Criteria KILLED_BY_TEAM_BLUE = InternalAPIBridge.get().getCriteria("killedByTeam.blue"); /** * Increments automatically when a player is killed by a player on the green team. */ - public static final Criteria KILLED_BY_TEAM_GREEN = Bukkit.getScoreboardCriteria("killedByTeam.green"); + Criteria KILLED_BY_TEAM_GREEN = InternalAPIBridge.get().getCriteria("killedByTeam.green"); /** * Increments automatically when a player is killed by a player on the aqua team. */ - public static final Criteria KILLED_BY_TEAM_AQUA = Bukkit.getScoreboardCriteria("killedByTeam.aqua"); + Criteria KILLED_BY_TEAM_AQUA = InternalAPIBridge.get().getCriteria("killedByTeam.aqua"); /** * Increments automatically when a player is killed by a player on the red team. */ - public static final Criteria KILLED_BY_TEAM_RED = Bukkit.getScoreboardCriteria("killedByTeam.red"); + Criteria KILLED_BY_TEAM_RED = InternalAPIBridge.get().getCriteria("killedByTeam.red"); /** * Increments automatically when a player is killed by a player on the light purple team. */ - public static final Criteria KILLED_BY_TEAM_LIGHT_PURPLE = Bukkit.getScoreboardCriteria("killedByTeam.light_purple"); + Criteria KILLED_BY_TEAM_LIGHT_PURPLE = InternalAPIBridge.get().getCriteria("killedByTeam.light_purple"); /** * Increments automatically when a player is killed by a player on the yellow team. */ - public static final Criteria KILLED_BY_TEAM_YELLOW = Bukkit.getScoreboardCriteria("killedByTeam.yellow"); + Criteria KILLED_BY_TEAM_YELLOW = InternalAPIBridge.get().getCriteria("killedByTeam.yellow"); /** * Increments automatically when a player is killed by a player on the white team. */ - public static final Criteria KILLED_BY_TEAM_WHITE = Bukkit.getScoreboardCriteria("killedByTeam.white"); + Criteria KILLED_BY_TEAM_WHITE = InternalAPIBridge.get().getCriteria("killedByTeam.white"); /** * Get the name of this criteria (its unique id). * * @return the name */ - @NotNull - public String getName(); + String getName(); /** * Get whether or not this criteria is read only. If read only, scoreboards with this criteria @@ -206,15 +206,14 @@ public interface Criteria { * * @return true if read only, false otherwise */ - public boolean isReadOnly(); + boolean isReadOnly(); /** * Get the {@link RenderType} used by default for this criteria. * * @return the default render type */ - @NotNull - public RenderType getDefaultRenderType(); + RenderType getDefaultRenderType(); /** * Get a {@link Criteria} for the specified statistic pertaining to blocks or items. @@ -241,43 +240,13 @@ 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 - public static Criteria statistic(@NotNull Statistic statistic, @NotNull Material material) { + @Deprecated(since = "1.21.11", forRemoval = true) + static Criteria statistic(Statistic statistic, Material material) { Preconditions.checkArgument(statistic != null, "statistic must not be null"); Preconditions.checkArgument(material != null, "material must not be null"); - - Type type = statistic.getType(); - Preconditions.checkArgument(type == Type.BLOCK || type == Type.ITEM, "statistic type must be either BLOCK or ITEM, given %s", type); - Preconditions.checkArgument(type != Type.BLOCK || material.isBlock(), "statistic type is BLOCK but got non-block Material, %s", material); - Preconditions.checkArgument(type != Type.ITEM || material.isItem(), "statistic type is ITEM but got non-item Material, %s", material); - - // Good use case for a switch expression - if (type == Type.BLOCK) { - switch (statistic) { - case MINE_BLOCK: - return Bukkit.getScoreboardCriteria("minecraft.mined:minecraft." + material.getKey().getKey()); - default: - break; - } - } else if (type == Type.ITEM) { - switch (statistic) { - case BREAK_ITEM: - return Bukkit.getScoreboardCriteria("minecraft.broken:minecraft." + material.getKey().getKey()); - case CRAFT_ITEM: - return Bukkit.getScoreboardCriteria("minecraft.crafted:minecraft." + material.getKey().getKey()); - case USE_ITEM: - return Bukkit.getScoreboardCriteria("minecraft.used:minecraft." + material.getKey().getKey()); - case PICKUP: - return Bukkit.getScoreboardCriteria("minecraft.picked_up:minecraft." + material.getKey().getKey()); - case DROP: - return Bukkit.getScoreboardCriteria("minecraft.dropped:minecraft." + material.getKey().getKey()); - default: - break; - } - } - - return statistic(statistic); // Fallback to a regular statistic + return statistic.toModern(null, material); } /** @@ -298,23 +267,13 @@ public static Criteria statistic(@NotNull Statistic statistic, @NotNull Material * @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 - public static Criteria statistic(@NotNull Statistic statistic, @NotNull EntityType entityType) { + @Deprecated(since = "1.21.11", forRemoval = true) + static Criteria statistic(Statistic statistic, EntityType entityType) { Preconditions.checkArgument(statistic != null, "statistic must not be null"); Preconditions.checkArgument(entityType != null, "entityType must not be null"); - Preconditions.checkArgument(statistic.getType() == Type.ENTITY, "statistic type must be ENTITY, given %s", statistic.getType()); - - switch (statistic) { - case KILL_ENTITY: - return Bukkit.getScoreboardCriteria("minecraft.killed:minecraft." + entityType.getKey().getKey()); - case ENTITY_KILLED_BY: - return Bukkit.getScoreboardCriteria("minecraft.killed_by:minecraft." + entityType.getKey().getKey()); - default: - break; - } - - return statistic(statistic); // Fallback to a regular statistic + return statistic.toModern(entityType, null); } /** @@ -331,11 +290,12 @@ public static Criteria statistic(@NotNull Statistic statistic, @NotNull EntityTy * * @param statistic the statistic for which to get a criteria * @return the criteria + * @deprecated Use {@link io.papermc.paper.statistic.Statistic} */ - @NotNull - public static Criteria statistic(@NotNull Statistic statistic) { + @Deprecated(since = "1.21.11", forRemoval = true) + static Criteria statistic(Statistic statistic) { Preconditions.checkArgument(statistic != null, "statistic must not be null"); - return Bukkit.getScoreboardCriteria(org.bukkit.Bukkit.getUnsafe().getStatisticCriteriaKey(statistic)); // Paper + return statistic.toModern(null, null); } /** @@ -343,10 +303,11 @@ public static Criteria statistic(@NotNull Statistic statistic) { * * @param name the criteria name * @return the created criteria + * @deprecated use the constants here, or {@link io.papermc.paper.statistic.Statistic} */ - @NotNull - public static Criteria create(@NotNull String name) { - return Bukkit.getScoreboardCriteria(name); + @Deprecated(since = "1.21.11", forRemoval = true) + static Criteria create(String name) { + return InternalAPIBridge.get().getCriteria(name); } } diff --git a/paper-api/src/test/java/org/bukkit/support/TestServer.java b/paper-api/src/test/java/org/bukkit/support/TestServer.java index 494419922f11..c18ec5d870ff 100644 --- a/paper-api/src/test/java/org/bukkit/support/TestServer.java +++ b/paper-api/src/test/java/org/bukkit/support/TestServer.java @@ -1,23 +1,17 @@ package org.bukkit.support; -import static org.mockito.Mockito.*; -import com.google.common.base.Preconditions; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; import java.util.logging.Logger; -import java.util.stream.Stream; import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.Server; import org.bukkit.UnsafeValues; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.SimplePluginManager; -import org.jetbrains.annotations.NotNull; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; public final class TestServer { @@ -27,8 +21,6 @@ public final class TestServer { Thread creatingThread = Thread.currentThread(); when(instance.isPrimaryThread()).then(mock -> Thread.currentThread().equals(creatingThread)); - // Paper - remove plugin manager for Paper Plugins - Logger logger = Logger.getLogger(TestServer.class.getCanonicalName()); when(instance.getLogger()).thenReturn(logger); @@ -38,19 +30,14 @@ public final class TestServer { when(instance.getBukkitVersion()).thenReturn("BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion()); - // Paper start - RegistryAccess when(instance.getRegistry(any())).then(invocationOnMock -> { return io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(((Class)invocationOnMock.getArgument(0))); }); - // Paper end - RegistryAccess UnsafeValues unsafeValues = mock(withSettings().stubOnly()); when(instance.getUnsafe()).thenReturn(unsafeValues); - // Paper start - testing changes when(instance.getTag(anyString(), any(NamespacedKey.class), any())).thenAnswer(ignored -> new io.papermc.paper.testing.EmptyTag()); - when(instance.getScoreboardCriteria(anyString())).thenReturn(null); - // Paper end - testing changes Bukkit.setServer(instance); } diff --git a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java index 6a337d533bff..41c23cca699d 100644 --- a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java +++ b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java @@ -20,12 +20,15 @@ import io.papermc.generator.rewriter.types.simple.MaterialRewriter; import io.papermc.generator.rewriter.types.simple.MemoryKeyRewriter; import io.papermc.generator.rewriter.types.simple.StatisticRewriter; +import io.papermc.generator.rewriter.types.simple.StatisticTypeRewriter; import io.papermc.generator.rewriter.types.simple.trial.VillagerProfessionRewriter; import io.papermc.generator.types.goal.MobGoalNames; import io.papermc.generator.utils.Formatting; import io.papermc.paper.datacomponent.item.SwingAnimation; import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.statistic.CustomStatistics; +import io.papermc.paper.statistic.StatisticTypes; import io.papermc.paper.world.WeatheringCopperState; import io.papermc.typewriter.preset.EnumCloneRewriter; import io.papermc.typewriter.preset.model.EnumValue; @@ -223,6 +226,8 @@ protected String rewriteFieldType(Holder.Reference(Registries.PIG_VARIANT, "getVariant")) .register("ZombieNautilusVariant", ZombieNautilus.Variant.class, new RegistryFieldRewriter<>(Registries.ZOMBIE_NAUTILUS_VARIANT, "getVariant")) .register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog")) + .register("CustomStatistics", CustomStatistics.class, new RegistryFieldRewriter<>(Registries.CUSTOM_STAT, "get")) + .register("StatisticTypes", StatisticTypes.class, new StatisticTypeRewriter()) .register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter()) // .register("ItemType", org.bukkit.inventory.ItemType.class, new io.papermc.generator.rewriter.types.simple.ItemTypeRewriter()) - disable for now, lynx want the generic type .register("BlockType", BlockType.class, new BlockTypeRewriter()) @@ -236,10 +241,6 @@ private static void bootstrapServer(PatternSourceSetRewriter sourceSet) { sourceSet .register("CraftBlockData#MAP", Types.CRAFT_BLOCK_DATA, new CraftBlockDataMapping()) .register("CraftBlockEntityStates", Types.CRAFT_BLOCK_STATES, new CraftBlockEntityStateMapping()) - .register(Types.CRAFT_STATISTIC, composite( - holder("CraftStatisticCustom", new StatisticRewriter.CraftCustom()), - holder("CraftStatisticType", new StatisticRewriter.CraftType()) - )) .register(Types.CRAFT_POTION_UTIL, composite( holder("CraftPotionUtil#upgradeable", new CraftPotionUtilRewriter("strong")), holder("CraftPotionUtil#extendable", new CraftPotionUtilRewriter("long")) diff --git a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java index 0a9f1e9a68f7..2e99992637a9 100644 --- a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java +++ b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java @@ -20,6 +20,10 @@ import io.papermc.paper.registry.data.WolfVariantRegistryEntry; import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry; import io.papermc.paper.registry.data.dialog.DialogRegistryEntry; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.CustomStatistics; +import io.papermc.paper.statistic.StatisticType; +import io.papermc.paper.statistic.StatisticTypes; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -39,6 +43,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.server.dialog.Dialogs; import net.minecraft.sounds.SoundEvents; +import net.minecraft.stats.Stats; import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -153,7 +158,9 @@ private static RegistryEntry inconsistentEntry(ResourceKey> REGISTRY_CLASS_NAME_BASED_ON_API = Set.of( BlockType.class, - ItemType.class + ItemType.class, + CustomStatistic.class, + StatisticType.class ); public static final List> BUILT_IN = List.of( @@ -170,7 +177,9 @@ private static RegistryEntry inconsistentEntry(ResourceKey> DATA_DRIVEN = List.of( diff --git a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/Types.java b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/Types.java index c84868cee130..68607171632c 100644 --- a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/Types.java +++ b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/Types.java @@ -12,8 +12,6 @@ public final class Types { public static final ClassNamed CRAFT_BLOCK_STATES = ClassNamed.of(BASE_PACKAGE + ".block", "CraftBlockStates"); - public static final ClassNamed CRAFT_STATISTIC = ClassNamed.of(BASE_PACKAGE, "CraftStatistic"); - public static final ClassNamed CRAFT_POTION_UTIL = ClassNamed.of(BASE_PACKAGE + ".potion", "CraftPotionUtil"); public static final ClassNamed FIELD_RENAME = ClassNamed.of(BASE_PACKAGE + ".legacy", "FieldRename"); diff --git a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticRewriter.java b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticRewriter.java index fa2b6d42a38c..e010e9c98f66 100644 --- a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticRewriter.java +++ b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticRewriter.java @@ -5,6 +5,7 @@ import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter; import io.papermc.generator.utils.Formatting; import io.papermc.typewriter.preset.model.EnumValue; +import java.util.List; import java.util.Map; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; @@ -60,7 +61,7 @@ public class StatisticRewriter { public static class Custom extends EnumRegistryRewriter { public Custom() { - super(Registries.CUSTOM_STAT, false); + super(Registries.CUSTOM_STAT, true); } @Override @@ -69,26 +70,6 @@ protected EnumValue.Builder rewriteEnumValue(Holder.Reference refere } } - public static class CraftCustom extends EnumRegistryRewriter { - - private static final Map INTERNAL_FIELD_RENAMES = Map.of( - "SNEAK_TIME", "CROUCH_TIME" - ); - - public CraftCustom() { - super(Registries.CUSTOM_STAT, false); - } - - @Override - protected EnumValue.Builder rewriteEnumValue(Holder.Reference reference) { - String keyedName = Formatting.formatKeyAsField(reference.key().identifier().getPath()); - - return super.rewriteEnumValue(reference) - .rename(name -> FIELD_RENAMES.getOrDefault(name, name)) - .argument("%s.%s".formatted(Stats.class.getSimpleName(), INTERNAL_FIELD_RENAMES.getOrDefault(keyedName, keyedName))); - } - } - public static class Type extends EnumRegistryRewriter> { private static final Map, String> TYPE_MAPPING = Map.of( @@ -98,7 +79,7 @@ public static class Type extends EnumRegistryRewriter> { ); public Type() { - super(Registries.STAT_TYPE, false); + super(Registries.STAT_TYPE, true); } @Override @@ -109,35 +90,18 @@ protected Iterable>> getValues() { @Override protected EnumValue.Builder rewriteEnumValue(Holder.Reference> reference) { - Class genericType = RegistryEntries.byRegistryKey(reference.value().getRegistry().key()).elementClass(); + final Class genericType = RegistryEntries.byRegistryKey(reference.value().getRegistry().key()).elementClass(); if (!TYPE_MAPPING.containsKey(genericType)) { throw new IllegalStateException("Unable to translate stat type generic " + genericType.getCanonicalName() + " into the api!"); } + final List arguments = List.of( + quoted(reference.key().identifier().getPath()), + "%s.%s".formatted(Statistic.Type.class.getSimpleName(), TYPE_MAPPING.get(genericType)) + ); return super.rewriteEnumValue(reference) .rename(name -> FIELD_RENAMES.getOrDefault(name, name)) - .argument("%s.%s".formatted(Statistic.Type.class.getSimpleName(), TYPE_MAPPING.get(genericType))); // find a more direct way? - - } - } - - public static class CraftType extends EnumRegistryRewriter> { - - public CraftType() { - super(Registries.STAT_TYPE, false); - } - - @Override - protected Iterable>> getValues() { - return BuiltInRegistries.STAT_TYPE.listElements().filter(reference -> reference.value() != Stats.CUSTOM) - .sorted(Formatting.HOLDER_ORDER)::iterator; - } - - @Override - protected EnumValue.Builder rewriteEnumValue(Holder.Reference> reference) { - return super.rewriteEnumValue(reference) - .rename(name -> FIELD_RENAMES.getOrDefault(name, name)) - .argument("%s.withDefaultNamespace(%s)".formatted(Identifier.class.getSimpleName(), quoted(reference.key().identifier().getPath()))); + .arguments(arguments); } } } diff --git a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticTypeRewriter.java b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticTypeRewriter.java new file mode 100644 index 000000000000..03b8413b52c4 --- /dev/null +++ b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/StatisticTypeRewriter.java @@ -0,0 +1,55 @@ +package io.papermc.generator.rewriter.types.simple; + +import io.papermc.generator.registry.RegistryEntries; +import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter; +import io.papermc.generator.utils.Formatting; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.StatisticType; +import java.util.Comparator; +import java.util.Map; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.Identifier; +import net.minecraft.stats.StatType; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import org.bukkit.block.BlockType; +import org.bukkit.inventory.ItemType; + +public class StatisticTypeRewriter extends RegistryFieldRewriter> { + + private static final Map, Class> BRIDGE = Map.of( + Item.class, ItemType.class, + Block.class, BlockType.class, + EntityType.class, org.bukkit.entity.EntityType.class, + Identifier.class, CustomStatistic.class + ); + + private static final Map>, String> FIELD_NAMES = RegistryEntries.byRegistryKey(Registries.STAT_TYPE).getFieldNames(); + + public StatisticTypeRewriter() { + super(Registries.STAT_TYPE, "get"); + } + + @Override + protected Comparator>> comparator() { + return Formatting.alphabeticKeyOrder(reference -> FIELD_NAMES.get(reference.key())); + } + + @Override + protected String rewriteFieldType(Holder.Reference> reference) { + final Class genericType = RegistryEntries.byRegistryKey(reference.value().getRegistry().key()).elementClass(); + if (!BRIDGE.containsKey(genericType)) { + throw new IllegalStateException("Unable to translate stat type generic " + genericType.getCanonicalName() + " into the api!"); + } + + return "%s<%s>".formatted(StatisticType.class.getSimpleName(), this.importCollector.getShortName(BRIDGE.get(genericType))); + } + + @Override + protected String rewriteFieldName(Holder.Reference> reference) { + return FIELD_NAMES.get(reference.key()); + } +} diff --git a/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch b/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch index 5b95b1fbe6b1..b4cea63f4e3d 100644 --- a/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch @@ -31,3 +31,25 @@ super.setValue(player, stat, value); this.dirty.add(stat); } +@@ -129,6 +_,21 @@ + for (Stat stat : this.getDirty()) { + map.put(stat, this.getValue(stat)); + } ++ ++ // 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(), ++ map.object2IntEntrySet() ++ .stream() ++ .collect(Object2IntOpenHashMap::new, (map1, entry) -> map1.put(io.papermc.paper.statistic.PaperStatistic.getPaperStatistic(entry.getKey()), entry.getIntValue()), Object2IntOpenHashMap::putAll) ++ ); ++ if (!statEvent.callEvent()) { ++ return; ++ } ++ map = statEvent.getStatisticMap().object2IntEntrySet().stream().collect(Object2IntOpenHashMap::new, (map1, entry) -> map1.put(io.papermc.paper.statistic.PaperStatistic.getNMSStatistic(entry.getKey()), entry.getIntValue()), Object2IntOpenHashMap::putAll); ++ } ++ // Paper end + + player.connection.send(new ClientboundAwardStatsPacket(map)); + } diff --git a/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch b/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch index a1a5baa22994..1f840e1fd3f3 100644 --- a/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch @@ -1,12 +1,11 @@ --- a/net/minecraft/stats/StatsCounter.java +++ b/net/minecraft/stats/StatsCounter.java -@@ -14,6 +_,12 @@ +@@ -14,6 +_,11 @@ public void increment(Player player, Stat stat, int amount) { int i = (int)Math.min((long)this.getValue(stat) + amount, 2147483647L); + // CraftBukkit start - fire Statistic events -+ org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(player, stat, this.getValue(stat), i); -+ if (cancellable != null && cancellable.isCancelled()) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callStatisticIncrementEvent(player, stat, this.getValue(stat), i)) { + return; + } + // CraftBukkit end diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java index 7435a9ebb62f..0db300c1c143 100644 --- a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -2,6 +2,7 @@ import com.destroystokyo.paper.PaperSkinParts; import com.destroystokyo.paper.SkinParts; +import com.google.common.base.Preconditions; import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.datacomponent.item.PaperResolvableProfile; @@ -18,6 +19,7 @@ import net.minecraft.commands.Commands; import net.minecraft.world.damagesource.FallLocation; import net.minecraft.world.entity.decoration.Mannequin; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.bukkit.GameRule; import org.bukkit.block.Biome; import org.bukkit.craftbukkit.CraftGameRule; @@ -26,10 +28,12 @@ import org.bukkit.craftbukkit.damage.CraftDamageSource; import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.craftbukkit.entity.CraftMannequin; +import org.bukkit.craftbukkit.scoreboard.CraftCriteria; import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Pose; +import org.bukkit.scoreboard.Criteria; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -124,4 +128,10 @@ public GameRule legacyGameRuleBridge(GameRule r public Set validMannequinPoses() { return CraftMannequin.VALID_POSES; } + + @Override + public Criteria getCriteria(final String key) { + Preconditions.checkArgument(ObjectiveCriteria.getCustomCriteriaNames().contains(key)); + return CraftCriteria.getFromNMS(ObjectiveCriteria.byName(key).orElseThrow()); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 121babcfdda5..879965d0e9a9 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -25,6 +25,10 @@ import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.statistic.CustomStatistics; +import io.papermc.paper.statistic.PaperCustomStatistic; +import io.papermc.paper.statistic.PaperStatisticType; +import io.papermc.paper.statistic.StatisticTypes; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -121,6 +125,8 @@ public final class PaperRegistries { start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new, true).create(PaperSoundEventRegistryEntry.PaperBuilder::new, RegistryEntryMeta.RegistryModificationApiSupport.NONE), start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(), start(Registries.GAME_RULE, RegistryKey.GAME_RULE).craft(GameRule.class, CraftGameRule::new).build(), + start(Registries.CUSTOM_STAT, RegistryKey.CUSTOM_STAT).craft(CustomStatistics.class, PaperCustomStatistic::new).build(), + start(Registries.STAT_TYPE, RegistryKey.STAT_TYPE).craft(StatisticTypes.class, PaperStatisticType::create).build(), // data-driven start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(), diff --git a/paper-server/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java b/paper-server/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java new file mode 100644 index 000000000000..38bf3a746fff --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/statistic/PaperCustomStatistic.java @@ -0,0 +1,27 @@ +package io.papermc.paper.statistic; + +import io.papermc.paper.registry.HolderableBase; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import org.bukkit.craftbukkit.CraftRegistry; + +public class PaperCustomStatistic extends HolderableBase implements CustomStatistic { + + public static CustomStatistic minecraftToBukkit(final Identifier minecraft) { + return CraftRegistry.minecraftToBukkit(minecraft, Registries.CUSTOM_STAT); + } + + public static Identifier bukkitToMinecraft(final CustomStatistic bukkit) { + return CraftRegistry.bukkitToMinecraft(bukkit); + } + + public PaperCustomStatistic(final Holder holder) { + super(holder); + } + + @Override + public String translationKey() { + return "stat." + this.getHandle().toString().replace(':', '.'); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatistic.java b/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatistic.java new file mode 100644 index 000000000000..f92bf992d466 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatistic.java @@ -0,0 +1,31 @@ +package io.papermc.paper.statistic; + +import net.minecraft.stats.Stat; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations; +import org.bukkit.scoreboard.RenderType; + +public record PaperStatistic(Stat handle, S owner, StatisticType type) implements Statistic { + + @Override + public String getName() { + return this.handle.getName(); + } + + @Override + public boolean isReadOnly() { + return this.handle.isReadOnly(); + } + + @Override + public RenderType getDefaultRenderType() { + return CraftScoreboardTranslations.toBukkitRender(this.handle.getDefaultRenderType()); + } + + public static Statistic getPaperStatistic(final Stat stat) { + return PaperStatisticType.minecraftToBukkit(stat.getType()).convertStat(stat); + } + + public static Stat getNMSStatistic(final Statistic statistic) { + return ((PaperStatistic) statistic).handle(); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java b/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java new file mode 100644 index 000000000000..72e89f6500ef --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/statistic/PaperStatisticType.java @@ -0,0 +1,96 @@ +package io.papermc.paper.statistic; + +import com.google.common.base.Preconditions; +import com.google.common.base.Suppliers; +import io.papermc.paper.registry.HolderableBase; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.stats.Stat; +import net.minecraft.stats.StatType; +import org.bukkit.Keyed; +import org.bukkit.craftbukkit.CraftRegistry; +import org.bukkit.craftbukkit.entity.CraftEntityType; +import org.bukkit.entity.EntityType; + +public class PaperStatisticType extends HolderableBase> implements StatisticType { + + private final Supplier> registryKey; + private final Map> statCacheMap; + private final Predicate typeCheck; + private final Function bukkitToMinecraft; + private final BiFunction>, S> minecraftToBukkit; + + private PaperStatisticType(final Holder> holder) { + this(holder, $ -> true, CraftRegistry::bukkitToMinecraft, CraftRegistry::minecraftToBukkit); + } + + private PaperStatisticType(final Holder> holder, final Predicate typeCheck, final Function bukkitToMinecraft, final BiFunction>, S> minecraftToBukkit) { + super(holder); + this.bukkitToMinecraft = bukkitToMinecraft; + this.minecraftToBukkit = minecraftToBukkit; + this.registryKey = Suppliers.memoize(() -> PaperRegistries.registryFromNms(this.getHandle().getRegistry().key())); + this.statCacheMap = new IdentityHashMap<>(); // identity cause keys are registry objects + this.typeCheck = typeCheck; + } + + @SuppressWarnings("unchecked") + public static StatisticType create(final Holder holder) { + // don't call .value() here, it's unbound + if (holder.is(Identifier.withDefaultNamespace("killed")) || holder.is(Identifier.withDefaultNamespace("killed_by"))) { + return new PaperStatisticType<>( + (Holder>>) holder, + t -> t != EntityType.UNKNOWN, + CraftEntityType::bukkitToMinecraft, + (entityType, $) -> CraftEntityType.minecraftToBukkit(entityType) + ); + } else { + return new PaperStatisticType<>((Holder>) holder); + } + } + + public static PaperStatisticType minecraftToBukkit(final StatType minecraft) { + return CraftRegistry.minecraftToBukkit(minecraft, Registries.STAT_TYPE); + } + + public static StatType bukkitToMinecraft(final StatisticType bukkit) { + return CraftRegistry.bukkitToMinecraft(bukkit); + } + + public Statistic convertStat(final Stat stat) { + return this.forValue(this.minecraftToBukkit.apply(stat.getValue(), this.getHandle().getRegistry().key())); + } + + @Override + public Statistic forValue(final S value) { + if (!this.typeCheck.test(value)) { + throw new IllegalArgumentException(value + " is not valid for stat type " + this.getKey()); + } + return this.statCacheMap.computeIfAbsent(value, newValue -> { + final M nmsValue = this.bukkitToMinecraft.apply(newValue); + final Stat stat = this.getHandle().get(nmsValue); + return new PaperStatistic<>(stat, newValue, this); + }); + } + + @Override + public RegistryKey registryKey() { + return this.registryKey.get(); + } + + @Override + public String translationKey() { + Preconditions.checkArgument(this != StatisticTypes.CUSTOM, this.getKey() + " does not have a translation key, see CustomStatistic#translationKey()"); + return "stat_type." + this.getKey().toString().replace(':', '.'); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/statistic/PaperTrackableStats.java b/paper-server/src/main/java/io/papermc/paper/statistic/PaperTrackableStats.java new file mode 100644 index 000000000000..1e65dff2dee1 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/statistic/PaperTrackableStats.java @@ -0,0 +1,88 @@ +package io.papermc.paper.statistic; + +import com.google.common.base.Preconditions; +import java.util.function.IntUnaryOperator; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.ServerStatsCounter; +import net.minecraft.stats.Stat; +import net.minecraft.world.scores.ScoreHolder; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager; +import org.jspecify.annotations.Nullable; + +public interface PaperTrackableStats { + + ServerStatsCounter getStatCounter(); + + @Nullable ScoreHolder getScoreHolder(); + + private ServerStatsCounter getUsableCounter() { + return this.getUsableCounter(this.getScoreHolder()); + } + + private ServerStatsCounter getUsableCounter(final @Nullable ScoreHolder from) { + return from instanceof ServerPlayer player ? player.getStats() : this.getStatCounter(); + } + + default void decrementStatistic(final Statistic statistic, final int amount) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); + + this.updateStatistic(PaperStatistic.getNMSStatistic(statistic), originalAmount -> { + final int newAmount = originalAmount - amount; + Preconditions.checkArgument(newAmount >= 0, "New amount must be greater than or equal to 0"); + return newAmount; + }); + } + + default void incrementStatistic(final Statistic statistic, final int amount) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); + + this.updateStatistic(PaperStatistic.getNMSStatistic(statistic), originalAmount -> (int) Math.min((long) originalAmount + amount, Integer.MAX_VALUE)); // clamp from StatsCounter + } + + default void setStatistic(final Statistic statistic, final int newAmount) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + Preconditions.checkArgument(newAmount >= 0, "New amount must be greater than or equal to 0"); + + this.setStatistic(PaperStatistic.getNMSStatistic(statistic), newAmount); + } + + private void updateStatistic(final Stat stat, final IntUnaryOperator operation) { + final ScoreHolder holder = this.getScoreHolder(); + final ServerStatsCounter counter = this.getUsableCounter(holder); + final int newAmount = operation.applyAsInt(counter.getValue(stat)); + setStatistic(counter, stat, newAmount, holder); + } + + private void setStatistic(final Stat stat, final int newAmount) { + final ScoreHolder holder = this.getScoreHolder(); + setStatistic(this.getUsableCounter(holder), stat, newAmount, holder); + } + + @SuppressWarnings("ConstantConditions") + private static void setStatistic(final ServerStatsCounter counter, final Stat stat, final int newAmount, final @Nullable ScoreHolder holder) { + final ServerPlayer player = holder instanceof ServerPlayer p ? p : null; + counter.setValue(player, stat, newAmount); + if (holder != null) { + ((CraftScoreboardManager) Bukkit.getScoreboardManager()).forAllObjectives(stat, holder, score -> { + score.set(newAmount); + }); + } + if (player == null) { // offline + counter.save(); + } + } + + default int getStatistic(final Statistic statistic) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + return this.getUsableCounter().getValue(PaperStatistic.getNMSStatistic(statistic)); + } + + default String getFormattedValue(final Statistic statistic) { + Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); + final Stat stat = PaperStatistic.getNMSStatistic(statistic); + return stat.format(this.getUsableCounter().getValue(stat)); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/statistic/package-info.java b/paper-server/src/main/java/io/papermc/paper/statistic/package-info.java new file mode 100644 index 000000000000..71a531e5f103 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/statistic/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.statistic; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java index 97b097eca4f1..da6a7c96f3bb 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -1,5 +1,8 @@ package org.bukkit.craftbukkit; +import com.mojang.authlib.GameProfile; +import io.papermc.paper.statistic.PaperTrackableStats; +import io.papermc.paper.statistic.Statistic; import java.io.File; import java.time.Duration; import java.time.Instant; @@ -9,6 +12,7 @@ import java.util.UUID; import net.minecraft.core.GlobalPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.NameAndId; @@ -17,24 +21,23 @@ import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.ScoreHolder; import org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.Statistic; import org.bukkit.World; import org.bukkit.ban.ProfileBanList; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.SerializableAs; import org.bukkit.craftbukkit.util.CraftLocation; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import org.jspecify.annotations.Nullable; @SerializableAs("Player") -public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable { +public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable, PaperTrackableStats { private final NameAndId nameAndId; private final CraftServer server; private final PlayerDataStorage storage; @@ -81,9 +84,13 @@ public UUID getUniqueId() { return this.nameAndId.id(); } + public GameProfile getProfile() { + return this.nameAndId.toUncompletedGameProfile(); + } + @Override public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { // Paper - return com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(this.nameAndId.toUncompletedGameProfile()); // Paper + return com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(this.getProfile()); // Paper } public Server getServer() { @@ -384,199 +391,47 @@ public Location getRespawnLocation(final boolean loadLocationAndValidate) { .orElse(null); } - private ServerStatsCounter getStatisticManager() { - return this.server.getHandle().getPlayerStats(this.nameAndId.toUncompletedGameProfile()); - } - - @Override - public void incrementStatistic(Statistic statistic) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, null); - manager.save(); - } - } - - @Override - public void decrementStatistic(Statistic statistic) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, null); - manager.save(); - } - } - - @Override - public int getStatistic(Statistic statistic) { - if (this.isOnline()) { - return this.getPlayer().getStatistic(statistic); - } else { - return CraftStatistic.getStatistic(this.getStatisticManager(), statistic); - } - } - - @Override - public void incrementStatistic(Statistic statistic, int amount) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, amount, null); - manager.save(); - } - } - - @Override - public void decrementStatistic(Statistic statistic, int amount) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, amount, null); - manager.save(); - } - } - - @Override - public void setStatistic(Statistic statistic, int newValue) { - if (this.isOnline()) { - this.getPlayer().setStatistic(statistic, newValue); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.setStatistic(manager, statistic, newValue, null); - manager.save(); - } - } - - @Override - public void incrementStatistic(Statistic statistic, Material material) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic, material); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, material, null); - manager.save(); - } - } - - @Override - public void decrementStatistic(Statistic statistic, Material material) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic, material); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, material, null); - manager.save(); - } - } - @Override - public int getStatistic(Statistic statistic, Material material) { - if (this.isOnline()) { - return this.getPlayer().getStatistic(statistic, material); - } else { - return CraftStatistic.getStatistic(this.getStatisticManager(), statistic, material); - } + public ServerStatsCounter getStatCounter() { + return this.server.getHandle().getPlayerStats(this.getProfile()); } @Override - public void incrementStatistic(Statistic statistic, Material material, int amount) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic, material, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, material, amount, null); - manager.save(); + public @Nullable ScoreHolder getScoreHolder() { + ServerPlayer onlinePlayer = MinecraftServer.getServer().getPlayerList().getPlayer(this.getUniqueId()); + if (onlinePlayer != null) { + return onlinePlayer; } - } - - @Override - public void decrementStatistic(Statistic statistic, Material material, int amount) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic, material, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, material, amount, null); - manager.save(); - } - } - @Override - public void setStatistic(Statistic statistic, Material material, int newValue) { - if (this.isOnline()) { - this.getPlayer().setStatistic(statistic, material, newValue); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.setStatistic(manager, statistic, material, newValue, null); - manager.save(); + if (this.nameAndId.name().isEmpty()) { + return null; } - } - @Override - public void incrementStatistic(Statistic statistic, EntityType entityType) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic, entityType); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, entityType, null); - manager.save(); - } + return ScoreHolder.fromGameProfile(this.getProfile()); } @Override - public void decrementStatistic(Statistic statistic, EntityType entityType) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic, entityType); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, entityType, null); - manager.save(); - } + public void decrementStatistic(final Statistic statistic, final int amount) { + PaperTrackableStats.super.decrementStatistic(statistic, amount); } @Override - public int getStatistic(Statistic statistic, EntityType entityType) { - if (this.isOnline()) { - return this.getPlayer().getStatistic(statistic, entityType); - } else { - return CraftStatistic.getStatistic(this.getStatisticManager(), statistic, entityType); - } + public void incrementStatistic(final Statistic statistic, final int amount) { + PaperTrackableStats.super.incrementStatistic(statistic, amount); } @Override - public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) { - if (this.isOnline()) { - this.getPlayer().incrementStatistic(statistic, entityType, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.incrementStatistic(manager, statistic, entityType, amount, null); - manager.save(); - } + public void setStatistic(final Statistic statistic, final int newAmount) { + PaperTrackableStats.super.setStatistic(statistic, newAmount); } @Override - public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { - if (this.isOnline()) { - this.getPlayer().decrementStatistic(statistic, entityType, amount); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.decrementStatistic(manager, statistic, entityType, amount, null); - manager.save(); - } + public int getStatistic(final Statistic statistic) { + return PaperTrackableStats.super.getStatistic(statistic); } @Override - public void setStatistic(Statistic statistic, EntityType entityType, int newValue) { - if (this.isOnline()) { - this.getPlayer().setStatistic(statistic, entityType, newValue); - } else { - ServerStatsCounter manager = this.getStatisticManager(); - CraftStatistic.setStatistic(manager, statistic, entityType, newValue, null); - manager.save(); - } + public String getFormattedValue(final Statistic statistic) { + return PaperTrackableStats.super.getFormattedValue(statistic); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java deleted file mode 100644 index e786c0cce9ce..000000000000 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java +++ /dev/null @@ -1,367 +0,0 @@ -package org.bukkit.craftbukkit; - -import com.google.common.base.Preconditions; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.stats.ServerStatsCounter; -import net.minecraft.stats.Stats; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.Statistic.Type; -import org.bukkit.block.BlockType; -import org.bukkit.craftbukkit.block.CraftBlockType; -import org.bukkit.craftbukkit.entity.CraftEntityType; -import org.bukkit.craftbukkit.inventory.CraftItemType; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemType; - -public enum CraftStatistic { - // Start generate - CraftStatisticCustom - ANIMALS_BRED(Stats.ANIMALS_BRED), - AVIATE_ONE_CM(Stats.AVIATE_ONE_CM), - BELL_RING(Stats.BELL_RING), - BOAT_ONE_CM(Stats.BOAT_ONE_CM), - ARMOR_CLEANED(Stats.CLEAN_ARMOR), - BANNER_CLEANED(Stats.CLEAN_BANNER), - CLEAN_SHULKER_BOX(Stats.CLEAN_SHULKER_BOX), - CLIMB_ONE_CM(Stats.CLIMB_ONE_CM), - CROUCH_ONE_CM(Stats.CROUCH_ONE_CM), - DAMAGE_ABSORBED(Stats.DAMAGE_ABSORBED), - DAMAGE_BLOCKED_BY_SHIELD(Stats.DAMAGE_BLOCKED_BY_SHIELD), - DAMAGE_DEALT(Stats.DAMAGE_DEALT), - DAMAGE_DEALT_ABSORBED(Stats.DAMAGE_DEALT_ABSORBED), - DAMAGE_DEALT_RESISTED(Stats.DAMAGE_DEALT_RESISTED), - DAMAGE_RESISTED(Stats.DAMAGE_RESISTED), - DAMAGE_TAKEN(Stats.DAMAGE_TAKEN), - DEATHS(Stats.DEATHS), - DROP_COUNT(Stats.DROP), - CAKE_SLICES_EATEN(Stats.EAT_CAKE_SLICE), - ITEM_ENCHANTED(Stats.ENCHANT_ITEM), - FALL_ONE_CM(Stats.FALL_ONE_CM), - CAULDRON_FILLED(Stats.FILL_CAULDRON), - FISH_CAUGHT(Stats.FISH_CAUGHT), - FLY_ONE_CM(Stats.FLY_ONE_CM), - HAPPY_GHAST_ONE_CM(Stats.HAPPY_GHAST_ONE_CM), - HORSE_ONE_CM(Stats.HORSE_ONE_CM), - DISPENSER_INSPECTED(Stats.INSPECT_DISPENSER), - DROPPER_INSPECTED(Stats.INSPECT_DROPPER), - HOPPER_INSPECTED(Stats.INSPECT_HOPPER), - INTERACT_WITH_ANVIL(Stats.INTERACT_WITH_ANVIL), - BEACON_INTERACTION(Stats.INTERACT_WITH_BEACON), - INTERACT_WITH_BLAST_FURNACE(Stats.INTERACT_WITH_BLAST_FURNACE), - BREWINGSTAND_INTERACTION(Stats.INTERACT_WITH_BREWINGSTAND), - INTERACT_WITH_CAMPFIRE(Stats.INTERACT_WITH_CAMPFIRE), - INTERACT_WITH_CARTOGRAPHY_TABLE(Stats.INTERACT_WITH_CARTOGRAPHY_TABLE), - CRAFTING_TABLE_INTERACTION(Stats.INTERACT_WITH_CRAFTING_TABLE), - FURNACE_INTERACTION(Stats.INTERACT_WITH_FURNACE), - INTERACT_WITH_GRINDSTONE(Stats.INTERACT_WITH_GRINDSTONE), - INTERACT_WITH_LECTERN(Stats.INTERACT_WITH_LECTERN), - INTERACT_WITH_LOOM(Stats.INTERACT_WITH_LOOM), - INTERACT_WITH_SMITHING_TABLE(Stats.INTERACT_WITH_SMITHING_TABLE), - INTERACT_WITH_SMOKER(Stats.INTERACT_WITH_SMOKER), - INTERACT_WITH_STONECUTTER(Stats.INTERACT_WITH_STONECUTTER), - JUMP(Stats.JUMP), - LEAVE_GAME(Stats.LEAVE_GAME), - MINECART_ONE_CM(Stats.MINECART_ONE_CM), - MOB_KILLS(Stats.MOB_KILLS), - NAUTILUS_ONE_CM(Stats.NAUTILUS_ONE_CM), - OPEN_BARREL(Stats.OPEN_BARREL), - CHEST_OPENED(Stats.OPEN_CHEST), - ENDERCHEST_OPENED(Stats.OPEN_ENDERCHEST), - SHULKER_BOX_OPENED(Stats.OPEN_SHULKER_BOX), - PIG_ONE_CM(Stats.PIG_ONE_CM), - NOTEBLOCK_PLAYED(Stats.PLAY_NOTEBLOCK), - RECORD_PLAYED(Stats.PLAY_RECORD), - PLAY_ONE_MINUTE(Stats.PLAY_TIME), - PLAYER_KILLS(Stats.PLAYER_KILLS), - FLOWER_POTTED(Stats.POT_FLOWER), - RAID_TRIGGER(Stats.RAID_TRIGGER), - RAID_WIN(Stats.RAID_WIN), - SLEEP_IN_BED(Stats.SLEEP_IN_BED), - SNEAK_TIME(Stats.CROUCH_TIME), - SPRINT_ONE_CM(Stats.SPRINT_ONE_CM), - STRIDER_ONE_CM(Stats.STRIDER_ONE_CM), - SWIM_ONE_CM(Stats.SWIM_ONE_CM), - TALKED_TO_VILLAGER(Stats.TALKED_TO_VILLAGER), - TARGET_HIT(Stats.TARGET_HIT), - TIME_SINCE_DEATH(Stats.TIME_SINCE_DEATH), - TIME_SINCE_REST(Stats.TIME_SINCE_REST), - TOTAL_WORLD_TIME(Stats.TOTAL_WORLD_TIME), - TRADED_WITH_VILLAGER(Stats.TRADED_WITH_VILLAGER), - TRAPPED_CHEST_TRIGGERED(Stats.TRIGGER_TRAPPED_CHEST), - NOTEBLOCK_TUNED(Stats.TUNE_NOTEBLOCK), - CAULDRON_USED(Stats.USE_CAULDRON), - WALK_ON_WATER_ONE_CM(Stats.WALK_ON_WATER_ONE_CM), - WALK_ONE_CM(Stats.WALK_ONE_CM), - WALK_UNDER_WATER_ONE_CM(Stats.WALK_UNDER_WATER_ONE_CM), - // End generate - CraftStatisticCustom - // Start generate - CraftStatisticType - BREAK_ITEM(Identifier.withDefaultNamespace("broken")), - CRAFT_ITEM(Identifier.withDefaultNamespace("crafted")), - DROP(Identifier.withDefaultNamespace("dropped")), - KILL_ENTITY(Identifier.withDefaultNamespace("killed")), - ENTITY_KILLED_BY(Identifier.withDefaultNamespace("killed_by")), - MINE_BLOCK(Identifier.withDefaultNamespace("mined")), - PICKUP(Identifier.withDefaultNamespace("picked_up")), - USE_ITEM(Identifier.withDefaultNamespace("used")); - // End generate - CraftStatisticType - private final Identifier minecraftKey; - private final org.bukkit.Statistic bukkit; - private static final BiMap statistics; - - static { - ImmutableBiMap.Builder statisticBuilder = ImmutableBiMap.builder(); - for (CraftStatistic statistic : CraftStatistic.values()) { - statisticBuilder.put(statistic.minecraftKey, statistic.bukkit); - } - - statistics = statisticBuilder.build(); - } - - private CraftStatistic(Identifier minecraftKey) { - this.minecraftKey = minecraftKey; - - this.bukkit = org.bukkit.Statistic.valueOf(this.name()); - Preconditions.checkState(this.bukkit != null, "Bukkit statistic %s does not exist", this.name()); - } - - public static org.bukkit.Statistic getBukkitStatistic(net.minecraft.stats.Stat statistic) { - Preconditions.checkArgument(statistic != null, "NMS Statistic cannot be null"); - Registry statRegistry = statistic.getType().getRegistry(); - Identifier nmsKey = BuiltInRegistries.STAT_TYPE.getKey(statistic.getType()); - - if (statRegistry == BuiltInRegistries.CUSTOM_STAT) { - nmsKey = (Identifier) statistic.getValue(); - } - - return statistics.get(nmsKey); - } - - public static net.minecraft.stats.Stat getNMSStatistic(org.bukkit.Statistic bukkit) { - Preconditions.checkArgument(bukkit.getType() == Statistic.Type.UNTYPED, "This method only accepts untyped statistics"); - - net.minecraft.stats.Stat nms = Stats.CUSTOM.get(statistics.inverse().get(bukkit)); - Preconditions.checkArgument(nms != null, "NMS Statistic %s does not exist", bukkit); - - return nms; - } - - private static net.minecraft.stats.Stat getBlockTypeStatistic(org.bukkit.Statistic stat, BlockType blockType) { - Preconditions.checkArgument(blockType != null, "BlockType cannot be null"); - try { - if (stat == Statistic.MINE_BLOCK) { - return Stats.BLOCK_MINED.get(CraftBlockType.bukkitToMinecraftNew(blockType)); - } - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - return null; - } - - private static net.minecraft.stats.Stat getItemTypeStatistic(org.bukkit.Statistic stat, ItemType itemType) { - Preconditions.checkArgument(itemType != null, "ItemType cannot be null"); - try { - if (stat == Statistic.CRAFT_ITEM) { - return Stats.ITEM_CRAFTED.get(CraftItemType.bukkitToMinecraftNew(itemType)); - } - if (stat == Statistic.USE_ITEM) { - return Stats.ITEM_USED.get(CraftItemType.bukkitToMinecraftNew(itemType)); - } - if (stat == Statistic.BREAK_ITEM) { - return Stats.ITEM_BROKEN.get(CraftItemType.bukkitToMinecraftNew(itemType)); - } - if (stat == Statistic.PICKUP) { - return Stats.ITEM_PICKED_UP.get(CraftItemType.bukkitToMinecraftNew(itemType)); - } - if (stat == Statistic.DROP) { - return Stats.ITEM_DROPPED.get(CraftItemType.bukkitToMinecraftNew(itemType)); - } - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - return null; - } - - public static net.minecraft.stats.Stat getEntityStatistic(org.bukkit.Statistic stat, EntityType entity) { - Preconditions.checkArgument(entity != null, "EntityType cannot be null"); - if (entity.getName() != null) { - net.minecraft.world.entity.EntityType nmsEntity = CraftEntityType.bukkitToMinecraft(entity); - - if (stat == org.bukkit.Statistic.KILL_ENTITY) { - return net.minecraft.stats.Stats.ENTITY_KILLED.get(nmsEntity); - } - if (stat == org.bukkit.Statistic.ENTITY_KILLED_BY) { - return net.minecraft.stats.Stats.ENTITY_KILLED_BY.get(nmsEntity); - } - } - return null; - } - - public static EntityType getEntityTypeFromStatistic(net.minecraft.stats.Stat> statistic) { - Preconditions.checkArgument(statistic != null, "NMS Statistic cannot be null"); - return CraftEntityType.minecraftToBukkit(statistic.getValue()); - } - - public static Material getMaterialFromStatistic(net.minecraft.stats.Stat statistic) { - if (statistic.getValue() instanceof Item statisticItemValue) { - return CraftItemType.minecraftToBukkit(statisticItemValue); - } - if (statistic.getValue() instanceof Block statisticBlockValue) { - return CraftBlockType.minecraftToBukkit(statisticBlockValue); - } - return null; - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, ServerPlayer player) { - incrementStatistic(manager, statistic, 1, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, ServerPlayer player) { - decrementStatistic(manager, statistic, 1, player); - } - - public static int getStatistic(ServerStatsCounter manager, Statistic statistic) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(statistic.getType() == Type.UNTYPED, "Must supply additional parameter for this statistic"); - return manager.getValue(CraftStatistic.getNMSStatistic(statistic)); - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, getStatistic(manager, statistic) + amount, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, getStatistic(manager, statistic) - amount, player); - } - - public static void setStatistic(ServerStatsCounter manager, Statistic statistic, int newValue, ServerPlayer player) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(statistic.getType() == Type.UNTYPED, "Must supply additional parameter for this statistic"); - Preconditions.checkArgument(newValue >= 0, "Value must be greater than or equal to 0"); - net.minecraft.stats.Stat nmsStatistic = CraftStatistic.getNMSStatistic(statistic); - manager.setValue(null, nmsStatistic, newValue); - - // Update scoreboards - if (player != null) { - player.level().getCraftServer().getScoreboardManager().forAllObjectives(nmsStatistic, player, score -> { - score.set(newValue); - }); - } - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, Material material, ServerPlayer player) { - incrementStatistic(manager, statistic, material, 1, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, Material material, ServerPlayer player) { - decrementStatistic(manager, statistic, material, 1, player); - } - - public static int getStatistic(ServerStatsCounter manager, Statistic statistic, Material material) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(material != null, "Material cannot be null"); - net.minecraft.stats.Stat nmsStatistic; - if (statistic.getType() == Type.BLOCK) { - Preconditions.checkArgument(material.isBlock(), "Material %s must be a block", material); - BlockType blockType = material.asBlockType(); - nmsStatistic = CraftStatistic.getBlockTypeStatistic(statistic, blockType); - } else if (statistic.getType() == Type.ITEM) { - Preconditions.checkArgument(material.isItem(), "Material %s must be an item", material); - ItemType itemType = material.asItemType(); - nmsStatistic = CraftStatistic.getItemTypeStatistic(statistic, itemType); - } else { - throw new IllegalArgumentException("This statistic does not take a Material parameter"); - } - Preconditions.checkArgument(nmsStatistic != null, "The supplied Material %s does not have a corresponding statistic", material); - return manager.getValue(nmsStatistic); - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, Material material, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, material, getStatistic(manager, statistic, material) + amount, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, Material material, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, material, getStatistic(manager, statistic, material) - amount, player); - } - - public static void setStatistic(ServerStatsCounter manager, Statistic statistic, Material material, int newValue, ServerPlayer player) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(material != null, "Material cannot be null"); - Preconditions.checkArgument(newValue >= 0, "Value must be greater than or equal to 0"); - net.minecraft.stats.Stat nmsStatistic; - if (statistic.getType() == Type.BLOCK) { - Preconditions.checkArgument(material.isBlock(), "Material %s must be a block", material); - BlockType blockType = material.asBlockType(); - nmsStatistic = CraftStatistic.getBlockTypeStatistic(statistic, blockType); - } else if (statistic.getType() == Type.ITEM) { - Preconditions.checkArgument(material.isItem(), "Material %s must be an item", material); - ItemType itemType = material.asItemType(); - nmsStatistic = CraftStatistic.getItemTypeStatistic(statistic, itemType); - } else { - throw new IllegalArgumentException("This statistic does not take a Material parameter"); - } - Preconditions.checkArgument(nmsStatistic != null, "The supplied Material %s does not have a corresponding statistic", material); - manager.setValue(null, nmsStatistic, newValue); - - // Update scoreboards - if (player != null) { - player.level().getCraftServer().getScoreboardManager().forAllObjectives(nmsStatistic, player, score -> { - score.set(newValue); - }); - } - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType, ServerPlayer player) { - incrementStatistic(manager, statistic, entityType, 1, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType, ServerPlayer player) { - decrementStatistic(manager, statistic, entityType, 1, player); - } - - public static int getStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); - Preconditions.checkArgument(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); - net.minecraft.stats.Stat nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); - Preconditions.checkArgument(nmsStatistic != null, "The supplied EntityType %s does not have a corresponding statistic", entityType); - return manager.getValue(nmsStatistic); - } - - public static void incrementStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, entityType, getStatistic(manager, statistic, entityType) + amount, player); - } - - public static void decrementStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType, int amount, ServerPlayer player) { - Preconditions.checkArgument(amount > 0, "Amount must be greater than 0"); - setStatistic(manager, statistic, entityType, getStatistic(manager, statistic, entityType) - amount, player); - } - - public static void setStatistic(ServerStatsCounter manager, Statistic statistic, EntityType entityType, int newValue, ServerPlayer player) { - Preconditions.checkArgument(statistic != null, "Statistic cannot be null"); - Preconditions.checkArgument(entityType != null, "EntityType cannot be null"); - Preconditions.checkArgument(newValue >= 0, "Value must be greater than or equal to 0"); - Preconditions.checkArgument(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); - net.minecraft.stats.Stat nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); - Preconditions.checkArgument(nmsStatistic != null, "The supplied EntityType %s does not have a corresponding statistic", entityType); - manager.setValue(null, nmsStatistic, newValue); - - // Update scoreboards - if (player != null) { - player.level().getCraftServer().getScoreboardManager().forAllObjectives(nmsStatistic, player, score -> { - score.set(newValue); - }); - } - } -} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 2a572ef0ba74..6c1ea5647985 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1219,7 +1219,7 @@ public Set getTrackedPlayers() { Set set = new java.util.HashSet<>(tracker.seenBy.size()); for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy) { - set.add(connection.getPlayer().getBukkitEntity().getPlayer()); + set.add(connection.getPlayer().getBukkitEntity()); } return set; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index d86ebc4cd182..9e09d16e19b1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -19,6 +19,8 @@ import io.papermc.paper.entity.PaperPlayerGiveResult; import io.papermc.paper.entity.PlayerGiveResult; import io.papermc.paper.math.Position; +import io.papermc.paper.statistic.PaperTrackableStats; +import io.papermc.paper.statistic.Statistic; import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSet; @@ -113,6 +115,7 @@ import net.minecraft.server.permissions.PermissionLevel; import net.minecraft.server.players.UserWhiteListEntry; import net.minecraft.sounds.SoundEvent; +import net.minecraft.stats.ServerStatsCounter; import net.minecraft.util.ProblemReporter; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; @@ -134,6 +137,7 @@ import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; +import net.minecraft.world.scores.ScoreHolder; import org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.Bukkit; @@ -151,7 +155,6 @@ import org.bukkit.Particle; import org.bukkit.ServerLinks; import org.bukkit.Sound; -import org.bukkit.Statistic; import org.bukkit.WeatherType; import org.bukkit.WorldBorder; import org.bukkit.ban.IpBanList; @@ -174,7 +177,6 @@ import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServerLinks; import org.bukkit.craftbukkit.CraftSound; -import org.bukkit.craftbukkit.CraftStatistic; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorldBorder; import org.bukkit.craftbukkit.advancement.CraftAdvancement; @@ -198,7 +200,6 @@ import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Item; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -221,7 +222,7 @@ import org.jspecify.annotations.Nullable; @DelegateDeserialization(CraftOfflinePlayer.class) -public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessageBridgeImpl { +public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessageBridgeImpl, PaperTrackableStats { private static final org.slf4j.Logger LOGGER = LogUtils.getClassLogger(); private static final PointersSupplier POINTERS_SUPPLIER = PointersSupplier.builder() .parent(CraftEntity.POINTERS_SUPPLIER) @@ -1436,93 +1437,38 @@ public Set getDiscoveredRecipes() { } @Override - public void incrementStatistic(Statistic statistic) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, this.getHandle()); + public ServerStatsCounter getStatCounter() { + return this.getHandle().getStats(); } @Override - public void decrementStatistic(Statistic statistic) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, this.getHandle()); + public ScoreHolder getScoreHolder() { + return this.getHandle(); } @Override - public int getStatistic(Statistic statistic) { - return CraftStatistic.getStatistic(this.getHandle().getStats(), statistic); + public void decrementStatistic(final Statistic statistic, final int amount) { + PaperTrackableStats.super.decrementStatistic(statistic, amount); } @Override - public void incrementStatistic(Statistic statistic, int amount) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, amount, this.getHandle()); + public void incrementStatistic(final Statistic statistic, final int amount) { + PaperTrackableStats.super.incrementStatistic(statistic, amount); } @Override - public void decrementStatistic(Statistic statistic, int amount) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, amount, this.getHandle()); + public void setStatistic(final Statistic statistic, final int newAmount) { + PaperTrackableStats.super.setStatistic(statistic, newAmount); } @Override - public void setStatistic(Statistic statistic, int newValue) { - CraftStatistic.setStatistic(this.getHandle().getStats(), statistic, newValue, this.getHandle()); + public int getStatistic(final Statistic statistic) { + return PaperTrackableStats.super.getStatistic(statistic); } @Override - public void incrementStatistic(Statistic statistic, Material material) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, material, this.getHandle()); - } - - @Override - public void decrementStatistic(Statistic statistic, Material material) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, material, this.getHandle()); - } - - @Override - public int getStatistic(Statistic statistic, Material material) { - return CraftStatistic.getStatistic(this.getHandle().getStats(), statistic, material); - } - - @Override - public void incrementStatistic(Statistic statistic, Material material, int amount) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, material, amount, this.getHandle()); - } - - @Override - public void decrementStatistic(Statistic statistic, Material material, int amount) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, material, amount, this.getHandle()); - } - - @Override - public void setStatistic(Statistic statistic, Material material, int newValue) { - CraftStatistic.setStatistic(this.getHandle().getStats(), statistic, material, newValue, this.getHandle()); - } - - @Override - public void incrementStatistic(Statistic statistic, EntityType entityType) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, entityType, this.getHandle()); - } - - @Override - public void decrementStatistic(Statistic statistic, EntityType entityType) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, entityType, this.getHandle()); - } - - @Override - public int getStatistic(Statistic statistic, EntityType entityType) { - return CraftStatistic.getStatistic(this.getHandle().getStats(), statistic, entityType); - } - - @Override - public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) { - CraftStatistic.incrementStatistic(this.getHandle().getStats(), statistic, entityType, amount, this.getHandle()); - } - - @Override - public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { - CraftStatistic.decrementStatistic(this.getHandle().getStats(), statistic, entityType, amount, this.getHandle()); - } - - @Override - public void setStatistic(Statistic statistic, EntityType entityType, int newValue) { - CraftStatistic.setStatistic(this.getHandle().getStats(), statistic, entityType, newValue, this.getHandle()); + public String getFormattedValue(final Statistic statistic) { + return PaperTrackableStats.super.getFormattedValue(statistic); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index cd83ca2ace1d..15414b25c9a3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -6,15 +6,6 @@ import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Either; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.block.bed.BedEnterProblem; import io.papermc.paper.connection.HorriblePlayerLoginEventHack; @@ -23,17 +14,32 @@ import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent; import io.papermc.paper.event.player.PlayerBedFailEnterEvent; +import io.papermc.paper.statistic.PaperStatistic; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.Connection; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ServerboundContainerClosePacket; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; +import net.minecraft.stats.Stat; +import net.minecraft.stats.Stats; import net.minecraft.tags.DamageTypeTags; import net.minecraft.util.Unit; +import net.minecraft.util.Util; import net.minecraft.world.Container; import net.minecraft.world.InteractionHand; import net.minecraft.world.LockCode; @@ -45,14 +51,9 @@ import net.minecraft.world.entity.Leashable; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.PathfinderMob; -import net.minecraft.world.entity.animal.fish.AbstractFish; -import net.minecraft.world.entity.animal.golem.AbstractGolem; import net.minecraft.world.entity.animal.Animal; -import net.minecraft.world.entity.animal.fish.WaterAnimal; +import net.minecraft.world.entity.animal.fish.AbstractFish; import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.monster.Ghast; -import net.minecraft.world.entity.monster.Monster; -import net.minecraft.world.entity.monster.Slime; import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.raid.Raid; @@ -84,7 +85,6 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.PortalType; -import org.bukkit.Statistic.Type; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; @@ -98,7 +98,6 @@ import org.bukkit.craftbukkit.CraftLootTable; import org.bukkit.craftbukkit.CraftRaid; import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftStatistic; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlockState; @@ -120,7 +119,6 @@ import org.bukkit.entity.AreaEffectCloud; import org.bukkit.entity.Bat; import org.bukkit.entity.Creeper; -import org.bukkit.entity.EntityType; import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.Explosive; import org.bukkit.entity.Firework; @@ -141,7 +139,6 @@ import org.bukkit.entity.Vehicle; import org.bukkit.entity.Villager; import org.bukkit.entity.Villager.Profession; -import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.Event.Result; import org.bukkit.event.block.Action; @@ -1700,53 +1697,27 @@ public static PlayerShearEntityEvent handlePlayerShearEntityEvent(net.minecraft. return event; // Paper - custom shear drops } - public static Cancellable handleStatisticsIncrease(net.minecraft.world.entity.player.Player entityHuman, net.minecraft.stats.Stat statistic, int current, int newValue) { - Player player = ((ServerPlayer) entityHuman).getBukkitEntity(); - 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: - case HAPPY_GHAST_ONE_CM: - case NAUTILUS_ONE_CM: - // Do not process event for these - too spammy - return null; - default: - } + private static final Set IGNORED_STATS_FOR_EVENT = Util.make(new HashSet<>(), set -> { + set.add(Stats.TIME_SINCE_DEATH); + set.add(Stats.TIME_SINCE_REST); + set.add(Stats.CROUCH_TIME); + set.add(Stats.TOTAL_WORLD_TIME); + set.add(Stats.PLAY_TIME); - final Event event; - 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>) statistic); - event = new PlayerStatisticIncrementEvent(player, stat, current, newValue, entityType); - } else { - Material material = CraftStatistic.getMaterialFromStatistic(statistic); - event = new PlayerStatisticIncrementEvent(player, stat, current, newValue, material); + BuiltInRegistries.CUSTOM_STAT.stream().forEach(key -> { + if (key.getPath().endsWith("_one_cm")) { + set.add(key); + } + }); + }); + + public static boolean callStatisticIncrementEvent(final net.minecraft.world.entity.player.Player player, final Stat stat, final int currentValue, final int newValue) { + if (stat.getType() == Stats.CUSTOM && stat.getValue() instanceof final Identifier key && IGNORED_STATS_FOR_EVENT.contains(key)) { + // Do not process event for these - too spammy + return true; } - event.callEvent(); - return (Cancellable) event; + + return new PlayerStatisticIncrementEvent(((ServerPlayer) player).getBukkitEntity(), PaperStatistic.getPaperStatistic(stat), currentValue, newValue).callEvent(); } public static boolean callFireworkExplodeEvent(FireworkRocketEntity firework) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java index 6f2e24bbe18b..736254435dfd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java @@ -1,28 +1,31 @@ package org.bukkit.craftbukkit.scoreboard; import com.google.common.collect.ImmutableMap; +import io.papermc.paper.statistic.PaperStatistic; import java.util.Map; +import java.util.Objects; +import net.minecraft.stats.Stat; import net.minecraft.world.scores.Objective; import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.bukkit.scoreboard.Criteria; import org.bukkit.scoreboard.RenderType; public final class CraftCriteria implements Criteria { - private static final Map DEFAULTS; + private static final Map DEFAULTS; private static final CraftCriteria DUMMY; static { - ImmutableMap.Builder defaults = ImmutableMap.builder(); + ImmutableMap.Builder defaults = ImmutableMap.builder(); for (Map.Entry entry : ObjectiveCriteria.CRITERIA_CACHE.entrySet()) { String name = entry.getKey(); ObjectiveCriteria criteria = entry.getValue(); - defaults.put(name, new CraftCriteria(criteria)); + defaults.put(name, convertFromNms(criteria)); } DEFAULTS = defaults.build(); - DUMMY = DEFAULTS.get(ObjectiveCriteria.DUMMY.getName()); + DUMMY = (CraftCriteria) DEFAULTS.get(ObjectiveCriteria.DUMMY.getName()); } final ObjectiveCriteria criteria; @@ -53,21 +56,25 @@ public RenderType getDefaultRenderType() { return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()]; } - public static CraftCriteria getFromNMS(ObjectiveCriteria criteria) { - return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(criteria.getName()), () -> new CraftCriteria(criteria)); + public static Criteria getFromNMS(ObjectiveCriteria criteria) { + return Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(criteria.getName()), () -> convertFromNms(criteria)); } - public static CraftCriteria getFromNMS(Objective objective) { + static Criteria convertFromNms(ObjectiveCriteria criteria) { + return criteria instanceof Stat stat ? PaperStatistic.getPaperStatistic(stat) : new CraftCriteria(criteria); + } + + public static Criteria getFromNMS(Objective objective) { return getFromNMS(objective.getCriteria()); } - public static CraftCriteria getFromBukkit(String name) { - CraftCriteria criteria = CraftCriteria.DEFAULTS.get(name); + public static Criteria getFromBukkit(String name) { + Criteria criteria = CraftCriteria.DEFAULTS.get(name); 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)); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java index f1a8529bac83..3b710ea1b9bd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java @@ -13,7 +13,7 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective { private final net.minecraft.world.scores.Objective objective; - private final CraftCriteria criteria; + private final Criteria criteria; CraftObjective(CraftScoreboard scoreboard, net.minecraft.world.scores.Objective objective) { super(scoreboard); @@ -66,7 +66,7 @@ public void setDisplayName(String displayName) { public String getCriteria() { this.checkState(); - return this.criteria.name; + return this.criteria.getName(); } @Override @@ -80,7 +80,7 @@ public Criteria getTrackedCriteria() { public boolean isModifiable() { this.checkState(); - return !this.criteria.criteria.isReadOnly(); + return !this.criteria.isReadOnly(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java index f2878a49fdca..782a1d0a5b85 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java @@ -4,11 +4,15 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import io.papermc.paper.adventure.PaperAdventure; +import java.util.Optional; import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.ScoreHolder; import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.craftbukkit.CraftOfflinePlayer; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.scoreboard.Criteria; import org.bukkit.scoreboard.DisplaySlot; @@ -41,12 +45,13 @@ public Objective registerNewObjective(String name, Criteria criteria, net.kyori. 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.getHandle().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) { + final Optional nmsCriteria = ObjectiveCriteria.byName(criteria.getName()); + if (nmsCriteria.isPresent() && nmsCriteria.get() != ObjectiveCriteria.DUMMY && !this.registeredGlobally) { net.minecraft.server.MinecraftServer.getServer().server.getScoreboardManager().registerScoreboardForVanilla(this); this.registeredGlobally = true; } // Paper end - lazily track plugin scoreboards - net.minecraft.world.scores.Objective objective = this.getHandle().addObjective(name, ((CraftCriteria) criteria).criteria, io.papermc.paper.adventure.PaperAdventure.asVanilla(displayName), CraftScoreboardTranslations.fromBukkitRender(renderType), true, null); + final net.minecraft.world.scores.Objective objective = this.getHandle().addObjective(name, nmsCriteria.orElse(ObjectiveCriteria.DUMMY), PaperAdventure.asVanilla(displayName), CraftScoreboardTranslations.fromBukkitRender(renderType), true, null); // Paper - stats API return new CraftObjective(this, objective); } @@ -237,10 +242,10 @@ static ScoreHolder getScoreHolder(String entry) { static ScoreHolder getScoreHolder(OfflinePlayer player) { Preconditions.checkArgument(player != null, "OfflinePlayer cannot be null"); - if (player instanceof CraftPlayer craft) { - return craft.getHandle(); - } else { - return CraftScoreboard.getScoreHolder(player.getName()); - } + return switch (player) { + case CraftPlayer craft -> craft.getHandle(); + case CraftOfflinePlayer craft -> ScoreHolder.fromGameProfile((craft.getProfile())); + default -> throw new UnsupportedOperationException("Cannot convert to a score holder"); + }; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java index c0101064438b..73e42d0d3348 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java @@ -16,11 +16,11 @@ public static net.minecraft.world.scores.DisplaySlot fromBukkitSlot(DisplaySlot return net.minecraft.world.scores.DisplaySlot.CODEC.byName(slot.getId()); // Paper } - static RenderType toBukkitRender(ObjectiveCriteria.RenderType display) { + public static RenderType toBukkitRender(ObjectiveCriteria.RenderType display) { return RenderType.valueOf(display.name()); } - static ObjectiveCriteria.RenderType fromBukkitRender(RenderType render) { + public static ObjectiveCriteria.RenderType fromBukkitRender(RenderType render) { return ObjectiveCriteria.RenderType.valueOf(render.name()); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index cfa4c67e80a5..1de65dc4b4ad 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -14,7 +14,7 @@ import com.mojang.serialization.JavaOps; import com.mojang.serialization.JsonOps; import io.papermc.paper.adventure.AdventureCodecs; -import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.entity.EntitySerializationFlag; import io.papermc.paper.registry.RegistryKey; import java.io.File; import java.io.IOException; @@ -28,7 +28,6 @@ import java.util.Set; import java.util.logging.Level; import java.util.stream.Stream; -import io.papermc.paper.entity.EntitySerializationFlag; import net.kyori.adventure.text.event.HoverEvent; import net.minecraft.SharedConstants; import net.minecraft.advancements.AdvancementHolder; @@ -61,7 +60,6 @@ import org.bukkit.Keyed; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.UnsafeValues; import org.bukkit.World; import org.bukkit.advancement.Advancement; @@ -827,12 +825,6 @@ public void setBiomeKey(org.bukkit.RegionAccessor accessor, int x, int y, int z, accessor.setBiome(x, y, z, org.bukkit.Registry.BIOME.getOrThrow(biomeKey)); } - @Override - public String getStatisticCriteriaKey(org.bukkit.Statistic statistic) { - if (statistic.getType() != org.bukkit.Statistic.Type.UNTYPED) return "minecraft.custom:minecraft." + statistic.getKey().getKey(); - return org.bukkit.craftbukkit.CraftStatistic.getNMSStatistic(statistic).getName(); - } - @Override public List computeTooltipLines(final ItemStack itemStack, final io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, final org.bukkit.entity.Player player) { Preconditions.checkArgument(tooltipContext != null, "tooltipContext cannot be null"); diff --git a/paper-server/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java b/paper-server/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java deleted file mode 100644 index 34548dcc730c..000000000000 --- a/paper-server/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.bukkit; - -import static org.bukkit.support.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; -import com.google.common.collect.HashMultiset; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.stats.StatType; -import org.bukkit.craftbukkit.CraftStatistic; -import org.bukkit.entity.EntityType; -import org.bukkit.support.environment.AllFeatures; -import org.junit.jupiter.api.Test; - -@AllFeatures -public class StatisticsAndAchievementsTest { - - @Test - public void verifyEntityMapping() { - for (Statistic statistic : Statistic.values()) { - if (statistic.getType() == Statistic.Type.ENTITY) { - for (EntityType entity : EntityType.values()) { - if (entity.getName() != null) { - assertNotNull(CraftStatistic.getEntityStatistic(statistic, entity), statistic + " missing for " + entity); - } - } - } - } - } - - @Test - @SuppressWarnings("unchecked") - public void verifyStatisticMapping() { - HashMultiset statistics = HashMultiset.create(); - for (StatType wrapper : BuiltInRegistries.STAT_TYPE) { - for (Object child : wrapper.getRegistry()) { - net.minecraft.stats.Stat statistic = wrapper.get(child); - String message = String.format("org.bukkit.Statistic is missing: '%s'", statistic); - - Statistic subject = CraftStatistic.getBukkitStatistic(statistic); - assertThat(subject, is(not(nullValue())), message); - - if (wrapper.getRegistry() == BuiltInRegistries.BLOCK || wrapper.getRegistry() == BuiltInRegistries.ITEM) { - assertNotNull(CraftStatistic.getMaterialFromStatistic(statistic), "Material type map missing for " + wrapper.getRegistry().getKey(child)); - } else if (wrapper.getRegistry() == BuiltInRegistries.ENTITY_TYPE) { - assertNotNull(CraftStatistic.getEntityTypeFromStatistic((net.minecraft.stats.Stat>) statistic), "Entity type map missing for " + net.minecraft.world.entity.EntityType.getKey((net.minecraft.world.entity.EntityType) child)); - } - - statistics.add(subject); - } - } - - for (Statistic statistic : Statistic.values()) { - String message = String.format("org.bukkit.Statistic.%s does not have a corresponding minecraft statistic", statistic.name()); - assertThat(statistics.remove(statistic, statistics.count(statistic)), is(greaterThan(0)), message); - } - } -} diff --git a/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java b/paper-server/src/test/java/org/bukkit/scoreboard/CriteriaTest.java similarity index 85% rename from paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java rename to paper-server/src/test/java/org/bukkit/scoreboard/CriteriaTest.java index 18a2c5a10752..3a070c69ad5b 100644 --- a/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java +++ b/paper-server/src/test/java/org/bukkit/scoreboard/CriteriaTest.java @@ -1,14 +1,17 @@ package org.bukkit.scoreboard; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.entity.EntityType; -import org.bukkit.support.AbstractTestingBase; +import org.bukkit.support.environment.VanillaFeature; import org.junit.jupiter.api.Test; -public class CriteriaTest extends AbstractTestingBase { +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@VanillaFeature +public class CriteriaTest { @Test public void testStatistic() { diff --git a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java index 00b949fc0982..61cb025bc192 100644 --- a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +++ b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java @@ -6,19 +6,27 @@ import io.papermc.paper.dialog.PaperDialog; import io.papermc.paper.registry.PaperRegistries; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.statistic.CustomStatistic; +import io.papermc.paper.statistic.CustomStatistics; +import io.papermc.paper.statistic.PaperCustomStatistic; +import io.papermc.paper.statistic.PaperStatisticType; +import io.papermc.paper.statistic.StatisticType; +import io.papermc.paper.statistic.StatisticTypes; import java.util.List; import java.util.stream.Stream; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; +import net.minecraft.stats.StatType; import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.entity.animal.chicken.ChickenVariant; import net.minecraft.world.entity.animal.cow.CowVariant; -import net.minecraft.world.entity.animal.pig.PigVariant; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.entity.animal.frog.FrogVariant; +import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.pig.PigVariant; import net.minecraft.world.entity.animal.wolf.WolfSoundVariant; import net.minecraft.world.entity.animal.wolf.WolfVariant; import net.minecraft.world.entity.decoration.painting.PaintingVariant; @@ -149,6 +157,8 @@ public Object[] get() { register(Registries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant.class, ZombieNautilusVariant.class); register(Registries.DIALOG, Dialog.class, PaperDialog.class, net.minecraft.server.dialog.Dialog.class); register(Registries.GAME_RULE, GameRule.class, GameRules.class, CraftGameRule.class, net.minecraft.world.level.gamerules.GameRule.class); + register(Registries.CUSTOM_STAT, CustomStatistic.class, CustomStatistics.class, PaperCustomStatistic.class, Identifier.class); + register(Registries.STAT_TYPE, StatisticType.class, StatisticTypes.class, PaperStatisticType.class, StatType.class); } private static void register(ResourceKey> registryKey, Class api, Class impl, Class internal) {