> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shdevelopment.org/llms.txt
> Use this file to discover all available pages before exploring further.

# sh-doorlocks: Door Lock Management System for RedM

> sh-doorlocks manages door lock states in RedM with a NUI admin panel, ACE access rules, lockpicking minigame, police alerts, and Discord logging.

sh-doorlocks is a server-authoritative door lock and unlock system for RedM. Every door state is stored in your server database and synced to all connected clients in real time, so there are no client-side de-sync issues when players lock or unlock a door. A full NUI admin panel handles all CRUD operations without requiring manual database edits, and per-door access rules let you restrict locks to specific jobs, grades, groups, characters, or key items. If you run sh-housing, sh-doorlocks provides the physical door management layer that housing needs to control property access.

## Overview

| Detail        | Value                               |
| ------------- | ----------------------------------- |
| Frameworks    | VORP, RSG, `auto`                   |
| Admin Panel   | `/doorlocks`                        |
| Door Creation | `/addDoor`, `/delDoor`, `/doorhash` |

## Features

<Accordion title="Admin NUI Panel">
  Open the full door management interface with `/doorlocks`. From the panel you can create new door records, edit existing ones, set access rules, enable lockpicking, configure auto-relock timers, and delete doors — all without touching the database directly.
</Accordion>

<Accordion title="In-World Door Creation">
  Use `/addDoor` while aiming at a door and fire to register it. The command captures the door's hash and coordinates automatically. Use `/doorhash` to read the hash of any door you are aiming at without creating a record, and `/delDoor` to remove a registered door by ID.
</Accordion>

<Accordion title="Access Rules">
  Set per-door access rules using any combination of:

  * **Job + grade** — restrict to a specific job at or above a minimum grade
  * **Group** — restrict to a named server group (ACE group)
  * **Gang** — restrict to a named gang affiliation
  * **Character ID** — restrict to a specific character record
  * **Key item** — require players to carry a specific inventory item to interact
</Accordion>

<Accordion title="Lockpicking">
  Enable lockpicking on a per-door basis. Choose your minigame provider from the supported list: bcc-minigames, sh-lockpick, SS-Lockpick, gum\_lockpick, or `auto` to detect whichever is running. Players without access can attempt to pick the lock using the configured minigame.
</Accordion>

<Accordion title="Police Alerts">
  When lockpicking is attempted or succeeds on an alerted door, sh-doorlocks notifies on-duty law enforcement roles. Configure whether the alert triggers on a successful pick or on the attempt itself. Alerts include the door's location so officers can respond.
</Accordion>

<Accordion title="Double-Door Sync">
  Link two door IDs together so locking or unlocking one automatically applies the same state to its paired door. Useful for saloon swing doors, barn gates, and any entrance with matching left and right panels.
</Accordion>

<Accordion title="Auto-Relock">
  Set a per-door relock timer in seconds. After the timer expires following an unlock, the door automatically locks itself again. Set to `0` to disable auto-relock on a given door.
</Accordion>

<Accordion title="Restart State Policy">
  Control what happens to door states when the server restarts. Choose from three policies:

  * `lock_all` — every door locks on restart regardless of its previous state
  * `default_locked` — doors revert to their configured default state
  * `persist_last_state` — doors restore the exact state they were in before the restart
</Accordion>

<Accordion title="Database Importers">
  Migrate existing door records from bcc-doorlocks, vorp\_doorlocks, or rsg-doorlock using the included import scripts. Run the importer once after installation to carry over your existing door configurations.
</Accordion>

<Accordion title="Discord Logging">
  Log admin panel actions, door state changes, lockpick attempts, and security violations (such as players trying to access restricted doors) to a Discord webhook. Each log entry includes the relevant player name, door ID, and timestamp.
</Accordion>

## Installation

