Skip to content

Installation

This guide will walk you through installing prp-housing-v2 on your FiveM server.

Installation steps

1
Download the resources
Download prp-housing-v2 from the CFX portal and place it in your resources folder. You also need the Housing Pack shells (prp-housing-loader, prp-housing-main, prp-housing) for interior assets.
2
Add to server.cfg
Add the resources in this order after dependencies:
ensure ox_lib
ensure oxmysql
ensure ox_doorlock
ensure prp-bridge
ensure prp-housing-loader
ensure prp-housing-main
ensure prp-housing
ensure prp-housing-v2
ox_doorlock must start before prp-housing-v2.
3
Doors (ox_doorlock)
Exterior property doors are registered in ox_doorlock automatically on first start — see ox_doorlock below.
4
Database
Tables are created automatically on first server start — see Database below.
5
Add items
Add the required items to your inventory resource — see Items below.
6
Configure
Review config/sh_config.lua and config/sv_config.lua — see Configuration.

Housing shells

The housing script does not include MLO interiors. Install the Housing Pack separately and ensure shell resources start before prp-housing-v2.

Garages

Start one supported garage resource before or with housing. Detection and placement: Garages. Almost all Lua is editable — Editable files.

Items

Add all required items to your inventory resource. HousingBridge.registerItem is a no-op in the bridge adapter — items must be defined manually.

lockpick

If your inventory already ships with a default lockpick item (common with ox_inventory), remove that entry before using the definitions below. The default item registers its own use handler (e.g. vehicle lockpicking) and will conflict with housing door lockpicking.

Add the items below to your ox_inventory into data/items.lua.

lua
["placeable_furniture_item"] = {
    label = "Placeable Furniture",
    weight = 1000,
    stack = false,
    close = true,
    consume = 0,
},

["collectable_furniture_item"] = {
    label = "Collectable Furniture",
    weight = 1000,
    stack = false,
    close = true,
    consume = 0,
},

["master_key"] = {
    label = "Master Key",
    weight = 200,
    stack = false,
},

["lockpick"] = {
    label = "Lockpick",
    weight = 160,
},

["improved_lockpick"] = {
    label = "Improved Lockpick",
    weight = 160,
},

["expert_lockpick"] = {
    label = "Expert Lockpick",
    weight = 160,
},

Real estate tablet

If you enable the real estate panel (Config.RealEstate), add the usable item that opens it (default name realtor_tablet). Example for ox_inventory (data/items.lua):

lua
["realtor_tablet"] = {
    label = "Realtor Tablet",
    weight = 1000,
    stack = false,
    close = true,
},

Only players allowed by Config.HousingStaff (mode + jobs) and admins (when mode permits) can open the panel — using the item without permission shows an error. Staff can also use the real estate radial when enabled. See Real estate.

Furniture item metadata

When a player receives a placeable_furniture_item or collectable_furniture_item, the item metadata must include:

FieldTypeDescription
modelstringProp model name (e.g. pr_furniture_chair_01)
categorystringFurniture category from the catalog

Using the item from inventory opens the placement mode for that model.

ox_doorlock

Exterior property doors use ox_doorlock (same pattern as prp-outposts). Doors are locked by default (state: 1). Lock/unlock from housing target options calls ToggleHousingDoor in server/editable.lua.

Automatic registration

On first start (and when new locations are created), prp-housing-v2 inserts missing rows into ox_doorlock:

FieldValue
Namehousing_loc_{locationId}_door_{doorIndex}
Datamodel, coords, heading, state: 1, hideUi: true, lockpick: false

Example for location 403:

sql
INSERT INTO `ox_doorlock` (`name`, `data`) VALUES (
    'housing_loc_403_door_1',
    '{"model":-514036887,"coords":{"x":960.0,"y":-669.0,"z":58.0},"heading":0.0,"state":1,"maxDistance":2,"hideUi":true,"doors":false,"lockpick":false,"autolock":false}'
);

A reference file is included at sql/housing_doorlock.sql. After bulk inserts on first boot, the script reloads ox_doorlock once so clients receive all doors.

Configuration

In config/sh_config.lua:

lua
Config.Doorlock = {
    enabled = true,
    openOnAccess = true,
    namePrefix = "housing_loc",
    maxDistance = 2.0,
    hideUi = true,
    holdOpen = true,
    defaultHeading = 0.0,
    autolock = false,
}
OptionDefaultDescription
enabledtrueSync exterior doors to ox_doorlock
openOnAccesstrueWith access, door interact opens the door

Set enabled = false only if you use a custom lock handler in server/editable.lua without ox_doorlock.

Custom lock resource

Swap only ToggleHousingDoor(doorName, doorState) in server/editable.luadoorState is true locked / false unlocked.

Database

The required tables are created automatically on first server start:

sql
CREATE TABLE IF NOT EXISTS `world_housing_locations` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `property_name` VARCHAR(100) NULL,
    `property_price` INT DEFAULT 0 NULL,
    `property_zone` LONGTEXT NULL,
    `property_settings` LONGTEXT NULL,
    `interior_type` VARCHAR(16) DEFAULT 'mlo' NOT NULL,
    `shell_id` VARCHAR(128) NULL,
    `exterior_coords` LONGTEXT NULL,
    `interior_location` LONGTEXT DEFAULT '{}' NULL,
    `doors` LONGTEXT NULL,
    `property_seller` VARCHAR(50) NULL,
    `property_sale_data` LONGTEXT NULL,
    `time_added` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NULL
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE IF NOT EXISTS `world_housing` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `location_id` INT NOT NULL,
    `owner` VARCHAR(50) NOT NULL,
    `upgrades` LONGTEXT DEFAULT '{}' NOT NULL,
    `settings` LONGTEXT DEFAULT '{}' NULL,
    `time_bought` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NULL
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE IF NOT EXISTS `world_housing_access` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `housing_id` INT NOT NULL,
    `stateId` VARCHAR(50) NOT NULL,
    `character_privileges` LONGTEXT DEFAULT '{}' NOT NULL,
    `access_gained` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NULL,
    FOREIGN KEY (`housing_id`) REFERENCES `world_housing` (`id`) ON DELETE CASCADE
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE IF NOT EXISTS `world_housing_furniture` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `housing_id` INT NULL,
    `placer_id` VARCHAR(50) NULL,
    `furniture_data` LONGTEXT NOT NULL,
    `furniture_health` INT DEFAULT 100 NOT NULL,
    `furniture_placed` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NULL,
    FOREIGN KEY (`housing_id`) REFERENCES `world_housing` (`id`) ON DELETE CASCADE
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE IF NOT EXISTS `world_housing_garages` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `housing_id` INT NULL,
    `access_location` LONGTEXT NOT NULL,
    `vehicle_location` LONGTEXT NULL,
    `time_created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NULL,
    FOREIGN KEY (`housing_id`) REFERENCES `world_housing` (`id`) ON DELETE CASCADE
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

Additional tables (world_housing_mdt, world_housing_rent, etc.) are created on first start. A full reference schema is in sql/housing_schema.sql.

The interior_type, shell_id, and exterior_coords columns drive shell properties (interior_type = 'shell'). MLO properties keep the default 'mlo'.

Fresh install only

prp-housing-v2 creates its tables from scratch and ships no migrations. The schema is treated as greenfield: if you change it during setup, drop and recreate the housing tables rather than expecting an in-place upgrade.