Player Insights

Version 1.0.0. The listing, or pick it up in the shop and run it.

Player Insights

Player Insights answers the question every server owner keeps asking: are new players sticking, and when do they leave? You spend money on ads, a new map or a big event, and a week later you still can't tell whether it worked. This tells you.

It records when each player joins and leaves, then turns that into the numbers a game studio would look at. That means new and returning players per day, how many come back after a day, a week and a month, how long sessions last, when your server is busiest, which regulars have stopped coming and why people disconnect. Staff read all of it on an in-game dashboard.

It runs with no framework at all. If ESX, Qbox or QBCore is running, the at-risk list also shows character names.

Overview

What you get

  • New and returning players per day for the last 30 days, with today's numbers live.
  • Retention by weekly cohort. Each row is the week players first joined, and each column is the share of them who played again at least 1, 7, 14 or 30 days after their first day. It's the same cohort table any app analytics tool uses.
  • The first hour signal. How long a new player's first session lasted, against whether they ever came back. If players who leave inside 10 minutes almost never return, your spawn, tutorial or whitelist wait is where you're losing them.
  • Session length, average and median, plus a breakdown of total playtime per player.
  • When players are online. A Monday to Sunday heatmap of the average peak for every hour, in your time zone, so you know when to run events and when to have staff on.
  • Regulars at risk. Players with real history whose time away is far longer than their usual gap between visits. Someone who played every day and has been gone ten days shows up, and someone who drops in every two weeks does not.
  • Why players leave. Disconnects grouped into quit, timed out, game crashed, kicked or banned, and server restart.
  • Daily Discord summary (optional) with yesterday's players, new players, peak, playtime and how many of the day before's new players came back.
  • Exports for welcome scripts and rewards: playtime, first seen and whether a player is new.
  • Fast with a year of data. Raw sessions get rolled up into daily totals, so the dashboard never scans a year of sessions. Old raw sessions are deleted after 90 days (you choose).

Retention Activity

Requirements

  • oxmysql
  • Any framework or none. ESX Legacy, Qbox and QBCore are detected on their own.

Install

  1. Unzip so that resources/player_insights/fxmanifest.lua exists (not resources/player_insights-v1.0.0/player_insights/..., which Windows "Extract All" creates).
  2. Add ensure player_insights to server.cfg, after ensure oxmysql. Start order for your framework doesn't matter. Player Insights waits up to 30 seconds for one that is installed, and it starts tracking sessions straight away while it waits.
  3. Give your staff the ACE (next section).
  4. Set your time zone in config.lua (Config.Timezone). Days, the heatmap and cohorts all follow it.
  5. Start the server. The tables create themselves.
  6. Staff open the dashboard with /insights.

Numbers fill in as players come and go. Day 1 retention needs two days of data, and day 30 needs a month.

ACE

add_ace group.admin playerinsights.view allow

# People: their Rockstar license
add_principal identifier.license:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b group.admin

The server checks the ACE every time the dashboard opens or refreshes. Nothing on the dashboard can change data, so there's only one permission.

How it counts

  • Players are counted by Rockstar license, so the numbers count people, and every character on one license is the same player. A player with no license (only possible on a server with sv_lan or a broken launcher) isn't tracked, and the console says so once.
  • A session runs from join to leave. A player who comes back within 3 minutes (IE a crash and a quick reconnect) continues the same session. A player who reconnects before the old connection has timed out also keeps one session.
  • Restarts. Open sessions are saved every 2 minutes. After a crash or restart they end at the last save, so you lose at most 2 minutes of playtime. Restarting only this resource keeps everyone's session running.
  • Long sessions are split at midnight in your time zone, so a session from 23:00 to 02:00 counts one hour on the first day and two on the next. Days are 23 or 25 hours long when the clocks change, and they're counted that way. No session counts for more than 24 hours.
  • The clock. If the host clock is set back, time holds until it catches up, so no session ever gets a negative length.
  • Retention is rolling. Day 7 is the share of new players who played again 7 or more days after their first day. Cells only count players old enough to be judged, and a dot marks a cell where part of the week is still too new.
  • Leaving reasons come from the reason text FiveM gives when a player drops. Anything it can't place goes into Other.

Config

The main settings, all in config.lua with comments:

Config.Timezone = { utcOffset = 0, dst = 'eu' }   -- 1 for Berlin, -5 with 'us' for New York, 'none' for no summer time
Config.Sessions = { resumeSeconds = 180, heartbeatSeconds = 120, maxHours = 24 }
Config.NewPlayer = { days = 3, hours = 5 }        -- what IsNewPlayer means
Config.RetentionDays = { 1, 7, 14, 30 }           -- cohort table columns
Config.Churn = { minVisits = 5, factor = 3, minGapDays = 4, maxGapDays = 45, limit = 25 }
Config.Storage = { keepSessionDays = 90, keepHourlyDays = 400, rollupMinutes = 10, cacheSeconds = 30 }

Discord summary

A summary of the day before gets posted once a day, shortly after midnight in your time zone. Put the webhook URL in server.cfg:

set player_insights_webhook "https://discord.com/api/webhooks/..."

Use set, not setr. A setr value is sent to every player, and anyone with the URL can post to your channel.

Exports

exports.player_insights:GetPlaytime(source)               -- seconds played in total, this session included
exports.player_insights:GetPlaytime('license:...')        -- the same for a license, online or not
exports.player_insights:GetFirstSeen(source)              -- Unix time of their first visit, or nil
exports.player_insights:IsNewPlayer(source)               -- true inside Config.NewPlayer's limits

A welcome message for new players:

AddEventHandler('playerJoining', function()
    local src = source
    SetTimeout(5000, function()
        if exports.player_insights:IsNewPlayer(src) then
            TriggerClientEvent('chat:addMessage', src, { args = { 'Welcome', 'First time here? Ask in /help.' } })
        end
    end)
end)

For a player who is offline, the exports read the database, so call them from a thread or an event handler.

Escrow

The core is encrypted with Cfx asset escrow. These stay open so you can adapt them: config.lua, server/bridge.lua (the optional framework code) and sql/install.sql.

Troubleshooting

  • Couldn't find resource player_insights: the folder is nested. Open resources/ and check that player_insights/fxmanifest.lua sits directly inside it. Windows "Extract All" adds an extra folder named after the zip.
  • "You don't have access to Player Insights." The player doesn't have playerinsights.view. Check the add_ace and add_principal lines and that the license matches. Run test_ace identifier.license:... playerinsights.view in the server console to see what the server thinks.
  • Database setup failed in the console: oxmysql isn't connected, or your database user can't create tables. Fix the connection or run sql/install.sql by hand.
  • The heatmap or days look an hour off: set Config.Timezone to your community's time zone, with the right summer time rule.
  • Retention shows dashes: nobody in that week is old enough yet. Day 7 needs players who joined at least 7 days ago.
  • The webhook posts nothing: the console prints a warning when player_insights_webhook is set but isn't a Discord webhook URL. Also, the summary only goes out once a day, shortly after midnight.
  • No character names in the at-risk list: they only show when ESX, Qbox or QBCore is running and the player loaded a character during a visit.

Support

Open a ticket in the Discord with your framework (or none), your oxmysql version and the server console output from startup.