geforkt von Mirrors/Paper
SPIGOT-5336: Field name parity with Minecraft keys
By: DerFrZocker <derrieple@gmail.com>
Dieser Commit ist enthalten in:
Ursprung
d122883f57
Commit
760899464e
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
125
paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java
Normale Datei
125
paper-server/src/main/java/org/bukkit/craftbukkit/util/ApiVersion.java
Normale Datei
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren