# Crowe Libs

A unified framework abstraction layer for RedM server development that provides consistent APIs across VORP and RSG frameworks.

## 🚀 Quick Start

### Installation

1. Download to your server's resources folder
2. Add `ensure crowe_libs` to your `server.cfg`
3. Start your server

### Basic Usage

```lua
-- Get the library functions
local CroweFunctions = exports['crowe_libs']:CroweFunctions()

-- Use any function - works with both VORP and RSG
local money = CroweFunctions.getMoney(source)
CroweFunctions.AddItem(source, "bread", 5)

-- Wait for player to be fully spawned
CroweFunctions.waitForPlayerSpawn(function()
    print("Player is ready!")
    -- Your initialization code here
end)
```

## 🎮 Client-Side Functions

### Player Spawn Detection (Centralized System)

**Problem Solved**: Previously, each script created its own spawn detection logic, leading to multiple redundant threads and inconsistent detection. Crowe Libs now provides a single, efficient centralized system.

#### **Main Function**

```lua
-- Wait for player to be fully ready (works with VORP & RSG)
CroweFunctions.waitForPlayerSpawn(function()
    -- Your code here - guaranteed to be fully ready
    print("^2[YOUR SCRIPT]^7 Player spawned - initializing...")
    -- Your script initialization code here
end)
```

#### **VORP-Specific Spawn Detection**

```lua
-- Wait specifically for VORP's final spawn event (recommended for VORP)
CroweFunctions.waitForVorpSpawn(function()
    -- Your code here - guaranteed to be after VORP's complete spawn sequence
    print("^2[YOUR SCRIPT]^7 VORP spawn completed - initializing...")
    -- Your script initialization code here
end)
```

#### **Advanced Usage**

```lua
-- Check if already spawned
if CroweFunctions.isSpawnDetectionComplete() then
    -- Player is already spawned, initialize immediately
    print("^2[YOUR SCRIPT]^7 Player already spawned - initializing immediately")
    initializeScript()
else
    -- Wait for spawn
    CroweFunctions.waitForPlayerSpawn(function()
        print("^2[YOUR SCRIPT]^7 Player spawned - initializing script...")
        initializeScript()
    end)
end
```

#### **Safe Initialization (Recommended for Script Restarts)**

For scripts that might restart while the player is already spawned, use this safer approach:

```lua
-- Get Crowe Functions for spawn detection
local CroweFunctions = nil

-- Safe function to get Crowe Functions
local function getCroweFunctions()
    if not CroweFunctions then
        if GetResourceState('crowe_libs') == 'started' then
            -- Try the safer export first
            CroweFunctions = exports['crowe_libs']:getCroweFunctions()
            if not CroweFunctions then
                -- Fallback to regular export
                CroweFunctions = exports['crowe_libs']:CroweFunctions()
            end
            if CroweFunctions then
                print("^2[YOUR SCRIPT]^7 Crowe Functions initialized successfully")
            end
        else
            print("^1[YOUR SCRIPT ERROR]^7 crowe_libs not found")
        end
    end
    return CroweFunctions
end

-- Safe function to check if spawn detection is complete
local function isSpawnDetectionComplete()
    local cf = getCroweFunctions()
    if cf and cf.isSpawnDetectionComplete then
        return cf.isSpawnDetectionComplete()
    end
    return false
end

-- Safe function to wait for player spawn
local function waitForPlayerSpawn(callback)
    local cf = getCroweFunctions()
    if cf and cf.waitForPlayerSpawn then
        cf.waitForPlayerSpawn(callback)
    else
        print("^1[YOUR SCRIPT ERROR]^7 Crowe Functions not available - using fallback")
        -- Fallback: simple spawn detection
        Citizen.CreateThread(function()
            while not IsPlayerSpawned() do
                Citizen.Wait(100)
            end
            if callback then callback() end
        end)
    end
end

-- Initialize your script safely
Citizen.CreateThread(function()
    local maxWaitTime = 30000 -- 30 seconds max wait
    local waitTime = 0
    
    -- Wait for Crowe Functions to be available
    while not getCroweFunctions() and waitTime < maxWaitTime do
        Citizen.Wait(500)
        waitTime = waitTime + 500
    end
    
    if not getCroweFunctions() then
        print("^1[YOUR SCRIPT ERROR]^7 Crowe Functions not available - using fallback")
        initializeScript()
        return
    end
    
    -- Check if player is already spawned using the centralized system
    if isSpawnDetectionComplete() then
        -- Player is already spawned, initialize immediately
        print("^2[YOUR SCRIPT]^7 Player already spawned - initializing immediately")
        initializeScript()
    else
        -- Wait for spawn using the centralized system
        print("^2[YOUR SCRIPT]^7 Waiting for player spawn...")
        waitForPlayerSpawn(function()
            print("^2[YOUR SCRIPT]^7 Player spawned - initializing script...")
            initializeScript()
        end)
    end
end)
```

#### **State Checkers**

