Frostyh Plugins

Plugins / Economy Watch

Economy Watch Coming soon

Finds duplicated items and runaway money before they ruin your economy, with the evidence and where it came from.

Get Economy Watch

Coming soon Not on sale yet. This page gets a buy button the day it is.

Overview

Economy Watch

A dupe ruins an economy before anyone notices. Economy Watch notices: it counts your valuable items, spots the shapes a dupe leaves, and shows staff where the items came from.

Every few minutes Economy Watch counts the diamonds, netherite, elytra, totems, shulker boxes, enchanted books and beacons on the whole server: online inventories and ender chests, every container in a loaded chunk, items on the ground, item frames, chest minecarts and boats. It compares each count with the ones before it. When 3,000 diamonds appear overnight, when one player gains forty times what everybody else gained, or when the same renamed elytra sits in three chests at once, staff get an alert with the evidence and a button to teleport there.

It never bans or wipes anything on its own. It counts, compares and tells you.

Plugin name in /plugins
EconomyWatch
Latest version
1.0.0
Server
Paper or Purpur, Java 21
BuiltByBit
coming soon
What changed in 1.0.0
  • A census of the watched items on the whole server every few minutes: online inventories and ender chests, containers in loaded chunks, items on the ground, item frames, chest minecarts and boats. Shulker boxes and bundles are opened and counted too.
  • Detects circulation jumps, runaway gains, identical unique items in several places, relog dupes, chest dupes, and with Vault money supply jumps and big balance gains. Every finding has a confidence.
  • Alerts in chat with a button to the evidence, in the console, and in Discord through a webhook.
  • A movement trace for watched items: pickups, drops, crafts, trades, mining, deaths, chests and give commands.
  • Freeze an item type while you investigate, by hand or automatically (off by default).
  • Native dialog screens: overview, item pages, alerts, players, trace, settings. CSV export.

Features

Why server owners pick it

What it detects

What you get

FAQ

Which servers does it run on?

Paper or Purpur 1.21.11 or a newer 1.21.x, with Java 21. Vault and an economy plugin are optional; without them, money is simply not watched.

Does it punish players?

No. It never bans, kicks or removes items. The only automatic action is the optional freeze, which only stops an item type from moving and is off by default.

Will it lag my server?

It is built not to: the count walks a slice of loaded chunks per tick (you set how many), and every database read and write runs on its own thread.

What about offline players?

Their inventories are not opened. Their items are counted again the moment they log in, and a stash that appears across a relog is exactly what the relog check looks for.

Will kits trigger the identical-items check?

Only if your kits hand out renamed or enchanted versions of watched items. Turn on detect.identical-items.require-wear and only worn items count.

Is there DRM or a licence key?

No. There is nothing to activate and no licence server to reach, and the code is not obfuscated.

Where do I get help?

In the active support Discord or in the discussion tab on this page.

Updates

The first release is v1.0.0. Every update is free, new Minecraft versions included.

Also by Frostyh

If this plugin helps your server, a short review on this page would mean a lot. Thank you!

Installation

Requirements

Server software
Paper, or a Paper fork such as Purpur. Spigot and CraftBukkit are not supported: the plugin uses Paper-only APIs.
Minecraft version
plugin.yml api-version 1.21; built against Paper API 1.21.11
Java
21 or newer
Required plugins
none
Optional plugins
Vault
Libraries
org.xerial:sqlite-jdbc:3.53.2.1, downloaded by the server at startup (plugin.yml libraries)

Steps

  1. Download EconomyWatch.jar from BuiltByBit. There is no licence key to enter.
  2. Stop the server and put the jar into the plugins folder.
  3. Start the server. The plugin creates plugins/EconomyWatch/config.yml with the defaults shown under Configuration.
  4. Edit config.yml to suit your server.
  5. Apply the changes with /ew reload (Read config.yml again), or restart the server.

Setup guide

  1. Drop EconomyWatch.jar into /plugins and restart. The first count runs two minutes later.
  2. Open /ew. The jump checks start after three counts (about half an hour with the default of one count every ten minutes); the relog and chest checks work from the first minute.
  3. Optional: add your own valuable items under watch.groups, set a Discord webhook under alerts.discord, and run /ew test to see an alert arrive.

Commands

From plugin.yml. "Not set" in the permission column means plugin.yml attaches no permission to the command itself; the nodes the plugin checks are listed under Permissions.

