This commit is contained in:
2026-01-18 17:55:22 -06:00
commit c3bea39cd6
14 changed files with 889 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
package dev.sillyangel.more_spear_enchantments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CompatibilityCheck {
private static final Logger LOGGER = LoggerFactory.getLogger(CompatibilityCheck.class);
private static Platform platform;
private static boolean runsAsync = false;
public enum Platform {
BUKKIT,
SPIGOT,
PAPER,
FOLIA,
PURPUR
}
/**
* Gets the detected platform, performing detection on first call
*/
public static Platform getPlatform() {
if (platform == null) {
platform = checkPlatform();
LOGGER.info("Detected platform: {}", platform);
}
return platform;
}
public static boolean isRunsAsync() {
return runsAsync;
}
/**
* Checks which platform the plugin is running on by attempting to load
* platform-specific classes. Order matters - checks from most specific to least specific.
*/
private static Platform checkPlatform() {
// Check for Folia (Paper fork with regionized threading)
if (classExists("io.papermc.paper.threadedregions.RegionizedServer")) {
return Platform.FOLIA;
}
// Check for Purpur (Paper fork with extra features)
if (classExists("org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent")) {
return Platform.PURPUR;
}
// Check for Paper (Spigot fork)
if (classExists("io.papermc.paper.event.player.AbstractChatEvent")) {
return Platform.PAPER;
}
// Check for Spigot (Bukkit fork)
if (classExists("org.spigotmc.CustomTimingsHandler")) {
return Platform.SPIGOT;
}
// Check for vanilla Bukkit
if (classExists("org.bukkit.Bukkit")) {
return Platform.BUKKIT;
}
throw new IllegalStateException("Unknown server platform - no recognized classes found!");
}
/**
* Helper method to safely check if a class exists
*/
private static boolean classExists(String className) {
try {
Class. forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Determines if the plugin should continue loading based on platform compatibility.
* @return true if compatible, false if the plugin should disable itself
*/
public static boolean isCompatible() {
Platform currentPlatform = getPlatform();
switch (currentPlatform) {
case BUKKIT:
case SPIGOT:
LOGGER. error("MoreSpearEnchantments has been loaded on an installation of {}!", currentPlatform);
LOGGER.error("MoreSpearEnchantments does not support anything other than Paper and derivatives!");
LOGGER.error("Spigot is considered legacy, and you should definitely consider upgrading!");
LOGGER.error("For further information, see https://docs.papermc.io/paper/migration");
LOGGER.error("MoreSpearEnchantments will shut down now...");
return false;
case PAPER:
case PURPUR:
return true;
case FOLIA:
LOGGER.info("MoreSpearEnchantments running in Folia-compatible mode, please report any issues!");
runsAsync = true;
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,52 @@
package dev.sillyangel.more_spear_enchantments;
import dev.sillyangel.more_spear_enchantments.listeners.EnchantmentListener;
import org.bukkit.Bukkit;
import org.bukkit. event.Listener;
import org.bukkit.plugin.java. JavaPlugin;
import org.slf4j.Logger;
import com.mojang.logging.LogUtils;
public class MoreSpearEnchantments extends JavaPlugin implements Listener {
public static final String PLUGIN_ID = "more_spear_enchantments";
public static final Logger LOGGER = LogUtils.getLogger();
private boolean shouldDisable = false;
@Override
public void onLoad() {
// Check platform compatibility
if (!CompatibilityCheck.isCompatible()) {
shouldDisable = true;
getServer().getPluginManager().disablePlugin(this);
return;
}
LOGGER.info("Platform check passed! Loading plugin...");
}
@Override
public void onEnable() {
// Safeguard: Don't enable if we flagged for disable
if (shouldDisable) {
LOGGER.warn("Plugin was marked for disable - skipping enable");
return;
}
// Register listeners
Bukkit.getPluginManager().registerEvents(this, this);
Bukkit.getPluginManager().registerEvents(new EnchantmentListener(), this);
LOGGER.info("More Spear Enchantments enabled!");
}
@Override
public void onDisable() {
// Safeguard: Don't run shutdown logic if we never fully enabled
if (shouldDisable) {
return;
}
LOGGER.info("More Spear Enchantments disabled!");
}
}

View File

@@ -0,0 +1,26 @@
package dev.sillyangel.more_spear_enchantments;
import dev.sillyangel.more_spear_enchantments.enchantment.SpearEnchantments;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.registry.event.RegistryEvents;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("UnstableApiUsage")
public class SpearEnchantmentsBootstrap implements PluginBootstrap {
@Override
public void bootstrap(@NotNull BootstrapContext context) {
context.getLogger().info("MoreSpearEnchantments bootstrap starting...");
LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
// Register enchantments during the registry compose event
manager.registerEventHandler(RegistryEvents.ENCHANTMENT.compose().newHandler(event -> {
context.getLogger().info("Registering custom enchantments...");
SpearEnchantments.registerEnchantments(event.registry());
}));
context.getLogger().info("MoreSpearEnchantments bootstrap complete!");
}
}

View File

@@ -0,0 +1,155 @@
package dev.sillyangel.more_spear_enchantments.enchantment;
import dev.sillyangel.more_spear_enchantments.MoreSpearEnchantments;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.data.EnchantmentRegistryEntry;
import io.papermc.paper.registry.set.RegistryKeySet;
import io.papermc.paper.registry.set.RegistrySet;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.EquipmentSlotGroup;
import org.bukkit.inventory.ItemType;
@SuppressWarnings("UnstableApiUsage")
public class SpearEnchantments {
// Define enchantment keys
public static final TypedKey<Enchantment> VAMPIRIC = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "vampiric")
);
public static final TypedKey<Enchantment> EXPLOSIVE = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "explosive")
);
public static final TypedKey<Enchantment> LIGHTNING = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "lightning")
);
public static final TypedKey<Enchantment> CRIPPLING = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "crippling")
);
public static final TypedKey<Enchantment> POISON = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "poison")
);
public static final TypedKey<Enchantment> WITHERING = TypedKey.create(
RegistryKey.ENCHANTMENT,
Key.key(MoreSpearEnchantments.PLUGIN_ID, "withering")
);
public static void registerEnchantments(io.papermc.paper.registry.event.WritableRegistry<Enchantment, EnchantmentRegistryEntry.Builder> registry) {
// Create spear item key set with all spear types
RegistryKeySet<ItemType> spearItems = RegistrySet.keySet(
RegistryKey.ITEM,
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "wooden_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "stone_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "copper_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "iron_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "golden_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "diamond_spear")),
TypedKey.create(RegistryKey.ITEM, Key.key("minecraft", "netherite_spear"))
);
// Vampiric - Heals on hit
registry.register(
VAMPIRIC,
builder -> builder
.description(Component.text("Vampiric"))
.maxLevel(3)
.weight(2)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(10, 8))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(50, 8))
.anvilCost(4)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
// Explosive - Creates explosion on hit
registry.register(
EXPLOSIVE,
builder -> builder
.description(Component.text("Explosive"))
.maxLevel(3)
.weight(1)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(20, 10))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(70, 10))
.anvilCost(8)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
// Lightning - Strikes lightning on hit
registry.register(
LIGHTNING,
builder -> builder
.description(Component.text("Lightning"))
.maxLevel(3)
.weight(2)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(15, 9))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(65, 9))
.anvilCost(4)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
// Crippling - Applies slowness and weakness
registry.register(
CRIPPLING,
builder -> builder
.description(Component.text("Crippling"))
.maxLevel(3)
.weight(2)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(10, 8))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(50, 8))
.anvilCost(4)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
// Poison - Applies poison effect
registry.register(
POISON,
builder -> builder
.description(Component.text("Poison"))
.maxLevel(3)
.weight(2)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(10, 8))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(50, 8))
.anvilCost(4)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
// Withering - Applies wither effect
registry.register(
WITHERING,
builder -> builder
.description(Component.text("Withering"))
.maxLevel(3)
.weight(1)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(15, 9))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(65, 9))
.anvilCost(6)
.supportedItems(spearItems)
.primaryItems(spearItems)
.activeSlots(EquipmentSlotGroup.MAINHAND)
);
}
}

