diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java index 0d44f4ce4e..5006119ed7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -9,7 +9,6 @@ import java.util.stream.Stream; import net.minecraft.core.Holder; import net.minecraft.core.IRegistry; import net.minecraft.core.IRegistryCustom; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import org.bukkit.GameEvent; @@ -25,6 +24,9 @@ import org.bukkit.block.BlockType; import org.bukkit.craftbukkit.block.CraftBlockType; import org.bukkit.craftbukkit.damage.CraftDamageType; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; +import org.bukkit.craftbukkit.entity.CraftCat; +import org.bukkit.craftbukkit.entity.CraftFrog; +import org.bukkit.craftbukkit.entity.CraftVillager; import org.bukkit.craftbukkit.entity.CraftWolf; import org.bukkit.craftbukkit.generator.structure.CraftStructure; import org.bukkit.craftbukkit.generator.structure.CraftStructureType; @@ -32,19 +34,24 @@ import org.bukkit.craftbukkit.inventory.CraftItemType; import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; import org.bukkit.craftbukkit.legacy.FieldRename; +import org.bukkit.craftbukkit.map.CraftMapCursor; import org.bukkit.craftbukkit.potion.CraftPotionEffectType; import org.bukkit.craftbukkit.util.ApiVersion; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Cat; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Frog; +import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.ItemType; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; +import org.bukkit.map.MapCursor; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; @@ -136,7 +143,13 @@ public class CraftRegistry implements Registry { return new CraftRegistry<>(Structure.class, registryHolder.registryOrThrow(Registries.STRUCTURE), CraftStructure::new, FieldRename.NONE); } if (bukkitClass == StructureType.class) { - return new CraftRegistry<>(StructureType.class, BuiltInRegistries.STRUCTURE_TYPE, CraftStructureType::new, FieldRename.NONE); + return new CraftRegistry<>(StructureType.class, registryHolder.registryOrThrow(Registries.STRUCTURE_TYPE), CraftStructureType::new, FieldRename.NONE); + } + if (bukkitClass == Villager.Type.class) { + return new CraftRegistry<>(Villager.Type.class, registryHolder.registryOrThrow(Registries.VILLAGER_TYPE), CraftVillager.CraftType::new, FieldRename.NONE); + } + if (bukkitClass == Villager.Profession.class) { + return new CraftRegistry<>(Villager.Profession.class, registryHolder.registryOrThrow(Registries.VILLAGER_PROFESSION), CraftVillager.CraftProfession::new, FieldRename.NONE); } if (bukkitClass == TrimMaterial.class) { return new CraftRegistry<>(TrimMaterial.class, registryHolder.registryOrThrow(Registries.TRIM_MATERIAL), CraftTrimMaterial::new, FieldRename.NONE); @@ -159,6 +172,15 @@ public class CraftRegistry implements Registry { if (bukkitClass == ItemType.class) { return new CraftRegistry<>(ItemType.class, registryHolder.registryOrThrow(Registries.ITEM), CraftItemType::new, FieldRename.NONE); } + if (bukkitClass == Frog.Variant.class) { + return new CraftRegistry<>(Frog.Variant.class, registryHolder.registryOrThrow(Registries.FROG_VARIANT), CraftFrog.CraftVariant::new, FieldRename.NONE); + } + if (bukkitClass == Cat.Type.class) { + return new CraftRegistry<>(Cat.Type.class, registryHolder.registryOrThrow(Registries.CAT_VARIANT), CraftCat.CraftType::new, FieldRename.NONE); + } + if (bukkitClass == MapCursor.Type.class) { + return new CraftRegistry<>(MapCursor.Type.class, registryHolder.registryOrThrow(Registries.MAP_DECORATION_TYPE), CraftMapCursor.CraftType::new, FieldRename.NONE); + } return null; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 820508c0a3..8506dc885b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -443,6 +443,14 @@ public final class CraftServer implements Server { logger.info("Using following compatibilities: `" + Joiner.on("`, `").join(activeCompatibilities) + "`, this will affect performance and other plugins behavior."); logger.info("Only use when necessary and prefer updating plugins if possible."); } + + if (activeCompatibilities.contains("enum-compatibility-mode")) { + getLogger().warning("Loading plugins in enum compatibility mode. This will affect plugin performance. Use only as a transition period or when absolutely necessary."); + } else if (System.getProperty("RemoveEnumBanner") == null) { + // TODO 2024-06-16: Remove in newer version + getLogger().info("*** This version of Spigot contains changes to some enums. If you notice that plugins no longer work after updating, please report this to the developers of those plugins first. ***"); + getLogger().info("*** If you cannot update those plugins, you can try setting `settings.compatibility.enum-compatibility-mode` to `true` in `bukkit.yml`. ***"); + } } public void loadPlugins() { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java index 632fa4647b..221bf57e94 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java @@ -1,17 +1,18 @@ package org.bukkit.craftbukkit.entity; import com.google.common.base.Preconditions; +import java.util.Locale; import net.minecraft.core.Holder; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.animal.CatVariant; import net.minecraft.world.entity.animal.EntityCat; import net.minecraft.world.item.EnumColor; import org.bukkit.DyeColor; +import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.entity.Cat; public class CraftCat extends CraftTameableAnimal implements Cat { @@ -52,14 +53,11 @@ public class CraftCat extends CraftTameableAnimal implements Cat { getHandle().setCollarColor(EnumColor.byId(color.getWoolData())); } - public static class CraftType { + public static class CraftType implements Type, Handleable { + private static int count = 0; public static Type minecraftToBukkit(CatVariant minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.CAT_VARIANT); - - return Registry.CAT_VARIANT.get(CraftNamespacedKey.fromMinecraft(registry.getKey(minecraft))); + return CraftRegistry.minecraftToBukkit(minecraft, Registries.CAT_VARIANT, Registry.CAT_VARIANT); } public static Type minecraftHolderToBukkit(Holder minecraft) { @@ -67,24 +65,80 @@ public class CraftCat extends CraftTameableAnimal implements Cat { } public static CatVariant bukkitToMinecraft(Type bukkit) { - Preconditions.checkArgument(bukkit != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.CAT_VARIANT); - - return registry.get(CraftNamespacedKey.toMinecraft(bukkit.getKey())); + return CraftRegistry.bukkitToMinecraft(bukkit); } public static Holder bukkitToMinecraftHolder(Type bukkit) { - Preconditions.checkArgument(bukkit != null); + return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.CAT_VARIANT); + } - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.CAT_VARIANT); + private final NamespacedKey key; + private final CatVariant catVariant; + private final String name; + private final int ordinal; - if (registry.wrapAsHolder(bukkitToMinecraft(bukkit)) instanceof Holder.c holder) { - return holder; + public CraftType(NamespacedKey key, CatVariant catVariant) { + this.key = key; + this.catVariant = catVariant; + // For backwards compatibility, minecraft values will still return the uppercase name without the namespace, + // in case plugins use for example the name as key in a config file to receive type specific values. + // Custom types will return the key with namespace. For a plugin this should look than like a new type + // (which can always be added in new minecraft versions and the plugin should therefore handle it accordingly). + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + this.name = key.getKey().toUpperCase(Locale.ROOT); + } else { + this.name = key.toString(); + } + this.ordinal = count++; + } + + @Override + public CatVariant getHandle() { + return catVariant; + } + + @Override + public NamespacedKey getKey() { + return key; + } + + @Override + public int compareTo(Type variant) { + return ordinal - variant.ordinal(); + } + + @Override + public String name() { + return name; + } + + @Override + public int ordinal() { + return ordinal; + } + + @Override + public String toString() { + // For backwards compatibility + return name(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; } - throw new IllegalArgumentException("No Reference holder found for " + bukkit - + ", this can happen if a plugin creates its own cat variant with out properly registering it."); + if (!(other instanceof CraftType)) { + return false; + } + + return getKey().equals(((CraftType) other).getKey()); + } + + @Override + public int hashCode() { + return getKey().hashCode(); } } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFrog.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFrog.java index 0b846994cb..4a8e20bf61 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFrog.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFrog.java @@ -1,15 +1,16 @@ package org.bukkit.craftbukkit.entity; import com.google.common.base.Preconditions; +import java.util.Locale; import net.minecraft.core.Holder; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.animal.FrogVariant; import net.minecraft.world.entity.animal.frog.Frog; +import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.entity.Entity; public class CraftFrog extends CraftAnimals implements org.bukkit.entity.Frog { @@ -54,17 +55,11 @@ public class CraftFrog extends CraftAnimals implements org.bukkit.entity.Frog { getHandle().setVariant(CraftVariant.bukkitToMinecraftHolder(variant)); } - public static class CraftVariant { + public static class CraftVariant implements Variant, Handleable { + private static int count = 0; public static Variant minecraftToBukkit(FrogVariant minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.FROG_VARIANT); - Variant bukkit = Registry.FROG_VARIANT.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.FROG_VARIANT, Registry.FROG_VARIANT); } public static Variant minecraftHolderToBukkit(Holder minecraft) { @@ -72,23 +67,80 @@ public class CraftFrog extends CraftAnimals implements org.bukkit.entity.Frog { } public static FrogVariant bukkitToMinecraft(Variant bukkit) { - Preconditions.checkArgument(bukkit != null); - - return CraftRegistry.getMinecraftRegistry(Registries.FROG_VARIANT) - .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow(); + return CraftRegistry.bukkitToMinecraft(bukkit); } public static Holder bukkitToMinecraftHolder(Variant bukkit) { - Preconditions.checkArgument(bukkit != null); + return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.FROG_VARIANT); + } - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.FROG_VARIANT); + private final NamespacedKey key; + private final FrogVariant frogVariant; + private final String name; + private final int ordinal; - if (registry.wrapAsHolder(bukkitToMinecraft(bukkit)) instanceof Holder.c holder) { - return holder; + public CraftVariant(NamespacedKey key, FrogVariant frogVariant) { + this.key = key; + this.frogVariant = frogVariant; + // For backwards compatibility, minecraft values will still return the uppercase name without the namespace, + // in case plugins use for example the name as key in a config file to receive variant specific values. + // Custom variants will return the key with namespace. For a plugin this should look than like a new variant + // (which can always be added in new minecraft versions and the plugin should therefore handle it accordingly). + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + this.name = key.getKey().toUpperCase(Locale.ROOT); + } else { + this.name = key.toString(); + } + this.ordinal = count++; + } + + @Override + public FrogVariant getHandle() { + return frogVariant; + } + + @Override + public NamespacedKey getKey() { + return key; + } + + @Override + public int compareTo(Variant variant) { + return ordinal - variant.ordinal(); + } + + @Override + public String name() { + return name; + } + + @Override + public int ordinal() { + return ordinal; + } + + @Override + public String toString() { + // For backwards compatibility + return name(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; } - throw new IllegalArgumentException("No Reference holder found for " + bukkit - + ", this can happen if a plugin creates its own frog variant with out properly registering it."); + if (!(other instanceof CraftVariant)) { + return false; + } + + return getKey().equals(((Variant) other).getKey()); + } + + @Override + public int hashCode() { + return getKey().hashCode(); } } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java index a3cc78d328..ccdf3af2d3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -1,8 +1,8 @@ package org.bukkit.craftbukkit.entity; import com.google.common.base.Preconditions; +import java.util.Locale; import net.minecraft.core.BlockPosition; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.monster.EntityZombie; import net.minecraft.world.entity.monster.EntityZombieVillager; @@ -12,11 +12,12 @@ import net.minecraft.world.entity.npc.VillagerType; import net.minecraft.world.level.block.BlockBed; import net.minecraft.world.level.block.state.IBlockData; import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.util.CraftLocation; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.entity.Villager; import org.bukkit.entity.ZombieVillager; import org.bukkit.event.entity.CreatureSpawnEvent; @@ -126,45 +127,165 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { return (entityzombievillager != null) ? (ZombieVillager) entityzombievillager.getBukkitEntity() : null; } - public static class CraftType { + public static class CraftType implements Type, Handleable { + private static int count = 0; public static Type minecraftToBukkit(VillagerType minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.VILLAGER_TYPE); - Type bukkit = Registry.VILLAGER_TYPE.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_TYPE, Registry.VILLAGER_TYPE); } public static VillagerType bukkitToMinecraft(Type bukkit) { - Preconditions.checkArgument(bukkit != null); + return CraftRegistry.bukkitToMinecraft(bukkit); + } - return CraftRegistry.getMinecraftRegistry(Registries.VILLAGER_TYPE) - .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow(); + private final NamespacedKey key; + private final VillagerType villagerType; + private final String name; + private final int ordinal; + + public CraftType(NamespacedKey key, VillagerType villagerType) { + this.key = key; + this.villagerType = villagerType; + // For backwards compatibility, minecraft values will still return the uppercase name without the namespace, + // in case plugins use for example the name as key in a config file to receive type specific values. + // Custom types will return the key with namespace. For a plugin this should look than like a new type + // (which can always be added in new minecraft versions and the plugin should therefore handle it accordingly). + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + this.name = key.getKey().toUpperCase(Locale.ROOT); + } else { + this.name = key.toString(); + } + this.ordinal = count++; + } + + @Override + public VillagerType getHandle() { + return villagerType; + } + + @Override + public NamespacedKey getKey() { + return key; + } + + @Override + public int compareTo(Type type) { + return ordinal - type.ordinal(); + } + + @Override + public String name() { + return name; + } + + @Override + public int ordinal() { + return ordinal; + } + + @Override + public String toString() { + // For backwards compatibility + return name(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof CraftType)) { + return false; + } + + return getKey().equals(((Type) other).getKey()); + } + + @Override + public int hashCode() { + return getKey().hashCode(); } } - public static class CraftProfession { + public static class CraftProfession implements Profession, Handleable { + private static int count = 0; public static Profession minecraftToBukkit(VillagerProfession minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.VILLAGER_PROFESSION); - Profession bukkit = Registry.VILLAGER_PROFESSION.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.VILLAGER_PROFESSION, Registry.VILLAGER_PROFESSION); } public static VillagerProfession bukkitToMinecraft(Profession bukkit) { - Preconditions.checkArgument(bukkit != null); + return CraftRegistry.bukkitToMinecraft(bukkit); + } - return CraftRegistry.getMinecraftRegistry(Registries.VILLAGER_PROFESSION) - .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow(); + private final NamespacedKey key; + private final VillagerProfession villagerProfession; + private final String name; + private final int ordinal; + + public CraftProfession(NamespacedKey key, VillagerProfession villagerProfession) { + this.key = key; + this.villagerProfession = villagerProfession; + // For backwards compatibility, minecraft values will still return the uppercase name without the namespace, + // in case plugins use for example the name as key in a config file to receive profession specific values. + // Custom professions will return the key with namespace. For a plugin this should look than like a new profession + // (which can always be added in new minecraft versions and the plugin should therefore handle it accordingly). + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + this.name = key.getKey().toUpperCase(Locale.ROOT); + } else { + this.name = key.toString(); + } + this.ordinal = count++; + } + + @Override + public VillagerProfession getHandle() { + return villagerProfession; + } + + @Override + public NamespacedKey getKey() { + return key; + } + + @Override + public int compareTo(Profession profession) { + return ordinal - profession.ordinal(); + } + + @Override + public String name() { + return name; + } + + @Override + public int ordinal() { + return ordinal; + } + + @Override + public String toString() { + // For backwards compatibility + return name(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof CraftProfession)) { + return false; + } + + return getKey().equals(((Profession) other).getKey()); + } + + @Override + public int hashCode() { + return getKey().hashCode(); } } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java index 71ad0775a6..117f72db73 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java @@ -51,11 +51,6 @@ public class FieldRename { }; } - @RerouteStatic("java/lang/Enum") - public static > T valueOf(Class enumClass, String name, @InjectPluginVersion ApiVersion apiVersion) { - return Enum.valueOf(enumClass, rename(apiVersion, enumClass.getName().replace('.', '/'), name)); - } - @RequireCompatibility("allow-old-keys-in-registry") public static T get(Registry registry, NamespacedKey namespacedKey) { // We don't have version-specific changes, so just use current, and don't inject a version diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/DummyEnum.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/DummyEnum.java new file mode 100644 index 0000000000..a07b5c70c8 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/DummyEnum.java @@ -0,0 +1,7 @@ +package org.bukkit.craftbukkit.legacy.enums; + +/** + * A crash dummy to use, instead of the old enums which matured to Abstracthood or Interfacehood and the baby enums which are still growing. + */ +public enum DummyEnum { +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/EnumEvil.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/EnumEvil.java new file mode 100644 index 0000000000..ea98e654b0 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/EnumEvil.java @@ -0,0 +1,275 @@ +package org.bukkit.craftbukkit.legacy.enums; + +import com.google.common.base.Converter; +import com.google.common.base.Enums; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.craftbukkit.legacy.FieldRename; +import org.bukkit.craftbukkit.legacy.reroute.DoNotReroute; +import org.bukkit.craftbukkit.legacy.reroute.InjectPluginVersion; +import org.bukkit.craftbukkit.legacy.reroute.NotInBukkit; +import org.bukkit.craftbukkit.legacy.reroute.RequireCompatibility; +import org.bukkit.craftbukkit.legacy.reroute.RequirePluginVersion; +import org.bukkit.craftbukkit.legacy.reroute.RerouteArgumentType; +import org.bukkit.craftbukkit.legacy.reroute.RerouteReturnType; +import org.bukkit.craftbukkit.legacy.reroute.RerouteStatic; +import org.bukkit.craftbukkit.util.ApiVersion; +import org.bukkit.craftbukkit.util.ClassTraverser; +import org.bukkit.entity.Cat; +import org.bukkit.entity.Frog; +import org.bukkit.entity.Villager; +import org.bukkit.map.MapCursor; +import org.bukkit.util.OldEnum; + +@NotInBukkit +@RequireCompatibility("enum-compatibility-mode") +@RequirePluginVersion(maxInclusive = "1.20.6") +public class EnumEvil { + + private static final Map, LegacyRegistryData> REGISTRIES = new HashMap<>(); + + static { + // Add Classes which got changed here + REGISTRIES.put(Villager.Type.class, new LegacyRegistryData(Registry.VILLAGER_TYPE, Villager.Type::valueOf)); + REGISTRIES.put(Villager.Profession.class, new LegacyRegistryData(Registry.VILLAGER_PROFESSION, Villager.Profession::valueOf)); + REGISTRIES.put(Frog.Variant.class, new LegacyRegistryData(Registry.FROG_VARIANT, Frog.Variant::valueOf)); + REGISTRIES.put(Cat.Type.class, new LegacyRegistryData(Registry.CAT_VARIANT, Cat.Type::valueOf)); + REGISTRIES.put(MapCursor.Type.class, new LegacyRegistryData(Registry.MAP_DECORATION_TYPE, MapCursor.Type::valueOf)); + } + + public static LegacyRegistryData getRegistryData(Class clazz) { + ClassTraverser it = new ClassTraverser(clazz); + LegacyRegistryData registryData; + while (it.hasNext()) { + registryData = REGISTRIES.get(it.next()); + if (registryData != null) { + return registryData; + } + } + + return null; + } + + @DoNotReroute + public static Registry getRegistry(Class clazz) { + LegacyRegistryData registryData = getRegistryData(clazz); + + if (registryData != null) { + return registryData.registry(); + } + + return null; + } + + @RerouteStatic("com/google/common/collect/Maps") + @RerouteReturnType("java/util/EnumSet") + public static ImposterEnumMap newEnumMap(Class objectClass) { + return new ImposterEnumMap(objectClass); + } + + @RerouteStatic("com/google/common/collect/Maps") + @RerouteReturnType("java/util/EnumSet") + public static ImposterEnumMap newEnumMap(Map map) { + return new ImposterEnumMap(map); + } + + @RerouteStatic("com/google/common/collect/Sets") + public static Collector toImmutableEnumSet() { + return Collectors.toUnmodifiableSet(); + } + + @RerouteStatic("com/google/common/collect/Sets") + @RerouteReturnType("java/util/EnumSet") + public static ImposterEnumSet newEnumSet(Iterable iterable, Class clazz) { + ImposterEnumSet set = ImposterEnumSet.noneOf(clazz); + + for (Object some : iterable) { + set.add(some); + } + + return set; + } + + @RerouteStatic("com/google/common/collect/Sets") + public static ImmutableSet immutableEnumSet(Iterable iterable) { + return ImmutableSet.of(iterable); + } + + @RerouteStatic("com/google/common/collect/Sets") + public static ImmutableSet immutableEnumSet(@RerouteArgumentType("java/lang/Enum") Object first, @RerouteArgumentType("[java/lang/Enum") Object... rest) { + return ImmutableSet.of(first, rest); + } + + @RerouteStatic("com/google/common/base/Enums") + public static Field getField(@RerouteArgumentType("java/lang/Enum") Object value) { + if (value instanceof Enum eValue) { + return Enums.getField(eValue); + } + + try { + return value.getClass().getField(((OldEnum) value).name()); + } catch (NoSuchFieldException impossible) { + throw new AssertionError(impossible); + } + } + + @RerouteStatic("com/google/common/base/Enums") + public static com.google.common.base.Optional getIfPresent(Class clazz, String name, @InjectPluginVersion ApiVersion apiVersion) { + if (clazz.isEnum()) { + return Enums.getIfPresent(clazz, name); + } + + Registry registry = getRegistry(clazz); + if (registry == null) { + return com.google.common.base.Optional.absent(); + } + + name = FieldRename.rename(apiVersion, clazz.getName().replace('.', '/'), name); + return com.google.common.base.Optional.fromNullable(registry.get(NamespacedKey.fromString(name.toLowerCase(Locale.ROOT)))); + } + + @RerouteStatic("com/google/common/base/Enums") + public static Converter stringConverter(Class clazz, @InjectPluginVersion ApiVersion apiVersion) { + if (clazz.isEnum()) { + return Enums.stringConverter(clazz); + } + + return new StringConverter(apiVersion, clazz); + } + + public static Object[] getEnumConstants(Class clazz) { + if (clazz.isEnum()) { + return clazz.getEnumConstants(); + } + + Registry registry = getRegistry(clazz); + + if (registry == null) { + return clazz.getEnumConstants(); + } + + // Need to do this in such away to avoid ClassCastException + List values = Lists.newArrayList(registry); + Object array = Array.newInstance(clazz, values.size()); + + for (int i = 0; i < values.size(); i++) { + Array.set(array, i, values.get(i)); + } + + return (Object[]) array; + } + + public static String name(@RerouteArgumentType("java/lang/Enum") Object object) { + if (object instanceof OldEnum) { + return ((OldEnum) object).name(); + } + + return ((Enum) object).name(); + } + + public static int compareTo(@RerouteArgumentType("java/lang/Enum") Object object, @RerouteArgumentType("java/lang/Enum") Object other) { + if (object instanceof OldEnum) { + return ((OldEnum) object).compareTo((OldEnum) other); + } + + return ((Enum) object).compareTo((Enum) other); + } + + public static Class getDeclaringClass(@RerouteArgumentType("java/lang/Enum") Object object) { + Class clazz = object.getClass(); + Class zuper = clazz.getSuperclass(); + return (zuper == Enum.class) ? clazz : zuper; + } + + public static Optional describeConstable(@RerouteArgumentType("java/lang/Enum") Object object) { + return getDeclaringClass(object) + .describeConstable() + .map(c -> Enum.EnumDesc.of(c, name(object))); + } + + @RerouteStatic("java/lang/Enum") + @RerouteReturnType("java/lang/Enum") + public static Object valueOf(Class enumClass, String name, @InjectPluginVersion ApiVersion apiVersion) { + name = FieldRename.rename(apiVersion, enumClass.getName().replace('.', '/'), name); + LegacyRegistryData registryData = getRegistryData(enumClass); + if (registryData != null) { + return registryData.function().apply(name); + } + + return Enum.valueOf(enumClass, name); + } + + public static String toString(@RerouteArgumentType("java/lang/Enum") Object object) { + return object.toString(); + } + + public static int ordinal(@RerouteArgumentType("java/lang/Enum") Object object) { + if (object instanceof OldEnum) { + return ((OldEnum) object).ordinal(); + } + + return ((Enum) object).ordinal(); + } + + public record LegacyRegistryData(Registry registry, Function function) { + } + + private static final class StringConverter> extends Converter implements Serializable { + + private final ApiVersion apiVersion; + private final Class clazz; + private transient LegacyRegistryData registryData; + + StringConverter(ApiVersion apiVersion, Class clazz) { + this.apiVersion = apiVersion; + this.clazz = clazz; + } + + @Override + protected T doForward(String value) { + if (registryData == null) { + registryData = getRegistryData(clazz); + } + value = FieldRename.rename(apiVersion, clazz.getName().replace('.', '/'), value); + return (T) registryData.function().apply(value); + } + + @Override + protected String doBackward(T enumValue) { + return enumValue.name(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof StringConverter that) { + return this.clazz.equals(that.clazz); + } + return false; + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + @Override + public String toString() { + return "Enums.stringConverter(" + clazz.getName() + ".class)"; + } + + private static final long serialVersionUID = 0L; + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumMap.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumMap.java new file mode 100644 index 0000000000..799e979097 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumMap.java @@ -0,0 +1,137 @@ +package org.bukkit.craftbukkit.legacy.enums; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * The "I can't believe it works" map. + * It replaces every EnumMap with the ImposterEnumMap and uses a HashMap instead of an object array. + * Used so that plugins which use an EnumMap still work. + */ +public class ImposterEnumMap extends AbstractMap { + + private final Class objectClass; + private final Map map; + + public ImposterEnumMap(Class objectClass) { + this.objectClass = objectClass; + this.map = getMap(objectClass); + } + + public ImposterEnumMap(EnumMap enumMap) { + this.objectClass = DummyEnum.class; + this.map = enumMap.clone(); + } + + public ImposterEnumMap(Map map) { + if (map instanceof ImposterEnumMap) { + this.objectClass = ((ImposterEnumMap) map).objectClass; + this.map = getMap(objectClass); + } else { + this.objectClass = DummyEnum.class; + this.map = new TreeMap(); + } + + this.map.putAll(map); + } + + private static Map getMap(Class objectClass) { + // Since we replace every enum map we might also replace some maps which are for real enums. + // If this is the case use a EnumMap instead of a HashMap + if (objectClass.isEnum()) { + return new EnumMap(objectClass); + } else { + return new HashMap(); + } + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public Object get(Object key) { + return map.get(key); + } + + @Override + public Object put(Object key, Object value) { + typeCheck(key); + return map.put(key, value); + } + + @Override + public Object remove(Object key) { + return map.remove(key); + } + + @Override + public void putAll(Map m) { + if (map instanceof EnumMap) { + map.putAll(m); + } + + super.putAll(m); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } + + @Override + public boolean equals(Object o) { + return map.equals(o); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public ImposterEnumMap clone() { + ImposterEnumMap enumMap = new ImposterEnumMap(objectClass); + enumMap.putAll(map); + return enumMap; + } + + private void typeCheck(Object object) { + if (objectClass != DummyEnum.class) { + if (!objectClass.isAssignableFrom(object.getClass())) { + throw new ClassCastException(object.getClass() + " != " + objectClass); + } + } + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumSet.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumSet.java new file mode 100644 index 0000000000..13ec790721 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/enums/ImposterEnumSet.java @@ -0,0 +1,346 @@ +package org.bukkit.craftbukkit.legacy.enums; + +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; +import org.bukkit.Registry; +import org.bukkit.util.OldEnum; +import org.jetbrains.annotations.NotNull; + +public class ImposterEnumSet extends AbstractSet { + + private final Class objectClass; + private final Set set; + + private static Set createSet(Class clazz) { + if (clazz.isEnum()) { + return EnumSet.noneOf((Class) clazz); + } else { + return new TreeSet(); + } + } + + public static ImposterEnumSet noneOf(Class clazz) { + Set set = createSet(clazz); + return new ImposterEnumSet(set, clazz); + } + + public static ImposterEnumSet allOf(Class clazz) { + Set set; + if (clazz.isEnum()) { + set = EnumSet.allOf((Class) clazz); + } else { + set = new HashSet(); + Registry registry = EnumEvil.getRegistry(clazz); + if (registry == null) { + throw new IllegalArgumentException("Class " + clazz + " is not an Enum nor an OldEnum"); + } + + for (Object object : registry) { + set.add(object); + } + } + return new ImposterEnumSet(set, clazz); + } + + public static ImposterEnumSet copyOf(Set set) { + Class clazz; + if (set instanceof ImposterEnumSet imposter) { + set = imposter.set; + clazz = imposter.objectClass; + } else { + if (!set.isEmpty()) { + clazz = (Class) set.stream() + .filter(val -> val != null) + .map(val -> val.getClass()) + .findAny() + .orElse(Object.class); + } else { + clazz = Object.class; + } + } + + Set newSet = createSet(clazz); + newSet.addAll(set); + + return new ImposterEnumSet(newSet, clazz); + } + + public static ImposterEnumSet copyOf(Collection collection) { + Class clazz; + if (collection instanceof ImposterEnumSet imposter) { + collection = imposter.set; + clazz = imposter.objectClass; + } else { + if (!collection.isEmpty()) { + clazz = (Class) collection.stream() + .filter(val -> val != null) + .map(val -> val.getClass()) + .findAny() + .orElse(Object.class); + } else { + clazz = Object.class; + } + } + + Set newSet = createSet(clazz); + newSet.addAll(collection); + + return new ImposterEnumSet(newSet, clazz); + } + + public static ImposterEnumSet complementOf(Set set) { + Class clazz = null; + if (set instanceof ImposterEnumSet imposter) { + set = imposter.set; + clazz = imposter.objectClass; + } + + if (set instanceof EnumSet enumSet) { + enumSet = EnumSet.complementOf(enumSet); + + if (clazz != null) { + return new ImposterEnumSet(enumSet, clazz); + } + + if (!set.isEmpty()) { + clazz = (Class) set.stream() + .filter(val -> val != null) + .map(val -> val.getClass()) + .findAny() + .orElse(Object.class); + } else { + clazz = (Class) enumSet.stream() + .filter(val -> val != null) + .map(val -> val.getClass()) + .map(val -> (Class) val) + .findAny() + .orElse(Object.class); + } + + return new ImposterEnumSet(enumSet, clazz); + } + + if (set.isEmpty() && clazz == null) { + throw new IllegalStateException("Class is null and set is empty, cannot get class!"); + } + + if (clazz == null) { + clazz = (Class) set.stream() + .filter(val -> val != null) + .map(val -> val.getClass()) + .findAny() + .orElse(Object.class); + } + + Registry registry = EnumEvil.getRegistry(clazz); + Set newSet = new HashSet(); + + for (Object value : registry) { + if (set.contains(value)) { + continue; + } + + newSet.add(value); + } + + return new ImposterEnumSet(newSet, clazz); + } + + public static ImposterEnumSet of(Object e) { + Set set = createSet(e.getClass()); + set.add(e); + + return new ImposterEnumSet(set, e.getClass()); + } + + public static ImposterEnumSet of(Object e1, Object e2) { + Set set = createSet(e1.getClass()); + set.add(e1); + set.add(e2); + + return new ImposterEnumSet(set, e1.getClass()); + } + + public static ImposterEnumSet of(Object e1, Object e2, Object e3) { + Set set = createSet(e1.getClass()); + set.add(e1); + set.add(e2); + set.add(e3); + + return new ImposterEnumSet(set, e1.getClass()); + } + + + public static ImposterEnumSet of(Object e1, Object e2, Object e3, Object e4) { + Set set = createSet(e1.getClass()); + set.add(e1); + set.add(e2); + set.add(e3); + set.add(e4); + + return new ImposterEnumSet(set, e1.getClass()); + } + + + public static ImposterEnumSet of(Object e1, Object e2, Object e3, Object e4, Object e5) { + Set set = createSet(e1.getClass()); + set.add(e1); + set.add(e2); + set.add(e3); + set.add(e4); + set.add(e5); + + return new ImposterEnumSet(set, e1.getClass()); + } + + + public static ImposterEnumSet of(Object e, Object... rest) { + Set set = createSet(e.getClass()); + set.add(e); + + Collections.addAll(set, rest); + + return new ImposterEnumSet(set, e.getClass()); + } + + public static ImposterEnumSet range(Object from, Object to) { + Set set; + if (from.getClass().isEnum()) { + set = EnumSet.range((Enum) from, (Enum) to); + } else { + set = new HashSet(); + Registry registry = EnumEvil.getRegistry(from.getClass()); + for (Object o : registry) { + if (((OldEnum) o).ordinal() < ((OldEnum) from).ordinal()) { + continue; + } + + if (((OldEnum) o).ordinal() > ((OldEnum) to).ordinal()) { + continue; + } + + set.add(o); + } + } + + return new ImposterEnumSet(set, from.getClass()); + } + + private ImposterEnumSet(Set set, Class objectClass) { + this.set = set; + this.objectClass = objectClass; + } + + @Override + public Iterator iterator() { + return set.iterator(); + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean equals(Object o) { + return set.equals(o); + } + + @Override + public int hashCode() { + return set.hashCode(); + } + + @Override + public boolean removeAll(Collection c) { + return set.removeAll(c); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(o); + } + + @NotNull + @Override + public Object[] toArray() { + return set.toArray(); + } + + @NotNull + @Override + public T[] toArray(@NotNull T[] a) { + return (T[]) set.toArray(a); + } + + @Override + public boolean add(Object o) { + typeCheck(o); + return set.add(o); + } + + @Override + public boolean remove(Object o) { + return set.remove(o); + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return set.containsAll(c); + } + + @Override + public boolean addAll(@NotNull Collection c) { + if (set instanceof EnumSet) { + set.addAll(c); + } + + return super.addAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + return set.retainAll(c); + } + + @Override + public void clear() { + set.clear(); + } + + @Override + public String toString() { + return set.toString(); + } + + public ImposterEnumSet clone() { + Set newSet; + if (set instanceof EnumSet enumSet) { + newSet = enumSet.clone(); + } else { + newSet = new HashSet(); + newSet.addAll(set); + } + + return new ImposterEnumSet(newSet, objectClass); + } + + private void typeCheck(Object object) { + if (objectClass != DummyEnum.class) { + if (!objectClass.isAssignableFrom(object.getClass())) { + throw new ClassCastException(object.getClass() + " != " + objectClass); + } + } + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/InjectCompatibility.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/InjectCompatibility.java new file mode 100644 index 0000000000..45c012026b --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/InjectCompatibility.java @@ -0,0 +1,13 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface InjectCompatibility { + + String value(); +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/NotInBukkit.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/NotInBukkit.java index 0da52baa89..c2dfc617b7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/NotInBukkit.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/NotInBukkit.java @@ -6,6 +6,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface NotInBukkit { } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java index ff1938c098..062bd31582 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequireCompatibility.java @@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface RequireCompatibility { String value(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersion.java new file mode 100644 index 0000000000..22b59294c8 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersion.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequirePluginVersion { + + String value() default ""; + + String minInclusive() default ""; + + String maxInclusive() default ""; +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersionData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersionData.java new file mode 100644 index 0000000000..8f344e5008 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RequirePluginVersionData.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import org.bukkit.craftbukkit.util.ApiVersion; + +public record RequirePluginVersionData(ApiVersion minInclusive, ApiVersion maxInclusive) { + + public static RequirePluginVersionData create(RequirePluginVersion requirePluginVersion) { + if (!requirePluginVersion.value().isBlank()) { + if (!requirePluginVersion.minInclusive().isBlank() || !requirePluginVersion.maxInclusive().isBlank()) { + throw new RuntimeException("When setting value, min inclusive and max inclusive data is not allowed."); + } + + return new RequirePluginVersionData(ApiVersion.getOrCreateVersion(requirePluginVersion.value()), ApiVersion.getOrCreateVersion(requirePluginVersion.value())); + } + + ApiVersion minInclusive = null; + ApiVersion maxInclusive = null; + + if (!requirePluginVersion.minInclusive().isBlank()) { + minInclusive = ApiVersion.getOrCreateVersion(requirePluginVersion.minInclusive()); + } + if (!requirePluginVersion.maxInclusive().isBlank()) { + maxInclusive = ApiVersion.getOrCreateVersion(requirePluginVersion.maxInclusive()); + } + + return new RequirePluginVersionData(minInclusive, maxInclusive); + } + + public boolean test(ApiVersion pluginVersion) { + if (minInclusive != null && pluginVersion.isOlderThan(minInclusive)) { + return false; + } + + if (maxInclusive != null && pluginVersion.isNewerThan(maxInclusive)) { + return false; + } + + return true; + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgument.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgument.java index c1a7bb31e7..ea56698dd8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgument.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgument.java @@ -1,9 +1,10 @@ package org.bukkit.craftbukkit.legacy.reroute; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -public record RerouteArgument(Type type, boolean injectPluginName, boolean injectPluginVersion) { +public record RerouteArgument(Type type, Type sourceType, boolean injectPluginName, boolean injectPluginVersion, @Nullable String injectCompatibility) { /** * Converts the type string to the correct load opcode. @@ -30,8 +31,8 @@ public record RerouteArgument(Type type, boolean injectPluginName, boolean injec * @return the opcode of the type */ public int instruction() { - if (injectPluginName() || injectPluginVersion()) { - throw new IllegalStateException(String.format("Cannot get instruction for plugin name / version argument: %s", this)); + if (injectPluginName() || injectPluginVersion() || injectCompatibility() != null) { + throw new IllegalStateException(String.format("Cannot get instruction for plugin name / version argument / compatibility: %s", this)); } return type.getOpcode(Opcodes.ILOAD); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgumentType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgumentType.java new file mode 100644 index 0000000000..cc9f86ed80 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteArgumentType.java @@ -0,0 +1,13 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface RerouteArgumentType { + + String value(); +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java index b5f38641d2..c4ff44aec8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteBuilder.java @@ -54,13 +54,16 @@ public class RerouteBuilder { for (Parameter parameter : method.getParameters()) { Type type = Type.getType(parameter.getType()); + int count = 0; boolean injectPluginName = false; boolean injectPluginVersion = false; + String injectCompatibility = null; if (parameter.isAnnotationPresent(InjectPluginName.class)) { if (parameter.getType() != String.class) { throw new RuntimeException("Plugin name argument must be of type name, but got " + parameter.getType()); } injectPluginName = true; + count++; } if (parameter.isAnnotationPresent(InjectPluginVersion.class)) { @@ -68,17 +71,39 @@ public class RerouteBuilder { throw new RuntimeException("Plugin version argument must be of type ApiVersion, but got " + parameter.getType()); } injectPluginVersion = true; + count++; } - if (injectPluginName && injectPluginVersion) { + if (parameter.isAnnotationPresent(InjectCompatibility.class)) { + if (parameter.getType() != boolean.class) { + throw new RuntimeException("Compatibility argument must be of type boolean, but got " + parameter.getType()); + } + injectCompatibility = parameter.getAnnotation(InjectCompatibility.class).value(); + count++; + } + + if (count > 1) { // This should not happen, since we check types, // and those two have different types -> it would already have failed throw new RuntimeException("Wtf?"); } - RerouteArgument argument = new RerouteArgument(type, injectPluginName, injectPluginVersion); + RerouteArgumentType rerouteArgumentType = parameter.getAnnotation(RerouteArgumentType.class); + if (count == 1 && rerouteArgumentType != null) { + // Why would you do this? + throw new RuntimeException("Wtf?"); + } + + Type sourceType; + if (rerouteArgumentType != null) { + sourceType = Type.getObjectType(rerouteArgumentType.value()); + } else { + sourceType = type; + } + + RerouteArgument argument = new RerouteArgument(type, sourceType, injectPluginName, injectPluginVersion, injectCompatibility); arguments.add(argument); - if (!injectPluginName && !injectPluginVersion) { + if (count == 0) { sourceArguments.add(argument); } } @@ -89,10 +114,18 @@ public class RerouteBuilder { sourceOwner = Type.getObjectType(rerouteStatic.value()); } else { RerouteArgument argument = sourceArguments.get(0); - sourceOwner = argument.type(); + sourceOwner = argument.sourceType(); sourceArguments.remove(argument); } - Type sourceDesc = Type.getMethodType(rerouteReturn.type(), sourceArguments.stream().map(RerouteArgument::type).toArray(Type[]::new)); + + RerouteReturnType rerouteReturnType = method.getAnnotation(RerouteReturnType.class); + Type returnType; + if (rerouteReturnType != null) { + returnType = Type.getObjectType(rerouteReturnType.value()); + } else { + returnType = rerouteReturn.type(); + } + Type sourceDesc = Type.getMethodType(returnType, sourceArguments.stream().map(RerouteArgument::sourceType).toArray(Type[]::new)); RerouteMethodName rerouteMethodName = method.getAnnotation(RerouteMethodName.class); String methodName; @@ -110,13 +143,22 @@ public class RerouteBuilder { Type targetType = Type.getType(method); - boolean inBukkit = !method.isAnnotationPresent(NotInBukkit.class); + boolean inBukkit = !method.isAnnotationPresent(NotInBukkit.class) && !method.getDeclaringClass().isAnnotationPresent(NotInBukkit.class); String requiredCompatibility = null; if (method.isAnnotationPresent(RequireCompatibility.class)) { requiredCompatibility = method.getAnnotation(RequireCompatibility.class).value(); + } else if (method.getDeclaringClass().isAnnotationPresent(RequireCompatibility.class)) { + requiredCompatibility = method.getDeclaringClass().getAnnotation(RequireCompatibility.class).value(); } - return new RerouteMethodData(methodKey, sourceDesc, sourceOwner, methodName, rerouteStatic != null, targetType, Type.getInternalName(method.getDeclaringClass()), method.getName(), arguments, rerouteReturn, inBukkit, requiredCompatibility); + RequirePluginVersionData requiredPluginVersion = null; + if (method.isAnnotationPresent(RequirePluginVersion.class)) { + requiredPluginVersion = RequirePluginVersionData.create(method.getAnnotation(RequirePluginVersion.class)); + } else if (method.getDeclaringClass().isAnnotationPresent(RequirePluginVersion.class)) { + requiredPluginVersion = RequirePluginVersionData.create(method.getDeclaringClass().getAnnotation(RequirePluginVersion.class)); + } + + return new RerouteMethodData(methodKey, sourceDesc, sourceOwner, methodName, rerouteStatic != null, targetType, Type.getInternalName(method.getDeclaringClass()), method.getName(), arguments, rerouteReturn, inBukkit, requiredCompatibility, requiredPluginVersion); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java index 19c31a1b31..6b6bf143ab 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteMethodData.java @@ -7,5 +7,5 @@ import org.objectweb.asm.Type; public record RerouteMethodData(String source, Type sourceDesc, Type sourceOwner, String sourceName, boolean staticReroute, Type targetType, String targetOwner, String targetName, List arguments, RerouteReturn rerouteReturn, boolean isInBukkit, - @Nullable String requiredCompatibility) { + @Nullable String requiredCompatibility, @Nullable RequirePluginVersionData requiredPluginVersion) { } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteReturnType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteReturnType.java new file mode 100644 index 0000000000..a5f4f5119d --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/reroute/RerouteReturnType.java @@ -0,0 +1,13 @@ +package org.bukkit.craftbukkit.legacy.reroute; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RerouteReturnType { + + String value(); +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCursor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCursor.java index 5d042196a9..d55fb01611 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCursor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCursor.java @@ -1,28 +1,23 @@ package org.bukkit.craftbukkit.map; -import com.google.common.base.Preconditions; +import java.util.Locale; import net.minecraft.core.Holder; -import net.minecraft.core.IRegistry; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.saveddata.maps.MapDecorationType; +import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.map.MapCursor; public final class CraftMapCursor { - public static final class CraftType { + public static final class CraftType implements MapCursor.Type, Handleable { + + private static int count = 0; public static MapCursor.Type minecraftToBukkit(MapDecorationType minecraft) { - Preconditions.checkArgument(minecraft != null); - - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.MAP_DECORATION_TYPE); - MapCursor.Type bukkit = Registry.MAP_DECORATION_TYPE.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location())); - - Preconditions.checkArgument(bukkit != null); - - return bukkit; + return CraftRegistry.minecraftToBukkit(minecraft, Registries.MAP_DECORATION_TYPE, Registry.MAP_DECORATION_TYPE); } public static MapCursor.Type minecraftHolderToBukkit(Holder minecraft) { @@ -30,23 +25,85 @@ public final class CraftMapCursor { } public static MapDecorationType bukkitToMinecraft(MapCursor.Type bukkit) { - Preconditions.checkArgument(bukkit != null); - - return CraftRegistry.getMinecraftRegistry(Registries.MAP_DECORATION_TYPE) - .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow(); + return CraftRegistry.bukkitToMinecraft(bukkit); } public static Holder bukkitToMinecraftHolder(MapCursor.Type bukkit) { - Preconditions.checkArgument(bukkit != null); + return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.MAP_DECORATION_TYPE); + } - IRegistry registry = CraftRegistry.getMinecraftRegistry(Registries.MAP_DECORATION_TYPE); + private final NamespacedKey key; + private final MapDecorationType mapDecorationType; + private final String name; + private final int ordinal; - if (registry.wrapAsHolder(bukkitToMinecraft(bukkit)) instanceof Holder.c holder) { - return holder; + public CraftType(NamespacedKey key, MapDecorationType mapDecorationType) { + this.key = key; + this.mapDecorationType = mapDecorationType; + // For backwards compatibility, minecraft values will still return the uppercase name without the namespace, + // in case plugins use for example the name as key in a config file to receive type specific values. + // Custom types will return the key with namespace. For a plugin this should look than like a new type + // (which can always be added in new minecraft versions and the plugin should therefore handle it accordingly). + if (NamespacedKey.MINECRAFT.equals(key.getNamespace())) { + this.name = key.getKey().toUpperCase(Locale.ROOT); + } else { + this.name = key.toString(); + } + this.ordinal = count++; + } + + @Override + public MapDecorationType getHandle() { + return mapDecorationType; + } + + @Override + public NamespacedKey getKey() { + return key; + } + + @Override + public int compareTo(MapCursor.Type type) { + return ordinal - type.ordinal(); + } + + @Override + public String name() { + return name; + } + + @Override + public int ordinal() { + return ordinal; + } + + @Override + public String toString() { + // For backwards compatibility + return name(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; } - throw new IllegalArgumentException("No Reference holder found for " + bukkit - + ", this can happen if a plugin creates its own map cursor type without properly registering it."); + if (!(other instanceof CraftType)) { + return false; + } + + return getKey().equals(((MapCursor.Type) other).getKey()); + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public byte getValue() { + return (byte) CraftRegistry.getMinecraftRegistry(Registries.MAP_DECORATION_TYPE).getId(getHandle()); } } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java index 97e01aeb7e..85ade9e375 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java @@ -1,10 +1,11 @@ package org.bukkit.craftbukkit.util; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.jetbrains.annotations.NotNull; -public final class ApiVersion implements Comparable { +public final class ApiVersion implements Comparable, Serializable { public static final ApiVersion CURRENT; public static final ApiVersion FLATTENING; @@ -122,4 +123,6 @@ public final class ApiVersion implements Comparable { public String toString() { return getVersionString(); } + + private static final long serialVersionUID = 0L; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java index 6a57617a0d..af4a3bdc21 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -24,6 +24,7 @@ import org.bukkit.Material; import org.bukkit.craftbukkit.legacy.FieldRename; import org.bukkit.craftbukkit.legacy.MaterialRerouting; import org.bukkit.craftbukkit.legacy.MethodRerouting; +import org.bukkit.craftbukkit.legacy.enums.EnumEvil; import org.bukkit.craftbukkit.legacy.reroute.RerouteArgument; import org.bukkit.craftbukkit.legacy.reroute.RerouteBuilder; import org.bukkit.craftbukkit.legacy.reroute.RerouteMethodData; @@ -61,6 +62,12 @@ public class Commodore { "org/bukkit/inventory/ItemStack (S)V setDurability" )); + private static final Map ENUM_RENAMES = Map.of( + "java/lang/Enum", "java/lang/Object", + "java/util/EnumSet", "org/bukkit/craftbukkit/legacy/enums/ImposterEnumSet", + "java/util/EnumMap", "org/bukkit/craftbukkit/legacy/enums/ImposterEnumMap" + ); + private static final Map RENAMES = Map.of( "org/bukkit/entity/TextDisplay$TextAligment", "org/bukkit/entity/TextDisplay$TextAlignment", // SPIGOT-7335 "org/spigotmc/event/entity/EntityMountEvent", "org/bukkit/event/entity/EntityMountEvent", @@ -68,7 +75,12 @@ public class Commodore { ); private static final Map CLASS_TO_INTERFACE = Map.of( - "org/bukkit/inventory/InventoryView", "org/bukkit/craftbukkit/inventory/CraftAbstractInventoryView" + "org/bukkit/inventory/InventoryView", "org/bukkit/craftbukkit/inventory/CraftAbstractInventoryView", + "org/bukkit/entity/Villager$Type", "NOP", + "org/bukkit/entity/Villager$Profession", "NOP", + "org/bukkit/entity/Frog$Variant", "NOP", + "org/bukkit/entity/Cat$Type", "NOP", + "org/bukkit/map/MapCursor$Type", "NOP" ); private static Map createReroutes(Class clazz) { @@ -82,6 +94,7 @@ public class Commodore { private static final Map FIELD_RENAME_METHOD_REROUTE = createReroutes(FieldRename.class); private static final Map MATERIAL_METHOD_REROUTE = createReroutes(MaterialRerouting.class); private static final Map METHOD_REROUTE = createReroutes(MethodRerouting.class); + private static final Map ENUM_METHOD_REROUTE = createReroutes(EnumEvil.class); public static void main(String[] args) { OptionParser parser = new OptionParser(); @@ -148,9 +161,14 @@ public class Commodore { public static byte[] convert(byte[] b, final String pluginName, final ApiVersion pluginVersion, final Set activeCompatibilities) { final boolean modern = pluginVersion.isNewerThanOrSameAs(ApiVersion.FLATTENING); ClassReader cr = new ClassReader(b); - ClassWriter cw = new ClassWriter(cr, 0); + ClassWriter cw = new ClassWriter(0); // TODO 2024-06-22: Open PR to ASM to included interface in handle hash - cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, cw) { + ClassVisitor visitor = cw; + if (pluginVersion.isOlderThanOrSameAs(ApiVersion.getOrCreateVersion("1.20.6")) && activeCompatibilities.contains("enum-compatibility-mode")) { + visitor = new LimitedClassRemapper(cw, new SimpleRemapper(ENUM_RENAMES)); + } + + cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, visitor) { final Set rerouteMethodData = new HashSet<>(); String className; boolean isInterface; @@ -179,8 +197,10 @@ public class Commodore { } else if (argument.injectPluginVersion()) { methodVisitor.visitLdcInsn(pluginVersion.getVersionString()); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(ApiVersion.class), "getOrCreateVersion", "(Ljava/lang/String;)L" + Type.getInternalName(ApiVersion.class) + ";", false); + } else if (argument.injectCompatibility() != null) { + methodVisitor.visitLdcInsn(activeCompatibilities.contains(argument.injectCompatibility())); } else { - methodVisitor.visitIntInsn(argument.instruction(), index); + methodVisitor.visitVarInsn(argument.instruction(), index); index++; // Long and double need two space @@ -313,6 +333,10 @@ public class Commodore { } } + if (checkReroute(visitor, ENUM_METHOD_REROUTE, opcode, owner, name, desc, samMethodType, instantiatedMethodType)) { + return; + } + // SPIGOT-4496 if (owner.equals("org/bukkit/map/MapView") && name.equals("getId") && desc.equals("()S")) { // Should be same size on stack so just call other method @@ -412,7 +436,7 @@ public class Commodore { } private boolean checkReroute(MethodPrinter visitor, Map rerouteMethodDataMap, int opcode, String owner, String name, String desc, Type samMethodType, Type instantiatedMethodType) { - return rerouteMethods(activeCompatibilities, rerouteMethodDataMap, opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.H_INVOKESTATIC, owner, name, desc, data -> { + return rerouteMethods(activeCompatibilities, pluginVersion, rerouteMethodDataMap, opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.H_INVOKESTATIC, owner, name, desc, data -> { visitor.visit(Opcodes.INVOKESTATIC, className, buildMethodName(data), buildMethodDesc(data), isInterface, samMethodType, instantiatedMethodType); rerouteMethodData.add(data); }); @@ -582,7 +606,7 @@ public class Commodore { But since it is only applied for each class and method call once when they get first loaded, it should not be that bad. (Although some load time testing could be done) */ - public static boolean rerouteMethods(Set activeCompatibilities, Map rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer consumer) { + public static boolean rerouteMethods(Set activeCompatibilities, ApiVersion pluginVersion, Map rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer consumer) { Type ownerType = Type.getObjectType(owner); Class ownerClass; try { @@ -609,6 +633,10 @@ public class Commodore { return false; } + if (data.requiredPluginVersion() != null && !data.requiredPluginVersion().test(pluginVersion)) { + return false; + } + consumer.accept(data); return true; } @@ -621,7 +649,7 @@ public class Commodore { } private static String buildMethodDesc(RerouteMethodData rerouteMethodData) { - return Type.getMethodDescriptor(rerouteMethodData.sourceDesc().getReturnType(), rerouteMethodData.arguments().stream().filter(a -> !a.injectPluginName()).filter(a -> !a.injectPluginVersion()).map(RerouteArgument::type).toArray(Type[]::new)); + return Type.getMethodDescriptor(rerouteMethodData.sourceDesc().getReturnType(), rerouteMethodData.arguments().stream().filter(a -> !a.injectPluginName()).filter(a -> !a.injectPluginVersion()).filter(a -> a.injectCompatibility() == null).map(RerouteArgument::sourceType).toArray(Type[]::new)); } @FunctionalInterface diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/LimitedClassRemapper.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/LimitedClassRemapper.java new file mode 100644 index 0000000000..6d84b4166c --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/LimitedClassRemapper.java @@ -0,0 +1,43 @@ +package org.bukkit.craftbukkit.util; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.MethodRemapper; +import org.objectweb.asm.commons.Remapper; + +public class LimitedClassRemapper extends ClassRemapper { + + public LimitedClassRemapper(ClassVisitor classVisitor, Remapper remapper) { + super(classVisitor, remapper); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.className = name; + // We do not want to remap superName and interfaces for the enums + cv.visit(version, access, this.remapper.mapType(name), this.remapper.mapSignature(signature, false), superName, interfaces); + } + + @Override + protected MethodVisitor createMethodRemapper(MethodVisitor methodVisitor) { + return new LimitedMethodRemapper(api, methodVisitor, remapper); + } + + private class LimitedMethodRemapper extends MethodRemapper { + + protected LimitedMethodRemapper(int api, MethodVisitor methodVisitor, Remapper remapper) { + super(api, methodVisitor, remapper); + } + + @Override + public void visitMethodInsn(int opcodeAndSource, String owner, String name, String descriptor, boolean isInterface) { + if (owner != null && owner.equals("java/lang/Enum") && name != null && name.equals("")) { + // We also do not want to remap the init method for enums + mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); + return; + } + super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); + } + } +} diff --git a/paper-server/src/main/resources/configurations/bukkit.yml b/paper-server/src/main/resources/configurations/bukkit.yml index 6615fffc4c..543e37737b 100644 --- a/paper-server/src/main/resources/configurations/bukkit.yml +++ b/paper-server/src/main/resources/configurations/bukkit.yml @@ -25,6 +25,7 @@ settings: use-map-color-cache: true compatibility: allow-old-keys-in-registry: false + enum-compatibility-mode: false spawn-limits: monsters: 70 animals: 10 diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java index 496574d9a1..feee0815f1 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java @@ -16,6 +16,7 @@ import java.util.stream.Stream; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.legacy.reroute.RerouteBuilder; import org.bukkit.craftbukkit.legacy.reroute.RerouteMethodData; +import org.bukkit.craftbukkit.util.ApiVersion; import org.bukkit.craftbukkit.util.Commodore; import org.bukkit.support.AbstractTestingBase; import org.junit.jupiter.api.AfterAll; @@ -93,7 +94,7 @@ public class MaterialReroutingTest extends AbstractTestingBase { } } - if (!Commodore.rerouteMethods(Collections.emptySet(), MATERIAL_METHOD_REROUTE, (methodNode.access & Opcodes.ACC_STATIC) != 0, classNode.name, methodNode.name, methodNode.desc, a -> { })) { + if (!Commodore.rerouteMethods(Collections.emptySet(), ApiVersion.CURRENT, MATERIAL_METHOD_REROUTE, (methodNode.access & Opcodes.ACC_STATIC) != 0, classNode.name, methodNode.name, methodNode.desc, a -> { })) { missingReroute.add(methodNode.name + " " + methodNode.desc + " " + methodNode.signature); } } 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 c4076fb3c1..4e1372567e 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,8 +6,13 @@ import java.util.stream.Stream; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.world.effect.MobEffectList; +import net.minecraft.world.entity.animal.CatVariant; +import net.minecraft.world.entity.animal.FrogVariant; import net.minecraft.world.entity.animal.WolfVariant; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; import net.minecraft.world.item.Instrument; +import net.minecraft.world.level.saveddata.maps.MapDecorationType; import org.bukkit.GameEvent; import org.bukkit.JukeboxSong; import org.bukkit.MusicInstrument; @@ -18,21 +23,29 @@ import org.bukkit.craftbukkit.CraftMusicInstrument; import org.bukkit.craftbukkit.block.CraftBlockType; import org.bukkit.craftbukkit.damage.CraftDamageType; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; +import org.bukkit.craftbukkit.entity.CraftCat; +import org.bukkit.craftbukkit.entity.CraftFrog; +import org.bukkit.craftbukkit.entity.CraftVillager; import org.bukkit.craftbukkit.entity.CraftWolf; import org.bukkit.craftbukkit.generator.structure.CraftStructure; import org.bukkit.craftbukkit.generator.structure.CraftStructureType; import org.bukkit.craftbukkit.inventory.CraftItemType; import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; +import org.bukkit.craftbukkit.map.CraftMapCursor; import org.bukkit.craftbukkit.potion.CraftPotionEffectType; import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Cat; +import org.bukkit.entity.Frog; +import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.ItemType; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; +import org.bukkit.map.MapCursor; import org.bukkit.potion.PotionEffectType; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.provider.Arguments; @@ -50,6 +63,8 @@ public class RegistriesArgumentProvider implements ArgumentsProvider { register(PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffectList.class); register(Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class); register(StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class); + register(Villager.Type.class, Registries.VILLAGER_TYPE, CraftVillager.CraftType.class, VillagerType.class); + register(Villager.Profession.class, Registries.VILLAGER_PROFESSION, CraftVillager.CraftProfession.class, VillagerProfession.class); register(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.armortrim.TrimMaterial.class); register(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.armortrim.TrimPattern.class); register(DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class); @@ -57,6 +72,9 @@ public class RegistriesArgumentProvider implements ArgumentsProvider { register(Wolf.Variant.class, Registries.WOLF_VARIANT, CraftWolf.CraftVariant.class, WolfVariant.class); register(ItemType.class, Registries.ITEM, CraftItemType.class, net.minecraft.world.item.Item.class, true); register(BlockType.class, Registries.BLOCK, CraftBlockType.class, net.minecraft.world.level.block.Block.class, true); + register(Frog.Variant.class, Registries.FROG_VARIANT, CraftFrog.CraftVariant.class, FrogVariant.class); + register(Cat.Type.class, Registries.CAT_VARIANT, CraftCat.CraftType.class, CatVariant.class); + register(MapCursor.Type.class, Registries.MAP_DECORATION_TYPE, CraftMapCursor.CraftType.class, MapDecorationType.class); }