```lua
-- Check if player is completely ready
if CroweFunctions.isPlayerFullyReady() then
    -- Player is ready
end

-- Individual state checks (for debugging)
CroweFunctions.isPlayerSpawned()        -- Basic spawn check
CroweFunctions.isCharacterSelected()     -- VORP: Character selected
CroweFunctions.isCharacterInitialized()  -- VORP: Character initialized
CroweFunctions.isVorpFullySpawned()      -- VORP: Fully spawned
CroweFunctions.isPlayerInWorld()         -- In game world
CroweFunctions.isPlayerInSession()       -- VORP: In session
CroweFunctions.isSpawnDetectionComplete() -- Check if detection is complete
```

#### **Advanced Functions**

```lua
-- Force execute callbacks for late registrations
CroweFunctions.forceExecuteCallbacks()

-- Get complete spawn state
local state = CroweFunctions.getSpawnState()
```

#### **Migration Guide**

**Before (Individual Detection):**

```lua
-- Each script had its own detection logic
Citizen.CreateThread(function()
    while not IsPlayerSpawned() do
        Citizen.Wait(100)
    end
    
    -- Initialize script
    print("Player spawned - initializing...")
end)
```

**After (Centralized Detection):**

```lua
-- Use Crowe Libs centralized detection
local CroweFunctions = exports['crowe_libs']:CroweFunctions()

CroweFunctions.waitForPlayerSpawn(function()
    -- Initialize script
    print("Player spawned - initializing...")
end)
```

#### **Benefits**

1. **Performance**: Single spawn detection thread instead of multiple
2. **Consistency**: All scripts use the same spawn detection logic
3. **Reliability**: Centralized timeout and error handling
4. **Simplicity**: One-line callback registration
5. **Debugging**: Built-in state inspection functions

#### **VORP Spawn Sequence**

The system automatically handles VORP's complete spawn sequence:

1. `vorp:SelectedCharacter` - Character selection
2. `vorp:initCharacter` - Character initialization
3. `vorp_core:Client:OnPlayerSpawned` - Final spawn
4. `vorp:PlayerSpawn` - Additional spawn event
5. Session validation and world checks

### Notifications

```lua
CroweFunctions.notify("Hello World!", 5000)
```

## 🖥️ Server-Side Functions

### Player Data

```lua
CroweFunctions.getPlayer(source)      -- Get player object
CroweFunctions.getIdentifier(source)  -- Get unique ID
CroweFunctions.getFirstname(source)   -- Get first name
CroweFunctions.getLastname(source)    -- Get last name
CroweFunctions.getFullname(source)    -- Get full name
CroweFunctions.getAge(source)         -- Get age
CroweFunctions.getGender(source)      -- Get gender
CroweFunctions.isdead(source)         -- Check if dead
```

### Currency Management

```lua
-- Money
CroweFunctions.getMoney(source)           -- Get money
CroweFunctions.addMoney(source, 100)      -- Add money
CroweFunctions.removeMoney(source, 50)    -- Remove money

-- Gold
CroweFunctions.getGold(source)            -- Get gold
CroweFunctions.addGold(source, 10)        -- Add gold
CroweFunctions.removeGold(source, 5)      -- Remove gold

-- Role
CroweFunctions.getRol(source)             -- Get role
CroweFunctions.setRol(source, "moderator") -- Set role

-- Generic currency (0=money, 1=gold, 2=rol)
CroweFunctions.addCurrency(source, 0, 100)    -- Add money
CroweFunctions.removeCurrency(source, 1, 10)  -- Remove gold
```

### Job Management

```lua
CroweFunctions.getJob(source)              -- Get job name
CroweFunctions.getJobLabel(source)         -- Get job label
CroweFunctions.getJobGrade(source)         -- Get job grade
CroweFunctions.setJob(source, "police", 1) -- Set job and grade
CroweFunctions.setJobGrade(source, 2)      -- Set job grade only
```

### Group Management

```lua
CroweFunctions.getGroup(source)                    -- Get group
CroweFunctions.setGroup(source, "admin")           -- Set group
```

### Inventory Management

```lua
-- Items
CroweFunctions.AddItem(source, "bread", 5)         -- Add item
CroweFunctions.RemoveItem(source, "bread", 2)      -- Remove item
CroweFunctions.getItemByName(source, "bread")      -- Get item by name
CroweFunctions.getInventory(source)                -- Get full inventory
CroweFunctions.getItemCount(source, "bread")       -- Get item count
CroweFunctions.getItem(source, "bread")            -- Get detailed item info
CroweFunctions.canCarryItem(source, "bread", 5)    -- Check if can carry

-- Weapons
CroweFunctions.addWeapon(source, "WEAPON_REVOLVER_CATTLEMAN", 50)  -- Add weapon
CroweFunctions.removeWeapon(source, "WEAPON_REVOLVER_CATTLEMAN")   -- Remove weapon
CroweFunctions.canCarryWeapons(source, "WEAPON_REVOLVER_CATTLEMAN", 1) -- Check if can carry weapon
```

