Your plugin already knows what happened. It knows when a player completed a quest, opened a rare crate, finished a race, or received a salary. If those actions only appear in console logs, you cannot easily compare them over time or see which players returned afterward. ServerStats lets the plugin send a small, structured event and lets you turn that event into a chart without editing the plugin again.
This guide uses a crate plugin as an example. The same pattern works for Minecraft plugins, FiveM resources, Rust plugins and Roblox server scripts. It explains what to send, how to avoid misleading counts, and what the chart builder can and cannot do today.
Design one useful event first
Suppose a crate plugin opens three kinds of crates. Send one event called crate_open, with crate as a category and cost as a number:
| Field | Example | Why it matters |
|---|---|---|
| Event name | crate_open | One stable action to count over time. |
| Player | The game's player object or ID | Lets the metric count unique players without putting a raw player ID in a property. |
crate | legendary | Lets a chart split the total by crate type. |
cost | 3 | Lets a chart sum or average the cost. |
Use a name for the action, not a chart you want today. If you later add a fourth crate, the same event and chart still work. A property is a good place for a bounded choice such as crate, job, arena or outcome. A numeric property is a good place for a reward, amount or duration. Values should mean the same thing on every server; cost should not be points on one server and dollars on another.
Give unrelated actions distinct event names. When two plugins implement the same action, the Event source option can separate their events if the integration reports a source. Minecraft's trackFrom records an explicit plugin name; FiveM/RedM's track export records the calling resource. Other integrations can use distinct names such as jobs_salary_paid and crates_opened when they do not send a source.
Send the event from your plugin
For a Minecraft Paper plugin, add the ServerStats jar as a provided or compileOnly dependency and softdepend: [ServerStats] to plugin.yml. Then call the Java API where the action succeeds:
import cc.serverstats.api.ServerStats;
import java.util.Map;
ServerStats.get().trackFrom("CratesPlus", "crate_open", player,
Map.of("crate", "legendary", "cost", 3));ServerStats.get() is a no-op if ServerStats is absent, so the crate plugin can still run without it. Pass your plugin descriptor's name to trackFrom; the agent lowercases it and records a source such as plugin:cratesplus. Paper, Purpur, Folia and Velocity use this Java API. Call it once after an opening succeeds. Calling it on both an attempted opening and the reward callback would double-count the same action. The Minecraft installation guide covers installing and connecting the agent.
On FiveM, another server resource can call its export after a completed action:
exports.serverstats:track('heist_complete', source,
{ heist = 'fleeca', payout = 42000, crew_size = 4 })source in this Lua call is the FiveM player's server ID, passed as the player argument. The agent separately records the calling resource as an event source such as resource:heists. See the FiveM and RedM installation guide for setup. Rust's Oxide/Carbon plugin API exposes ServerStats?.Call("Track", ...), and the Roblox server ModuleScript exposes SS.track(...). Their Rust setup guide and Roblox analytics page are the starting points for those agents.
Only send flat properties: numbers, booleans and short strings. Event and property names use lowercase snake_case and must start with a letter. Do not send names, email addresses, IPs, chat text or raw platform IDs as properties. Pass the native player to the SDK instead; the ingest service stores a pseudonymous player identifier. The agents validate these rules before sending, and ingest checks them again.
Build the chart in the dashboard
After events arrive, open the server's Custom tracking page. The event list shows types seen during the last seven days, with recent source names when available. Choose Make chart beside crate_open; if it is not in the short list, use New custom metric and enter its name. The builder suggests observed properties, numeric fields and recent event sources, but you can also type one manually.
Choose a calculation that matches your question:
- Count of events answers “How many crates opened?”
- Unique players answers “How many different players opened one?”
- Sum of
costanswers “How much did openings cost altogether?” - Average of
costanswers “What was the average cost per opening?”
Choose Group by crate to see each crate type. Add a property filter such as crate is legendary for a metric devoted to legendary crates. One metric can group by one property. It cannot currently overlay two independently defined metrics in the same tile; make two tiles if you need both questions in a dashboard.
To compare plugins or resources that send the same event, create one metric for each source. Set Event source to plugin:cratesplus or resource:heists, then put the resulting tiles side by side. Leave that field empty to include every source. Source filtering applies to new metrics and to historical events that already carried a source; it cannot infer a source for older source-less events.
Save the metric, then open Custom dashboard, select it under Add a metric, and choose a view. A line or area chart is useful for trend, columns for recent buckets, stacked columns for the mix of crate types, a leaderboard for their totals, and a number for a quick current-range summary. You can change a tile's view after adding it. A leaderboard ranks groups using the full selected time range, so an average is calculated from the underlying events rather than by averaging chart buckets.
Follow players through a funnel
The Player funnel on Custom tracking lets you choose two to five different event types, such as tutorial_started, tutorial_finished, and first_purchase. Choose 24 hours, seven days, or 30 days, then select Show funnel. Each row shows the number of distinct players who reached that step, the share of the starting group, and the share who reached the previous step. Your choice of steps is remembered in this browser for this server; it is not a shared saved dashboard.
The starting group is players whose first step-one event occurred in the selected period. Later steps must happen at or after the preceding step, within the same period. Only events with a player ID count, so use the SDK's player argument when you send a player action. The funnel uses stored event history within your plan and does not include players whose first step happened before the period. It is available on Operator and Network.
Read the result without fooling yourself
An event count is an action count, not a player count. One player who opens ten crates contributes ten crate_open events but one unique player. If your plugin retries a completed action and emits the event twice, the chart will reflect both sends; emit once at the completed transaction boundary. If you are comparing servers, keep event names, property meanings and units consistent across them.
The builder queries raw events still within your plan's history: 90 days on Operator and two years on Network. It can calculate a new metric from already stored events within that window, but it cannot recover events from before the agent was installed. Sending custom events is available through the agents; seeing and charting custom events requires Operator or Network. The Free plan's 24-hour history does not include the custom metric builder.
For recurring operational numbers, use the SDK's count or gauge call instead of making an event for every sample. track is for something that happened to a player or in the game; count accumulates an interval total, and gauge records a current level such as queue size. That distinction keeps a high-frequency timer from filling your event log with readings that belong in a metric.
What to build next
Start with one event whose answer would change a decision: a quest that players abandon, a job with low payouts, or a crate that never gets opened. Check the event and its properties in Custom tracking, make the first chart, and confirm the numbers against a small known period in your plugin's own records. Once that agrees, add the next event. For a wider measurement plan, use the ten game-server KPIs guide to choose questions before adding instrumentation.
Minecraft
Roblox