CommandAliasesUsageDescriptionPermission
/economywatch/ew, /ecowatch/economywatch [census|alerts|alert|group|player|trace|freeze|unfreeze|export|test|mute|status|reload|help]Open the Economy Watch panel, or a page or action by name.not set

Subcommands in detail

/ew                          The overview. Also /economywatch, /ecowatch.
/ew census                   Count now instead of waiting for the next count.
/ew alerts [all]             Open alerts, or all of them.
/ew alert <id>               One alert with its evidence.
/ew group <item>             Holders, stashes and history of one item group.
/ew player <name>            A player's holdings and recent moves.
/ew trace <item|*> [player]  Where items moved, newest first.
/ew freeze <item>            Hold an item type. /ew unfreeze <item> lets it move again.
/ew export [days]            CSV files of the counts and the moves.
/ew test                     A test alert through chat, console and Discord.
/ew mute                     Mute or unmute alerts for yourself.
/ew status                   The last count in chat (works from the console).
/ew reload                   Read config.yml again.

Permissions

From plugin.yml.

NodeDefaultDescriptionChildren
economywatch.useoperatorsOpen the panel and the pages.none
economywatch.alertsoperatorsReceive alerts in chat.none
economywatch.traceoperatorsRead the movement trace.none
economywatch.teleportoperatorsTeleport to stashes and alert locations.none
economywatch.manageoperatorsRun a census, resolve and dismiss alerts, freeze item types, change settings, export, reload.none
economywatch.bypassoperatorsNever held by a freeze.none
economywatch.adminoperatorsEverything above.economywatch.use, economywatch.alerts, economywatch.trace, economywatch.teleport, economywatch.manage, economywatch.bypass

economywatch.admin is declared in plugin.yml, but this version's code never checks it, so taking it away does not restrict anything yet.

Placeholders

This plugin registers no PlaceholderAPI placeholders.

Configuration reference

The default files as they ship in the jar, with their comments. The plugin writes them to plugins/EconomyWatch/ on first start.

config.yml

Top-level keys:

KeyDefaultComment above it
config-version1none
watchsection, 2 keysWhich items are watched. A group is one thing of value counted in one unit: "Diamonds" counts a diamond as 1 and a diamond block as 9, so turning blocks into diamonds never looks like inflation. Each line is a material or a pattern with a star, with an...
censussection, 5 keysA census counts every watched item on the server: online inventories and ender chests, every container in a loaded chunk, items on the ground, item frames, chest minecarts and boats. Chunks are walked a slice per tick, so the server never freezes for it....
detectsection, 6 keysWhat counts as suspicious. Every finding gets a confidence: low, medium or high.
moneysection, 1 keynone
provenancesection, 1 keynone
freezesection, 2 keysA frozen item type cannot be dropped, picked up, put in a container, moved by hoppers or traded, until staff unfreeze it. Players keep what they hold. economywatch.bypass is never held.
alertssection, 4 keysnone
storagesection, 1 keynone
menusection, 2 keysnone
themesection, 12 keysnone
messagessection, 207 keysnone
plugins/EconomyWatch/config.yml
# ------------------------------------------------------------------------------------------------
#  Economy Watch by Frostyh
#  Finds duplicated items and runaway money before they ruin your economy.
#  /ew opens the panel. Nothing is ever punished automatically: staff get an alert with the
#  evidence and decide. Nothing leaves the server unless you set the Discord webhook below.
# ------------------------------------------------------------------------------------------------
config-version: 1

