# Battlepass

## Overview

<figure><img src="https://3626236831-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fa8uHYewwKFFVyUWKAUtL%2Fuploads%2FxgXSnZb7piPKQLMMMGXE%2F1f8629532859f5af5cb413599890228f29452a4d.jpg?alt=media&#x26;token=dc19ce0a-016b-4789-98c8-c6c8cbb934bd" alt=""><figcaption></figcaption></figure>

XeX BattlePass is a premium daily reward and battle pass system for FiveM servers. Players claim one reward per day across a 30-day calendar. A premium VIP tier gives access to exclusive bonus rewards. The system features a dark gaming-inspired NUI, Discord webhook logging, and automatic database management.

### Supported Frameworks

| Framework  | Version | Status         |
| ---------- | ------- | -------------- |
| ESX Legacy | 1.6.0+  | ✅ Full Support |
| QBCore     | Latest  | ✅ Full Support |
| QBox       | Latest  | ✅ Full Support |

## Features Summary

| Category    | Feature          | Description                                               |
| ----------- | ---------------- | --------------------------------------------------------- |
| **Rewards** | 30-Day Schedule  | Configurable normal + VIP reward per day                  |
| **Rewards** | 5 Reward Types   | Items, weapons, money, vehicles, furniture                |
| **Rewards** | Daily Claims     | One claim per day, sequential progression                 |
| **Premium** | VIP Tier         | Purchasable premium subscription for exclusive rewards    |
| **Premium** | Renewals         | Extend premium duration with configurable packages        |
| **Premium** | Retroactive      | VIP purchase retroactively awards missed VIP rewards      |
| **UI**      | Dark Theme       | Modern gaming-inspired NUI with Inter font                |
| **UI**      | Progress Grid    | Visual 30-day calendar with claim/locked/available states |
| **System**  | Auto DB          | Tables created automatically on first start               |
| **System**  | Season State     | KVP-based persistence for season day tracking             |
| **System**  | Discord Webhooks | Log every reward claim to Discord                         |
| **System**  | Auto Updater     | Version check via GitHub Gist JSON                        |
| **I18n**    | 2 Languages      | English, Spanish                                          |
| **Compat**  | loaf\_housing    | Furniture reward type integration                         |
| **Compat**  | xex\_lootboxes   | Lootbox item integration                                  |

## Installation

### Requirements

