Why
Spawn, arena, team base, marketplace — all are "an area where something happens on enter/exit". In Bukkit that is manual coordinate checks on every PlayerMoveEvent plus your own storage of borders.
EffectiveZone is a kind of area. Concrete regions (boxes by two corners) are selected in-game, stored in the world, and as entities move the framework fires regular Bukkit enter, exit and inside events.
Minimal example
object ArenaZone : EffectiveZone() {
override fun getNamespacedData() = MyPlugin.instance to "arena"
override fun doRememberOwner() = false
override fun getZoneColor() = Color.RED
fun init() {
MyPlugin.instance.event<EffectiveZoneEnterEvent> {
if (it.zone !== this@ArenaZone) return@event
it.entity.sendMessage(Component.text("Arena"))
}
}
}val box = EffectiveZone.registerSelection(
Triple(EffectiveBlockPos(0, 60, 0), EffectiveBlockPos(20, 80, 20), world.uid),
ArenaZone.getNamespacedName(),
)
box.isInside(player.location)
box.getEntitiesInside()
EffectiveZone.deleteZoneBoxById(box.id)In-game /ezone does the same: take ZONE_SELECTOR, click two corners, /ezone add arena.
Events and regions
event<EffectiveZoneEnterEvent> {
if (it.zone !== ArenaZone) return@event
it.entity.sendMessage(Component.text("entered box " + it.zoneBox.id))
}
event<EffectiveZoneExitEvent> { … }
event<EffectiveZoneInsideEvent> {
if (it.zone === LavaZone && it.entity is Player) it.entity.fireTicks = 20
}
event<EffectiveZoneRegisteredEvent> {
it.zone; it.zoneBox
}Enter/Exit fire once on crossing the border, Inside on every move inside. All carry entity (LivingEntity), zone, zoneBox. Registered — when a new region is added in-game or from code.
val boxes = EffectiveZone.getZoneBoxesByOwner(player.uniqueId)
val box = EffectiveZone.getZoneBoxById(3) ?: return
box.isInside(location)
box.getCenter()
box.getBlocksInside().filter { it.material == Material.CHEST }
box.getEntitiesInside().filterIsInstance<Player>()
EffectiveZone.deleteZoneBoxById(box.id)A region stores id, two corners, the world and (if doRememberOwner) the owner. getBlocksInside returns EffectiveBlockData (x, y, z, material) without touching chunks.
What to override
- getNamespacedData() *Plugin and zone id
- doRememberOwner() *Remember who created a region
- getZoneColor() *Border particle colour
* required
Pitfalls
- Events fire for all zones at once — compare it.zone with yours in the handler.
- EffectiveZoneInsideEvent fires on every move inside — keep it light.
- getBlocksInside relies on the internal world block cache (EffectiveWorld), which has known bugs — the result may lag behind the real world. Verify with world.getBlockAt when precision matters.
- Regions live in the world PDC: delete the world folder and the regions are gone.