# ------------------------------------------------------------------------------------------------
#  █   █  ██  ███  ███ █  █
#  █   █ █  █  █  █    █  █
#  █ █ █ ████  █  █    ████
#  ██ ██ █  █  █  █    █  █
#  █   █ █  █  █   ███ █  █
# ------------------------------------------------------------------------------------------------
#  Which items are watched. A group is one thing of value counted in one unit: "Diamonds" counts a
#  diamond as 1 and a diamond block as 9, so turning blocks into diamonds never looks like
#  inflation. Each line is a material or a pattern with a star, with an optional weight
#  ("DIAMOND_BLOCK x9"). The first matching line wins; weight 0 leaves a material out on purpose.
#  jump-min: the smallest rise of the whole server's total that can be called a jump.
#  gain-min: the smallest rise one player can have between two counts before it is looked at.
watch:
  # Count what is packed inside shulker boxes and bundles too (a box of diamonds is 1,728 diamonds).
  look-inside-boxes: true
  groups:
    diamond:
      name: "Diamonds"
      icon: DIAMOND
      items: ["DIAMOND", "DIAMOND_BLOCK x9"]
      jump-min: 256
      gain-min: 128
    netherite:
      name: "Netherite"
      icon: NETHERITE_INGOT
      items: ["NETHERITE_INGOT", "NETHERITE_BLOCK x9", "NETHERITE_SCRAP x0.25", "ANCIENT_DEBRIS x0.25",
              "NETHERITE_UPGRADE_SMITHING_TEMPLATE x0", "NETHERITE_*"]
      jump-min: 32
      gain-min: 16
    elytra:
      name: "Elytra"
      icon: ELYTRA
      items: ["ELYTRA"]
      jump-min: 4
      gain-min: 3
    totem:
      name: "Totems"
      icon: TOTEM_OF_UNDYING
      items: ["TOTEM_OF_UNDYING"]
      jump-min: 16
      gain-min: 8
    shulker:
      name: "Shulker boxes"
      icon: SHULKER_BOX
      items: ["SHULKER_BOX", "*_SHULKER_BOX"]
      jump-min: 16
      gain-min: 8
    enchanted-book:
      name: "Enchanted books"
      icon: ENCHANTED_BOOK
      items: ["ENCHANTED_BOOK"]
      jump-min: 64
      gain-min: 32
    beacon:
      name: "Beacons"
      icon: BEACON
      items: ["BEACON"]
      jump-min: 4
      gain-min: 3

# ------------------------------------------------------------------------------------------------
#   ███ ████ █  █  ███ █  █  ███
#  █    █    ██ █ █    █  █ █
#  █    ███  █ ██  ██  █  █  ██
#  █    █    █  █    █ █  █    █
#   ███ ████ █  █ ███   ██  ███
# ------------------------------------------------------------------------------------------------
#  A census counts every watched item on the server: online inventories and ender chests, every
#  container in a loaded chunk, items on the ground, item frames, chest minecarts and boats.
#  Chunks are walked a slice per tick, so the server never freezes for it. Offline players are
#  not opened; their items count again the moment they log in.
census:
  every-minutes: 10
  first-after-seconds: 120
  # Loaded chunks counted per tick. Lower it on a weak machine, raise it for a faster count.
  chunks-per-tick: 40
  ignored-worlds: []
  # One line in the console after each census.
  log: true

# ------------------------------------------------------------------------------------------------
#  ███  ████ ███ ████  ███ ███
#  █  █ █     █  █    █     █
#  █  █ ███   █  ███  █     █
#  █  █ █     █  █    █     █
#  ███  ████  █  ████  ███  █
# ------------------------------------------------------------------------------------------------
#  What counts as suspicious. Every finding gets a confidence: low, medium or high.
detect:
  circulation:
    # A group's total compared with the median of its recent counts.
    enabled: true
    baseline-samples: 12
    min-samples: 3
    # A rise this many percent above the median (and at least the group's jump-min) is a jump.
    jump-percent: 15
  runaway-gains:
    # One player gaining many times what the other gainers gained between two counts.
    enabled: true
    peer-factor: 8
    # With fewer other gainers than this, only a gain of three times the group's gain-min counts.
    min-peers: 3
  identical-items:
    # The same unique item (renamed, lored, enchanted, worn, or a filled shulker box) held in
    # several places at once. Identical kit items handed to everybody would trip this; set
    # require-wear to true on servers that give out named or enchanted kits.
    enabled: true
    min-places: 3
    require-wear: false
  relog:
    # More of a watched item after logging back in than on the way out.
    enabled: true
    # How long after joining the inventory is counted again (20 ticks = 1 second). Raise it if a
    # join reward plugin gives watched items a few seconds after login.
    check-after-ticks: 20
    # The smallest gain that counts, as a share of the group's gain-min (at least 1 item).
    min-share: 0.25
  containers:
    # A player and a chest together hold more after closing it than before opening it.
    enabled: true
    min-share: 0.25
  money:
    # Needs Vault and an economy plugin. Off by itself when there is none.
    enabled: true
    jump-percent: 25
    jump-min: 100000
    # One player's balance rising by at least this much between two counts is looked at.
    gain-min: 50000

money:
  # Read balances through Vault. With no economy plugin installed this does nothing.
  enabled: true

provenance:
  # Keep a light log of where watched items move: pickups, drops, crafts, trades, mining, deaths,
  # chests, and give commands. This is what answers "where did these come from".
  enabled: true

