Kotlin · Paper · framework

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.

minimal item
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"))
    }
}
01
Apply the gradle plugin

One line in the plugins block — Kotlin, shadow, relocation and the framework dependency are already wired.

02
Extend Effective*

An item, mob, block, menu or zone is a class with a couple of overridden methods.

03
Start the server

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.

A key instead of checks

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()
object is lazy — call init()

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()
Behaviour on demand

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.

01
EffectiveItem

Custom items: model, PDC identity, behaviour through interfaces.

+
02
EffectiveEntity

Custom entities with a registry, cache and interaction handling.

+
03
EffectiveBlock work in progress

Custom blocks on note blocks: 800 states, own model, mining time, sounds.

+
04
EffectiveMenu

Inventory GUIs from a string layout with per-slot handlers.

+
05
EffectiveZone

Trigger regions with enter, exit and inside events.

+
06
EffectiveResourcepack

Resource pack built and served automatically for every framework plugin.

+
07
EffectiveScreenEffects experimental

Player screen: fade, camera shake, images and text at any point.

+
08
EffectiveAdvancement

Custom advancements: tab, toast, tree — granting is up to the plugin.

+
09
EffectiveCommand

Brigadier commands with an argument tree and completion.

+
10
EffectiveLocale

Multilingual messages from yml, legacy codes and MiniMessage.

+
11
Events & coroutines

One-line listeners and coroutines instead of schedulers.

+
12
Utils

Small things otherwise rewritten in every plugin.

+
13
EffectiveDataContainerUtils

PersistentDataContainer without boilerplate.

+