[Release] [Custom/Standalone] Server Statistics

Stats!

Pleased to announce a very basic but powerful script simply called Stats. What this does is every 5-5.5 minutes it will run some client-side code to poll data on each one of your citizens. On our server, we have it collecting things like:

  • Economy
  • On duty times

You can swap out all of the stuff for your own framework. It would be very easy to convert this to ESX or vRP.

After collected this gets sent to MySQL where you can then query it. I dump it out to Splunk! and use it to build very useful dashboards.

I have used this data to do the following things:

  • Give bonuses out to active PD/EMS member.
  • Find modders and people exploiting bugs.
  • Find the top users of the server.
  • Trend server population over time.
  • Track economy over time.

Many FiveM Communities fail because economies get too bloated which makes players feel bored. We strive at PassionRP to make sure our economy never gets stale by closely monitoring the macro economy.

Here are some screenshots of the dashboards we’ve created. This intended to provide you with some creative juices to do your own.

Screenshots

Police Activity


As you can see here you can track your PD’s activity as a whole on your server. This helps you manage PD on your server and ensure you have a thriving PD. The PD is likely the lifeblood of your server.

Economy Tracking

^ The most interesting graph here is the scatter graph. This compares a players PLAY TIME with the players MONEY AMOUNT. It plots two numeric values (Hours vs Money) on a scatter graph. Each dot represents a player and each area in the graph represents different things.

If a player is in the bottom right hand corner of the graph it means that the player has a VERY HIGH amount of MONEY and a VERY LOW amount of play time. Definite modder/exploiter.

Bottom Right: Modder/Hacker/Exploiter
Top Right: Grinders. People who play a lot and make a lot of money.
Top Left: Have fun people. People who play a lot but don’t have a lot of money.
Bottom Left: People who don’t play a lot and don’t have a lot of money.

^ Bottom left is the busiest part of the graph.

Staff Activity

This code as based off / inspired from dfs_framemonitor which records the FPS of all clients on a regular basis.

XML

Economy: XML
PD Activity: XML

22 Likes

Very basic, and simple however very effective, thanks for sharing this!
This could be used for so many things, I can already see this being implemented in some powerful administrative tools.

1 Like

I wouldn’t call this basic, lol.

3 Likes

It’s just a client loop, and a event to insert into DB. I guess that’s the simple part. The more complicated part is making use of the data.

If anyone wants the Splunk Dashboards in XML format let me know I’d be happy to provide.

1 Like

The current version is QBus if I am not wrong? I will need to remove the on duty part and stuff because its different in ESX.

Highly customized QBus variant, yes. This was never meant to be a plug-in-play - you should customize the client side to include your framework and what stats you want and then however you’re insert into the DB.

Yes PLEASE, i would love to see that XML as well, wouldn’t be using the exact same, but will save some work, for sure, and a lot of time :stuck_out_tongue:

I see a whole LOT of opportunities coming with this, thank you so much for creating such a thing…:heart:

can you give us a guide how to set up this ? because this is very useful to monitor the economy and balance it

Well, if I may be completely honest with you, the first mistake is starting with an unbalanced economy and trying to “monitor the economy and balance it”…but, OK, being that said, I don’t really think you undestood how this works, this won’t tell you how broken your economy is, but it WILL show you WHO is breaking it, and maybe even tell you if that person is breaking it Legitimately, or exploiting/bugging/cheating…in a play time / money amount relation…
I hope I made my point.

I liked it , BUT why not make it server-side completely ? or do a prevention for excessive trigger on that event cause if they spam the event stats:upload it will make your server cry like a angel + you are getting infos from clients ! which you CANT trust at all

1 Like

If you really can give the XML format file I will appreciate it ty my friend!

I think a guide to create the stats would be nice.

And one thing why are you using a Thread insted of a Timeout?

How to install it, and how to connect it to the Splunk program?

Thx ! can you make tutorial who to add this to Splunk pliz

Thanks to @awojo we have been able to keep good track of stats! Amazing work!

Economy: XML
PD Activity: XML

Here is the XML. I’ve edited the original post.

1 Like

I can create a tutorial on how to do this. I’ll work on that.

1 Like

How to make it work for ESX?

Take out the bit at the top where it says PRPCore, and put in the ESX bits.

Then adjust the client loop to include the things you want.

Then adjust the server event to insert into your DB with whatever mysql resource you’re using.

1 Like

stats_sv.lua

ESX = nil

TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)

RegisterNetEvent('stats:upload')
AddEventHandler('stats:upload', function (Name, FirstLast, Citizenid, MoneyTotal, Job, JobLabel, OnDuty)
    MySQL.Async.fetchAll("INSERT INTO stats (Name, SourceID, FirstLast, Citizenid, MoneyTotal, Job, JobLabel, OnDuty) VALUES(@Name, @SourceID, @FirstLast, @Citizenid, @MoneyTotal, @Job, @JobLabel, @OnDuty)",
        {['@Name'] = Name, ['@SourceID'] = SourceID, ['@FirstLast'] = FirstLast, ['@Citizenid'] = Citizenid, ['@MoneyTotal'] = MoneyTotal, ['@Job'] = Job, ['@JobLabel'] = JobLabel, ['@OnDuty'] = OnDuty}
    )
end)

stats_cl.lua

ESX              = nil
local PlayerData = {}

Citizen.CreateThread(function()
	while ESX == nil do
		TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
		Citizen.Wait(0)
	end
end)

RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(xPlayer)
  PlayerData = xPlayer   
end)

RegisterNetEvent('esx:setJob')
AddEventHandler('esx:setJob', function(job)
  PlayerData.job = job
end)



---------------
-- Main Loop --
---------------

Citizen.CreateThread(function()
    while true do

        Citizen.Wait(300000)
        Citizen.Wait(math.random(1, 31))
        local PlayerData = ESX.GetPlayerData()
        local Citizenid = PlayerData.citizenid
        local MoneyTable = PlayerData.money
        local Total = (MoneyTable.cash + MoneyTable.bank)
        local Name = GetPlayerName(PlayerId())
        local Charinfo = PlayerData.charinfo
        local FirstLast = Charinfo.firstname.." "..Charinfo.lastname
        local JobName = PlayerData.job.name
        local JobLabel = PlayerData.job.grade.label
        local JobOnDuty = PlayerData.job.onduty

        if JobOnDuty == true then
            JobOnDuty = "true"
        else
            JobOnDuty = "false"
        end
        TriggerServerEvent("stats:upload", Name, FirstLast, Citizenid, Total, JobName, JobLabel, JobOnDuty)
        Citizen.Wait(5000)

    end
end)

function dump(o)
    if type(o) == 'table' then
       local s = '{ '
       for k,v in pairs(o) do
          if type(k) ~= 'number' then k = '"'..k..'"' end
          s = s .. '['..k..'] = ' .. dump(v) .. ','
       end
       return s .. '} '
    else
       return tostring(o)
    end
 end

Still does not seem to work, might have done something wrongs. Wondering if anyone could help.