For developers

Hook your own plugins into Counter. Read violation levels, add exempts, and listen for flags, punishments and setbacks.

Getting started

There's no public Maven repo yet, so build against the Counter jar you download. Put it in a libs folder and add it as compile only.

dependencies {
    compileOnly(files("libs/Counter.jar"))
}

With Maven, install the jar into your local repo once, then depend on it with provided scope.

mvn install:install-file -Dfile=Counter.jar -DgroupId=C -DartifactId=Counter -Dversion=1.3.0 -Dpackaging=jar

Then tell Bukkit to load Counter first.

softdepend: [Counter]

The API

Everything lives in C.counter.api. Counter registers CounterApi with the Bukkit services manager on startup.

CounterApi api = Bukkit.getServicesManager().load(CounterApi.class);
if (api == null) return;

double reach = api.vl(player, "ReachA");

CounterApi.get() returns the same instance, or null before Counter has started. Check names aren't case sensitive, so ReachA and reacha both work.

Methods

vl(Player, String check)Violation level on one check
vl(Player)Total violation level
vls(Player)Every check with a violation level, as a map
resetVl(Player)Clear all violation levels
resetVl(Player, String check)Clear one check
exempt(Player, long ms)Exempt from every check for a while. 0 or less removes it
exempt(Player, String check, long ms)Exempt from one check, or a category like move, combat or player
active(Player, String check)Whether a check is running for this player right now
enabled(String check)Whether a check is turned on
checks()The name of every check
client(Player)The player's client, like Fabric or Lunar Client
bedrock(Player)Whether the player joined through Geyser
ping(Player)Ping in milliseconds, as Counter measures it
frozen(Player)Whether staff froze the player

Events

All three events can be cancelled. They can fire off the main thread when a check runs on the packet thread, so don't touch the world from inside them. Hand that work to the scheduler instead.

FlagEvent

Fires before a flag counts. Cancel it and the flag is dropped, with no violation level and no alert.

getPlayer()The player
getCheck()Check name, like ReachA
getCategory()move, combat or player
getReason()The short reason shown in alerts
getInfo()The raw flag details
getVl()Violation level after this flag
getConfidence()0 to 1
getTier()1 low, 2 medium, 3 high, 4 extreme

PunishEvent

Fires before a punishment runs. Cancel it to stop the punishment.

getPlayer()The player
getCheck()The check that reached its punish level
getAction()The punishment about to run
getVl()Violation level
getOffense()Which offense this is for the player

SetbackEvent

Fires before a player is set back. Cancel it to let the move through.

getPlayer()The player
getCheck()The check that caused it
getFrom()Where the player was
getTo()Where they'll be sent back to

Example

Log strong flags, and skip punishments for players in your own event mode.

public final class Hook implements Listener {

    private final Set<UUID> eventMode = ConcurrentHashMap.newKeySet();

    @EventHandler
    public void onFlag(FlagEvent e) {
        if (e.getTier() < 3) return;
        Bukkit.getLogger().info(e.getPlayer().getName() + " " + e.getCheck() + ": " + e.getReason());
    }

    @EventHandler(ignoreCancelled = true)
    public void onPunish(PunishEvent e) {
        if (eventMode.contains(e.getPlayer().getUniqueId())) e.setCancelled(true);
    }
}

Placeholders and permissions

If you only need numbers on a scoreboard or in chat, the PlaceholderAPI placeholders may be enough. To keep a player out of checks without code, give them counter.bypass, or narrow it with counter.bypass.combat, counter.bypass.fly or counter.bypass.flya.

Found a bug in the API? Run /counter dump and post the file on Discord or the SpigotMC page.