View File

@@ -0,0 +1,140 @@
package dev.sillyangel.more_spear_enchantments.listeners;
import dev.sillyangel.more_spear_enchantments.enchantment.SpearEnchantments;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
public class EnchantmentListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void onEntityDamage(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Player player)) {
return;
}
if (!(event.getEntity() instanceof LivingEntity target)) {
return;
}
ItemStack weapon = player.getInventory().getItemInMainHand();
if (weapon.isEmpty()) {
return;
}
// Get enchantments from registry
var registry = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT);
// Vampiric enchantment
Enchantment vampiric = registry.get(SpearEnchantments.VAMPIRIC);
if (vampiric != null) {
int level = weapon.getEnchantmentLevel(vampiric);
if (level > 0) {
handleVampiric(player, event.getFinalDamage(), level);
}
}
// Explosive enchantment
Enchantment explosive = registry.get(SpearEnchantments.EXPLOSIVE);
if (explosive != null) {
int level = weapon.getEnchantmentLevel(explosive);
if (level > 0) {
handleExplosive(target.getLocation(), level);
}
}
// Lightning enchantment
Enchantment lightning = registry.get(SpearEnchantments.LIGHTNING);
if (lightning != null) {
int level = weapon.getEnchantmentLevel(lightning);
if (level > 0) {
handleLightning(target, level);
}
}
// Crippling enchantment
Enchantment crippling = registry.get(SpearEnchantments.CRIPPLING);
if (crippling != null) {
int level = weapon.getEnchantmentLevel(crippling);
if (level > 0) {
handleCrippling(target, level);
}
}
// Poison enchantment
Enchantment poison = registry.get(SpearEnchantments.POISON);
if (poison != null) {
int level = weapon.getEnchantmentLevel(poison);
if (level > 0) {
handlePoison(target, level);
}
}
// Withering enchantment
Enchantment withering = registry.get(SpearEnchantments.WITHERING);
if (withering != null) {
int level = weapon.getEnchantmentLevel(withering);
if (level > 0) {
handleWithering(target, level);
}
}
}
private void handleVampiric(Player player, double damage, int level) {
// Heal based on damage dealt and level
double healAmount = damage * (0.1 * level); // 10%, 20%, 30% of damage at levels 1, 2, 3
AttributeInstance maxHealthAttr = player.getAttribute(Attribute.MAX_HEALTH);
if (maxHealthAttr == null) return;
double maxHealth = maxHealthAttr.getValue();
double newHealth = Math.min(player.getHealth() + healAmount, maxHealth);
player.setHealth(newHealth);
}
private void handleExplosive(Location location, int level) {
// Higher levels = higher chance and bigger explosion
double chance = 0.1 * level; // 10%, 20%, 30% chance at levels 1, 2, 3
if (Math.random() < chance) {
float power = 1.0f + (0.5f * level); // 1.5, 2.0, 2.5 power at levels 1, 2, 3
location.getWorld().createExplosion(location, power, false, false);
}
}
private void handleLightning(LivingEntity target, int level) {
// Strikes lightning based on level
for (int i = 0; i < level; i++) {
target.getWorld().strikeLightning(target.getLocation());
}
}
private void handleCrippling(LivingEntity target, int level) {
// Apply slowness and weakness
int duration = level * 50; // 2.5s, 5s, 7.5s at levels 1, 2, 3
target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, duration, level - 1, false, true));
target.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, duration, level - 1, false, true));
}
private void handlePoison(LivingEntity target, int level) {
// Apply poison effect
int duration = level * 50; // 2.5s, 5s, 7.5s at levels 1, 2, 3
target.addPotionEffect(new PotionEffect(PotionEffectType.POISON, duration, level - 1, false, true));
}
private void handleWithering(LivingEntity target, int level) {
// Apply wither effect
int duration = level * 50; // 2s, 4s, 6s at levels 1, 2, 3
target.addPotionEffect(new PotionEffect(PotionEffectType.WITHER, duration, level - 1, false, true));
}
}

View File

@@ -0,0 +1,9 @@
name: MoreSpearEnchantments
version: 1.1.0
main: dev.sillyangel.more_spear_enchantments.MoreSpearEnchantments
bootstrapper: dev.sillyangel.more_spear_enchantments.SpearEnchantmentsBootstrap
description: more enchantments for the newly added spear from the Mounts of Mayhem update
author: sillyangel
website: https://modrinth.com/mod/more-spear-enchantments
api-version: '1.21.11'