13
0
geforkt von Mirrors/Paper

SPIGOT-5336: Field name parity with Minecraft keys

By: DerFrZocker <derrieple@gmail.com>
Dieser Commit ist enthalten in:
CraftBukkit/Spigot 2024-04-24 01:15:00 +10:00
Ursprung d122883f57
Commit 760899464e
26 geänderte Dateien mit 1306 neuen und 156 gelöschten Zeilen

Datei anzeigen

@ -195,6 +195,7 @@ import org.bukkit.craftbukkit.tag.CraftBlockTag;
import org.bukkit.craftbukkit.tag.CraftEntityTag;
import org.bukkit.craftbukkit.tag.CraftFluidTag;
import org.bukkit.craftbukkit.tag.CraftItemTag;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftIconCache;
import org.bukkit.craftbukkit.util.CraftLocation;
@ -288,7 +289,7 @@ public final class CraftServer implements Server {
private final Object2IntOpenHashMap<SpawnCategory> spawnCategoryLimit = new Object2IntOpenHashMap<>();
private File container;
private WarningState warningState = WarningState.DEFAULT;
public String minimumAPI;
public ApiVersion minimumAPI;
public CraftScoreboardManager scoreboardManager;
public CraftDataPackManager dataPackManager;
private CraftServerTickManager serverTickManager;
@ -371,7 +372,7 @@ public final class CraftServer implements Server {
console.autosavePeriod = configuration.getInt("ticks-per.autosave");
warningState = WarningState.value(configuration.getString("settings.deprecated-verbose"));
TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks");
minimumAPI = configuration.getString("settings.minimum-api");
minimumAPI = ApiVersion.getOrCreateVersion(configuration.getString("settings.minimum-api"));
loadIcon();
// Set map color cache
@ -888,7 +889,7 @@ public final class CraftServer implements Server {
overrideSpawnLimits();
warningState = WarningState.value(configuration.getString("settings.deprecated-verbose"));
TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks");
minimumAPI = configuration.getString("settings.minimum-api");
minimumAPI = ApiVersion.getOrCreateVersion(configuration.getString("settings.minimum-api"));
printSaveWarning = false;
console.autosavePeriod = configuration.getInt("ticks-per.autosave");
loadIcon();

Datei anzeigen

@ -274,8 +274,8 @@ public final class CraftEntityTypes {
register(new EntityTypeData<>(EntityType.CHICKEN, Chicken.class, CraftChicken::new, createLiving(EntityTypes.CHICKEN)));
register(new EntityTypeData<>(EntityType.SQUID, Squid.class, CraftSquid::new, createLiving(EntityTypes.SQUID)));
register(new EntityTypeData<>(EntityType.WOLF, Wolf.class, CraftWolf::new, createLiving(EntityTypes.WOLF)));
register(new EntityTypeData<>(EntityType.MUSHROOM_COW, MushroomCow.class, CraftMushroomCow::new, createLiving(EntityTypes.MOOSHROOM)));
register(new EntityTypeData<>(EntityType.SNOWMAN, Snowman.class, CraftSnowman::new, createLiving(EntityTypes.SNOW_GOLEM)));
register(new EntityTypeData<>(EntityType.MOOSHROOM, MushroomCow.class, CraftMushroomCow::new, createLiving(EntityTypes.MOOSHROOM)));
register(new EntityTypeData<>(EntityType.SNOW_GOLEM, Snowman.class, CraftSnowman::new, createLiving(EntityTypes.SNOW_GOLEM)));
register(new EntityTypeData<>(EntityType.OCELOT, Ocelot.class, CraftOcelot::new, createLiving(EntityTypes.OCELOT)));
register(new EntityTypeData<>(EntityType.IRON_GOLEM, IronGolem.class, CraftIronGolem::new, createLiving(EntityTypes.IRON_GOLEM)));
register(new EntityTypeData<>(EntityType.HORSE, Horse.class, CraftHorse::new, createLiving(EntityTypes.HORSE)));
@ -349,11 +349,11 @@ public final class CraftEntityTypes {
// Move no rotation
register(new EntityTypeData<>(EntityType.ARROW, Arrow.class, CraftArrow::new, createAndMoveEmptyRot(EntityTypes.ARROW)));
register(new EntityTypeData<>(EntityType.ENDER_PEARL, EnderPearl.class, CraftEnderPearl::new, createAndMoveEmptyRot(EntityTypes.ENDER_PEARL)));
register(new EntityTypeData<>(EntityType.THROWN_EXP_BOTTLE, ThrownExpBottle.class, CraftThrownExpBottle::new, createAndMoveEmptyRot(EntityTypes.EXPERIENCE_BOTTLE)));
register(new EntityTypeData<>(EntityType.EXPERIENCE_BOTTLE, ThrownExpBottle.class, CraftThrownExpBottle::new, createAndMoveEmptyRot(EntityTypes.EXPERIENCE_BOTTLE)));
register(new EntityTypeData<>(EntityType.SPECTRAL_ARROW, SpectralArrow.class, CraftSpectralArrow::new, createAndMoveEmptyRot(EntityTypes.SPECTRAL_ARROW)));
register(new EntityTypeData<>(EntityType.ENDER_CRYSTAL, EnderCrystal.class, CraftEnderCrystal::new, createAndMoveEmptyRot(EntityTypes.END_CRYSTAL)));
register(new EntityTypeData<>(EntityType.END_CRYSTAL, EnderCrystal.class, CraftEnderCrystal::new, createAndMoveEmptyRot(EntityTypes.END_CRYSTAL)));
register(new EntityTypeData<>(EntityType.TRIDENT, Trident.class, CraftTrident::new, createAndMoveEmptyRot(EntityTypes.TRIDENT)));
register(new EntityTypeData<>(EntityType.LIGHTNING, LightningStrike.class, CraftLightningStrike::new, createAndMoveEmptyRot(EntityTypes.LIGHTNING_BOLT)));
register(new EntityTypeData<>(EntityType.LIGHTNING_BOLT, LightningStrike.class, CraftLightningStrike::new, createAndMoveEmptyRot(EntityTypes.LIGHTNING_BOLT)));
// Move
register(new EntityTypeData<>(EntityType.SHULKER_BULLET, ShulkerBullet.class, CraftShulkerBullet::new, createAndMove(EntityTypes.SHULKER_BULLET)));
@ -370,7 +370,7 @@ public final class CraftEntityTypes {
register(new EntityTypeData<>(EntityType.TEXT_DISPLAY, TextDisplay.class, CraftTextDisplay::new, createAndSetPos(EntityTypes.TEXT_DISPLAY)));
// MISC
register(new EntityTypeData<>(EntityType.DROPPED_ITEM, Item.class, CraftItem::new, spawnData -> {
register(new EntityTypeData<>(EntityType.ITEM, Item.class, CraftItem::new, spawnData -> {
// We use stone instead of empty, to give the plugin developer a visual clue, that the spawn method is working,
// and that the item stack should probably be changed.
net.minecraft.world.item.ItemStack itemStack = new net.minecraft.world.item.ItemStack(Items.STONE);
@ -384,31 +384,31 @@ public final class CraftEntityTypes {
));
register(new EntityTypeData<>(EntityType.AREA_EFFECT_CLOUD, AreaEffectCloud.class, CraftAreaEffectCloud::new, spawnData -> new EntityAreaEffectCloud(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.EGG, Egg.class, CraftEgg::new, spawnData -> new EntityEgg(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.LEASH_HITCH, LeashHitch.class, CraftLeash::new, spawnData -> new EntityLeash(spawnData.minecraftWorld(), BlockPosition.containing(spawnData.x(), spawnData.y(), spawnData.z())))); // SPIGOT-5732: LeashHitch has no direction and is always centered at a block
register(new EntityTypeData<>(EntityType.LEASH_KNOT, LeashHitch.class, CraftLeash::new, spawnData -> new EntityLeash(spawnData.minecraftWorld(), BlockPosition.containing(spawnData.x(), spawnData.y(), spawnData.z())))); // SPIGOT-5732: LeashHitch has no direction and is always centered at a block
register(new EntityTypeData<>(EntityType.SNOWBALL, Snowball.class, CraftSnowball::new, spawnData -> new EntitySnowball(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.ENDER_SIGNAL, EnderSignal.class, CraftEnderSignal::new, spawnData -> new EntityEnderSignal(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.SPLASH_POTION, ThrownPotion.class, CraftThrownPotion::new, spawnData -> {
register(new EntityTypeData<>(EntityType.EYE_OF_ENDER, EnderSignal.class, CraftEnderSignal::new, spawnData -> new EntityEnderSignal(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.POTION, ThrownPotion.class, CraftThrownPotion::new, spawnData -> {
EntityPotion entity = new EntityPotion(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z());
entity.setItem(CraftItemStack.asNMSCopy(new ItemStack(Material.SPLASH_POTION, 1)));
return entity;
}));
register(new EntityTypeData<>(EntityType.PRIMED_TNT, TNTPrimed.class, CraftTNTPrimed::new, spawnData -> new EntityTNTPrimed(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), null)));
register(new EntityTypeData<>(EntityType.TNT, TNTPrimed.class, CraftTNTPrimed::new, spawnData -> new EntityTNTPrimed(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), null)));
register(new EntityTypeData<>(EntityType.FALLING_BLOCK, FallingBlock.class, CraftFallingBlock::new, spawnData -> {
BlockPosition pos = BlockPosition.containing(spawnData.x(), spawnData.y(), spawnData.z());
return EntityFallingBlock.fall(spawnData.minecraftWorld(), pos, spawnData.world().getBlockState(pos));
}));
register(new EntityTypeData<>(EntityType.FIREWORK, Firework.class, CraftFirework::new, spawnData -> new EntityFireworks(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), net.minecraft.world.item.ItemStack.EMPTY)));
register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new EntityFireworks(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), net.minecraft.world.item.ItemStack.EMPTY)));
register(new EntityTypeData<>(EntityType.EVOKER_FANGS, EvokerFangs.class, CraftEvokerFangs::new, spawnData -> new EntityEvokerFangs(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), (float) Math.toRadians(spawnData.yaw()), 0, null)));
register(new EntityTypeData<>(EntityType.MINECART_COMMAND, CommandMinecart.class, CraftMinecartCommand::new, spawnData -> new EntityMinecartCommandBlock(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.COMMAND_BLOCK_MINECART, CommandMinecart.class, CraftMinecartCommand::new, spawnData -> new EntityMinecartCommandBlock(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART, RideableMinecart.class, CraftMinecartRideable::new, spawnData -> new EntityMinecartRideable(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART_CHEST, StorageMinecart.class, CraftMinecartChest::new, spawnData -> new EntityMinecartChest(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART_FURNACE, PoweredMinecart.class, CraftMinecartFurnace::new, spawnData -> new EntityMinecartFurnace(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART_TNT, ExplosiveMinecart.class, CraftMinecartTNT::new, spawnData -> new EntityMinecartTNT(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART_HOPPER, HopperMinecart.class, CraftMinecartHopper::new, spawnData -> new EntityMinecartHopper(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.MINECART_MOB_SPAWNER, SpawnerMinecart.class, CraftMinecartMobSpawner::new, spawnData -> new EntityMinecartMobSpawner(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.CHEST_MINECART, StorageMinecart.class, CraftMinecartChest::new, spawnData -> new EntityMinecartChest(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.FURNACE_MINECART, PoweredMinecart.class, CraftMinecartFurnace::new, spawnData -> new EntityMinecartFurnace(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.TNT_MINECART, ExplosiveMinecart.class, CraftMinecartTNT::new, spawnData -> new EntityMinecartTNT(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.HOPPER_MINECART, HopperMinecart.class, CraftMinecartHopper::new, spawnData -> new EntityMinecartHopper(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.SPAWNER_MINECART, SpawnerMinecart.class, CraftMinecartMobSpawner::new, spawnData -> new EntityMinecartMobSpawner(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
// None spawn able
register(new EntityTypeData<>(EntityType.FISHING_HOOK, FishHook.class, CraftFishHook::new, null)); // Cannot spawn a fish hook
register(new EntityTypeData<>(EntityType.FISHING_BOBBER, FishHook.class, CraftFishHook::new, null)); // Cannot spawn a fish hook
register(new EntityTypeData<>(EntityType.PLAYER, Player.class, CraftPlayer::new, null)); // Cannot spawn a player
}

Datei anzeigen

@ -1362,10 +1362,10 @@ public class CraftEventFactory {
org.bukkit.entity.Entity bukkitIgniter = igniter.getBukkitEntity();
IgniteCause cause;
switch (bukkitIgniter.getType()) {
case ENDER_CRYSTAL:
case END_CRYSTAL:
cause = IgniteCause.ENDER_CRYSTAL;
break;
case LIGHTNING:
case LIGHTNING_BOLT:
cause = IgniteCause.LIGHTNING;
break;
case SMALL_FIREBALL:

Datei anzeigen

@ -281,7 +281,7 @@ public final class CraftLegacy {
SPAWN_EGGS.put((byte) EntityType.HUSK.getTypeId(), Material.HUSK_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.LLAMA.getTypeId(), Material.LLAMA_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.MAGMA_CUBE.getTypeId(), Material.MAGMA_CUBE_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.MUSHROOM_COW.getTypeId(), Material.MOOSHROOM_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.MOOSHROOM.getTypeId(), Material.MOOSHROOM_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.MULE.getTypeId(), Material.MULE_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.OCELOT.getTypeId(), Material.OCELOT_SPAWN_EGG);
SPAWN_EGGS.put((byte) EntityType.PARROT.getTypeId(), Material.PARROT_SPAWN_EGG);

Datei anzeigen

@ -0,0 +1,340 @@
package org.bukkit.craftbukkit.legacy;
import org.bukkit.Particle;
import org.bukkit.block.Biome;
import org.bukkit.block.banner.PatternType;
import org.bukkit.craftbukkit.legacy.fieldrename.FieldRenameData;
import org.bukkit.craftbukkit.legacy.reroute.DoNotReroute;
import org.bukkit.craftbukkit.legacy.reroute.InjectPluginVersion;
import org.bukkit.craftbukkit.legacy.reroute.RerouteMethodName;
import org.bukkit.craftbukkit.legacy.reroute.RerouteStatic;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
import org.bukkit.loot.LootTables;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionType;
public class FieldRename {
@DoNotReroute
public static String rename(ApiVersion apiVersion, String owner, String from) {
if (owner == null) {
return from;
}
return switch (owner) {
case "org/bukkit/block/banner/PatternType" -> convertPatternTypeName(apiVersion, from);
case "org/bukkit/enchantments/Enchantment" -> convertEnchantmentName(apiVersion, from);
case "org/bukkit/block/Biome" -> convertBiomeName(apiVersion, from);
case "org/bukkit/entity/EntityType" -> convertEntityTypeName(apiVersion, from);
case "org/bukkit/potion/PotionEffectType" -> convertPotionEffectTypeName(apiVersion, from);
case "org/bukkit/potion/PotionType" -> convertPotionTypeName(apiVersion, from);
case "org/bukkit/MusicInstrument" -> convertMusicInstrumentName(apiVersion, from);
case "org/bukkit/Particle" -> convertParticleName(apiVersion, from);
case "org/bukkit/loot/LootTables" -> convertLootTablesName(apiVersion, from);
case "org/bukkit/attribute/Attribute" -> convertAttributeName(apiVersion, from);
default -> from;
};
}
// PatternType
private static final FieldRenameData PATTERN_TYPE_DATA = FieldRenameData.Builder.newBuilder()
.forVersionsBefore(ApiVersion.FIELD_NAME_PARITY)
.change("DIAGONAL_RIGHT", "DIAGONAL_UP_RIGHT")
.forAllVersions()
.change("STRIPE_SMALL", "SMALL_STRIPES")
.change("DIAGONAL_LEFT_MIRROR", "DIAGONAL_UP_LEFT")
.change("DIAGONAL_RIGHT_MIRROR", "DIAGONAL_RIGHT")
.change("CIRCLE_MIDDLE", "CIRCLE")
.change("RHOMBUS_MIDDLE", "RHOMBUS")
.change("HALF_VERTICAL_MIRROR", "HALF_VERTICAL_RIGHT")
.change("HALF_HORIZONTAL_MIRROR", "HALF_HORIZONTAL_BOTTOM")
.build();
@DoNotReroute
public static String convertPatternTypeName(ApiVersion version, String from) {
return PATTERN_TYPE_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/block/banner/PatternType")
public static PatternType valueOf_PatternType(String value, @InjectPluginVersion ApiVersion version) {
return PatternType.valueOf(convertPatternTypeName(version, value));
}
// Enchantment
private static final FieldRenameData ENCHANTMENT_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("PROTECTION_ENVIRONMENTAL", "PROTECTION")
.change("PROTECTION_FIRE", "FIRE_PROTECTION")
.change("PROTECTION_FALL", "FEATHER_FALLING")
.change("PROTECTION_EXPLOSIONS", "BLAST_PROTECTION")
.change("PROTECTION_PROJECTILE", "PROJECTILE_PROTECTION")
.change("OXYGEN", "RESPIRATION")
.change("WATER_WORKER", "AQUA_AFFINITY")
.change("DAMAGE_ALL", "SHARPNESS")
.change("DAMAGE_UNDEAD", "SMITE")
.change("DAMAGE_ARTHROPODS", "BANE_OF_ARTHROPODS")
.change("LOOT_BONUS_MOBS", "LOOTING")
.change("SWEEPING_EDGE", "SWEEPING")
.change("DIG_SPEED", "EFFICIENCY")
.change("DURABILITY", "UNBREAKING")
.change("LOOT_BONUS_BLOCKS", "FORTUNE")
.change("ARROW_DAMAGE", "POWER")
.change("ARROW_KNOCKBACK", "PUNCH")
.change("ARROW_FIRE", "FLAME")
.change("ARROW_INFINITY", "INFINITY")
.change("LUCK", "LUCK_OF_THE_SEA")
.build();
@DoNotReroute
public static String convertEnchantmentName(ApiVersion version, String from) {
return ENCHANTMENT_DATA.getReplacement(version, from);
}
@RerouteMethodName("getByName")
@RerouteStatic("org/bukkit/enchantments/Enchantment")
public static Enchantment getByName_Enchantment(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return Enchantment.getByName(convertEnchantmentName(ApiVersion.CURRENT, name));
}
// Biome
private static final FieldRenameData BIOME_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("NETHER", "NETHER_WASTES")
.change("TALL_BIRCH_FOREST", "OLD_GROWTH_BIRCH_FOREST")
.change("GIANT_TREE_TAIGA", "OLD_GROWTH_PINE_TAIGA")
.change("GIANT_SPRUCE_TAIGA", "OLD_GROWTH_SPRUCE_TAIGA")
.change("SNOWY_TUNDRA", "SNOWY_PLAINS")
.change("JUNGLE_EDGE", "SPARSE_JUNGLE")
.change("STONE_SHORE", "STONY_SHORE")
.change("MOUNTAINS", "WINDSWEPT_HILLS")
.change("WOODED_MOUNTAINS", "WINDSWEPT_FOREST")
.change("GRAVELLY_MOUNTAINS", "WINDSWEPT_GRAVELLY_HILLS")
.change("SHATTERED_SAVANNA", "WINDSWEPT_SAVANNA")
.change("WOODED_BADLANDS_PLATEAU", "WOODED_BADLANDS")
.build();
@DoNotReroute
public static String convertBiomeName(ApiVersion version, String from) {
return BIOME_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/block/Biome")
public static Biome valueOf_Biome(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return Biome.valueOf(convertBiomeName(ApiVersion.CURRENT, name));
}
// EntityType
private static final FieldRenameData ENTITY_TYPE_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("XP_ORB", "EXPERIENCE_ORB")
.change("EYE_OF_ENDER_SIGNAL", "EYE_OF_ENDER")
.change("XP_BOTTLE", "EXPERIENCE_BOTTLE")
.change("FIREWORKS_ROCKET", "FIREWORK_ROCKET")
.change("EVOCATION_FANGS", "EVOKER_FANGS")
.change("EVOCATION_ILLAGER", "EVOKER")
.change("VINDICATION_ILLAGER", "VINDICATOR")
.change("ILLUSION_ILLAGER", "ILLUSIONER")
.change("COMMANDBLOCK_MINECART", "COMMAND_BLOCK_MINECART")
.change("SNOWMAN", "SNOW_GOLEM")
.change("VILLAGER_GOLEM", "IRON_GOLEM")
.change("ENDER_CRYSTAL", "END_CRYSTAL")
.change("ZOMBIE_PIGMAN", "ZOMBIFIED_PIGLIN")
.change("PIG_ZOMBIE", "ZOMBIFIED_PIGLIN")
.change("DROPPED_ITEM", "ITEM")
.change("LEASH_HITCH", "LEASH_KNOT")
.change("ENDER_SIGNAL", "EYE_OF_ENDER")
.change("SPLASH_POTION", "POTION")
.change("THROWN_EXP_BOTTLE", "EXPERIENCE_BOTTLE")
.change("PRIMED_TNT", "TNT")
.change("FIREWORK", "FIREWORK_ROCKET")
.change("MINECART_COMMAND", "COMMAND_BLOCK_MINECART")
.change("MINECART_CHEST", "CHEST_MINECART")
.change("MINECART_FURNACE", "FURNACE_MINECART")
.change("MINECART_TNT", "TNT_MINECART")
.change("MINECART_HOPPER", "HOPPER_MINECART")
.change("MINECART_MOB_SPAWNER", "SPAWNER_MINECART")
.change("MUSHROOM_COW", "MOOSHROOM")
.change("SNOWMAN", "SNOW_GOLEM")
.change("ENDER_CRYSTAL", "END_CRYSTAL")
.change("FISHING_HOOK", "FISHING_BOBBER")
.change("LIGHTNING", "LIGHTNING_BOLT")
.build();
@DoNotReroute
public static String convertEntityTypeName(ApiVersion version, String from) {
return ENTITY_TYPE_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/entity/EntityType")
public static EntityType valueOf_EntityType(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return EntityType.valueOf(convertEntityTypeName(ApiVersion.CURRENT, name));
}
@RerouteMethodName("fromName")
@RerouteStatic("org/bukkit/entity/EntityType")
public static EntityType fromName_EntityType(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return EntityType.fromName(convertEntityTypeName(ApiVersion.CURRENT, name));
}
// PotionEffectType
private static final FieldRenameData POTION_EFFECT_TYPE_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("SLOW", "SLOWNESS")
.change("FAST_DIGGING", "HASTE")
.change("SLOW_DIGGING", "MINING_FATIGUE")
.change("INCREASE_DAMAGE", "STRENGTH")
.change("HEAL", "INSTANT_HEALTH")
.change("HARM", "INSTANT_DAMAGE")
.change("JUMP", "JUMP_BOOST")
.change("CONFUSION", "NAUSEA")
.change("DAMAGE_RESISTANCE", "RESISTANCE")
.build();
@DoNotReroute
public static String convertPotionEffectTypeName(ApiVersion version, String from) {
return POTION_EFFECT_TYPE_DATA.getReplacement(version, from);
}
@RerouteMethodName("getByName")
@RerouteStatic("org/bukkit/potion/PotionEffectType")
public static PotionEffectType getByName_PotionEffectType(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return PotionEffectType.getByName(convertPotionEffectTypeName(ApiVersion.CURRENT, name));
}
// PotionType
private static final FieldRenameData POTION_TYPE_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("UNCRAFTABLE", "EMPTY")
.change("JUMP", "LEAPING")
.change("SPEED", "SWIFTNESS")
.change("INSTANT_HEAL", "HEALING")
.change("INSTANT_DAMAGE", "HARMING")
.change("REGEN", "REGENERATION")
.build();
@DoNotReroute
public static String convertPotionTypeName(ApiVersion version, String from) {
return POTION_TYPE_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/potion/PotionType")
public static PotionType valueOf_PotionType(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return PotionType.valueOf(convertPotionTypeName(ApiVersion.CURRENT, name));
}
// MusicInstrument
private static final FieldRenameData MUSIC_INSTRUMENT_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("PONDER", "PONDER_GOAT_HORN")
.change("SING", "SING_GOAT_HORN")
.change("SEEK", "SEEK_GOAT_HORN")
.change("ADMIRE", "ADMIRE_GOAT_HORN")
.change("CALL", "CALL_GOAT_HORN")
.change("YEARN", "YEARN_GOAT_HORN")
.change("DREAM", "DREAM_GOAT_HORN")
.build();
@DoNotReroute
public static String convertMusicInstrumentName(ApiVersion version, String from) {
return MUSIC_INSTRUMENT_DATA.getReplacement(version, from);
}
// Particle
private static final FieldRenameData PARTICLE_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("EXPLOSION_NORMAL", "POOF")
.change("EXPLOSION_LARGE", "EXPLOSION")
.change("EXPLOSION_HUGE", "EXPLOSION_EMITTER")
.change("FIREWORKS_SPARK", "FIREWORK")
.change("WATER_BUBBLE", "BUBBLE")
.change("WATER_SPLASH", "SPLASH")
.change("WATER_WAKE", "FISHING")
.change("SUSPENDED", "UNDERWATER")
.change("SUSPENDED_DEPTH", "UNDERWATER")
.change("CRIT_MAGIC", "ENCHANTED_HIT")
.change("SMOKE_NORMAL", "SMOKE")
.change("SMOKE_LARGE", "LARGE_SMOKE")
.change("SPELL", "EFFECT")
.change("SPELL_INSTANT", "INSTANT_EFFECT")
.change("SPELL_MOB", "ENTITY_EFFECT")
.change("SPELL_MOB_AMBIENT", "AMBIENT_ENTITY_EFFECT")
.change("SPELL_WITCH", "WITCH")
.change("DRIP_WATER", "DRIPPING_WATER")
.change("DRIP_LAVA", "DRIPPING_LAVA")
.change("VILLAGER_ANGRY", "ANGRY_VILLAGER")
.change("VILLAGER_HAPPY", "HAPPY_VILLAGER")
.change("TOWN_AURA", "MYCELIUM")
.change("ENCHANTMENT_TABLE", "ENCHANT")
.change("REDSTONE", "DUST")
.change("SNOWBALL", "ITEM_SNOWBALL")
.change("SNOW_SHOVEL", "ITEM_SNOWBALL")
.change("SLIME", "ITEM_SLIME")
.change("ITEM_CRACK", "ITEM")
.change("BLOCK_CRACK", "BLOCK")
.change("BLOCK_DUST", "BLOCK")
.change("WATER_DROP", "RAIN")
.change("MOB_APPEARANCE", "ELDER_GUARDIAN")
.change("TOTEM", "TOTEM_OF_UNDYING")
.build();
@DoNotReroute
public static String convertParticleName(ApiVersion version, String from) {
return PARTICLE_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/Particle")
public static Particle valueOf_Particle(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return Particle.valueOf(convertParticleName(ApiVersion.CURRENT, name));
}
// LootTables
private static final FieldRenameData LOOT_TABLES_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("ZOMBIE_PIGMAN", "ZOMBIFIED_PIGLIN")
.build();
@DoNotReroute
public static String convertLootTablesName(ApiVersion version, String from) {
return LOOT_TABLES_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/loot/LootTables")
public static LootTables valueOf_LootTables(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return LootTables.valueOf(convertLootTablesName(ApiVersion.CURRENT, name));
}
// LootTables
private static final FieldRenameData ATTRIBUTE_DATA = FieldRenameData.Builder.newBuilder()
.forAllVersions()
.change("HORSE_JUMP_STRENGTH", "GENERIC_JUMP_STRENGTH")
.build();
@DoNotReroute
public static String convertAttributeName(ApiVersion version, String from) {
return ATTRIBUTE_DATA.getReplacement(version, from);
}
@RerouteMethodName("valueOf")
@RerouteStatic("org/bukkit/attribute/Attribute")
public static LootTables valueOf_Attribute(String name) {
// We don't have version-specific changes, so just use current, and don't inject a version
return LootTables.valueOf(convertAttributeName(ApiVersion.CURRENT, name));
}
}

Datei anzeigen

@ -0,0 +1,64 @@
package org.bukkit.craftbukkit.legacy.fieldrename;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.bukkit.craftbukkit.util.ApiVersion;
public record FieldRenameData(NavigableMap<ApiVersion, Map<String, String>> versionData, Map<String, String> data) {
public String getReplacement(ApiVersion apiVersion, String from) {
if (from == null) {
return null;
}
from = from.toUpperCase(Locale.ROOT);
from = data.getOrDefault(from, from);
for (Map.Entry<ApiVersion, Map<String, String>> entry : versionData.entrySet()) {
if (apiVersion.isNewerThanOrSameAs(entry.getKey())) {
continue;
}
from = entry.getValue().getOrDefault(from, from);
}
return from;
}
public static class Builder {
private final Map<String, String> data = new HashMap<>();
private final NavigableMap<ApiVersion, Map<String, String>> versionData = new TreeMap<>();
private ApiVersion currentVersion;
public static Builder newBuilder() {
return new Builder();
}
public Builder forVersionsBefore(ApiVersion apiVersion) {
this.currentVersion = apiVersion;
return this;
}
public Builder forAllVersions() {
this.currentVersion = null;
return this;
}
public Builder change(String from, String to) {
if (currentVersion != null) {
versionData.computeIfAbsent(currentVersion, d -> new HashMap<>()).put(from, to);
} else {
data.put(from, to);
}
return this;
}
public FieldRenameData build() {
return new FieldRenameData(versionData, data);
}
}
}

Datei anzeigen

@ -0,0 +1,11 @@
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 DoNotReroute {
}

Datei anzeigen

@ -0,0 +1,11 @@
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 InjectPluginName {
}

Datei anzeigen

@ -0,0 +1,11 @@
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 InjectPluginVersion {
}

Datei anzeigen

@ -0,0 +1,39 @@
package org.bukkit.craftbukkit.legacy.reroute;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public record RerouteArgument(Type type, boolean injectPluginName, boolean injectPluginVersion) {
/**
* Converts the type string to the correct load opcode.
* <br>
* References:
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.3.2-200">Interpretation of field descriptors</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.iload">iload Opcode</a> /
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.iload_n">{@literal iload_<n> Opcode}</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.lload">lload Opcode</a> /
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.lload_n">{@literal lload_<n> Opcode}</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.fload">fload Opcode</a> /
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.fload">{@literal fload_<n> Opcode}</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.dload">dload Opcode</a> /
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.dload_n">{@literal dload_<n> Opcode}</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.aload">aload Opcode</a> /
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.aload_n">{@literal aload_<n> Opcode}</a>
*
* @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));
}
return type.getOpcode(Opcodes.ILOAD);
}
}

Datei anzeigen

@ -0,0 +1,115 @@
package org.bukkit.craftbukkit.legacy.reroute;
import com.google.common.base.Preconditions;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.objectweb.asm.Type;
public class RerouteBuilder {
public static Map<String, RerouteMethodData> buildFromClass(Class<?> clazz) {
Preconditions.checkArgument(!clazz.isInterface(), "Interface Classes are currently not supported");
Map<String, RerouteMethodData> result = new HashMap<>();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isBridge()) {
continue;
}
if (method.isSynthetic()) {
continue;
}
if (!Modifier.isPublic(method.getModifiers())) {
continue;
}
if (!Modifier.isStatic(method.getModifiers())) {
continue;
}
if (method.isAnnotationPresent(DoNotReroute.class)) {
continue;
}
RerouteMethodData rerouteMethodData = buildFromMethod(method);
result.put(rerouteMethodData.source(), rerouteMethodData);
}
return Collections.unmodifiableMap(result);
}
public static RerouteMethodData buildFromMethod(Method method) {
RerouteReturn rerouteReturn = new RerouteReturn(Type.getReturnType(method));
List<RerouteArgument> arguments = new ArrayList<>();
List<RerouteArgument> sourceArguments = new ArrayList<>();
for (Parameter parameter : method.getParameters()) {
Type type = Type.getType(parameter.getType());
boolean injectPluginName = false;
boolean injectPluginVersion = false;
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;
}
if (parameter.isAnnotationPresent(InjectPluginVersion.class)) {
if (parameter.getType() != ApiVersion.class) {
throw new RuntimeException("Plugin version argument must be of type ApiVersion, but got " + parameter.getType());
}
injectPluginVersion = true;
}
if (injectPluginName && injectPluginVersion) {
// 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);
arguments.add(argument);
if (!injectPluginName && !injectPluginVersion) {
sourceArguments.add(argument);
}
}
RerouteStatic rerouteStatic = method.getAnnotation(RerouteStatic.class);
Type sourceOwner;
if (rerouteStatic != null) {
sourceOwner = Type.getObjectType(rerouteStatic.value());
} else {
RerouteArgument argument = sourceArguments.get(0);
sourceOwner = argument.type();
sourceArguments.remove(argument);
}
Type sourceDesc = Type.getMethodType(rerouteReturn.type(), sourceArguments.stream().map(RerouteArgument::type).toArray(Type[]::new));
RerouteMethodName rerouteMethodName = method.getAnnotation(RerouteMethodName.class);
String methodName;
if (rerouteMethodName != null) {
methodName = rerouteMethodName.value();
} else {
methodName = method.getName();
}
String methodKey = sourceOwner.getInternalName()
+ " "
+ sourceDesc.getDescriptor()
+ " "
+ methodName;
Type targetType = Type.getType(method);
return new RerouteMethodData(methodKey, sourceDesc, sourceOwner, methodName, rerouteStatic != null, targetType, Type.getInternalName(method.getDeclaringClass()), method.getName(), arguments, rerouteReturn);
}
}

Datei anzeigen

@ -0,0 +1,9 @@
package org.bukkit.craftbukkit.legacy.reroute;
import java.util.List;
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) {
}

Datei anzeigen

@ -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 RerouteMethodName {
String value();
}

Datei anzeigen

@ -0,0 +1,32 @@
package org.bukkit.craftbukkit.legacy.reroute;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public record RerouteReturn(Type type) {
/**
* Converts the type string to the correct return opcode.
* <br>
* References:
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.3.2-200">Interpretation of field descriptors</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.return">return Opcode</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.ireturn">ireturn Opcode</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.lreturn">lreturn Opcode</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.freturn">freturn Opcode</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.dreturn">dreturn Opcode</a>
* <br>
* <a href="https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.areturn">areturn Opcode</a>
*
* @return the opcode of the type
*/
public int instruction() {
return type.getOpcode(Opcodes.IRETURN);
}
}

Datei anzeigen

@ -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 RerouteStatic {
String value();
}

Datei anzeigen

@ -0,0 +1,125 @@
package org.bukkit.craftbukkit.util;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
public final class ApiVersion implements Comparable<ApiVersion> {
public static final ApiVersion CURRENT;
public static final ApiVersion FLATTENING;
public static final ApiVersion FIELD_NAME_PARITY;
public static final ApiVersion NONE;
private static final Map<String, ApiVersion> versions;
static {
versions = new HashMap<>();
CURRENT = getOrCreateVersion("1.20.5");
FLATTENING = getOrCreateVersion("1.13");
FIELD_NAME_PARITY = getOrCreateVersion("1.20.5");
NONE = getOrCreateVersion("none");
}
private final boolean none;
private final int major;
private final int minor;
private final int patch;
private ApiVersion() {
this.none = true;
this.major = Integer.MIN_VALUE;
this.minor = Integer.MIN_VALUE;
this.patch = Integer.MIN_VALUE;
}
private ApiVersion(int major, int minor, int patch) {
this.none = false;
this.major = major;
this.minor = minor;
this.patch = patch;
}
public static ApiVersion getOrCreateVersion(String versionString) {
if (versionString == null || versionString.trim().isEmpty() || versionString.equalsIgnoreCase("none")) {
return versions.computeIfAbsent("none", s -> new ApiVersion());
}
ApiVersion version = versions.get(versionString);
if (version != null) {
return version;
}
String[] versionParts = versionString.split("\\.");
if (versionParts.length != 2 && versionParts.length != 3) {
throw new IllegalArgumentException(String.format("API version string should be of format \"major.minor.patch\" or \"major.minor\", where \"major\", \"minor\" and \"patch\" are numbers. For example \"1.18.2\" or \"1.13\", but got '%s' instead.", versionString));
}
int major = parseNumber(versionParts[0]);
int minor = parseNumber(versionParts[1]);
int patch;
if (versionParts.length == 3) {
patch = parseNumber(versionParts[2]);
} else {
patch = 0;
}
versionString = toVersionString(major, minor, patch);
return versions.computeIfAbsent(versionString, s -> new ApiVersion(major, minor, patch));
}
private static int parseNumber(String number) {
return Integer.parseInt(number);
}
private static String toVersionString(int major, int minor, int patch) {
return major + "." + minor + "." + patch;
}
@Override
public int compareTo(@NotNull ApiVersion other) {
int result = Integer.compare(major, other.major);
if (result == 0) {
result = Integer.compare(minor, other.minor);
}
if (result == 0) {
result = Integer.compare(patch, other.patch);
}
return result;
}
public String getVersionString() {
if (none) {
return "none";
}
return toVersionString(major, minor, patch);
}
public boolean isNewerThan(ApiVersion apiVersion) {
return compareTo(apiVersion) > 0;
}
public boolean isOlderThan(ApiVersion apiVersion) {
return compareTo(apiVersion) < 0;
}
public boolean isNewerThanOrSameAs(ApiVersion apiVersion) {
return compareTo(apiVersion) >= 0;
}
public boolean isOlderThanOrSameAs(ApiVersion apiVersion) {
return compareTo(apiVersion) <= 0;
}
@Override
public String toString() {
return getVersionString();
}
}

Datei anzeigen

@ -0,0 +1,46 @@
package org.bukkit.craftbukkit.util;
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class ClassTraverser implements Iterator<Class<?>> {
private final Set<Class<?>> visit = new HashSet<>();
private final Set<Class<?>> toVisit = new HashSet<>();
private Class<?> next;
public ClassTraverser(Class<?> next) {
this.next = next;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Class<?> next() {
Class<?> clazz = next;
visit.add(next);
Set<Class<?>> classes = Sets.newHashSet(clazz.getInterfaces());
classes.add(clazz.getSuperclass());
classes.remove(null); // Super class can be null, remove it if this is the case
classes.removeAll(visit);
toVisit.addAll(classes);
if (toVisit.isEmpty()) {
next = null;
return clazz;
}
next = toVisit.iterator().next();
toVisit.remove(next);
return clazz;
}
}

Datei anzeigen

@ -11,6 +11,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
@ -19,18 +20,28 @@ import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.bukkit.Material;
import org.bukkit.craftbukkit.legacy.FieldRename;
import org.bukkit.craftbukkit.legacy.reroute.RerouteArgument;
import org.bukkit.craftbukkit.legacy.reroute.RerouteBuilder;
import org.bukkit.craftbukkit.legacy.reroute.RerouteMethodData;
import org.bukkit.plugin.AuthorNagException;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.RecordComponentVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.SimpleRemapper;
public class Commodore {
private static final String BUKKIT_GENERATED_METHOD_PREFIX = "BUKKIT_CUSTOM_METHOD_";
private static final Set<String> EVIL = new HashSet<>(Arrays.asList(
"org/bukkit/World (III)I getBlockTypeIdAt",
@ -51,6 +62,8 @@ public class Commodore {
"org/spigotmc/event/entity/EntityDismountEvent", "org/bukkit/event/entity/EntityDismountEvent"
);
private static final Map<String, RerouteMethodData> FIELD_RENAME_METHOD_REROUTE = RerouteBuilder.buildFromClass(FieldRename.class);
public static void main(String[] args) {
OptionParser parser = new OptionParser();
OptionSpec<File> inputFlag = parser.acceptsAll(Arrays.asList("i", "input")).withRequiredArg().ofType(File.class).required();
@ -95,7 +108,7 @@ public class Commodore {
byte[] b = ByteStreams.toByteArray(is);
if (entry.getName().endsWith(".class")) {
b = convert(b, false);
b = convert(b, "dummy", ApiVersion.NONE);
entry = new JarEntry(entry.getName());
}
@ -113,81 +126,74 @@ public class Commodore {
}
}
public static byte[] convert(byte[] b, final boolean modern) {
public static byte[] convert(byte[] b, final String pluginName, final ApiVersion pluginVersion) {
final boolean modern = pluginVersion.isNewerThanOrSameAs(ApiVersion.FLATTENING);
ClassReader cr = new ClassReader(b);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, cw) {
final Set<RerouteMethodData> rerouteMethodData = new HashSet<>();
String className;
boolean isInterface;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
className = name;
isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitEnd() {
for (RerouteMethodData rerouteMethodData : rerouteMethodData) {
MethodVisitor methodVisitor = super.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC, buildMethodName(rerouteMethodData), buildMethodDesc(rerouteMethodData), null, null);
methodVisitor.visitCode();
int index = 0;
int extraSize = 0;
for (RerouteArgument argument : rerouteMethodData.arguments()) {
if (argument.injectPluginName()) {
methodVisitor.visitLdcInsn(pluginName);
} 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 {
methodVisitor.visitIntInsn(argument.instruction(), index);
index++;
// Long and double need two space
// https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.3
// https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-2.html#jvms-2.6.1
// https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-2.html#jvms-2.6.2
extraSize += argument.type().getSize() - 1;
}
}
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, rerouteMethodData.targetOwner(), rerouteMethodData.targetName(), rerouteMethodData.targetType().getDescriptor(), false);
methodVisitor.visitInsn(rerouteMethodData.rerouteReturn().instruction());
methodVisitor.visitMaxs(rerouteMethodData.arguments().size() + extraSize, index + extraSize);
methodVisitor.visitEnd();
}
super.visitEnd();
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitAnnotation(descriptor, visible));
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodVisitor(api, super.visitMethod(access, name, desc, signature, exceptions)) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (owner.equals("org/bukkit/block/Biome")) {
switch (name) {
case "NETHER":
super.visitFieldInsn(opcode, owner, "NETHER_WASTES", desc);
return;
case "TALL_BIRCH_FOREST":
super.visitFieldInsn(opcode, owner, "OLD_GROWTH_BIRCH_FOREST", desc);
return;
case "GIANT_TREE_TAIGA":
super.visitFieldInsn(opcode, owner, "OLD_GROWTH_PINE_TAIGA", desc);
return;
case "GIANT_SPRUCE_TAIGA":
super.visitFieldInsn(opcode, owner, "OLD_GROWTH_SPRUCE_TAIGA", desc);
return;
case "SNOWY_TUNDRA":
super.visitFieldInsn(opcode, owner, "SNOWY_PLAINS", desc);
return;
case "JUNGLE_EDGE":
super.visitFieldInsn(opcode, owner, "SPARSE_JUNGLE", desc);
return;
case "STONE_SHORE":
super.visitFieldInsn(opcode, owner, "STONY_SHORE", desc);
return;
case "MOUNTAINS":
super.visitFieldInsn(opcode, owner, "WINDSWEPT_HILLS", desc);
return;
case "WOODED_MOUNTAINS":
super.visitFieldInsn(opcode, owner, "WINDSWEPT_FOREST", desc);
return;
case "GRAVELLY_MOUNTAINS":
super.visitFieldInsn(opcode, owner, "WINDSWEPT_GRAVELLY_HILLS", desc);
return;
case "SHATTERED_SAVANNA":
super.visitFieldInsn(opcode, owner, "WINDSWEPT_SAVANNA", desc);
return;
case "WOODED_BADLANDS_PLATEAU":
super.visitFieldInsn(opcode, owner, "WOODED_BADLANDS", desc);
return;
}
}
if (owner.equals("org/bukkit/entity/EntityType")) {
switch (name) {
case "PIG_ZOMBIE":
super.visitFieldInsn(opcode, owner, "ZOMBIFIED_PIGLIN", desc);
return;
}
}
if (owner.equals("org/bukkit/attribute/Attribute")) {
switch (name) {
case "HORSE_JUMP_STRENGTH":
super.visitFieldInsn(opcode, owner, "GENERIC_JUMP_STRENGTH", desc);
return;
}
}
if (owner.equals("org/bukkit/loot/LootTables")) {
switch (name) {
case "ZOMBIE_PIGMAN":
super.visitFieldInsn(opcode, owner, "ZOMBIFIED_PIGLIN", desc);
return;
}
}
name = FieldRename.rename(pluginVersion, owner, name);
if (modern) {
if (owner.equals("org/bukkit/Material")) {
@ -268,6 +274,10 @@ public class Commodore {
}
private void handleMethod(MethodPrinter visitor, int opcode, String owner, String name, String desc, boolean itf, Type samMethodType, Type instantiatedMethodType) {
if (checkReroute(visitor, FIELD_RENAME_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
@ -373,6 +383,13 @@ public class Commodore {
visitor.visit(opcode, owner, name, desc, itf, samMethodType, instantiatedMethodType);
}
private boolean checkReroute(MethodPrinter visitor, Map<String, RerouteMethodData> rerouteMethodDataMap, int opcode, String owner, String name, String desc, Type samMethodType, Type instantiatedMethodType) {
return rerouteMethods(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);
});
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
handleMethod((newOpcode, newOwner, newName, newDescription, newItf, newSam, newInstantiated) -> {
@ -419,6 +436,71 @@ public class Commodore {
// But as with the todo above, I encourage everyone who is reading this to to give it a shot
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitAnnotation(descriptor, visible));
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return createAnnotationVisitor(pluginVersion, api, super.visitAnnotationDefault());
}
@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitInsnAnnotation(typeRef, typePath, descriptor, visible));
}
@Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible));
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitParameterAnnotation(parameter, descriptor, visible));
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible));
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
}
};
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
return new FieldVisitor(api, super.visitField(access, name, descriptor, signature, value)) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitAnnotation(descriptor, visible));
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
}
};
}
@Override
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
return new RecordComponentVisitor(api, super.visitRecordComponent(name, descriptor, signature)) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitAnnotation(descriptor, visible));
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return createAnnotationVisitor(pluginVersion, api, super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
}
};
}
}, new SimpleRemapper(RENAMES)), 0);
@ -426,6 +508,90 @@ public class Commodore {
return cw.toByteArray();
}
private static AnnotationVisitor createAnnotationVisitor(ApiVersion apiVersion, int api, AnnotationVisitor delegate) {
return new AnnotationVisitor(api, delegate) {
@Override
public void visitEnum(String name, String descriptor, String value) {
super.visitEnum(name, descriptor, FieldRename.rename(apiVersion, Type.getType(descriptor).getInternalName(), value));
}
@Override
public AnnotationVisitor visitArray(String name) {
return createAnnotationVisitor(apiVersion, api, super.visitArray(name));
}
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
return createAnnotationVisitor(apiVersion, api, super.visitAnnotation(name, descriptor));
}
};
}
/*
This method looks (and probably is) overengineered, but it gives the most flexible when it comes to remapping normal methods to static one.
The problem with normal owner and desc replacement is that child classes have them as an owner, instead there parents for there parents methods
For example, if we have following two interfaces org.bukkit.BlockData and org.bukkit.Orientable extents BlockData
and BlockData has the method org.bukkit.Material getType which we want to reroute to the static method
org.bukkit.Material org.bukkit.craftbukkit.legacy.EnumEvil#getType(org.bukkit.BlockData)
If we now call BlockData#getType we get as the owner org/bukkit/BlockData and as desc ()Lorg/bukkit/Material;
Which we can nicely reroute by checking if the owner is BlockData and the name getType
The problem, starts if we use Orientable#getType no we get as owner org/bukkit/Orientable and as desc ()Lorg/bukkit/Material;
Now we can now longer safely say to which getType method we need to reroute (assume there are multiple getType methods from different classes,
which are not related to BlockData), simple using the owner class will not work, since would reroute to
EnumEvil#getType(org.bukkit.Orientable) which is not EnumEvil#getType(org.bukkit.BlockData) and will throw a method not found error
at runtime.
Meaning we would need to add checks for each subclass, which would be pur insanity.
To solve this, we go through each super class and interfaces (and their super class and interfaces etc.) and try to get an owner
which matches with one of our replacement methods. Based on how inheritance works in java, this method should be safe to use.
As a site note: This method could also be used for the other method reroute, e.g. legacy method rerouting, where only the replacement
method needs to be written, and this method figures out the rest, which could reduce the size and complexity of the Commodore class.
The question then becomes one about performance (since this is not the most performance way) and convenience.
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(Map<String, RerouteMethodData> rerouteMethodDataMap, boolean staticCall, String owner, String name, String desc, Consumer<RerouteMethodData> consumer) {
Type ownerType = Type.getObjectType(owner);
Class<?> ownerClass;
try {
ownerClass = Class.forName(ownerType.getClassName());
} catch (ClassNotFoundException e) {
return false;
}
ClassTraverser it = new ClassTraverser(ownerClass);
while (it.hasNext()) {
Class<?> clazz = it.next();
String methodKey = Type.getInternalName(clazz) + " " + desc + " " + name;
RerouteMethodData data = rerouteMethodDataMap.get(methodKey);
if (data == null) {
if (staticCall) {
return false;
}
continue;
}
consumer.accept(data);
return true;
}
return false;
}
private static String buildMethodName(RerouteMethodData rerouteMethodData) {
return BUKKIT_GENERATED_METHOD_PREFIX + rerouteMethodData.targetOwner().replace('/', '_') + "_" + rerouteMethodData.targetName();
}
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));
}
@FunctionalInterface
private interface MethodPrinter {

Datei anzeigen

@ -13,9 +13,7 @@ import com.mojang.serialization.Dynamic;
import com.mojang.serialization.JsonOps;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
@ -290,30 +288,27 @@ public final class CraftMagicNumbers implements UnsafeValues {
return file.delete();
}
private static final List<String> SUPPORTED_API = Arrays.asList("1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20");
@Override
public void checkSupported(PluginDescriptionFile pdf) throws InvalidPluginException {
String minimumVersion = MinecraftServer.getServer().server.minimumAPI;
int minimumIndex = SUPPORTED_API.indexOf(minimumVersion);
ApiVersion toCheck = ApiVersion.getOrCreateVersion(pdf.getAPIVersion());
ApiVersion minimumVersion = MinecraftServer.getServer().server.minimumAPI;
if (pdf.getAPIVersion() != null) {
int pluginIndex = SUPPORTED_API.indexOf(pdf.getAPIVersion());
if (toCheck.isNewerThan(ApiVersion.CURRENT)) {
// Newer than supported
throw new InvalidPluginException("Unsupported API version " + pdf.getAPIVersion());
}
if (pluginIndex == -1) {
throw new InvalidPluginException("Unsupported API version " + pdf.getAPIVersion());
}
if (toCheck.isOlderThan(minimumVersion)) {
// Older than supported
throw new InvalidPluginException("Plugin API version " + pdf.getAPIVersion() + " is lower than the minimum allowed version. Please update or replace it.");
}
if (pluginIndex < minimumIndex) {
throw new InvalidPluginException("Plugin API version " + pdf.getAPIVersion() + " is lower than the minimum allowed version. Please update or replace it.");
}
} else {
if (minimumIndex == -1) {
CraftLegacy.init();
Bukkit.getLogger().log(Level.WARNING, "Legacy plugin " + pdf.getFullName() + " does not specify an api-version.");
} else {
throw new InvalidPluginException("Plugin API version " + pdf.getAPIVersion() + " is lower than the minimum allowed version. Please update or replace it.");
}
if (toCheck.isOlderThan(ApiVersion.FLATTENING)) {
CraftLegacy.init();
}
if (toCheck == ApiVersion.NONE) {
Bukkit.getLogger().log(Level.WARNING, "Legacy plugin " + pdf.getFullName() + " does not specify an api-version.");
}
}
@ -324,7 +319,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
@Override
public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) {
try {
clazz = Commodore.convert(clazz, !isLegacy(pdf));
clazz = Commodore.convert(clazz, pdf.getName(), ApiVersion.getOrCreateVersion(pdf.getAPIVersion()));
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Fatal error trying to convert " + pdf.getFullName() + ":" + path, ex);
}

Datei anzeigen

@ -35,14 +35,14 @@ public class CompositeSerialization extends AbstractTestingBase {
stacks.add(new ItemStack(Material.OAK_LEAVES, 32, (short) 2));
ItemStack item7 = new ItemStack(Material.IRON_SHOVEL);
item7.addUnsafeEnchantment(Enchantment.PROTECTION_FIRE, 1);
item7.addUnsafeEnchantment(Enchantment.FIRE_PROTECTION, 1);
stacks.add(item7);
ItemStack item8 = new ItemStack(Material.IRON_PICKAXE);
item8.addUnsafeEnchantment(Enchantment.PROTECTION_FALL, 2);
item8.addUnsafeEnchantment(Enchantment.PROTECTION_EXPLOSIONS, 1);
item8.addUnsafeEnchantment(Enchantment.PROTECTION_PROJECTILE, 5);
item8.addUnsafeEnchantment(Enchantment.OXYGEN, 4);
item8.addUnsafeEnchantment(Enchantment.FEATHER_FALLING, 2);
item8.addUnsafeEnchantment(Enchantment.BLAST_PROTECTION, 1);
item8.addUnsafeEnchantment(Enchantment.PROJECTILE_PROTECTION, 5);
item8.addUnsafeEnchantment(Enchantment.RESPIRATION, 4);
stacks.add(item8);
ItemStack item9 = new ItemStack(Material.APPLE);

Datei anzeigen

@ -98,46 +98,46 @@ public class ItemMetaTest extends AbstractTestingBase {
@Test
public void testConflictingEnchantment() {
ItemMeta itemMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE);
assertThat(itemMeta.hasConflictingEnchant(Enchantment.DURABILITY), is(false));
assertThat(itemMeta.hasConflictingEnchant(Enchantment.UNBREAKING), is(false));
itemMeta.addEnchant(Enchantment.SILK_TOUCH, 1, false);
assertThat(itemMeta.hasConflictingEnchant(Enchantment.DURABILITY), is(false));
assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true));
assertThat(itemMeta.hasConflictingEnchant(Enchantment.UNBREAKING), is(false));
assertThat(itemMeta.hasConflictingEnchant(Enchantment.FORTUNE), is(true));
assertThat(itemMeta.hasConflictingEnchant(null), is(false));
}
@Test
public void testConflictingStoredEnchantment() {
EnchantmentStorageMeta itemMeta = (EnchantmentStorageMeta) Bukkit.getItemFactory().getItemMeta(Material.ENCHANTED_BOOK);
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.DURABILITY), is(false));
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.UNBREAKING), is(false));
itemMeta.addStoredEnchant(Enchantment.SILK_TOUCH, 1, false);
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.DURABILITY), is(false));
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true));
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.UNBREAKING), is(false));
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.FORTUNE), is(true));
assertThat(itemMeta.hasConflictingStoredEnchant(null), is(false));
}
@Test
public void testConflictingEnchantments() {
ItemMeta itemMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE);
itemMeta.addEnchant(Enchantment.DURABILITY, 6, true);
itemMeta.addEnchant(Enchantment.DIG_SPEED, 6, true);
assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(false));
itemMeta.addEnchant(Enchantment.UNBREAKING, 6, true);
itemMeta.addEnchant(Enchantment.EFFICIENCY, 6, true);
assertThat(itemMeta.hasConflictingEnchant(Enchantment.FORTUNE), is(false));
itemMeta.addEnchant(Enchantment.SILK_TOUCH, 1, false);
assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true));
assertThat(itemMeta.hasConflictingEnchant(Enchantment.FORTUNE), is(true));
assertThat(itemMeta.hasConflictingEnchant(null), is(false));
}
@Test
public void testConflictingStoredEnchantments() {
EnchantmentStorageMeta itemMeta = (EnchantmentStorageMeta) Bukkit.getItemFactory().getItemMeta(Material.ENCHANTED_BOOK);
itemMeta.addStoredEnchant(Enchantment.DURABILITY, 6, true);
itemMeta.addStoredEnchant(Enchantment.DIG_SPEED, 6, true);
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(false));
itemMeta.addStoredEnchant(Enchantment.UNBREAKING, 6, true);
itemMeta.addStoredEnchant(Enchantment.EFFICIENCY, 6, true);
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.FORTUNE), is(false));
itemMeta.addStoredEnchant(Enchantment.SILK_TOUCH, 1, false);
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true));
assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.FORTUNE), is(true));
assertThat(itemMeta.hasConflictingStoredEnchant(null), is(false));
}
@ -276,7 +276,7 @@ public class ItemMetaTest extends AbstractTestingBase {
@Override ItemStack operate(final ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.setBasePotionType(PotionType.WATER);
meta.addCustomEffect(PotionEffectType.CONFUSION.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.NAUSEA.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -292,7 +292,7 @@ public class ItemMetaTest extends AbstractTestingBase {
new StackProvider(Material.ENCHANTED_BOOK) {
@Override ItemStack operate(final ItemStack cleanStack) {
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addStoredEnchant(Enchantment.ARROW_FIRE, 1, true);
meta.addStoredEnchant(Enchantment.FLAME, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -369,7 +369,7 @@ public class ItemMetaTest extends AbstractTestingBase {
new StackProvider(Material.SUSPICIOUS_STEW) {
@Override ItemStack operate(ItemStack cleanStack) {
final CraftMetaSuspiciousStew meta = ((CraftMetaSuspiciousStew) cleanStack.getItemMeta());
meta.addCustomEffect(PotionEffectType.CONFUSION.createEffect(1, 0), false);
meta.addCustomEffect(PotionEffectType.NAUSEA.createEffect(1, 0), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -402,7 +402,7 @@ public class ItemMetaTest extends AbstractTestingBase {
new StackProvider(Material.GOAT_HORN) {
@Override ItemStack operate(ItemStack cleanStack) {
final CraftMetaMusicInstrument meta = (CraftMetaMusicInstrument) cleanStack.getItemMeta();
meta.setInstrument(MusicInstrument.ADMIRE);
meta.setInstrument(MusicInstrument.ADMIRE_GOAT_HORN);
cleanStack.setItemMeta(meta);
return cleanStack;
}

Datei anzeigen

@ -29,7 +29,7 @@ public class ItemStackEnchantStorageTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addStoredEnchant(Enchantment.DURABILITY, 1, true);
meta.addStoredEnchant(Enchantment.UNBREAKING, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -67,7 +67,7 @@ public class ItemStackEnchantStorageTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addStoredEnchant(Enchantment.DAMAGE_UNDEAD, 1, true);
meta.addStoredEnchant(Enchantment.SMITE, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -76,7 +76,7 @@ public class ItemStackEnchantStorageTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addStoredEnchant(Enchantment.DAMAGE_UNDEAD, 1, true);
meta.addStoredEnchant(Enchantment.SMITE, 1, true);
meta.addStoredEnchant(Enchantment.FIRE_ASPECT, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
@ -89,7 +89,7 @@ public class ItemStackEnchantStorageTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addStoredEnchant(Enchantment.PROTECTION_FIRE, 1, true);
meta.addStoredEnchant(Enchantment.FIRE_PROTECTION, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -98,7 +98,7 @@ public class ItemStackEnchantStorageTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta();
meta.addEnchant(Enchantment.PROTECTION_FIRE, 2, true);
meta.addEnchant(Enchantment.FIRE_PROTECTION, 2, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}

Datei anzeigen

@ -149,7 +149,7 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
new Operator() {
@Override
public ItemStack operate(ItemStack cleanStack) {
cleanStack.addUnsafeEnchantment(Enchantment.DIG_SPEED, 2);
cleanStack.addUnsafeEnchantment(Enchantment.EFFICIENCY, 2);
return cleanStack;
}
},
@ -165,7 +165,7 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
new Operator() {
@Override
public ItemStack operate(ItemStack cleanStack) {
cleanStack.addUnsafeEnchantment(Enchantment.OXYGEN, 1);
cleanStack.addUnsafeEnchantment(Enchantment.RESPIRATION, 1);
return cleanStack;
}
},
@ -183,14 +183,14 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
new Operator() {
@Override
public ItemStack operate(ItemStack cleanStack) {
cleanStack.addUnsafeEnchantment(Enchantment.ARROW_DAMAGE, 1);
cleanStack.addUnsafeEnchantment(Enchantment.POWER, 1);
return cleanStack;
}
},
new Operator() {
@Override
public ItemStack operate(ItemStack cleanStack) {
cleanStack.addUnsafeEnchantment(Enchantment.ARROW_FIRE, 1);
cleanStack.addUnsafeEnchantment(Enchantment.FLAME, 1);
return cleanStack;
}
},
@ -201,7 +201,7 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
ItemMeta meta = cleanStack.getItemMeta();
meta.addEnchant(Enchantment.DURABILITY, 1, true);
meta.addEnchant(Enchantment.UNBREAKING, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -239,7 +239,7 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
ItemMeta meta = cleanStack.getItemMeta();
meta.addEnchant(Enchantment.PROTECTION_FIRE, 1, true);
meta.addEnchant(Enchantment.FIRE_PROTECTION, 1, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -248,7 +248,7 @@ public class ItemStackLoreEnchantmentTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
ItemMeta meta = cleanStack.getItemMeta();
meta.addEnchant(Enchantment.PROTECTION_FIRE, 2, true);
meta.addEnchant(Enchantment.FIRE_PROTECTION, 2, true);
cleanStack.setItemMeta(meta);
return cleanStack;
}

Datei anzeigen

@ -29,7 +29,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.CONFUSION.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.NAUSEA.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -47,7 +47,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.HARM.createEffect(2, 1), false);
meta.addCustomEffect(PotionEffectType.INSTANT_DAMAGE.createEffect(2, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -67,7 +67,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.SLOW_DIGGING.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.MINING_FATIGUE.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -76,7 +76,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.FAST_DIGGING.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.HASTE.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -88,7 +88,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.JUMP.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.JUMP_BOOST.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -97,7 +97,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.JUMP.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.JUMP_BOOST.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.REGENERATION.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
@ -131,7 +131,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.INCREASE_DAMAGE.createEffect(1, 1), false);
meta.addCustomEffect(PotionEffectType.STRENGTH.createEffect(1, 1), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}
@ -140,7 +140,7 @@ public class ItemStackPotionsTest extends ItemStackTest {
@Override
public ItemStack operate(ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();
meta.addCustomEffect(PotionEffectType.INCREASE_DAMAGE.createEffect(1, 2), false);
meta.addCustomEffect(PotionEffectType.STRENGTH.createEffect(1, 2), false);
cleanStack.setItemMeta(meta);
return cleanStack;
}

Datei anzeigen

@ -0,0 +1,158 @@
package org.bukkit.craftbukkit.util;
import static org.junit.jupiter.api.Assertions.*;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public class ApiVersionTest {
public static Stream<Arguments> parseData() {
return Stream.of(
Arguments.of(null, "none"),
Arguments.of("", "none"),
Arguments.of("none", "none"),
Arguments.of("1.12", "1.12.0"),
Arguments.of("1.13.3", "1.13.3"),
Arguments.of("1.+20.3", "1.20.3")
);
}
public static Stream<Arguments> compareData() {
return Stream.of(
Arguments.of("none", "none", CompareResult.SAME),
Arguments.of("none", "1.20", CompareResult.SMALLER),
Arguments.of("2.20.3", "1.30.4", CompareResult.BIGGER),
Arguments.of("1.13", "1.12", CompareResult.BIGGER),
Arguments.of("1.13.2", "1.13.3", CompareResult.SMALLER)
);
}
public static Stream<Arguments> newerData() {
return Stream.of(
Arguments.of("1.12", "1.12", false),
Arguments.of("1.12", "1.12.2", false),
Arguments.of("1.12.2", "1.12", true)
);
}
public static Stream<Arguments> olderData() {
return Stream.of(
Arguments.of("1.12", "1.12", false),
Arguments.of("1.12", "1.12.2", true),
Arguments.of("1.12.2", "1.12", false)
);
}
public static Stream<Arguments> newerOrSameData() {
return Stream.of(
Arguments.of("1.12", "1.12", true),
Arguments.of("1.12", "1.12.2", false),
Arguments.of("1.12.2", "1.12", true)
);
}
public static Stream<Arguments> olderOrSameData() {
return Stream.of(
Arguments.of("1.12", "1.12", true),
Arguments.of("1.12", "1.12.2", true),
Arguments.of("1.12.2", "1.12", false)
);
}
@ParameterizedTest
@MethodSource("parseData")
public void testParsing(String parse, String expected) {
ApiVersion apiVersion = ApiVersion.getOrCreateVersion(parse);
assertEquals(expected, apiVersion.getVersionString());
}
@Test
public void testSameInstance() {
ApiVersion one = ApiVersion.getOrCreateVersion("1.23.3");
ApiVersion second = ApiVersion.getOrCreateVersion("1.+23.3");
assertSame(one, second);
}
@ParameterizedTest
@MethodSource("compareData")
public void testCompareTo(String first, String second, CompareResult compareResult) {
ApiVersion firstApi = ApiVersion.getOrCreateVersion(first);
ApiVersion secondApi = ApiVersion.getOrCreateVersion(second);
int result = firstApi.compareTo(secondApi);
assertSame(compareResult, CompareResult.toCompareResult(result));
}
@ParameterizedTest
@MethodSource("newerData")
public void testNewerThan(String first, String second, boolean newer) {
ApiVersion firstApi = ApiVersion.getOrCreateVersion(first);
ApiVersion secondApi = ApiVersion.getOrCreateVersion(second);
boolean result = firstApi.isNewerThan(secondApi);
assertSame(newer, result);
}
@ParameterizedTest
@MethodSource("olderData")
public void testOlderThan(String first, String second, boolean older) {
ApiVersion firstApi = ApiVersion.getOrCreateVersion(first);
ApiVersion secondApi = ApiVersion.getOrCreateVersion(second);
boolean result = firstApi.isOlderThan(secondApi);
assertSame(older, result);
}
@ParameterizedTest
@MethodSource("newerOrSameData")
public void testNewerOrSame(String first, String second, boolean newerOrSame) {
ApiVersion firstApi = ApiVersion.getOrCreateVersion(first);
ApiVersion secondApi = ApiVersion.getOrCreateVersion(second);
boolean result = firstApi.isNewerThanOrSameAs(secondApi);
assertSame(newerOrSame, result);
}
@ParameterizedTest
@MethodSource("olderOrSameData")
public void testOlderOrSame(String first, String second, boolean olderOrSame) {
ApiVersion firstApi = ApiVersion.getOrCreateVersion(first);
ApiVersion secondApi = ApiVersion.getOrCreateVersion(second);
boolean result = firstApi.isOlderThanOrSameAs(secondApi);
assertSame(olderOrSame, result);
}
public enum CompareResult {
SMALLER,
BIGGER,
SAME;
public static CompareResult toCompareResult(int i) {
if (i == 0) {
return CompareResult.SAME;
}
if (i < 0) {
return CompareResult.SMALLER;
}
return CompareResult.BIGGER;
}
}
}

Datei anzeigen

@ -9,6 +9,7 @@ import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectList;
import net.minecraft.world.item.alchemy.PotionRegistry;
import org.bukkit.craftbukkit.legacy.FieldRename;
import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
import org.bukkit.support.AbstractTestingBase;
import org.junit.jupiter.api.Test;
@ -42,7 +43,7 @@ public class PotionTest extends AbstractTestingBase {
assertNotNull(bukkit, "No Bukkit type for " + key);
assertFalse(bukkit.getName().contains("UNKNOWN"), "No name for " + key);
PotionEffectType byName = PotionEffectType.getByName(bukkit.getName());
PotionEffectType byName = FieldRename.getByName_PotionEffectType(bukkit.getName());
assertEquals(bukkit, byName, "Same type not returned by name " + key);
}
}