X3D Daily Login
Free daily check-in with streaks and a fully editable reward table, no database required, a full bag never costs the reward, works with any framework.
Buy / Get freeConfig builderInstallation
- Unzip and rename the folder to
x3d_dailylogin(other scripts call the exports by this name) - Drop it in
resources/ - Add to
server.cfg:
ensure x3d_dailylogin
No database, no license required. Works with ESX out of the box, and standalone with no framework installed (QBCore needs its adapter filled in first).
Open the panel with F6 or /dailylogin — or let it open itself (Config.AutoOpen = true, default) whenever there is something to claim.
Auto-open options in config.lua:
Config.AutoOpen = 'always'— open once per connection (afterConfig.AutoDelay), not on every respawnConfig.AutoDelay— delay before it opensConfig.AutoNotify— notify each auto-claimed reward (reward name + track and day)
Back-claim (off by default)
Let a player who missed days collect the rewards they skipped. In config.lua:
Config.BackClaim = {
enabled = true,
maxDays = 3, -- reach back at most three days
}
| Situation | Off (default) | On, maxDays = 3 |
|---|---|---|
| Missed 3 days, then returns | One claim, today's reward only, the missed days are gone | Four claims, walking the gap one day at a time up to today |
| Missed 9 days, then returns | Same, one claim | Only the last three days plus today are reachable |
| Brand new player's first claim | Starts today | Also starts today — no free days for a time they never played |
Walking the gap day by day means the streak does not break, and the panel shows how many catch-up days are left.
Reward table
Edit config.lua — any number of days.
Config.Rewards = {
[1] = { money = { { account = 'money', amount = 500 } } },
[2] = { items = { { item = 'bread', count = 2 } } },
[3] = { weapon = { { name = 'WEAPON_PISTOL', ammo = 30 } } },
[7] = { money = { { account = 'bank', amount = 5000 } }, label = 'Weekly reward' },
}
Config.LoopRewards = true -- loops back to the first day once the table is done (keys don't need to be consecutive — this 3-day example reaches day 7 on the 3rd claim)
Config.ResetOnMiss = true -- missing a day resets the streak to 1
Config.AutoClaim = false -- true = claim automatically on spawn
Want several tracks at once (a regular calendar plus a time-limited launch event)? Set Config.Tracks — each track keeps its own streak and held rewards, with an optional from/to (unix) window. Leave it unset and it works exactly as one 'default' track, as above.
- Per-track
loopinConfig.Tracks(unset =Config.LoopRewards):loop = falsemakes a one-time track (e.g. a 31-day new-player calendar) that ends after its last day Config.TokenLabels— readable names for token rewardsConfig.Mailbox+Reward.ToMailbox— send that reward's items/money to a mailbox (x3d_mailbox safe by default) instead of the bag- If you use the X3D Admin Panel: edits made in the panel are stored under the storage key
__adminand overrideConfig.Rewards/Config.Tracks/Config.PlaytimeBonusHoursat boot — the config only seeds the first run.Daily.Rebuild()rebuilds the reward tables fromConfigat runtime
Playtime bonus
Playing N hours today unlocks another reward to claim by hand (separate from the calendar, keeps counting across a reconnect):
Config.PlaytimeBonusHours = {
[2] = { money = { { account = 'money', amount = 300 } } },
[5] = { money = { { account = 'bank', amount = 800 } } },
}
Leave it empty to turn the whole feature off — it won't show in the panel at all.
Config.PlaytimeMode = 'extra_day' — each playtime tier claims the next day of every active track at once, instead of a separate reward.
Day boundary
Config.TimezoneOffset = 7 * 3600 -- Thailand GMT+7
Computed from the server clock, never the player's — turning back the player's own system clock does nothing.
Full bag at claim time
The reward is not lost, and the day is not wasted — it is kept as a held reward for that player. Claim it later, once there is room, from the same panel.
| Setting | Result |
|---|---|
Config.HoldWhenFull = true (default) |
Held as a pending reward, the day is consumed as normal, the streak keeps running |
Config.HoldWhenFull = false |
The day is not consumed, clear the bag and claim again |
Config.MaxPending |
Maximum held rewards per player (default 10) — at the cap the day is not consumed either |
Held rewards live inside x3d_dailylogin itself, with no involvement from the mailbox (the mailbox is for staff sends and redeem codes):
exports['x3d_dailylogin']:GetPending(src) --> the held-reward list
exports['x3d_dailylogin']:ClaimPending(src, 1) --> ok, reason
AddEventHandler('x3d_dailylogin:onPending', function(src, entry, ok, trackId) end)
Servers running their own inventory
| Setting | Purpose |
|---|---|
Config.CanCarryExport |
Your inventory's own export used to check capacity — seeing "bag full" on an empty bag? set this (ESX's own weight numbers go stale when another inventory owns them) |
Config.ItemImage |
Path to the icons of the inventory in use ({item} = item name), empty = no icon |
Config.ItemLabels / Config.AccountLabels |
Thai display names for items and money accounts |
Ready-made examples already in config.lua: x3d_inventory, ox_inventory, qb-inventory
Calling from another resource
exports['x3d_dailylogin']:GetStreak(src, trackId) --> number — trackId omitted = 'default'
exports['x3d_dailylogin']:CanClaim(src, trackId) --> true / false
exports['x3d_dailylogin']:Claim(src, trackId) --> ok, reason
exports['x3d_dailylogin']:ClaimPlaytime(src, hours) --> ok, reason
AddEventHandler('x3d_dailylogin:onClaim', function(src, streak, reward, ok, trackId)
-- ok = false means delivery was incomplete (some part didn't land) — log it if you want to check (an old listener with no trackId still works)
print(('player %s claimed day %d on track %s (complete: %s)'):format(src, streak, trackId, tostring(ok)))
end)
Admin commands
/dailyreset <server id> — wipes that player's streak, last day and every held reward. For fixing a problem for a player, not a toy. Always callable from the console; a player needs the Config.AdminAce ace (default x3d_dailylogin.admin) in-game. Running it pushes the now-cleared state to the target's screen without stealing focus — the panel only shows it if that player already had it open; it does not force the panel open.
Storage with oxmysql instead of the JSON file
Default storage is data/dailylogin.json — no database required at all. To use a database instead: open addons/storage/server.lua, copy the oxmysql block at the bottom over the four functions above it, then run install.sql.
Saving happens every 30 seconds (Config.Storage.FlushSeconds), on player drop, and on a normal resource stop · a sudden server crash can lose at most whatever happened since the last save.
Support
Discord: discord.gg/x3d-developer · Web: x3d-developer.com