* FiveM Server Build 5181+
* [oxmysql](https://github.com/overextended/oxmysql)
* Framework: ESX Legacy or QBCore

### Optional Dependencies

| Resource                                                                                 | Purpose               |
| ---------------------------------------------------------------------------------------- | --------------------- |
| [loaf\_housing](https://store.loaf-scripts.com/package/4310850)                          | Furniture reward type |
| [Deco Items Pack](https://www.gta5-mods.com/maps/murmods-furniture-pack-add-on-sp-fivem) | Props for furniture   |
| [xex\_lootboxes](https://xex.tebex.io/package/5496291)                                   | Lootbox item rewards  |

### Quick Start

{% stepper %}
{% step %}

### Place resource

Place 'xex\_battlepass' in your resources/\[xex]/ folder
{% endstep %}

{% step %}

### Add to server.cfg

Add to server.cfg:

```
ensure oxmysql
ensure xex_battlepass
```

{% endstep %}

{% step %}

### Configure

Configure config.lua
{% endstep %}

{% step %}

### Restart

Restart server (database tables are created automatically)
{% endstep %}
{% endstepper %}

> Note: Database tables (`user_battlepass`, `user_premium`) and season state (KVP) are created automatically. No manual SQL import is required.

## Configuration

### General Settings

```lua
Config.ModActivated = true            -- Master on/off switch
Config.Debug = false                  -- Enable debug commands
Config.MenuCommand = 'battlepass'     -- Command to open battle pass UI
Config.PremiumCommand = 'premium'     -- Command to open premium shop
Config.ExtraDaysToEnd = 5             -- Grace days after day 30 before season resets
```

| Option                  | Default        | Description                                                     |
| ----------------------- | -------------- | --------------------------------------------------------------- |
| `Config.ModActivated`   | `true`         | Master switch for the entire system                             |
| `Config.Debug`          | `false`        | Enables debug commands (getbpdata, restclaimedbpday, restbpday) |
| `Config.MenuCommand`    | `'battlepass'` | Chat command to open the battle pass calendar                   |
| `Config.PremiumCommand` | `'premium'`    | Chat command to open the premium shop                           |
| `Config.ExtraDaysToEnd` | `5`            | Days of grace period after all 30 days have passed              |

### Framework Settings

```lua
Config.Framework = 'esx'              -- 'esx' | 'qb'
Config.Language = 'en'                -- 'en' | 'es'
Config.CheckForUpdates = true
```

### Premium System

```lua
Config.PremiumCoin = 'bank'           -- Account used for premium payments

Config.PremiumOptions = {
    {
        months = 1,
        price = 50000,
        label = '1 Month VIP',
        img = 'vip_1month.png',
    },
    -- ... more packages
}
```

### Reward Schedule (Days 1-30)

Each day defines a `normal` reward (for all players) and a `vip` reward (premium only):

```lua
Config.Schedule = {
    {
        day = 1,
        items = {
            normal = {
                type = 'item',
                name = 'bread',
                label = 'Bread',
                quantity = 10,
                img = 'bread.png',
            },
            vip = {
                type = 'furniture',
                name = 'murm_dec_pack_chair_1',
                label = 'Deco Chair',
                quantity = 1,
                img = 'chair.png',
            },
        },
    },
    -- ... days 2-30
}
```

### Reward Types

| Type        | Fields                                       | Description                           |
| ----------- | -------------------------------------------- | ------------------------------------- |
| `item`      | `name`, `label`, `quantity`, `img`           | Inventory item (e.g., bread, bandage) |
| `weapon`    | `name`, `label`, `img`                       | Weapon (quantity always 1)            |
| `money`     | `name` (account), `label`, `quantity`, `img` | Currency (money, bank, black\_money)  |
| `vehicle`   | `name` (spawn name), `label`, `img`          | Vehicle (spawned + stored in garage)  |
| `furniture` | `name` (object hash), `label`, `img`         | loaf\_housing furniture piece         |

### Discord Webhooks

```lua
Config.WebhookEnabled = true
Config.Webhook = 'https://discord.com/api/webhooks/...'
Config.BotName = 'BattlePass'
Config.WebhookServerName = 'My Server'
Config.IconUrl = 'https://i.imgur.com/...'
Config.DateFormat = '%d/%m/%Y %H:%M'
```

## Database

Tables are **automatically created** on first start:

### user\_battlepass

Tracks player claim progress per season.

### user\_premium

Tracks premium subscription start/end dates.

If you need to create them manually, use the included `scripts.sql` file.

## How It Works

### Complete Flow

```
Player types /battlepass → Client requests data from server
→ Server checks player's claim history + premium status + current day
→ NUI shows 30-day grid:
   ├── Green: Already claimed
   ├── Gold: Available today (click to claim)
   ├── Locked: Future days
   └── VIP badge: Premium-only rewards
Player types /premium → Premium shop opens
   ├── Select package (1 month, 3 months, etc.)
   └── Purchase with bank account
```

### Daily Claim Logic

* Server tracks global "current day" via KVP (`xex_bp:state`)
* Player can only claim the current day if all previous days are claimed
* Each claim is recorded in `user_battlepass` table
* Normal rewards given to all players
* VIP rewards given only to premium subscribers

### Premium VIP System

* Players purchase VIP through `/premium` command
* VIP subscription has start and end dates stored in `user_premium`
* VIP unlocks exclusive `vip` tier rewards on each day
* Multiple package durations can be configured (1 month, 3 months, etc.)
* Renewal extends the existing end date

### Season Lifecycle

1. Season starts at day 1
2. Each real day advances the season by 1
3. After day 30, grace period of `Config.ExtraDaysToEnd` days
4. After grace period, season resets (KVP cleared, new season begins)

### Retroactive VIP Rewards

When a player purchases VIP mid-season, they retroactively receive all VIP rewards for previously claimed days. This ensures no VIP content is missed.

## NUI Interface

### Battle Pass Grid

* 30-day calendar grid layout
* Visual states: Claimed (green check), Available (golden glow), Locked (grey)
* Normal reward icon on left, VIP reward icon on right
* Claim button for available days
* VIP badge for premium rewards

### Premium Shop

* Package cards with duration, price, and image
* Purchase confirmation
* Active subscription status display
* Renewal option for existing subscribers

## Exports & Events

### Client Exports

```lua
-- Check if current player has active premium
local hasPremium = exports['xex_battlepass']:isPremium()
-- Returns: boolean
```

### Server Exports

```lua
-- Check if a specific player has active premium
local hasPremium = exports['xex_battlepass']:isPremium(playerId)
-- Returns: boolean

-- Trigger manual update check
exports['xex_battlepass']:CheckForUpdates()
```

### Server Callbacks

| Callback                         | Parameters   | Returns | Description                        |
| -------------------------------- | ------------ | ------- | ---------------------------------- |
| `xex_battlePass:canClaim`        | —            | boolean | Check if player can claim today    |
| `xex_battlePass:claimDayRewards` | —            | result  | Process and deliver today's reward |
| `xex_battlePass:buy`             | packageIndex | result  | Purchase premium subscription      |
| `xex_battlePass:renew`           | packageIndex | result  | Renew premium subscription         |

### Net Events

| Event                          | Direction       | Description                        |
| ------------------------------ | --------------- | ---------------------------------- |
| `xex_battlePass:loadData`      | Server → Client | Send battle pass state to client   |
| `xex_battlePass:rewardVehicle` | Server → Client | Spawn rewarded vehicle client-side |

### NUI Callbacks

| Callback      | Description                              |
| ------------- | ---------------------------------------- |
| `claimDay`    | Claim the current available day's reward |
| `closeButton` | Close the battle pass UI                 |

### Commands

| Command             | Context        | Description                    |
| ------------------- | -------------- | ------------------------------ |
| `/battlepass`       | Client         | Opens the battle pass calendar |
| `/premium`          | Client         | Opens the premium shop         |
| `battlepass_update` | Server Console | Force update check             |

### Debug Commands

> Only available when `Config.Debug = true`

| Command             | Description                                  |
| ------------------- | -------------------------------------------- |
| `/getbpdata`        | Reload your battle pass data from server     |
| `/restclaimedbpday` | Roll back your last claimed day              |
| `/restbpday`        | Roll back the global battle pass day counter |

## Localization

### Supported Languages

| Code | Language |
| ---- | -------- |
| `en` | English  |
| `es` | Español  |

### Adding Languages

Add a new entry in `locales.lua`:

```lua
Locales['fr'] = {
    ['received'] = 'Reçu',
    ['daily_limit'] = 'Déjà réclamé aujourd\'hui',
    -- ... all keys
}
```

Then set `Config.Language = 'fr'` in `config.lua`.

## File Structure

```
xex_battlepass/
├── client/
│   ├── esx.lua               # ESX client logic
│   ├── qb.lua                # QBCore client logic
│   └── scrow.lua             # Shared NUI bridge (escrow)
├── server/
│   ├── esx.lua               # ESX server — rewards, premium, claims
│   ├── qb.lua                # QBCore server mirror
│   ├── scrow.lua             # Auto DB creation, KVP helpers (escrow)
│   └── updater.lua           # Version checker
├── ui/
│   ├── ui.html               # NUI markup
│   ├── style.css             # Dark themed styles
│   ├── script.js             # NUI logic (vanilla JS)
│   └── img/                  # Reward images
├── config.lua                # Full configuration (563 lines)
├── locales.lua               # Translations (en/es)
├── scripts.sql               # Database schema (optional manual import)
├── fxmanifest.lua            # Resource manifest
├── readme.md                 # Quick start readme
└── DOCS.md                   # This documentation
```

## Troubleshooting

| Issue                           | Solution                                                                  |
| ------------------------------- | ------------------------------------------------------------------------- |
| UI doesn't open                 | Verify `Config.ModActivated = true` and resource is started               |
| "Missing locale key" in console | Add the missing key to `locales.lua`                                      |
| Rewards not given               | Check `Config.Schedule` — reward `name` must match your inventory items   |
| Webhook not working             | Verify `Config.WebhookEnabled = true` and URL is valid                    |
| oxmysql errors                  | Ensure `oxmysql` is started before `xex_battlepass` in server.cfg         |
| Premium purchase fails          | Check `Config.PremiumCoin` matches a valid account type (`bank`, `money`) |
| Vehicle reward doesn't spawn    | Check vehicle model name in `Config.Schedule` is valid                    |
| Furniture reward fails          | Requires `loaf_housing` resource installed and running                    |
| Season not advancing            | Check KVP state — use debug commands to inspect/reset                     |
| Day counter stuck               | Use `/restbpday` (debug mode) to correct the global counter               |

## Changelog

### v2.0.0

* Migrated UI from jQuery to vanilla JavaScript
* Redesigned UI with dark gaming aesthetic (Inter font, CSS custom properties)
* Migrated from mysql-async to oxmysql
* Added centralized XeX updater system
* Fixed critical bugs: undefined variables, typos, exposed webhook URLs
* Fixed security: removed exploitable net event for notifications
* Modernized ESX/QB init patterns (exports instead of events)
* Improved identifier matching (prefix-based instead of loose `string.find`)
* Added nil guards throughout server logic
* Cleaned up duplicate NUI callbacks
* Removed debug prints from production code
* Added proper database indexes and column types
* Full code cleanup and section organization

### v1.x

* Initial release with ESX + QBCore support

## Support

| Channel | Link                                                   |
| ------- | ------------------------------------------------------ |
| Discord | [discord.gg/9yY3Jxnhh8](https://discord.gg/9yY3Jxnhh8) |
