# Exports & Events

### <mark style="color:yellow;">Client Exports</mark>

#### Close Crafting UI

```lua
exports['devhub_crafting']:Close()
```

Closes the crafting UI if it's currently open. Useful for:

* Closing crafting when player dies
* Closing when player enters a vehicle
* Integration with other UI systems

**Example Usage:**

```lua
-- Close crafting when player dies
AddEventHandler('esx:onPlayerDeath', function()
    exports['devhub_crafting']:Close()
end)

-- Close crafting when entering vehicle
AddEventHandler('baseevents:enteredVehicle', function()
    exports['devhub_crafting']:Close()
end)
```

***

### <mark style="color:yellow;">Server Events</mark>

#### Community Project Completed

```lua
AddEventHandler("devhub_crafting:server:communityProjectCompleted", function(projectId, projectData)
    -- projectId: string - Unique ID of the completed project
    -- projectData: table - Full project configuration data
end)
```

Triggered when a community project reaches 100% completion. Use this to:

* Give rewards to players
* Send Discord announcements
* Trigger other server events
* Log completion statistics

**Example Usage:**

```lua
AddEventHandler("devhub_crafting:server:communityProjectCompleted", function(projectId, projectData)
    print("[Crafting] Community project completed: " .. projectData.label)
    
    -- Reward all online players
    local players = GetPlayers()
    for _, playerId in ipairs(players) do
        local xPlayer = ESX.GetPlayerFromId(playerId)
        if xPlayer then
            xPlayer.addMoney(5000)
            TriggerClientEvent('esx:showNotification', playerId, 
                'Community project "' .. projectData.label .. '" completed! You received $5,000!')
        end
        Wait(100)
    end
    
    -- Discord webhook notification
    PerformHttpRequest("YOUR_WEBHOOK_URL", function() end, "POST", 
        json.encode({
            content = "🎉 Community Project **" .. projectData.label .. "** has been completed!"
        }), 
        {["Content-Type"] = "application/json"}
    )
end)
```

***

### <mark style="color:yellow;">Client Function Hooks</mark>

Define these in `configs/client.lua` to hook into crafting events:

#### CanOpenCrafting

```lua
Config.OpenFunctions["CanOpenCrafting"] = function(stationId)
    -- Validate if player can open crafting
    -- Return false to prevent opening
    
    local playerPed = PlayerPedId()
    
    -- Example: Prevent opening if dead
    if IsEntityDead(playerPed) then
        return false
    end
    
    -- Example: Prevent opening in vehicle
    if IsPedInAnyVehicle(playerPed, false) then
        return false
    end
    
    -- Example: Check job requirement
    local job = ESX.GetPlayerData().job.name
    if stationId == "illegal_crafting" and job ~= "criminal" then
        return false
    end
    
    return true
end
```

#### OnCraftingOpened

```lua
Config.OpenFunctions["OnCraftingOpened"] = function(stationId)
    -- Called when crafting UI opens
    
    -- Example: Play animation
    local playerPed = PlayerPedId()
    TaskStartScenarioInPlace(playerPed, "PROP_HUMAN_BUM_BIN", 0, true)
    
    -- Example: Disable controls
    LocalPlayer.state:set('isCrafting', true, true)
end
```

#### OnCraftingClosed

```lua
Config.OpenFunctions["OnCraftingClosed"] = function(stationId)
    -- Called when crafting UI closes
    
    -- Example: Stop animation
    local playerPed = PlayerPedId()
    ClearPedTasks(playerPed)
    
    -- Example: Re-enable controls
    LocalPlayer.state:set('isCrafting', false, true)
end
```

#### OnCraftStart

```lua
Config.OpenFunctions["OnCraftStart"] = function(stationId, recipeId, amount)
    -- Called when player starts crafting
    
    -- Example: Log crafting activity
    print(string.format("Player started crafting %s x%d at %s", recipeId, amount, stationId))
    
    -- Example: Play sound effect
    PlaySoundFrontend(-1, "PICK_UP", "HUD_FRONTEND_DEFAULT_SOUNDSET", true)
end
```

***

### <mark style="color:yellow;">Server Function Hooks</mark>

Define these in `configs/server.lua` to hook into server-side events:

#### CanCraft

```lua
Config.ServerFunctions["CanCraft"] = function(source, stationId, recipeId, amount)
    -- Validate if player can craft this item
    -- Return false to prevent crafting
    
    local xPlayer = ESX.GetPlayerFromId(source)
    
    -- Example: Job requirement for certain recipes
    if recipeId:find("illegal") and xPlayer.job.name ~= "criminal" then
        TriggerClientEvent('esx:showNotification', source, "You don't have access to this recipe!")
        return false
    end
    
    -- Example: Level requirement
    local playerLevel = GetPlayerLevel(source) -- Your level system
    if recipeId:find("advanced") and playerLevel < 10 then
        TriggerClientEvent('esx:showNotification', source, "You need level 10 to craft this!")
        return false
    end
    
    return true
end
```

