Effective Spigot
EffectiveSpigot is a Paper/Kotlin framework that speeds up plugin development by turning common Minecraft building blocks — custom items, blocks, entities, zones, GUIs, advancements, commands, resource packs — into small declarative base classes. You subclass an Effective* type, override a few methods, and the framework handles registration, identity, persistence and events.
This is the API reference. For a guided tour — why each subsystem exists, minimal examples, what to override and common pitfalls — see the roadmap at effectivespigot.hukm.dev. Requires Paper 1.21.4+ (26.x included), Java 21 and Kotlin 2.2.
Setup
The framework is consumed through the convention plugin ru.hukm.effective-plugin.
settings.gradle.kts:
pluginManagement {
repositories {
gradlePluginPortal()
maven("https://maven.hukm.dev/repository/maven-public/")
}
}
rootProject.name = "MyPlugin"build.gradle.kts:
plugins {
kotlin("jvm") version "2.2.0"
id("com.gradleup.shadow") version "8.3.6"
id("ru.hukm.effective-plugin") version "1.0.0-SNAPSHOT"
}The version above is the convention plugin's version; the framework itself resolves as latest.integration. The convention plugin already brings paper-api, the Kotlin stdlib and the framework as compileOnly, and relocates Kotlin/coroutines to match the framework — do not add your own relocate rules.
plugin.yml — declare the dependency:
depend: [EffectiveSpigot]Main class — initialize features and enable the resource pack:
class MyPlugin : JavaPlugin() {
companion object { lateinit var instance: MyPlugin }
override fun onLoad() {
instance = this
MyCommand.init()
}
override fun onEnable() {
RubyItem.init()
RubyOre.init()
EffectiveResourcepack.addServerResourcepack(this, "", "")
}
}addServerResourcepack is required for anything that uses the resource pack (item/block textures, glyphs, textured menus): without it no pack is built for the plugin. Call it after the features' init().
Status
| Part | State |
|---|---|
EffectiveItem, EffectiveEntity, EffectiveMenu, EffectiveZone, EffectiveCommand, EffectiveLocale, EffectiveConfig, EffectiveAdvancement, EffectiveResourcepack, EffectiveDataContainerUtils | stable |
EffectiveScreenEffects (fade, shake) | stable |
EffectiveScreenImage, EffectiveScreenText | experimental — core-shader technique; API and behaviour may change, not guaranteed with shader packs |
EffectiveBlock, EffectiveBlockWithEntity | work in progress — mining/drops/sounds less tested; API may change |
EffectiveWorld (internal block cache) | known bugs — may drift from the real world; affects ZoneBox.getBlocksInside() and EffectiveBlock.getCustomBlocks() |
Core concepts
Namespaced identity. Every feature declares
getNamespacedData()->(plugin, id), forming a unique"<plugin>:<id>"name. Generated items/entities carry this key in their persistent data, so a plain BukkitItemStack/Entitycan be matched back to itsEffective*type — for example,EffectiveEntity.equalByNamespacedKey(firstEntity, secondEntity)tells whether two entities are the same custom type.Initialization via
init(). A Kotlinobjectis lazy — its code runs only when something first references it. Call each feature'sfun init()fromonEnableso the class is actually loaded and the server picks it up at startup (a few must be called fromonLoadinstead — e.g.EffectiveCommand, since Brigadier commands register during the load phase). A forgotteninit()typically shows up as the feature simply not being there (e.g. an item missing from/egive). (Loading also adds it to the framework's internal registry, so two features with the same namespaced name will clash.)Events and coroutines. Listeners are registered with
plugin.event<PlayerJoinEvent> { … }(seeru.hukm.effectiveSpigot.minecraft.events), and scheduling is done with MCCoroutine —plugin.launch { delay(20.ticks) }instead ofBukkitRunnable. The framework itself is written on both; a child plugin addscompileOnly("com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.22.0").Opt-in behaviours.
EffectiveItem,EffectiveEntityand the other bases gain functionality through dedicated interfaces — e.g.EffectiveWearableonEffectiveItemlets an item be worn on the head. Such an interface can be used two ways: called from the base class itself (EffectiveItem,EffectiveEntity, …), which passes the feature into the function automatically so you don't wire it up yourself; or through the interface directly. The difference is that the interface form accepts plain vanilla types —Material(for items) andEntityType(for entities) — instead ofEffective*. So you can, for example, make any diamond wearable on the head, or make any pig look toward the player (EffectiveEntityLookable).