Docs

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 builder

Installation

  1. Unzip and rename the folder to x3d_dailylogin (other scripts call the exports by this name)
  2. Drop it in resources/
  3. 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 (after Config.AutoDelay), not on every respawn
  • Config.AutoDelay — delay before it opens
  • Config.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 loop in Config.Tracks (unset = Config.LoopRewards): loop = false makes a one-time track (e.g. a 31-day new-player calendar) that ends after its last day
  • Config.TokenLabels — readable names for token rewards
  • Config.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 __admin and override Config.Rewards / Config.Tracks / Config.PlaytimeBonusHours at boot — the config only seeds the first run. Daily.Rebuild() rebuilds the reward tables from Config at 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

View product page