#1029: Trial changing a small number of inner enums to classes/interfaces to better support custom values

This PR is a subset of the enum PR #660 and is designed as a low impact
trial run of the design and backwards compatibility to inform subsequent
development.

Additional plugin compatibility features may be available by setting
`settings.compatibility.enum-compatibility-mode` to `true` in
`bukkit.yml`.

By: DerFrZocker <derrieple@gmail.com>
This commit is contained in:
Bukkit/Spigot 2024-07-06 17:14:17 +10:00
parent 250d585147
commit abc756fce8
7 changed files with 293 additions and 128 deletions

View file

@ -134,7 +134,7 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
*
* @see Cat.Type
*/
Registry<Cat.Type> CAT_VARIANT = new SimpleRegistry<>(Cat.Type.class);
Registry<Cat.Type> CAT_VARIANT = Objects.requireNonNull(Bukkit.getRegistry(Cat.Type.class), "No registry present for Cat Type. This is a bug.");
/**
* Server enchantments.
*
@ -248,13 +248,13 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
*
* @see Villager.Profession
*/
Registry<Villager.Profession> VILLAGER_PROFESSION = new SimpleRegistry<>(Villager.Profession.class);
Registry<Villager.Profession> VILLAGER_PROFESSION = Objects.requireNonNull(Bukkit.getRegistry(Villager.Profession.class), "No registry present for Villager Profession. This is a bug.");
/**
* Villager type.
*
* @see Villager.Type
*/
Registry<Villager.Type> VILLAGER_TYPE = new SimpleRegistry<>(Villager.Type.class);
Registry<Villager.Type> VILLAGER_TYPE = Objects.requireNonNull(Bukkit.getRegistry(Villager.Type.class), "No registry present for Villager Type. This is a bug.");
/**
* Memory Keys.
*
@ -291,7 +291,7 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
*
* @see Frog.Variant
*/
Registry<Frog.Variant> FROG_VARIANT = new SimpleRegistry<>(Frog.Variant.class);
Registry<Frog.Variant> FROG_VARIANT = Objects.requireNonNull(Bukkit.getRegistry(Frog.Variant.class), "No registry present for Frog Variant. This is a bug.");
/**
* Wolf variants.
*
@ -304,7 +304,7 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
* @see MapCursor.Type
*/
@ApiStatus.Internal
Registry<MapCursor.Type> MAP_DECORATION_TYPE = new SimpleRegistry<>(MapCursor.Type.class);
Registry<MapCursor.Type> MAP_DECORATION_TYPE = Objects.requireNonNull(Bukkit.getRegistry(MapCursor.Type.class), "No registry present for MapCursor Type. This is a bug.");
/**
* Game events.
*

View file

@ -1,8 +1,13 @@
package org.bukkit.entity;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.Locale;
import org.bukkit.DyeColor;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
/**
@ -43,29 +48,50 @@ public interface Cat extends Tameable, Sittable {
/**
* Represents the various different cat types there are.
*/
public enum Type implements Keyed {
TABBY("tabby"),
BLACK("black"),
RED("red"),
SIAMESE("siamese"),
BRITISH_SHORTHAIR("british_shorthair"),
CALICO("calico"),
PERSIAN("persian"),
RAGDOLL("ragdoll"),
WHITE("white"),
JELLIE("jellie"),
ALL_BLACK("all_black");
interface Type extends OldEnum<Type>, Keyed {
private final NamespacedKey key;
Type TABBY = getType("tabby");
Type BLACK = getType("black");
Type RED = getType("red");
Type SIAMESE = getType("siamese");
Type BRITISH_SHORTHAIR = getType("british_shorthair");
Type CALICO = getType("calico");
Type PERSIAN = getType("persian");
Type RAGDOLL = getType("ragdoll");
Type WHITE = getType("white");
Type JELLIE = getType("jellie");
Type ALL_BLACK = getType("all_black");
private Type(String key) {
this.key = NamespacedKey.minecraft(key);
@NotNull
private static Type getType(@NotNull String key) {
NamespacedKey namespacedKey = NamespacedKey.minecraft(key);
Type type = Registry.CAT_VARIANT.get(namespacedKey);
Preconditions.checkNotNull(type, "No cat type found for %s. This is a bug.", namespacedKey);
return type;
}
@Override
/**
* @param name of the cat type.
* @return the cat type with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
public NamespacedKey getKey() {
return key;
@Deprecated(since = "1.21")
static Type valueOf(@NotNull String name) {
Type type = Registry.CAT_VARIANT.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)));
Preconditions.checkArgument(type != null, "No cat type found with the name %s", name);
return type;
}
/**
* @return an array of all known cat types.
* @deprecated use {@link Registry#iterator()}.
*/
@NotNull
@Deprecated(since = "1.21")
static Type[] values() {
return Lists.newArrayList(Registry.CAT_VARIANT).toArray(new Type[0]);
}
}
}

View file

@ -1,8 +1,12 @@
package org.bukkit.entity;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.Locale;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -44,30 +48,51 @@ public interface Frog extends Animals {
/**
* Represents the variant of a frog - ie its color.
*/
public enum Variant implements Keyed {
interface Variant extends OldEnum<Variant>, Keyed {
/**
* Temperate (brown-orange) frog.
*/
TEMPERATE,
Variant TEMPERATE = getVariant("temperate");
/**
* Warm (gray) frog.
*/
WARM,
Variant WARM = getVariant("warm");
/**
* Cold (green) frog.
*/
COLD;
private final NamespacedKey key;
private Variant() {
this.key = NamespacedKey.minecraft(name().toLowerCase(Locale.ROOT));
}
Variant COLD = getVariant("cold");
@NotNull
@Override
public NamespacedKey getKey() {
return key;
private static Variant getVariant(@NotNull String key) {
NamespacedKey namespacedKey = NamespacedKey.minecraft(key);
Variant variant = Registry.FROG_VARIANT.get(namespacedKey);
Preconditions.checkNotNull(variant, "No frog variant found for %s. This is a bug.", namespacedKey);
return variant;
}
/**
* @param name of the frog variant.
* @return the frog variant with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
@Deprecated(since = "1.21")
static Variant valueOf(@NotNull String name) {
Variant variant = Registry.FROG_VARIANT.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)));
Preconditions.checkArgument(variant != null, "No frog variant found with the name %s", name);
return variant;
}
/**
* @return an array of all known frog variants.
* @deprecated use {@link Registry#iterator()}.
*/
@NotNull
@Deprecated(since = "1.21")
static Variant[] values() {
return Lists.newArrayList(Registry.FROG_VARIANT).toArray(new Variant[0]);
}
}
}

View file

@ -1,9 +1,13 @@
package org.bukkit.entity;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.Locale;
import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -118,25 +122,46 @@ public interface Villager extends AbstractVillager {
* Represents Villager type, usually corresponding to what biome they spawn
* in.
*/
public enum Type implements Keyed {
interface Type extends OldEnum<Type>, Keyed {
DESERT,
JUNGLE,
PLAINS,
SAVANNA,
SNOW,
SWAMP,
TAIGA;
private final NamespacedKey key;
private Type() {
this.key = NamespacedKey.minecraft(this.name().toLowerCase(Locale.ROOT));
}
Type DESERT = getType("desert");
Type JUNGLE = getType("jungle");
Type PLAINS = getType("plains");
Type SAVANNA = getType("savanna");
Type SNOW = getType("snow");
Type SWAMP = getType("swamp");
Type TAIGA = getType("taiga");
@NotNull
@Override
public NamespacedKey getKey() {
return key;
private static Type getType(@NotNull String key) {
NamespacedKey namespacedKey = NamespacedKey.minecraft(key);
Type type = Registry.VILLAGER_TYPE.get(namespacedKey);
Preconditions.checkNotNull(type, "No villager type found for %s. This is a bug.", namespacedKey);
return type;
}
/**
* @param name of the villager type.
* @return the villager type with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
@Deprecated(since = "1.21")
static Type valueOf(@NotNull String name) {
Type type = Registry.VILLAGER_TYPE.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)));
Preconditions.checkArgument(type != null, "No villager type found with the name %s", name);
return type;
}
/**
* @return an array of all known villager types.
* @deprecated use {@link Registry#iterator()}.
*/
@NotNull
@Deprecated(since = "1.21")
static Type[] values() {
return Lists.newArrayList(Registry.VILLAGER_TYPE).toArray(new Type[0]);
}
}
@ -144,88 +169,110 @@ public interface Villager extends AbstractVillager {
* Represents the various different Villager professions there may be.
* Villagers have different trading options depending on their profession,
*/
public enum Profession implements Keyed {
NONE,
interface Profession extends OldEnum<Profession>, Keyed {
Profession NONE = getProfession("none");
/**
* Armorer profession. Wears a black apron. Armorers primarily trade for
* iron armor, chainmail armor, and sometimes diamond armor.
*/
ARMORER,
Profession ARMORER = getProfession("armorer");
/**
* Butcher profession. Wears a white apron. Butchers primarily trade for
* raw and cooked food.
*/
BUTCHER,
Profession BUTCHER = getProfession("butcher");
/**
* Cartographer profession. Wears a white robe. Cartographers primarily
* trade for explorer maps and some paper.
*/
CARTOGRAPHER,
Profession CARTOGRAPHER = getProfession("cartographer");
/**
* Cleric profession. Wears a purple robe. Clerics primarily trade for
* rotten flesh, gold ingot, redstone, lapis, ender pearl, glowstone,
* and bottle o' enchanting.
*/
CLERIC,
Profession CLERIC = getProfession("cleric");
/**
* Farmer profession. Wears a brown robe. Farmers primarily trade for
* food-related items.
*/
FARMER,
Profession FARMER = getProfession("farmer");
/**
* Fisherman profession. Wears a brown robe. Fisherman primarily trade
* for fish, as well as possibly selling string and/or coal.
*/
FISHERMAN,
Profession FISHERMAN = getProfession("fisherman");
/**
* Fletcher profession. Wears a brown robe. Fletchers primarily trade
* for string, bows, and arrows.
*/
FLETCHER,
Profession FLETCHER = getProfession("fletcher");
/**
* Leatherworker profession. Wears a white apron. Leatherworkers
* primarily trade for leather, and leather armor, as well as saddles.
*/
LEATHERWORKER,
Profession LEATHERWORKER = getProfession("leatherworker");
/**
* Librarian profession. Wears a white robe. Librarians primarily trade
* for paper, books, and enchanted books.
*/
LIBRARIAN,
Profession LIBRARIAN = getProfession("librarian");
/**
* Mason profession.
*/
MASON,
Profession MASON = getProfession("mason");
/**
* Nitwit profession. Wears a green apron, cannot trade. Nitwit
* villagers do not do anything. They do not have any trades by default.
*/
NITWIT,
Profession NITWIT = getProfession("nitwit");
/**
* Sheperd profession. Wears a brown robe. Shepherds primarily trade for
* wool items, and shears.
*/
SHEPHERD,
Profession SHEPHERD = getProfession("shepherd");
/**
* Toolsmith profession. Wears a black apron. Tool smiths primarily
* trade for iron and diamond tools.
*/
TOOLSMITH,
Profession TOOLSMITH = getProfession("toolsmith");
/**
* Weaponsmith profession. Wears a black apron. Weapon smiths primarily
* trade for iron and diamond weapons, sometimes enchanted.
*/
WEAPONSMITH;
private final NamespacedKey key;
private Profession() {
this.key = NamespacedKey.minecraft(this.name().toLowerCase(Locale.ROOT));
}
Profession WEAPONSMITH = getProfession("weaponsmith");
@NotNull
@Override
public NamespacedKey getKey() {
return key;
private static Profession getProfession(@NotNull String key) {
NamespacedKey namespacedKey = NamespacedKey.minecraft(key);
Profession profession = Registry.VILLAGER_PROFESSION.get(namespacedKey);
Preconditions.checkNotNull(profession, "No villager profession found for %s. This is a bug.", namespacedKey);
return profession;
}
/**
* @param name of the villager profession.
* @return the villager profession with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
@Deprecated(since = "1.21")
static Profession valueOf(@NotNull String name) {
Profession profession = Registry.VILLAGER_PROFESSION.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)));
Preconditions.checkArgument(profession != null, "No villager profession found with the name %s", name);
return profession;
}
/**
* @return an array of all known villager professions.
* @deprecated use {@link Registry#iterator()}.
*/
@NotNull
@Deprecated(since = "1.21")
static Profession[] values() {
return Lists.newArrayList(Registry.VILLAGER_PROFESSION).toArray(new Profession[0]);
}
}
}

View file

@ -1,8 +1,12 @@
package org.bukkit.map;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.Locale;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -129,7 +133,7 @@ public final class MapCursor {
*/
@Deprecated
public byte getRawType() {
return type.value;
return type.getValue();
}
/**
@ -225,56 +229,51 @@ public final class MapCursor {
* index in the file './assets/minecraft/textures/map/map_icons.png' from minecraft.jar or from a
* resource pack.
*/
public enum Type implements Keyed {
PLAYER(0, "player"),
FRAME(1, "frame"),
RED_MARKER(2, "red_marker"),
BLUE_MARKER(3, "blue_marker"),
TARGET_X(4, "target_x"),
TARGET_POINT(5, "target_point"),
PLAYER_OFF_MAP(6, "player_off_map"),
PLAYER_OFF_LIMITS(7, "player_off_limits"),
MANSION(8, "mansion"),
MONUMENT(9, "monument"),
BANNER_WHITE(10, "banner_white"),
BANNER_ORANGE(11, "banner_orange"),
BANNER_MAGENTA(12, "banner_magenta"),
BANNER_LIGHT_BLUE(13, "banner_light_blue"),
BANNER_YELLOW(14, "banner_yellow"),
BANNER_LIME(15, "banner_lime"),
BANNER_PINK(16, "banner_pink"),
BANNER_GRAY(17, "banner_gray"),
BANNER_LIGHT_GRAY(18, "banner_light_gray"),
BANNER_CYAN(19, "banner_cyan"),
BANNER_PURPLE(20, "banner_purple"),
BANNER_BLUE(21, "banner_blue"),
BANNER_BROWN(22, "banner_brown"),
BANNER_GREEN(23, "banner_green"),
BANNER_RED(24, "banner_red"),
BANNER_BLACK(25, "banner_black"),
RED_X(26, "red_x"),
VILLAGE_DESERT(27, "village_desert"),
VILLAGE_PLAINS(28, "village_plains"),
VILLAGE_SAVANNA(29, "village_savanna"),
VILLAGE_SNOWY(30, "village_snowy"),
VILLAGE_TAIGA(31, "village_taiga"),
JUNGLE_TEMPLE(32, "jungle_temple"),
SWAMP_HUT(33, "swamp_hut"),
TRIAL_CHAMBERS(34, "trial_chambers")
;
public interface Type extends OldEnum<Type>, Keyed {
private final byte value;
private final NamespacedKey key;
Type(int value, String key) {
this.value = (byte) value;
this.key = NamespacedKey.minecraft(key);
}
Type PLAYER = getType("player");
Type FRAME = getType("frame");
Type RED_MARKER = getType("red_marker");
Type BLUE_MARKER = getType("blue_marker");
Type TARGET_X = getType("target_x");
Type TARGET_POINT = getType("target_point");
Type PLAYER_OFF_MAP = getType("player_off_map");
Type PLAYER_OFF_LIMITS = getType("player_off_limits");
Type MANSION = getType("mansion");
Type MONUMENT = getType("monument");
Type BANNER_WHITE = getType("banner_white");
Type BANNER_ORANGE = getType("banner_orange");
Type BANNER_MAGENTA = getType("banner_magenta");
Type BANNER_LIGHT_BLUE = getType("banner_light_blue");
Type BANNER_YELLOW = getType("banner_yellow");
Type BANNER_LIME = getType("banner_lime");
Type BANNER_PINK = getType("banner_pink");
Type BANNER_GRAY = getType("banner_gray");
Type BANNER_LIGHT_GRAY = getType("banner_light_gray");
Type BANNER_CYAN = getType("banner_cyan");
Type BANNER_PURPLE = getType("banner_purple");
Type BANNER_BLUE = getType("banner_blue");
Type BANNER_BROWN = getType("banner_brown");
Type BANNER_GREEN = getType("banner_green");
Type BANNER_RED = getType("banner_red");
Type BANNER_BLACK = getType("banner_black");
Type RED_X = getType("red_x");
Type VILLAGE_DESERT = getType("village_desert");
Type VILLAGE_PLAINS = getType("village_plains");
Type VILLAGE_SAVANNA = getType("village_savanna");
Type VILLAGE_SNOWY = getType("village_snowy");
Type VILLAGE_TAIGA = getType("village_taiga");
Type JUNGLE_TEMPLE = getType("jungle_temple");
Type SWAMP_HUT = getType("swamp_hut");
Type TRIAL_CHAMBERS = getType("trial_chambers");
@NotNull
@Override
public NamespacedKey getKey() {
return key;
private static Type getType(@NotNull String key) {
NamespacedKey namespacedKey = NamespacedKey.minecraft(key);
Type type = Registry.MAP_DECORATION_TYPE.get(namespacedKey);
Preconditions.checkNotNull(type, "No type found for %s. This is a bug.", namespacedKey);
return type;
}
/**
@ -284,9 +283,7 @@ public final class MapCursor {
* @deprecated Magic value
*/
@Deprecated
public byte getValue() {
return value;
}
byte getValue();
/**
* Get a cursor by its internal value.
@ -297,12 +294,35 @@ public final class MapCursor {
*/
@Deprecated
@Nullable
public static Type byValue(byte value) {
static Type byValue(byte value) {
for (Type t : values()) {
if (t.value == value) return t;
if (t.getValue() == value) return t;
}
return null;
}
/**
* @param name of the type.
* @return the type with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
@Deprecated(since = "1.21")
static Type valueOf(@NotNull String name) {
Type type = Registry.MAP_DECORATION_TYPE.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)));
Preconditions.checkArgument(type != null, "No Type found with the name %s", name);
return type;
}
/**
* @return an array of all known map cursor types.
* @deprecated use {@link Registry#iterator()}.
*/
@NotNull
@Deprecated(since = "1.21")
static Type[] values() {
return Lists.newArrayList(Registry.MAP_DECORATION_TYPE).toArray(new Type[0]);
}
}
}

View file

@ -0,0 +1,42 @@
package org.bukkit.util;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Class which holds common methods which are present in an enum.
*
* @param <T> the type of the old enum.
* @deprecated only for backwards compatibility.
*/
@ApiStatus.Internal
@Deprecated(since = "1.21")
public interface OldEnum<T extends OldEnum<T>> extends Comparable<T> {
/**
* @param other to compare to.
* @return negative if this old enum is lower, zero if equal and positive if
* higher than the given old enum.
* @deprecated only for backwards compatibility, old enums can not be
* compared.
*/
@Deprecated(since = "1.21")
@Override
int compareTo(@NotNull T other);
/**
* @return the name of the old enum.
* @deprecated only for backwards compatibility.
*/
@NotNull
@Deprecated(since = "1.21")
String name();
/**
* @return the ordinal of the old enum.
* @deprecated only for backwards compatibility, it is not guaranteed that
* an old enum always has the same ordinal.
*/
@Deprecated(since = "1.21")
int ordinal();
}

View file

@ -31,5 +31,10 @@
<li>Implementation classes. Concrete implementation classes packaged with
Bukkit (eg those beginning with Simple) are not API. You should access them
via their interfaces instead.</li>
<li>The "enumness" of enums which implement {@link org.bukkit.Keyed}.
Such enums are liable to conversion to regular classes and therefore
usages deriving from their status as an enum (including, but not limited
to, use in switch statements and classes such as {@link java.util.EnumSet})
should be avoided.</li>
</ul>
</body>