Server functions that reach players and staff: notifications, sounds, client state, Discord logs and the version check.

## Notifications

```lua
ObsiLib.notify(source, {
    title = 'Garage', description = 'Vehicle **stored**.', type = 'success', -- success | error | warning | info
    duration = 5000, tag = 'Pillbox', position = 'top-right',
})
ObsiLib.notify(-1, 'Server restart in 5 minutes')   -- string shorthand, -1 = everyone
```

Descriptions support `**bold**`, `` `mono` `` and line breaks.
HTML is shown as plain text.

:::frame{caption="The four notification types"}
![Notifications](/media/lib/notify.webp)
:::

## Sounds

```lua
ObsiLib.sound.play(source, 'door_bell', { volume = 0.6 })
ObsiLib.sound.play(-1, 'nui://my_script/sounds/siren.ogg', { id = 'siren', loop = true })
ObsiLib.sound.playAt(vec3(200.0, -800.0, 31.0), 'alarm', { range = 40.0, id = 'bank_alarm', loop = true })
ObsiLib.sound.stop(-1, 'bank_alarm')
```

Short names play `web/build/sounds/<name>.mp3`; put the files in `web/public/sounds` before building the UI.

## Client state

Push a value that the client reads with `ObsiLib.state.get`:

```lua
ObsiLib.state.push(source, 'gangZone', 'grove')
```

## Discord logs

```lua
-- 'main' is a key in editable/server/webhooks.lua; a raw https URL works too.
ObsiLib.log('main', {
    title = 'Item sold', message = 'A player sold an item.', color = 0xE5122E,
    fields = {
        { name = 'Item', value = 'gold_bar', inline = true },
        { name = 'Price', value = '$800', inline = true },
    },
})
ObsiLib.webhook.send('main', { content = 'Raw payload' })
```

Messages are queued and spaced out, so bursts do not hit Discord's rate limit.

## Version check

Obsi Lib checks its own releases on start (`ObsiConfig.VersionCheck`).
Your resources can reuse it:

```lua
ObsiLib.checkVersion(GetCurrentResourceName(), 'Obsi-Scripts/obsi-whatever')
```
