mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-03 23:07:40 +01:00
#1424: 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 #931 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:
parent
f59f0d1c9b
commit
41b8d833db
@ -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<B extends Keyed, M> implements Registry<B> {
|
||||
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<B extends Keyed, M> implements Registry<B> {
|
||||
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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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<CatVariant> {
|
||||
private static int count = 0;
|
||||
|
||||
public static Type minecraftToBukkit(CatVariant minecraft) {
|
||||
Preconditions.checkArgument(minecraft != null);
|
||||
|
||||
IRegistry<CatVariant> 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<CatVariant> minecraft) {
|
||||
@ -67,24 +65,80 @@ public class CraftCat extends CraftTameableAnimal implements Cat {
|
||||
}
|
||||
|
||||
public static CatVariant bukkitToMinecraft(Type bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
|
||||
IRegistry<CatVariant> registry = CraftRegistry.getMinecraftRegistry(Registries.CAT_VARIANT);
|
||||
|
||||
return registry.get(CraftNamespacedKey.toMinecraft(bukkit.getKey()));
|
||||
return CraftRegistry.bukkitToMinecraft(bukkit);
|
||||
}
|
||||
|
||||
public static Holder<CatVariant> bukkitToMinecraftHolder(Type bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.CAT_VARIANT);
|
||||
}
|
||||
|
||||
IRegistry<CatVariant> 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<CatVariant> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<FrogVariant> {
|
||||
private static int count = 0;
|
||||
|
||||
public static Variant minecraftToBukkit(FrogVariant minecraft) {
|
||||
Preconditions.checkArgument(minecraft != null);
|
||||
|
||||
IRegistry<FrogVariant> 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<FrogVariant> 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<FrogVariant> bukkitToMinecraftHolder(Variant bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.FROG_VARIANT);
|
||||
}
|
||||
|
||||
IRegistry<FrogVariant> 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<FrogVariant> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<VillagerType> {
|
||||
private static int count = 0;
|
||||
|
||||
public static Type minecraftToBukkit(VillagerType minecraft) {
|
||||
Preconditions.checkArgument(minecraft != null);
|
||||
|
||||
IRegistry<VillagerType> 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<VillagerProfession> {
|
||||
private static int count = 0;
|
||||
|
||||
public static Profession minecraftToBukkit(VillagerProfession minecraft) {
|
||||
Preconditions.checkArgument(minecraft != null);
|
||||
|
||||
IRegistry<VillagerProfession> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,11 +51,6 @@ public class FieldRename {
|
||||
};
|
||||
}
|
||||
|
||||
@RerouteStatic("java/lang/Enum")
|
||||
public static <T extends Enum<T>> T valueOf(Class<T> 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 extends Keyed> T get(Registry<T> registry, NamespacedKey namespacedKey) {
|
||||
// We don't have version-specific changes, so just use current, and don't inject a version
|
||||
|
@ -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 {
|
||||
}
|
@ -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<Class<?>, 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<Enum.EnumDesc> 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<String, ?> function) {
|
||||
}
|
||||
|
||||
private static final class StringConverter<T extends OldEnum<T>> extends Converter<String, T> implements Serializable {
|
||||
|
||||
private final ApiVersion apiVersion;
|
||||
private final Class<T> clazz;
|
||||
private transient LegacyRegistryData registryData;
|
||||
|
||||
StringConverter(ApiVersion apiVersion, Class<T> 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;
|
||||
}
|
||||
}
|
@ -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<Object, Object> {
|
||||
|
||||
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<? extends Object, ?> m) {
|
||||
if (map instanceof EnumMap<?, ?>) {
|
||||
map.putAll(m);
|
||||
}
|
||||
|
||||
super.putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> keySet() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Object, Object>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Object> {
|
||||
|
||||
private final Class<?> objectClass;
|
||||
private final Set set;
|
||||
|
||||
private static Set createSet(Class<?> clazz) {
|
||||
if (clazz.isEnum()) {
|
||||
return EnumSet.noneOf((Class<Enum>) 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<Enum>) 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<Object> 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> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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 {
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 "";
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<RerouteArgument> arguments, RerouteReturn rerouteReturn, boolean isInBukkit,
|
||||
@Nullable String requiredCompatibility) {
|
||||
@Nullable String requiredCompatibility, @Nullable RequirePluginVersionData requiredPluginVersion) {
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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<MapDecorationType> {
|
||||
|
||||
private static int count = 0;
|
||||
|
||||
public static MapCursor.Type minecraftToBukkit(MapDecorationType minecraft) {
|
||||
Preconditions.checkArgument(minecraft != null);
|
||||
|
||||
IRegistry<MapDecorationType> 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<MapDecorationType> 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<MapDecorationType> bukkitToMinecraftHolder(MapCursor.Type bukkit) {
|
||||
Preconditions.checkArgument(bukkit != null);
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.MAP_DECORATION_TYPE);
|
||||
}
|
||||
|
||||
IRegistry<MapDecorationType> 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<MapDecorationType> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<ApiVersion> {
|
||||
public final class ApiVersion implements Comparable<ApiVersion>, Serializable {
|
||||
|
||||
public static final ApiVersion CURRENT;
|
||||
public static final ApiVersion FLATTENING;
|
||||
@ -122,4 +123,6 @@ public final class ApiVersion implements Comparable<ApiVersion> {
|
||||
public String toString() {
|
||||
return getVersionString();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
}
|
||||
|
@ -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<String, String> 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<String, String> 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<String, String> 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<String, RerouteMethodData> createReroutes(Class<?> clazz) {
|
||||
@ -82,6 +94,7 @@ public class Commodore {
|
||||
private static final Map<String, RerouteMethodData> FIELD_RENAME_METHOD_REROUTE = createReroutes(FieldRename.class);
|
||||
private static final Map<String, RerouteMethodData> MATERIAL_METHOD_REROUTE = createReroutes(MaterialRerouting.class);
|
||||
private static final Map<String, RerouteMethodData> METHOD_REROUTE = createReroutes(MethodRerouting.class);
|
||||
private static final Map<String, RerouteMethodData> 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<String> 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> 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<String, RerouteMethodData> 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<String> activeCompatibilities, Map<String, RerouteMethodData> rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer<RerouteMethodData> consumer) {
|
||||
public static boolean rerouteMethods(Set<String> activeCompatibilities, ApiVersion pluginVersion, Map<String, RerouteMethodData> rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer<RerouteMethodData> 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
|
||||
|
@ -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("<init>")) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user