#### OnCraftStart

```lua
Config.ServerFunctions["OnCraftStart"] = function(source, stationId, recipeId, amount)
    -- Called when crafting begins (server-side)
    
    -- Example: Log to database
    MySQL.insert('INSERT INTO crafting_logs (player_id, recipe, amount, station, timestamp) VALUES (?, ?, ?, ?, NOW())',
        {GetPlayerIdentifier(source), recipeId, amount, stationId})
    
    -- Example: Discord logging
    local xPlayer = ESX.GetPlayerFromId(source)
    SendDiscordLog("Crafting", string.format("%s started crafting %s x%d", xPlayer.getName(), recipeId, amount))
end
```

#### OnAttachmentsApplied

```lua
Config.ServerFunctions["OnAttachmentsApplied"] = function(source, weaponModel, weaponSlot, attachmentsApplied)
    -- Called when attachments are saved to a weapon
    -- attachmentsApplied = { {category="scope", component="COMPONENT_...", label="Macro Scope"}, ... }
    
    local xPlayer = ESX.GetPlayerFromId(source)
    
    -- Example: Log attachment changes
    for _, att in ipairs(attachmentsApplied) do
        print(string.format("[Attachments] %s applied %s to %s", 
            xPlayer.getName(), att.label, weaponModel))
    end
    
    -- Example: Achievement tracking
    local totalAttachments = #attachmentsApplied
    if totalAttachments >= 5 then
        GiveAchievement(source, "weapon_customizer")
    end
end
```

#### CanUnlockBlueprint

```lua
Config.ServerFunctions["CanUnlockBlueprint"] = function(source, blueprintId)
    -- Validate if player can unlock this blueprint
    -- Return false to prevent unlocking
    
    local xPlayer = ESX.GetPlayerFromId(source)
    
    -- Example: Reputation requirement
    local rep = GetPlayerReputation(source, "crafting")
    local requiredRep = {
        ["legendary_weapon_bp"] = 1000,
        ["epic_armor_bp"] = 500,
    }
    
    if requiredRep[blueprintId] and rep < requiredRep[blueprintId] then
        TriggerClientEvent('esx:showNotification', source, 
            "You need " .. requiredRep[blueprintId] .. " crafting reputation!")
        return false
    end
    
    return true
end
```

#### OnBlueprintUnlocked

```lua
Config.ServerFunctions["OnBlueprintUnlocked"] = function(source, blueprintId)
    -- Called when a blueprint is successfully unlocked
    
    local xPlayer = ESX.GetPlayerFromId(source)
    
    -- Example: Give XP
    AddPlayerXP(source, "crafting", 100)
    
    -- Example: Discord announcement for rare blueprints
    local blueprint = Shared.Blueprints[blueprintId]
    if blueprint and blueprint.rarity == "legendary" then
        SendDiscordAnnouncement(string.format("🎉 %s unlocked the legendary blueprint: %s!", 
            xPlayer.getName(), blueprint.label))
    end
    
    -- Example: Achievement
    local unlockedCount = GetPlayerBlueprintCount(source)
    if unlockedCount >= 50 then
        GiveAchievement(source, "blueprint_collector")
    end
end
```

***

### <mark style="color:yellow;">Integration Examples</mark>

#### Close Crafting on Death (ESX)

```lua
-- client-side
AddEventHandler('esx:onPlayerDeath', function(data)
    exports['devhub_crafting']:Close()
end)
```

#### Close Crafting on Death (QBCore)

```lua
-- client-side
RegisterNetEvent('hospital:client:SetDeathState', function()
    exports['devhub_crafting']:Close()
end)
```

#### Job-Restricted Crafting Station

```lua
-- configs/client.lua
Config.OpenFunctions["CanOpenCrafting"] = function(stationId)
    if stationId == "police_armory" then
        local job = ESX.GetPlayerData().job.name
        return job == "police" or job == "sheriff"
    end
    return true
end
```

#### VIP Queue Size Bonus

```lua
-- configs/shared.lua (in crafting station config)
queueSize = {
    amount = 3,
    custom = function(source)
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer.getGroup() == "vip" then
            return 6  -- VIP gets 6 queue slots
        end
        return nil  -- Use default (3)
    end
}
```

#### Discord Webhook for Craft Completion

```lua
-- configs/server.lua
-- Player sets their webhook in UI settings, handled automatically

-- For server-wide logging:
Config.ServerFunctions["OnCraftStart"] = function(source, stationId, recipeId, amount)
    local xPlayer = ESX.GetPlayerFromId(source)
    PerformHttpRequest("YOUR_WEBHOOK", function() end, "POST",
        json.encode({
            embeds = {{
                title = "Craft Started",
                description = string.format("**%s** started crafting **%s** x%d", 
                    xPlayer.getName(), recipeId, amount),
                color = 3447003
            }}
        }),
        {["Content-Type"] = "application/json"}
    )
end
```


---

# 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://docs.devhub.gg/crafting/exports-and-events.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.
