# Extending Players

BDSC supports dynamic, runtime-safe extension of player objects with custom data and methods. \
This lets you build modular logic without touching the core.

***

## Add Data

Adds custom data to the player. Optionally replicated to the client.

**Example**

```lua
player:add_data("stats", { health = 100, stamina = 50 }, true)
```

**Parameters**

* `stats` – key name for the data entry.
* `{ health = 100, stamina = 50 }` – table to store.
* `true` – replicate to client (optional).

***

## Add Methods

Attach custom methods that can later be executed via `run_method`.

**Example**

```lua
player:add_method("stats", "get_health", function(self)
    local stats = self:get_data("stats")
    return stats and stats.health or 0
end)
```

**Usage**

```lua
player:run_method("stats", "get_health")
```

***

## Update Data

Modify an existing data block and optionally sync it.

**Example**

```lua
local stats = player:get_data("stats")
stats.health = stats.health - 10
player:set_data("stats", stats, true)
```

***

## Remove Data

Removes a data key.

**Example**

```lua
player:remove_data("job")
```

***

## Sync Data

Force a sync of all replicated data to the client.

**Example**

```lua
player:sync_data()
```

***

## Dump All Data

Logs all currently stored player data.

**Example**

```lua
local data = player:get_data()
for k, v in pairs(data) do
    print(k .. ": " .. json.encode(v))
end
```

***

Here’s a concise section explaining how the `on_save` and `on_destroy` methods work when registered via namespaces:

***

## Lifecycle Hooks

BDSC supports **namespace-specific lifecycle methods** that automatically run when certain player events occur.

These methods are defined under a specific namespace using `add_method` and are called internally during key moments in the player's lifecycle.

#### Supported Hooks

| Hook Name    | Description                                                                 |
| ------------ | --------------------------------------------------------------------------- |
| `on_save`    | Called when `player:save()` is triggered (e.g., on disconnect or manually). |
| `on_destroy` | Called when `player:destroy()` is triggered (e.g., on player drop).         |

#### How It Works

When a lifecycle event runs, BDSC will automatically check each namespace for these methods and call them in order.

#### Example

```lua
player:add_method("stats", "on_save", function(self)
    print("Saving stats for player:", self.meta.username)
end)

player:add_method("inventory", "on_destroy", function(self)
    print("Cleaning up inventory for:", self.meta.username)
end)
```

You **do not** need to manually call these - BDSC will handle them as long as they are added under a namespace using `add_method(namespace, "on_save" | "on_destroy", fn)`.

> Avoid long-running operations inside these hooks — keep them lightweight for stability.

***

## Missing Method Safety

Calling a non-existent method will return `nil`.

**Example**

```lua
local result = player:run_method("missing")
print("Result:", result or "nil")
```

***

## No Internal Access

Only the `meta` table is exposed. All other functionality data, methods, syncing - must be accessed via the public API.

Direct access to `_extensions._data`, `_extensions._methods`, or other internals is not possible and never required.

***

## Quick Example

Below is a minimal, self-contained example that demonstrates how to create a player object and extend it externally using basic commands.

This includes:

* Adding a `stats` data block
* Defining custom methods (like `get_health`)
* Syncing data to the client
* Simulating damage
* Handling save logic

```lua
local core = exports.bdsc:import()

-- Create and initialize a new player
RegisterCommand("make", function(src)
    local player = core.create_player(src)
    if not player then print("Failed to create player") return end

    -- Add initial stats
    player:add_data("stats", {
        health = 100,
        stamina = 50
    }, true)

    -- Add method to retrieve health
    player:add_method("stats", "get_health", function(self)
        local stats = self:get_data("stats")
        return stats and stats.health or 0
    end)

    -- Add save lifecycle method
    player:add_method("stats", "on_save", function()
        print("saving player stats")
    end)

    print("Player created and methods attached.")
end, false)

-- Check player health
RegisterCommand("hp", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local hp = player:run_method("stats", "get_health")
    print("Health:", hp)
end, false)

-- Apply damage to the player
RegisterCommand("damage", function(src, args)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local amount = tonumber(args[1]) or 10
    local stats = player:get_data("stats")
    stats.health = stats.health - amount

    player:set_data("stats", stats, true)
    print("Damaged player for", amount)
end, false)
```

> **Tip:** Every player object supports runtime extension. You can safely add new data or methods at any point — even after creation.