# ------------------------------------------------------------------------------------------------
#  ████ ███  ████ ████ ████ ████
#  █    █  █ █    █      █  █
#  ███  ███  ███  ███   █   ███
#  █    █ █  █    █    █    █
#  █    █  █ ████ ████ ████ ████
# ------------------------------------------------------------------------------------------------
#  A frozen item type cannot be dropped, picked up, put in a container, moved by hoppers or
#  traded, until staff unfreeze it. Players keep what they hold. economywatch.bypass is never held.
freeze:
  # Freeze an item type by itself when an alert about it reaches min-confidence. Off by default.
  auto: false
  min-confidence: high

# ------------------------------------------------------------------------------------------------
#   ██  █    ████ ███  ███  ███
#  █  █ █    █    █  █  █  █
#  ████ █    ███  ███   █   ██
#  █  █ █    █    █ █   █     █
#  █  █ ████ ████ █  █  █  ███
# ------------------------------------------------------------------------------------------------
alerts:
  # Alerts below this confidence are stored but not announced in chat.
  min-confidence: low
  # The same finding about the same player or item is announced once in this window.
  cooldown-minutes: 60
  console: true
  discord:
    # Server Settings > Integrations > Webhooks. Empty means off. Only the alert text is sent.
    webhook: ""
    username: "Economy Watch"
    min-confidence: medium
    cooldown-seconds: 60

# ------------------------------------------------------------------------------------------------
#   ███ ███  ██  ███   ██   ███ ████
#  █     █  █  █ █  █ █  █ █    █
#   ██   █  █  █ ███  ████ █ ██ ███
#     █  █  █  █ █ █  █  █ █  █ █
#  ███   █   ██  █  █ █  █  ███ ████
# ------------------------------------------------------------------------------------------------
storage:
  # Movement log, holdings and census history older than this are deleted.
  keep-days: 30

# ------------------------------------------------------------------------------------------------
#  █   █ ████ █  █ █  █
#  ██ ██ █    ██ █ █  █
#  █ █ █ ███  █ ██ █  █
#  █   █ █    █  █ █  █
#  █   █ ████ █  █  ██
# ------------------------------------------------------------------------------------------------
menu:
  width: 340
  button-width: 200

# ------------------------------------------------------------------------------------------------
#  ███ █  █ ████ █   █ ████
#   █  █  █ █    ██ ██ █
#   █  ████ ███  █ █ █ ███
#   █  █  █ █    █   █ █
#   █  █  █ ████ █   █ ████
# ------------------------------------------------------------------------------------------------
theme:
  accent: "#ffb84d"
  accent2: "#ffe0a8"
  title: "#f7f3e8"
  text: "#d4ccb8"
  muted: "#7c7566"
  value: "#ffffff"
  good: "#57e08a"
  warn: "#ffc857"
  bad: "#ff6b6b"
  # Item pictures in the menus (no resource pack needed, Minecraft 1.21.9 or newer).
  item-icons: true
  small-caps: false
  sounds: true

