Write plugins, not the same code again
Once your SMP server runs ten plugins instead of one, they start repeating each other: custom items, mobs, menus, resource pack, configs, localisation. At some point you think about your own library. EffectiveSpigot is that library, already written.
object RubyItem : EffectiveItem() {
override fun getMaterial() =
Material.FIREWORK_STAR
override fun getNamespacedData() =
MyPlugin.instance to "ruby"
override fun getResourcePackData() =
ResourcePackData("textures/item/ruby.png")
override fun editMeta(meta: ItemMeta) {
meta.displayName(Component.text("Ruby"))
}
}One line in the plugins block — Kotlin, shadow, relocation and the framework dependency are already wired.
An item, mob, block, menu or zone is a class with a couple of overridden methods.
Registration, recipes and the resource pack with textures and fonts are built on startup.
Three rules
Everything in the framework works the same way. Get these and you get every section.
Every item, mob, block or menu declares a plugin and an id — together they form a key like myplugin:ruby. It is written to PDC on creation, so any ItemStack or Entity from the world can be asked "whose are you?" — no comparing names, lore or materials.
EffectiveItem.getNamespacedKeyByItem(stack) == RubyItem.getNamespacedName()A Kotlin object is created on first access. Until something touches RubyItem it does not exist: not registered, not in /egive, recipes do not work. That is why each has an empty init() called from onEnable (commands — from onLoad). Registration happens in the constructor; a duplicate key throws.
onEnable → RubyItem.init()A base class does almost nothing by itself: clicks, recipes, throwing, durability are enabled by separate calls. Each wraps an Effective* interface that can also be called directly with a vanilla object: hook a click on any ItemStack or make any pig look at the player.
makeWearable() · EffectiveEntityLookable.doEntityLookable(pig)What is inside
Base classes and interfaces for the routine work of an SMP plugin. Extend, override a couple of methods — the framework does the rest.
Custom items: model, PDC identity, behaviour through interfaces.
Custom entities with a registry, cache and interaction handling.
Custom blocks on note blocks: 800 states, own model, mining time, sounds.
Inventory GUIs from a string layout with per-slot handlers.
Trigger regions with enter, exit and inside events.
Resource pack built and served automatically for every framework plugin.
Player screen: fade, camera shake, images and text at any point.
Custom advancements: tab, toast, tree — granting is up to the plugin.
Brigadier commands with an argument tree and completion.
Multilingual messages from yml, legacy codes and MiniMessage.
One-line listeners and coroutines instead of schedulers.
Small things otherwise rewritten in every plugin.
PersistentDataContainer without boilerplate.