<Steps>
  <Step title="Add the resource">
    Place the `sh-doorlocks` folder inside your server's `resources` directory.
  </Step>

  <Step title="Import the database schema">
    Run `sh-doorlocks/sql/install.sql` against your server database to create the doors table.
  </Step>

  <Step title="Set your framework">
    Open `config.lua` and set `Config.Framework` to `'vorp'`, `'rsg'`, or `'auto'` to match your server.
  </Step>

  <Step title="Configure admin access">
    Set `Config.AdminAccess.ace` to the ACE permission string your admins hold (`shdoorlocks.admin` by default), or add job entries to `Config.AdminAccess.jobs` if you prefer job-based admin access.
  </Step>

  <Step title="Choose a lockpick provider">
    If you want lockpicking enabled, set `Config.Lockpick.defaultProvider` to the minigame resource you are running, or leave it as `'auto'` to detect automatically.
  </Step>

  <Step title="Set your restart policy">
    Choose a `Config.RestartStatePolicy` value that fits your server's expectations. `'default_locked'` is recommended for most servers.
  </Step>

  <Step title="Configure logging">
    Set `Config.Logging.enabled` to `true` and add your Discord webhook URL to `Config.Logging.webhook` to enable logging.
  </Step>

  <Step title="Start the resource">
    Add `ensure sh-doorlocks` to your `server.cfg` and restart your server.
  </Step>
</Steps>

## Configuration

```lua config.lua theme={null}
Config.Framework          = 'auto'            -- 'auto', 'vorp', 'rsg'
Config.RestartStatePolicy = 'default_locked'  -- 'lock_all', 'default_locked', 'persist_last_state'

Config.Interaction = {
    Distance      = 1.8,           -- Distance to interact with door
    ToggleKey     = 0x760A9C6F,    -- G — lock/unlock
    LockpickKey   = 0xCEFD9220,    -- E — lockpick attempt
}

Config.AdminAccess = {
    ace   = 'shdoorlocks.admin',
    groups = { 'admin', 'god' },
    jobs  = {
        -- police = 0,
    },
}

Config.Lockpick = {
    defaultProvider = 'auto',  -- auto, bcc-minigames, sh-lockpick, SS-Lockpick, gum_lockpick
}

Config.Alerts = {
    enabled     = true,
    triggerOn   = 'success',   -- 'success' or 'attempt'
    requireDuty = true,
    lawRoles    = { 'police', 'sheriff', 'marshal' },
}

Config.Logging = {
    enabled  = false,
    webhook  = '',   -- Discord webhook URL
    username = 'Doorlocks Ledger',
}
```

## Server Exports

Use these exports to manage doors from other resources running on your server.

```lua theme={null}
-- Create a new door record; returns the new door record or false on failure
exports['sh-doorlocks']:CreateDoor(payload)

-- Set a door's locked state; returns true on success
exports['sh-doorlocks']:SetDoorState(doorId, locked)

-- Get a single door's full record; returns a table or nil
exports['sh-doorlocks']:GetDoor(doorId)

-- Get all doors matching an optional filter table; returns a table array
exports['sh-doorlocks']:GetDoors(filter)
```

## Client Exports

Use these exports from client-side scripts to read nearby door state.

```lua theme={null}
-- Get the ID of the nearest registered door within maxDistance units
exports['sh-doorlocks']:GetNearbyDoor(maxDistance)  -- returns doorId or nil

-- Request the current locked state of a specific door
exports['sh-doorlocks']:RequestDoorState(doorId)    -- returns bool or nil
```

<Note>
  sh-housing uses sh-doorlocks for property door management. If you run both resources, start sh-doorlocks first in your `server.cfg` so sh-housing can find the provider at initialisation.
</Note>

## Adding Doors In-World

<Tabs>
  <Tab title="/addDoor">
    Aim your crosshair at the door model you want to register, then type `/addDoor` and press enter. The command fires a ray from your camera to detect the door hash and capture its world position. A confirmation message shows the new door ID once the record is saved.
  </Tab>

  <Tab title="/doorhash">
    Aim at any door and run `/doorhash` to read its model hash without creating a database record. Use this to verify you are targeting the correct door model before adding it.
  </Tab>

  <Tab title="/delDoor">
    Run `/delDoor [doorId]` to permanently delete a door record from the database. The door returns to its default game state immediately on all connected clients.
  </Tab>
</Tabs>

<Warning>
  Deleting a door record that is linked to a sh-housing property will break access control for that property. Always remove housing-managed doors through the sh-housing admin panel rather than directly through `/delDoor`.
</Warning>

<Tip>
  Use the `persist_last_state` restart policy during active roleplay events so doors left open for a scene stay open after a server restart. Switch back to `default_locked` for normal server operation.
</Tip>
