EffectiveBlock work in progress
This subsystem is not finished yet: some mechanics may work partially or with bugs, the API may change.
Blocks
Why
Minecraft has no custom blocks. The usual trick is the note block: it has 800 states (instrument × note × powered), and a resource pack can replace the model of each. But vanilla resets the state at every touch, plays the note, refuses placement onto it and breaks it like wood.
EffectiveBlock handles all of it: generates the model and blockstates into the pack, forces the state on placement, mutes the note, computes mining time with the tool in mind and draws cracks, places blocks from hand onto itself and protects against pistons.
Minimal example
object RubyOre : EffectiveBlock() {
override fun getNamespacedData() = MyPlugin.instance to "ruby_ore"
override fun getVariation() = 0
override fun getResourcePackData() =
ResourcePackData(texture = "textures/block/ruby_ore.png")
override fun editItemMeta(meta: ItemMeta) {
meta.displayName(Component.text("Ruby ore"))
}
override fun getHardness() = 3.0
override fun getCorrectTools() = setOf(EffectiveToolType.PICKAXE)
override fun getMinTier() = EffectiveToolTier.IRON
override fun requiresCorrectTool() = true
override fun getDrop() = arrayListOf(
CustomLootable.ItemCellData(RubyItem.createItemStack(), 1.0)
)
fun init() {}
}getVariation is a number 0..799, unique across all blocks of all plugins on the server. Two blocks with the same variation are the same note-block state.
override fun onPlace(event: BlockPlaceEvent) {
event.player.sendMessage("placed")
}
override fun onBreak(event: BlockBreakEvent) {
event.player.sendMessage("broken")
}
fun init() {
addInteractHandler(Click.RIGHT) { e ->
e.player.sendMessage("clicked " + e.clickedBlock.location)
Result.CANCEL_EVENT
}
}Interfaces and tools
fun init() {
addInteractHandler(Click.RIGHT) { e ->
e.player.sendMessage("face: " + e.blockFace)
Result.CANCEL_EVENT
}
addInteractHandler(Click.LEFT_SHIFT) { e -> … Result.ALLOW_EVENT }
}In e: player, clickedBlock, effectiveBlock, blockFace, hand, click. Blocks have no cooldown. The vanilla note-block interaction is already suppressed by the framework, Result decides the PlayerInteractEvent.
object Altar : EffectiveBlockWithEntity() {
…
override fun onPlace(event: BlockPlaceEvent) {
val marker = getMarkerEntity(event.blockPlaced) ?: return
EffectiveDataContainerUtils.setContainerValue(marker, OWNER, event.player.uniqueId.toString())
}
}
val marker = Altar.getMarkerEntity(block)
val owner = EffectiveDataContainerUtils.getContainerValue<String>(marker!!, OWNER)On placement a Marker entity (invisible, no hitbox) spawns at the block centre, on break it is removed; removing the marker removes the block. The marker is where per-block PDC data lives: owner, charge, contents.
override fun getCorrectTools() = setOf(EffectiveToolType.PICKAXE, EffectiveToolType.AXE)
override fun getMinTier() = EffectiveToolTier.DIAMOND
override fun requiresCorrectTool() = trueEffectiveToolType: PICKAXE, AXE, SHOVEL, HOE. EffectiveToolTier: HAND, WOOD, GOLD (same level as wood), STONE, IRON, DIAMOND, NETHERITE. The correct tool mines faster; requiresCorrectTool — no drop without it; getMinTier — a tier below the minimum drops nothing either.
What to override
- getNamespacedData() *Plugin and block id
- getVariation() *Note-block state 0..799, unique on the server
- getResourcePackData() *Texture; per-face overrides via up/down/north/…
- editItemMeta(meta) *Meta of the block item in the inventory
- getHardness()Default: 0.8Hardness like vanilla (stone 1.5, obsidian 50)
- getCorrectTools()Default: emptySet()Tools that mine faster
- getMinTier()Default: HANDMinimum tier for the drop
- requiresCorrectTool()Default: falseNo drop without the correct tool
- getDrop()Default: nullOwn loot with chances; null — the block itself drops
- isIgnitable()Default: falseFlammable: lit by flint and steel and burns like wood
- getPlaceSound() / getBreakSound() / getStepSound()Default: required.wood.*Sounds; vanilla note-block sounds are muted by the pack
- onPlace(event) / onBreak(event)Default: —Called after placement / break
* required
Pitfalls
- Work in progress: getCustomBlocks() goes through the internal world cache (EffectiveWorld) which has known bugs; mining/drops/sounds are less battle-tested than items.
- The block places but looks like a note block — EffectiveResourcepack.addServerResourcepack(this, "", "") in onEnable after init() is missing: the model and blockstates are generated only into a built pack.
- paper-global.yml must have block-updates.disable-noteblock-updates: true — otherwise the server resets the note-block state on neighbour updates.
- Variations are a server-wide resource. Keep a registry of your plugins' variations so they do not collide.
- Right-click with an item in hand: the framework denies the vanilla interaction (the note), so items like flint and steel only work through isIgnitable / addInteractHandler.
- EffectiveBlockWithEntity puts a marker entity into the block — removing the marker removes the block and vice versa.