# ------------------------------------------------------------------------------------------------
#  █   █ ████  ███  ███  ██   ███ ████  ███
#  ██ ██ █    █    █    █  █ █    █    █
#  █ █ █ ███   ██   ██  ████ █ ██ ███   ██
#  █   █ █       █    █ █  █ █  █ █       █
#  █   █ ████ ███  ███  █  █  ███ ████ ███
# ------------------------------------------------------------------------------------------------
messages:
  prefix: "<accent><b>EW</b></accent> <muted>|</muted> "
  no-permission: "<bad>You don't have permission for that.</bad>"
  players-only: "<bad>Open that in game; from the console use /ew status or /ew alerts.</bad>"
  usage: "<muted>Usage:</muted> <value>{usage}</value>"
  unknown-sub: "<bad>Unknown subcommand.</bad> <muted>/{label} help lists them.</muted>"
  unknown-group: "<bad>No such item group.</bad> <muted>Groups: {groups}</muted>"
  unknown-player: "<bad>No player called {player} has played here.</bad>"
  unknown-world: "<bad>The world {world} is not loaded.</bad>"
  teleported: "<good>Teleported to</good> <value>{where}</value><good>.</good>"
  reloaded: "<good>Config reloaded.</good>"

  # ---- census
  census-running: "<warn>A census is already running.</warn>"
  census-started: "<text>Counting watched items in</text> <value>{chunks}</value> <text>loaded chunks...</text>"
  census-done: "<good>Census done:</good> <value>{chunks}</value> <text>chunks in</text> <value>{seconds} s</value><text>.</text>"
  status-no-census: "<muted>No census yet. /ew census runs one now.</muted>"
  status-head: "<title>Last census</title> <muted>{ago} ago, {chunks} chunks</muted>"
  status-row: "<text>{group}:</text> <value>{total}</value> <muted>(players {players}, containers {containers}, ground {ground})</muted> {frozen}"
  status-money: "<text>Money supply:</text> <value>{supply}</value> <muted>across {players} players</muted>"

  # ---- alerts in chat
  alert-chat: "{line}"
  alert-line: "<bad>Alert #{id}</bad> <muted>{kind}</muted> {confidence} <text>{summary}</text>"
  alert-view: "<accent>[View]</accent>"
  alert-view-tip: "<text>Open the evidence</text>"
  conf-high: "<bad>[high]</bad>"
  conf-medium: "<warn>[medium]</warn>"
  conf-low: "<muted>[low]</muted>"
  kind-circulation: "circulation jump"
  kind-gain: "runaway gain"
  kind-identical: "identical items"
  kind-money-supply: "money supply jump"
  kind-money-gain: "money gain"
  kind-relog: "items after relog"
  kind-container: "items appeared in a chest"
  kind-test: "test"
  status-open: "<warn>open</warn>"
  status-resolved: "<good>resolved</good>"
  status-dismissed: "<muted>dismissed</muted>"

  # ---- alert summaries (one line each)
  sum-circulation: "{group} in circulation rose {change} ({pct}) above the recent median."
  sum-gain: "{player} gained {gain} {group} in {minutes} min, far more than other players."
  sum-identical: "The same unique {group} item is in {places} places at once."
  sum-money-supply: "The money supply rose {change} ({pct}) above the recent median."
  sum-money-gain: "{player}'s balance rose {gain} since the last count."
  sum-relog: "{player} has {gain} more {group} after logging back in."
  sum-container: "{gain} {group} appeared while {player} had a {what} open."
  sum-test: "Test alert from {who}. If you can read this, alerts work."

  # ---- alert evidence (shown on the alert page and sent to Discord)
  ev-circulation: "<text>{group}: median</text> <value>{base}</value> <text>over the last {samples} counts, now</text> <value>{now}</value> <warn>({change})</warn><text>.</text>"
  ev-gainer: "<muted>Gained since the last count:</muted> <value>{player}</value> <warn>{gain}</warn>"
  ev-biggest-stash: "<muted>Biggest stash now:</muted> <value>{amount}</value> <text>in a {what} at {where}</text>"
  ev-gain: "<value>{player}</value> <text>gained</text> <warn>{gain}</warn> <text>{group} in {minutes} min.</text>"
  ev-peers: "<text>The other {peers} gainers got</text> <value>{median}</value> <text>at the median: this is</text> <warn>{factor}x</warn> <text>that.</text>"
  ev-few-peers: "<muted>Only {peers} other players gained any, so this is judged on size alone.</muted>"
  ev-now-holds: "<text>Holds now:</text> <value>{amount}</value>"
  ev-identical: "<text>{places} holders share one unique {group} item</text> <muted>(fingerprint {print}):</muted>"
  ev-holder: "<muted>-</muted> <value>{who}</value>"
  ev-more: "<muted>and {n} more</muted>"
  ev-money-supply: "<text>Money supply: median</text> <value>{base}</value><text>, now</text> <value>{now}</value> <muted>({players} players known)</muted>"
  ev-money-gainer: "<muted>Gained since the last count:</muted> <value>{player}</value> <warn>+{gain}</warn>"
  ev-money-gain: "<value>{player}</value> <text>gained</text> <warn>{gain}</warn> <text>between two counts.</text>"
  ev-relog: "<value>{player}</value> <text>left with</text> <value>{before}</value> <text>{group} and came back {seconds} s later with</text> <warn>{after}</warn><text>.</text>"
  ev-relog-note: "<muted>Inventories do not change while a player is offline, unless a plugin or staff edits them.</muted>"
  ev-container: "<value>{player}</value> <text>plus the {what} held</text> <value>{before}</value> <text>{group} when it opened and</text> <warn>{after}</warn> <text>when it closed, {seconds} s later.</text>"
  ev-container-viewers: "<muted>{n} players had it open, so another player may have added items.</muted>"
  ev-container-hopper: "<muted>A hopper, dropper or crafter touches it, which can move items in.</muted>"
  ev-test: "<muted>This is only a test. Nothing was detected.</muted>"

  # ---- what staff can do about each kind (shown under the evidence)
  alert-advice-circulation: "<muted>Open the item's page for the biggest holders and stashes, then the trace for where they came from.</muted>"
  alert-advice-gain: "<muted>Check the player's trace: mining and trades explain steady gains, a sudden pile does not.</muted>"
  alert-advice-identical: "<muted>Identical worn or named items rarely happen by chance. Look at who held it first in the trace.</muted>"
  alert-advice-money-supply: "<muted>A money jump usually comes from a shop price mistake, a sell exploit or a command. Check recent transactions in your economy plugin.</muted>"
  alert-advice-money-gain: "<muted>Compare with your shop or auction logs: what did this player sell, and for how much?</muted>"
  alert-advice-relog: "<muted>Gains across a relog are the classic disconnect dupe. Check join rewards before acting.</muted>"
  alert-advice-container: "<muted>Items appearing while a chest is open point to a container dupe. Teleport there and look.</muted>"
  alert-advice-test: "<muted>Nothing to do. Dismiss it.</muted>"

  # ---- freeze
  frozen-staff: "<warn>Frozen:</warn> <value>{group}</value> <text>({by}). It cannot be dropped, picked up, stored or traded until unfrozen.</text>"
  frozen-player: "<warn>That item is on hold while staff check something. You keep it; it just cannot move for now.</warn>"
  unfrozen: "<good>{group} is no longer frozen.</good>"

  # ---- overview
  home-title: "<accent><white><icon:circulation></white> Economy Watch</accent>"
  home-no-census: "<muted>No census yet. The first one runs a couple of minutes after startup.</muted>"
  home-census-running: "<warn>A census is running now.</warn>"
  home-census: "<text>Last census</text> <value>{ago}</value> <text>ago:</text> <muted>{chunks} chunks in {seconds} s</muted>"
  home-alerts: "<bad>{n} open alerts</bad> <muted>waiting for a look.</muted>"
  home-no-alerts: "<good>No open alerts.</good>"
  home-group: "<title>{group}</title> {frozen}<br><value>{total}</value> <muted>in circulation</muted> {trend}"
  trend-none: "<muted>(trend after a few counts)</muted>"
  tag-frozen: "<bad>[frozen]</bad>"
  home-money: "<text>Money supply:</text> <value>{supply}</value> <muted>across {players} players</muted>"
  home-money-off: "<muted>No Vault economy found, so money is not watched.</muted>"
  btn-group: "{group}"
  btn-group-tip: "Holders, stashes and history of this item"
  btn-alerts: "<white><icon:alerts></white> Alerts ({n})"
  btn-alerts-tip: "Everything Economy Watch found, with the evidence"
  btn-trace-all: "<white><icon:trace></white> Trace"
  btn-trace-tip: "Where watched items moved, newest first"
  btn-census: "<white><icon:census></white> Count now"
  btn-census-running: "<white><icon:census></white> Counting..."
  btn-census-tip: "Run a census now instead of waiting for the next one"
  btn-settings: "<white><icon:settings></white> Settings"
  btn-settings-tip: "Turn detectors on or off, test alerts, export"
  btn-mute: "<white><icon:alerts></white> Mute alerts"
  btn-unmute: "<white><icon:alerts></white> Unmute alerts"
  btn-mute-tip: "Stop or start alerts in your chat (they are still stored)"
  muted: "<text>Alerts muted for you. They are still stored.</text>"
  unmuted: "<good>You get alerts in chat again.</good>"
  btn-help: "<white><icon:help></white> Help"
  btn-help-tip: "Commands and what each check looks for"
  btn-home: "<white><icon:back></white> Overview"
  btn-home-tip: "Back to the overview"
  btn-back: "<white><icon:back></white> Back"
  btn-back-tip: "Back"
  btn-close: "Close"
  none: "<muted>None.</muted>"

  # ---- one item group
  group-title: "<accent>{group}</accent>"
  group-head: "<title>{group}</title> {frozen}<br><value>{total}</value> <muted>in circulation</muted>"
  group-graph: "<text>Last {n} counts:</text> {graph} <muted>low {low}, high {high}</muted>"
  group-graph-empty: "<muted>The history graph appears after a couple of counts.</muted>"
  group-split: "<text>Players</text> <value>{players}</value> <muted>|</muted> <text>containers</text> <value>{containers}</value> <muted>|</muted> <text>ground</text> <value>{ground}</value>"
  group-holders-head: "<accent>Biggest holders online</accent>"
  group-holder-row: "<muted>{n}.</muted> <value>{player}</value> <text>{amount}</text>"
  group-spots-head: "<accent>Biggest stashes</accent>"
  group-spot-row: "<muted>{n}.</muted> <value>{amount}</value> <text>in a {what}</text> <muted>at {where}</muted>"
  btn-tp: "<white><icon:tp></white> Teleport there"
  btn-tp-n: "<white><icon:tp></white> Go to {n}"
  btn-tp-tip: "Teleport to {where}"
  btn-player-n: "<white><icon:player></white> {player}"
  btn-player-tip: "This player's holdings and recent moves"
  btn-trace: "<white><icon:trace></white> Trace"
  btn-freeze: "<white><icon:freeze></white> Freeze"
  btn-freeze-tip: "Hold this item type: no dropping, storing, hoppers or trading until unfrozen"
  btn-unfreeze: "<white><icon:freeze></white> Unfreeze"
  btn-unfreeze-tip: "Let this item type move again"

  # ---- alerts list and page
  alerts-title: "<accent><white><icon:alerts></white> Alerts</accent>"
  alerts-head-open: "<text>Open alerts, newest first.</text>"
  alerts-head-all: "<text>All alerts, newest first.</text>"
  alerts-none: "<good>Nothing to look at.</good>"
  alerts-row: "<value>#{id}</value> <muted>{ago} ago</muted> {confidence} <text>{summary}</text> <muted>{status}</muted>"
  btn-alert-n: "Alert #{id}"
  btn-alert-tip: "Open the evidence"
  btn-alerts-all: "Show all"
  btn-alerts-open: "Show open only"
  btn-alerts-filter-tip: "Switch between open and all alerts"
  alert-unknown: "<bad>No alert #{id}.</bad>"
  alert-title: "<accent>Alert #{id}</accent>"
  alert-head: "<value>#{id}</value> <text>{kind}</text> {confidence} <muted>{ago} ago,</muted> {status}"
  alert-evidence-head: "<accent>Evidence</accent>"
  btn-resolve: "<white><icon:resolve></white> Resolved"
  btn-resolve-tip: "Mark it handled"
  btn-dismiss: "<white><icon:dismiss></white> Dismiss"
  btn-dismiss-tip: "Not a problem"
  alert-resolved: "<good>Alert #{id} marked resolved.</good>"
  alert-dismissed: "<muted>Alert #{id} dismissed.</muted>"

  # ---- a player
  player-title: "<accent>{player}</accent>"
  player-head-online: "<value>{player}</value> <good>online</good> <muted>(counted now)</muted>"
  player-head-offline: "<value>{player}</value> <muted>offline (as of the last count)</muted>"
  player-unknown: "<muted>Not counted yet. Players are counted while online.</muted>"
  player-row: "<title>{group}</title><br><value>{amount}</value>"
  player-money: "<text>Balance:</text> <value>{balance}</value>"
  player-events-head: "<accent>Recent moves</accent>"
  btn-tp-player: "<white><icon:tp></white> Go to player"
  btn-tp-player-tip: "Teleport to this player"

  # ---- trace
  trace-title: "<accent><white><icon:trace></white> Trace</accent>"
  trace-head: "<text>{group}, {who}, newest first.</text>"
  trace-everyone: "everyone"
  trace-all-groups: "all items"
  trace-none: "<muted>No moves logged yet.</muted>"
  trace-num: "<muted>{n}.</muted> "
  event-row: "<muted>{ago} ago</muted> <value>{player}</value> <text>{kind}</text> <warn>{amount}</warn> <text>{group}</text> <muted>at {where}</muted>{other}"
  event-other: " <muted>({text})</muted>"
  ev-kind-pickup: "picked up"
  ev-kind-drop: "dropped"
  ev-kind-craft: "crafted"
  ev-kind-trade: "traded for"
  ev-kind-mined: "mined"
  ev-kind-death-drop: "dropped on death"
  ev-kind-give-command: "was given by command"
  ev-kind-put: "put in"
  ev-kind-take: "took out"
  ev-kind-appeared: "saw appear"
  ev-kind-vanished: "saw vanish"

  # ---- settings
  settings-title: "<accent><white><icon:settings></white> Settings</accent>"
  settings-head: "<text>Each check can be switched off. Changes save to config.yml.</text>"
  switch-on: "<good>ON</good> {name}"
  switch-off: "<muted>OFF</muted> {name}"
  set-circulation: "Circulation jumps"
  set-circulation-about: "<muted>Circulation jumps: an item group rising far above its recent median.</muted>"
  set-gains: "Runaway gains"
  set-gains-about: "<muted>Runaway gains: one player gaining many times what others gain.</muted>"
  set-identical: "Identical items"
  set-identical-about: "<muted>Identical items: the same unique item in several places.</muted>"
  set-relog: "Relog gains"
  set-relog-about: "<muted>Relog gains: more items after logging back in than on the way out.</muted>"
  set-containers: "Chest gains"
  set-containers-about: "<muted>Chest gains: items appearing while a chest is open.</muted>"
  set-money: "Money checks"
  set-money-about: "<muted>Money: supply jumps and big balance gains (needs Vault).</muted>"
  set-provenance: "Movement log"
  set-provenance-about: "<muted>Movement log: where watched items move, for the trace.</muted>"
  set-freeze-auto: "Auto freeze"
  set-freeze-auto-about: "<muted>Auto freeze: hold an item type when a high-confidence alert is about it.</muted>"
  btn-test: "<white><icon:test></white> Send test alert"
  btn-test-tip: "Checks chat, console and Discord alerts"
  test-sent: "<good>Test alert sent.</good>"
  btn-export: "<white><icon:export></white> Export 7 days"
  btn-export-tip: "CSV files of the counts and the movement log"
  btn-reload: "<white><icon:reload></white> Reload config"
  btn-reload-tip: "Read config.yml again"
  export-started: "<text>Exporting the last {days} days...</text>"
  export-failed: "<bad>The export failed; see the console.</bad>"
  exported: "<good>Exported:</good> <value>plugins/EconomyWatch/exports/{a}</value> <text>and</text> <value>{b}</value>"

  # ---- help
  help-title: "<accent><white><icon:help></white> Economy Watch help</accent>"
  help-lines:
    - "<accent>/ew</accent> <text>the overview: every watched item, its trend and the open alerts.</text>"
    - "<accent>/ew census</accent> <text>count now instead of waiting for the next count.</text>"
    - "<accent>/ew alerts</accent> <muted>(all)</muted> <text>what was found, with the evidence.</text>"
    - "<accent>/ew group</accent> <muted>(item)</muted> <text>holders, stashes and history of one item group.</text>"
    - "<accent>/ew player</accent> <muted>(name)</muted> <text>one player's holdings and recent moves.</text>"
    - "<accent>/ew trace</accent> <muted>(item or *) (player)</muted> <text>where items moved, newest first.</text>"
    - "<accent>/ew freeze</accent> <muted>(item)</muted> <text>hold an item type; /ew unfreeze lets it move again.</text>"
    - "<accent>/ew export</accent> <muted>(days)</muted> <text>CSV files of the counts and moves.</text>"
    - "<accent>/ew test</accent> <text>a test alert, to check chat, console and Discord.</text>"
    - "<muted>Economy Watch never punishes anyone by itself. It counts, compares and tells staff.</muted>"

FAQ

Which servers and versions does it run on?

It needs Paper or a Paper fork such as Purpur, because it uses Paper-only APIs (the dialog API for its menus, Adventure text components). Spigot and CraftBukkit are not supported. plugin.yml declares api-version 1.21, the jar is built against Paper API 1.21.11, and it needs Java 21 or newer.

Does it need other plugins?

No other plugin is required (plugin.yml has no hard dependencies). Optional: Vault (plugin.yml softdepend), used when present. The server downloads org.xerial:sqlite-jdbc:3.53.2.1 at startup, so it needs internet access on first start.

How do I apply config changes?

Edit plugins/EconomyWatch/config.yml and run /ew reload (read config.yml again). A restart works too.

Does it run on Folia, or for Bedrock players?

Not tested on Folia, and plugin.yml does not declare folia-supported. Bedrock players (through Geyser) have not been tested either.

What licence do I get?

The licence terms are the BuiltByBit EULA shown on the listing. There is no licence key to enter, and the plugin never contacts a licence server.