Skip to content

Zones

A zone is a region plus the rules that apply inside it. Zones opened through the API behave exactly like the ones defined in zones.yml, and both appear in /sh list.

public interface SomaHider {
ZoneBuilder hide();
ZoneBuilder hide(Region region);
Collection<HiderZone> zones();
void refresh();
}
MethodDoes
hide()Starts building a zone that covers the whole server.
hide(Region)Starts building a zone bound to a region.
zones()Returns the zones currently open, yours and the configured ones alike.
refresh()Recomputes visibility now instead of waiting for the next pass.

hide() with no region is a zone with no boundary: every online player is inside it. Use it for a server-wide event rather than a located one.

refresh() is rarely needed. SomaHider already recomputes visibility a few times a second, and reacts to joins, quits and teleports on its own. Call it when you have just changed something a custom rule reads and you do not want to wait for the next pass. It is safe to call from any thread: off the main thread it schedules the work rather than doing it inline.

Region decides who is inside:

@FunctionalInterface
public interface Region {
boolean contains(Location location);
}

A circle is built in:

Region arena = Region.circle(center, 40.0);

It compares x and z against the centre and checks the world, so it is a cylinder with no height limit, the same shape as a zone defined in zones.yml.

Any other shape is a lambda:

Region cuboid = location -> box.getWorld().equals(location.getWorld())
&& box.contains(location.toVector());

contains runs on the main thread, once per online player per pass. Keep it to arithmetic. Do not put a database call, a web request or a lock in there.

public interface ZoneBuilder {
ZoneBuilder named(String name);
ZoneBuilder disguise(DisguiseProfile disguise);
ZoneBuilder disguise(ConfigurationSection section);
ZoneBuilder duration(Duration duration);
ZoneBuilder groupResolver(GroupResolver resolver);
ZoneBuilder reveal(VisibilityRule... rules);
HiderZone open();
}
MethodDefault when omitted
named(String)A generated name such as zone4.
disguise(...)The disguise section of config.yml.
duration(Duration)No expiry. The zone runs until you close it.
groupResolver(GroupResolver)A resolver returning null for everyone, so group rules never match.
reveal(VisibilityRule...)No rules. Everyone inside is hidden from everyone else.

open() registers the zone and returns it. The zone starts hiding players on the next pass, a few ticks later.

Give your zones a name you can recognise in /sh list, and make sure it does not collide with a zone id from zones.yml: /sh stop <name> matches by name, ignoring case.

@FunctionalInterface
public interface GroupResolver {
String groupOf(Player player);
}

The group id is what Reveal.sameGroup() and its siblings compare. Returning null means the player belongs to no group, and a null group is never revealed by a group rule.

groupOf is called during visibility passes, so keep it cheap. Reading a field or a cached map is fine; querying a database is not.

public interface HiderZone {
String name();
void close();
}

A zone closes when you call close(), or on its own once its duration has elapsed. Closing releases every player it was hiding, and the disguises they were given are forgotten.

Nothing closes your zones for you when your plugin disables, so close them in onDisable() if a leftover zone would be a problem.

@Override
public void onDisable() {
if (zone != null) {
zone.close();
}
}