### Notifications

```lua
CroweFunctions.notify(source, "You received $100!", 5000)
```

### Utility Functions

```lua
CroweFunctions.getUserByCid(12345)  -- Get user by character ID
```

## 📝 Usage Examples

### Complete Player Management Script

```lua
local CroweFunctions = exports['crowe_libs']:CroweFunctions()

RegisterNetEvent('givePlayerMoney')
AddEventHandler('givePlayerMoney', function()
    local source = source
    local player = CroweFunctions.getPlayer(source)
    
    if player then
        local currentMoney = CroweFunctions.getMoney(source)
        CroweFunctions.addMoney(source, 100)
        CroweFunctions.notify(source, "You received $100! Total: $" .. (currentMoney + 100), 5000)
    end
end)
```

### Inventory Management

```lua
RegisterNetEvent('givePlayerItem')
AddEventHandler('givePlayerItem', function(item, amount)
    local source = source
    
    if CroweFunctions.canCarryItem(source, item, amount) then
        CroweFunctions.AddItem(source, item, amount)
        CroweFunctions.notify(source, "You received " .. amount .. "x " .. item, 3000)
    else
        CroweFunctions.notify(source, "You cannot carry that many items!", 3000)
    end
end)
```

### Client-Side Initialization

```lua
local CroweFunctions = exports['crowe_libs']:CroweFunctions()

-- Wait for player to be fully spawned
CroweFunctions.waitForPlayerSpawn(function()
    print("Player is ready!")
    
    -- Initialize your script here
    TriggerEvent('myScript:initialize')
end)
```

### Multiple Initialization Callbacks

```lua
-- Register multiple callbacks - all execute when player is ready
CroweFunctions.waitForPlayerSpawn(function()
    -- Initialize UI
end)

CroweFunctions.waitForPlayerSpawn(function()
    -- Load player data
end)

CroweFunctions.waitForPlayerSpawn(function()
    -- Set up events
end)
```

## 🔧 Framework Compatibility

### VORP Framework

* ✅ Complete spawn sequence handling
* ✅ Character selection and initialization
* ✅ All inventory and currency functions
* ✅ Job and group management

### RSG Framework

* ✅ Standard spawn detection
* ✅ All inventory and currency functions
* ✅ Job and group management

### Automatic Detection

The library automatically detects your framework and adapts accordingly.

## 📚 API Reference

### Client Functions

| Function                       | Description                    | Returns   |
| ------------------------------ | ------------------------------ | --------- |
| `waitForPlayerSpawn(callback)` | Wait for player spawn          | `void`    |
| `waitForVorpSpawn(callback)`   | Wait for VORP spawn completion | `void`    |
| `isPlayerFullyReady()`         | Check if player is ready       | `boolean` |
| `isSpawnDetectionComplete()`   | Check if detection is complete | `boolean` |
| `isPlayerSpawned()`            | Check if spawned               | `boolean` |
| `isCharacterSelected()`        | VORP: Character selected       | `boolean` |
| `isCharacterInitialized()`     | VORP: Character initialized    | `boolean` |
| `isVorpFullySpawned()`         | VORP: Fully spawned            | `boolean` |
| `isPlayerInWorld()`            | Check if in world              | `boolean` |
| `isPlayerInSession()`          | VORP: In session               | `boolean` |
| `getSpawnState()`              | Get complete spawn state       | `table`   |
| `notify(text, duration)`       | Send notification              | `void`    |

### Server Functions

| Function                            | Description       | Returns   |
| ----------------------------------- | ----------------- | --------- |
| `getPlayer(source)`                 | Get player object | `object`  |
| `getMoney(source)`                  | Get money         | `number`  |
| `addMoney(source, amount)`          | Add money         | `void`    |
| `AddItem(source, item, amount)`     | Add item          | `boolean` |
| `getJob(source)`                    | Get job           | `string`  |
| `setJob(source, job, grade)`        | Set job           | `void`    |
| `notify(source, message, duration)` | Send notification | `void`    |

## 🆘 Troubleshooting

### Common Issues

**Script runs before player is ready:**

```lua
-- ✅ Solution: Use waitForPlayerSpawn
CroweFunctions.waitForPlayerSpawn(function()
    -- Your code here
end)
```

**Script restart causes nil value error:**

```lua
-- ✅ Solution: Use safe initialization
local function getCroweFunctions()
    if not CroweFunctions then
        if GetResourceState('crowe_libs') == 'started' then
            CroweFunctions = exports['crowe_libs']:getCroweFunctions()
            if not CroweFunctions then
                CroweFunctions = exports['crowe_libs']:CroweFunctions()
            end
        end
    end
    return CroweFunctions
end
```

## 📄 License

MIT License - see LICENSE file for details.

***

**Made with ❤️ by Crowe**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://crowescripts.gitbook.io/documentation/about-our-scripts/redm-script-documentation/crowe-libs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
