Overview
About Touch Grass
A player stays on your server for an hour and a small sunset banner comes up with their gift, $250 and a water by default. The gift is paid on the spot, so there is nothing to claim. Every eight hours they go up a level, and a bigger synthwave banner plays across the top of the screen with the new rank.
Time is read on the server, from its own online timer. Each check only adds the new time, so a resource restart doesn't count anything twice. The client checks movement, camera and voice chat, and after 10 idle minutes by default (Config.afk.minutes) a player stops earning time until they move again. Playtime is saved per license by default, so all characters on one account share a level.
Levels and gifts
The default ladder goes up to level 8 at 64 hours. You can change the hours or add rows. Rank names like Newcomer, Regular and Icon are in the language files.
Milestone hours pay more, for example $5,000 in the bank at hour 8. One gift can mix money, items, a server event and a console command, so you can hand out a vehicle or VIP from another script. If the inventory is full, the gift waits and is tried again, even after a restart. The default items are water, burger, phone and lockpick, so rename them to items your inventory has.
Level gates
Your other resources can ask with one line if a player has the level for something, like level 3 for buying a weapon. The gates are only names and levels, so each resource decides for itself what to do with the answer.
Panel and admin
Players see their level, a live countdown, the next gift and the gates in a panel on /levels or PAGE UP. Admins use /levelsadmin, and its info, add, sethours, setlevel and reset also work on offline players. Level-ups, gifts and admin changes can be logged to a webhook.
Requirements
- oxmysql
- An FXServer build with the
GetPlayerTimeOnlineserver native. Without it tracking does not start, and the console says why. - ESX, QBCore or QBox if you use one. Without a framework it runs standalone, and money gifts then need ncore_inventory.
- An inventory for item gifts. ncore_inventory, ox_inventory and qb-inventory are detected, otherwise the framework's own item functions are used.
English and Hungarian are included.
Editions
Escrow and Full source have the same features. In Escrow the Lua code is protected by Cfx.re escrow, and the config files, the language files and server/bridge.lua (the framework and inventory calls) stay open to edit. In Full source every file is readable.
New versions are announced on intelnpc.com. When you update, your config and language files stay as they are.
The full feature list is in README.md, also on intelnpc.com.
Support is on intelnpc.com and I reply within 72 hours. Refunds are handled by Tebex under their terms.
Not approved, sponsored or endorsed by Rockstar Games.
What you get
Config open, core escrowed
Docs
Read it before you buy
The same docs that ship with the script, updated with every release.
Playtime levels, hourly gifts and level gates for FiveM, with a sunset-themed panel and webhook logs.
nCore Development · intelnpc.com · Resource: ncore_touchgrass · Version: see fxmanifest.lua
What it is#
Touch Grass rewards the players who stay. It counts how long each player has really been on the server, using the server's own online timer, and stops counting while they're AFK. Played hours become levels with named ranks, every hour pays out a gift, and other resources can lock things behind a level.
It runs on ESX, QBCore, QBox or standalone. It needs oxmysql and a server artifact with the GetPlayerTimeOnline native. Everything else is optional.
Features#
Playtime#
- Time comes from the server's own online timer. The client only reports whether the player is AFK.
- Each check only adds the new time, so a resource restart never counts time twice.
- The last minutes are saved when a player leaves and when the resource stops. Players are also saved on a timer (
ServerConfig.save_seconds, 300 by default). - Playtime is stored per license by default (
ServerConfig.identifier), with other identifiers as backups. It belongs to the account, so on a multi-character server all characters share one level, and gifts go to the character that is loaded when the hour is reached. - A player with no usable identifier is not tracked, and the console warns.
- If the server build has no online timer, tracking does not start and the console explains why.
AFK detection#
- The client checks movement, turning, camera and voice chat every 5 seconds.
- After the set idle time (
Config.afk.minutes, 10 by default) the player stops earning time and gets a message. Time resumes as soon as they're active again. - Time earned before AFK was detected still counts.
- The movement limits are in
Config.afk.
Levels and ranks#
- Default ladder: level 1 at 8 hours, then one level every 8 hours, up to level 8 at 64 hours.
- Add or remove rows and change the hours in
Config.levels. The max level follows the number of rows. - A row that doesn't need more hours than the one before it is refused in the console, and the ladder stops there.
- Stored levels are corrected when a player joins.
- Nine rank names ship in the locale files: Newcomer, Visitor, Local, Regular, Known Face, Resident, Veteran, Legend and Icon. Rename them in
locales/*.lua. A level without a name shows as "Level N".
Gifts#
- A gift after every played hour, plus bigger gifts on milestone hours. It is paid the moment the hour is reached, there is nothing to claim.
- Defaults: $250 cash and a water every hour. Milestones at hour 1 (2 water, 2 burgers), hour 8 ($5,000 bank and a phone), hour 24 ($15,000 bank and 3 lockpicks), hour 40 ($25,000 bank) and hour 64 ($50,000 bank). Rename the items to match your server.
- With
ServerConfig.gifts.stackon, a milestone hour pays both gifts. With it off, the milestone gift replaces the hourly one. ServerConfig.gifts.enabled = falseturns all gifts off.- A gift entry takes
money = { account = 'cash', amount = 250 }(cashorbank, plusblack_moneyon ESX andcryptoon QBCore and QBox),items = { { name = 'water', count = 1 } }with optionalmetadata,event = 'name'andcommand = '...'. - One gift can combine money, items, a server event and a console command. The event is triggered on the server with the player id, the hour and the identifier. The command gets
{id},{hour}and{identifier}, never the player name, so nobody can inject commands through their name. Use events and commands to give vehicles, VIP or XP from any other script. - Item names are checked first, so a typo gives a clear console error instead of endless retries.
- A gift that can't be given yet, for example because the inventory is full or the character isn't loaded, is saved as waiting and tried again. This survives a reconnect and a server restart. The player is told it's waiting and told again when it arrives.
- Gifts that can never work, like a misspelled item, are dropped, and the console names the problem.
- When a player jumps many hours at once, only the last few hourly gifts are paid (24 by default,
ServerConfig.gifts.max_catch_up), and the console says so. Only those hours fire the hour event. - Setting hours or level never pays the skipped hours, and removing time never takes gifts back.
- Gift text is built from the locale files. Add
gift_label_everyorgift_label_<hour>to write your own text for an hour.
Level gates#
- Three gates ship in
Config.gates:buy_vehicleat level 1,buy_weaponat level 3 andown_businessat level 5. Rename them or add your own. - The gates are names and levels. Your other resources ask with one line, on the server or on the client, and decide what to do with the answer. Nothing is enforced by this resource itself.
- On the server you can ask silently (
CanDo) or let the script tell the player why they were refused (RequireAction,RequireLevel). The client check is silent. - Gates show in the player's panel with lock and check icons.
- An action without a gate is always allowed.
- Other resources can add, replace or remove gates while the server runs, with a label in one or more languages. Gates added this way live in memory and are gone when
ncore_touchgrassrestarts. - Refusal messages name the level, for example "You need level 3 (Regular) for this."
Screens#
- The level-up banner is a full-width synthwave sunset banner across the top, with palms, twinkling stars and GTA's rank-up sound. It shows the new level, the rank and the total time played, with a countdown bar. Banners queue so they never overlap, and only level-ups are announced.
Config.ui.banner_secondssets how long it stays. - The gift banner is a smaller banner with a sound when a gift arrives, next to the normal message.
Config.ui.gift_banner = falsekeeps only the message. - The levels panel opens with
/levelsor PAGE UP. It shows the current level and rank, time played, a progress bar with a liveNext level incountdown, the next hourly gift and the next milestone gift, the level ladder and the gates. It closes with ESC, the X or the Close button, and fits small screens. Opened while the player is AFK, it shows an AFK badge and the countdown stands still. - The panel doesn't open over the pause menu or another nCore panel. Opened from chat, it waits a moment for chat to close first.
- The level-up and gift sounds can be any GTA menu sound. If a sound doesn't work, the console warns once and the banner plays silently.
- Fonts ship inside the resource, so nothing loads from the internet.
- Panel data requests are limited to one per second per player.
Admin commands#
/levelsadmincoversinfo,add,sethours,setlevel,reset,gift,levelup,event,gateanddiscord. The table below has the details.info,add,sethours,setlevelandresetalso work on offline players by identifier.- Access is the ACE
ncore_touchgrass.admin. The server console can always use it. A player without access is told the exact line to add. - Replies go to chat with your own prefix and color (
ServerConfig.admin_chat). - Preview tools: show the level-up banner, fire the level and hour events in preview mode, check a gate for a player, post a sample message to the webhook. Previews change nothing.
Webhook logging#
- Level-ups, gifts (what arrived and what failed) and every admin change are posted as embeds. Admin changes name the staff member or the console.
- Entries about a player show their name, server id and identifier.
ServerConfig.discord.mention(on by default) also mentions the player's account when the server has that identifier for them. - Each log type can be turned on or off. The webhook name, avatar and colors are yours to set.
- Keep the webhook out of the config with the
ncore_touchgrass:webhookconvar inserver.cfg. - Messages are queued about one per second and retried up to 3 times when the webhook is rate limited. The webhook must be https.
- Other resources can post their own embed through the same queue with
SendDiscord.
Frameworks, inventories and other resources#
- ESX, QBCore and QBox are detected automatically. Without one, it runs standalone. Detection runs again when a framework or inventory starts or stops.
- Standalone: levels, gates, the panel and the banners work. Money gifts need ncore_inventory, and item gifts need an inventory resource.
- ncore_inventory is preferred and also works on standalone servers. ox_inventory and qb-inventory are detected too, and without any of them items go through the framework's own item functions.
- Messages go through ncore_hud when it runs, otherwise through your framework, ox_lib or the standard GTA feed.
- Other resources can grant bonus minutes that count toward levels and gifts, remove minutes, or set playtime directly. ncore_npc_pro uses this to reward quests.
- Other resources can show the sunset banner with their own text, full or compact and with or without sound.
- Each player's level and AFK state, and the list of gates, are shared as state bags, so any script can read them without an export.
- Keep the folder name
ncore_touchgrass, because other nCore resources look it up by name.
Data#
- The database table is created on start.
sql/install.sqlis optional. - On first start, if the new table is empty, it can import names and minutes once from another playtime table. Levels are recalculated and no past gifts are paid. The source table needs the columns
identifier_kind,account_id,display_name,total_minutes,first_seenandlast_seen. SetServerConfig.migrate_fromtofalseto skip it. - Gifts and the webhook live in
config_server.lua, which only the server loads. Players never download it.
Languages and console#
- English and Hungarian. All texts are in the locale files. Missing texts fall back to English, and a new language is one copied file.
Config.locale = 'auto'reads your server's locale setting (the convarslr:locale,locale,ox:locale,esx:localeandtxAdmin-locale, in that order), and English when none of them has a language that ships.- The console prints a colored startup summary with the detected framework and inventory, every level change, and clear errors.
Config.debugadds detail. tools/fetch_refs.shdownloads GTA's sound name list so you can find valid sounds. The resource doesn't load it.
Commands and keys#
| Command or key | Who | What it does |
|---|---|---|
/levels |
Everyone | Opens the playtime panel (name set by Config.ui.command) |
| PAGE UP | Everyone | Opens the panel (rebindable in the game's key settings, Config.ui.key, '' for no key) |
| ESC | Everyone | Closes the panel |
/levelsadmin |
Admin / console | Lists the subcommands |
/levelsadmin info <id\|identifier> |
Admin / console | Level, played time, online status, gift hour and waiting gifts |
/levelsadmin add <id\|identifier> <minutes> [reason] |
Admin / console | Adds minutes, negative removes. Logged to the webhook |
/levelsadmin sethours <id\|identifier> <hours> |
Admin / console | Sets played hours. Skipped hours pay no gifts. Logged to the webhook |
/levelsadmin setlevel <id\|identifier> <0-max> |
Admin / console | Jumps to the start of a level. Skipped hours pay no gifts. Logged to the webhook |
/levelsadmin reset <id\|identifier> |
Admin / console | Clears time, bonus, gift hour and waiting gifts. Logged to the webhook |
/levelsadmin gift <id> <hour> |
Admin / console | Gives that hour's gift now, for real (online only). Logged to the webhook |
/levelsadmin levelup <id> [level] |
Admin / console | Previews the level-up banner. Nothing changes |
/levelsadmin event <id> levelup [level] |
Admin / console | Fires the level event in preview mode and shows the banner |
/levelsadmin event <id> hour [hour] |
Admin / console | Fires the hour event in preview mode |
/levelsadmin gate <id> <action> |
Admin / console | Checks whether the player passes a gate. An unknown action lists the known gates |
/levelsadmin discord |
Admin / console | Posts a sample message to the webhook |
For developers#
Server exports: GetLevel, GetPlaytime, GetPlayedHours, GetProgress, GetMaxLevel, GetLevelTitle, HasLevel, RequireLevel, CanDo, RequireAction, RegisterGate, RemoveGate, GetGates, GrantBonusMinutes, RemoveMinutes, SetPlaytime, ShowBanner, SendDiscord, IsAfk.
Client exports: GetLevel, HasLevel, CanDo, GetGates, IsAfk, OpenPanel, ClosePanel.
A few calls as examples:
-- server
local allowed, needed = exports.ncore_touchgrass:CanDo(source, 'buy_weapon') -- silent
if not exports.ncore_touchgrass:RequireAction(source, 'buy_weapon') then return end -- tells the player
exports.ncore_touchgrass:RegisterGate('rent_hangar', 4, { en = 'Rent a hangar' })
exports.ncore_touchgrass:GrantBonusMinutes(source, 30, 'quest')
exports.ncore_touchgrass:ShowBanner(source, { kicker = 'Race', title = 'First place', subtitle = 'Sandy Shores', variant = 'gift' })
exports.ncore_touchgrass:SendDiscord({ title = 'Race', description = 'Finished first', player = source })
-- client
local allowed, needed = exports.ncore_touchgrass:CanDo('buy_weapon')CanDo returns true and nil for an action without a gate. ShowBanner with variant = 'gift' shows the smaller banner, and sound = false plays it silent. SetPlaytime(src, seconds, give_gifts) pays the skipped hours only when give_gifts is true.
| Event | Side | When |
|---|---|---|
ncore_touchgrass:levelChanged(src, new, old, preview) |
Server and client | A level goes up or down |
ncore_touchgrass:hourReached(src, hour, preview) |
Server and client | A played hour is reached |
ncore_touchgrass:giftDelivered(src, hour, delivered, failed, preview) |
Server | A gift was handled |
preview is true only when fired by the admin preview or gift commands. Clients can't fire the server events. State bags: Player(src).state.lrLevel, Player(src).state.lrAfk, GlobalState.lrLevelGates.
Configuration#
config.lua: language, debug, level ladder, level gates, AFK rules, panel command and key, banner time, gift banner and sounds.config_server.lua: framework, identifier, tick and save timing, import table, gifts, admin command and chat style, webhook and logs.locales/en.lua,locales/hu.lua: all texts, including rank names, gate names, gift text, messages, admin replies and log text.
Settings you'll change first:
Config.levelsfor the hours each level needs.Config.gatesfor which action needs which level.ServerConfig.gifts.every_hourandServerConfig.gifts.hoursfor the gifts.ServerConfig.gifts.stackto pay milestone and hourly gifts together or not.Config.afk.minutesfor the idle time before AFK.ServerConfig.discord.webhook, or thencore_touchgrass:webhookconvar.ServerConfig.admin_acefor the admin permission.
Other settings:
Config.afk.enabled = falseturns AFK detection off, so all online time counts.ServerConfig.frameworkis'auto'. Set'esx','qb'or'qbox'to pick one when more than one is running.ServerConfig.tick_seconds(30, at least 10) is how often time is added and waiting gifts are tried again.ServerConfig.identifieris the identifier kind playtime is saved under. If a player has none of that kind,license,license2,fivem,discordandsteamare tried in that order.ServerConfig.admin_commandandConfig.ui.commandrename/levelsadminand/levels.
Troubleshooting#
| What you see | What to do |
|---|---|
The console says there is no GetPlayerTimeOnline native |
Update your FXServer artifact. Nothing is tracked until it has the native. |
| Admin commands reply "not ready" | The console names the cause, usually the missing native above or a database error while the table was prepared. |
| A gift item never arrives | The console names the item. An item your inventory doesn't know is dropped, so rename the default items (water, burger, phone, lockpick) in config_server.lua. |
| Money gifts fail on a standalone server | Without a framework only cash can be given, and only through ncore_inventory. Use an event or command gift for anything else. |
| "You are missing the ace" | Add add_ace group.admin ncore_touchgrass.admin allow to server.cfg. The reply prints the same line. |
| Nothing reaches the webhook | Run /levelsadmin discord and watch the console. The URL must start with https://, and the console prints the HTTP answer when a post fails. |
| The panel doesn't open | It stays closed over the pause menu and while another nCore panel has focus. From chat it waits up to 1.5 seconds for chat to close. |
| A crash lost some minutes | The timed save runs every ServerConfig.save_seconds (300 by default), so a crash can lose the time since the last one. A normal resource stop saves everything. |
Requirements#
- oxmysql.
- An FXServer build that has the
GetPlayerTimeOnlineserver native.
Optional, detected automatically:
- ESX, QBCore or QBox. Without one it runs standalone.
- ncore_inventory, ox_inventory or qb-inventory. Without one, items go through the framework's own item functions.
- ncore_hud for messages, with ox_lib as a fallback.
- ncore_npc_pro for quest bonus time.
- Any chat resource that uses the standard chat events, such as ncore_chat.
- A webhook URL for logging.
Setup steps are in INSTALLATION.md.
Support#
Support is on intelnpc.com and I reply within 72 hours. It covers installing, configuring and bugs in the script as shipped. Payments and refunds are handled by Tebex under their terms.
The escrow edition is protected with Cfx.re Asset Escrow. config.lua, config_server.lua, server/bridge.lua (the framework and inventory calls) and the locale files stay editable. The full source edition has every file open.
nCore Touch Grass is made by nCore Development. It is not approved, sponsored or endorsed by Rockstar Games.
-
Put the
ncore_touchgrassfolder intoresources/[ncore](create the[ncore]folder if you do not have one yet). -
Optional: import
sql/install.sql. The table is also created automatically on the first start. -
Add these lines to
server.cfg, after your framework and inventory:Codeensure oxmysql ensure ncore_touchgrass add_ace group.admin ncore_touchgrass.admin allow -
Optional: for logs, add your webhook URL with
set ncore_touchgrass:webhook "https://..."./levelsadmin discordposts a test message. -
Rename the gift items in
config_server.luato items your inventory has. -
Restart the server and type
/levelsin game (or press PAGE UP).
Important#
- Required:
oxmysql, and an FXServer artifact that has theGetPlayerTimeOnlineserver native. - Detected automatically: ESX, QBCore, QBox,
ncore_inventory,ox_inventory,qb-inventory,ncore_hudandox_lib. - Admin replies use the standard chat events, so any chat resource that handles them works, such as
ncore_chat. - Updating: keep your own
config.lua,config_server.luaandlocales/files and copy every other file over the old ones.CHANGELOG.mdlists new settings per version. - Do not rename the
ncore_touchgrassfolder: other nCore resources look for it by this name.
Updates don't install themselves. When a new version is out, download it again (escrow from your Cfx.re account, full source from Tebex), keep your own config.lua, config_server.lua and locales/ files, and copy every other file over the old ones. Settings added in a new version are listed under that version, so you can copy them into your config. New versions are announced on intelnpc.com.
1.0.5#
First public release. Everything the script does is listed in README.md.
€9.99
Ready when you are
Instant delivery, checkout right here on the site, and a Discord full of people who will answer before you pay.

