From the reference libraryRedM

:house: Housing

jo_libs/redm_scripts/housing.md

:house: Housing

Documentation related to the jo_housing script.

:::: tabs ::: tab BUY Buy it on the shop :::

::: tab PREVIEW <iframe width="560" height="315" src="https://www.youtube.com/embed/1A_ubufvh80?si=Vrw1RxNKarTFS2_6" title="RedM - Housing" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> ::: ::::

1. Installation

jo_housing works on all frameworks compatible with jo_libs (the list).

To install jo_housing :

  • Download the library: jo_libs
  • Unzip the folder and drop it in your resource folder
  • Download jo_housing from your account
  • Unzip the folder and drop both jo_housing AND jo_housing_interiors in your resource folder
  • Add those two ensure in your server.cfg :
    • ensure jo_libs
    • ensure jo_housing (jo_housing_interiors will be automatically ensured)
  • This script uses the raw keys module, if you have any problem with the prompts you should set the keyboard layout
  • If you want to use house key item, you must add House Key Item in your inventory:

:::: tabs

::: tab Example : VORP Framework Add this item configuration into your items database table:

INSERT INTO `items` (`item`, `label`, `limit`, `can_remove`, `type`, `usable`, `groupId`, `desc`, `weight`)
VALUES ('house_key', 'House key', '10', '1', 'item_standard', '0', '1', 'A house key', '0.10');

:::

::: tab Example : RSG Framework V2 Add this item configuration to your RSG items file:

house_key = { 
    name = "house_key", 
    label = "A house key", 
    weight = 0, 
    type = "item", 
    image = "your_key_image.png", 
    unique = true, 
    useable = false, 
    shouldClose = false, 
    description = "A house key" 
}

:::

::::

Congratulation, the Housing script is ready to be used! :::warning Be sure you have oxmysql ensure in your server.cfg ::: :::tip πŸ’‘ No SQL File The script automatically creates all necessary database tables during its first startup ::: :::danger πŸ”— Script requirements Stable, Wagon and Wardrobe are not included. You have to link them with your other scripts. :::

2. Usage > Create / Update / Delete houses

The housing system provides an admin interface to manage properties on your server.

[!NOTE] jo_housing supports three distinct types of properties: MLOs, Zones and Shells. MLO and Zone houses are physically part of the game world, so they now share the same outside flow for buying, door interactions, inside/outside detection and build mode. In contrast, Shell houses use pre-defined propset interiors that exist separately from the main map. When a player enters a Shell house, they are teleported from the front door to an instance of the interior, which the script spawns on demand.

:::tip πŸ”’ Permission Control (By job, grade,etc.) By default, any player can use the /houseManager command to create and manage houses.
You can restrict access by using the canUseHouseManagerCommand filter to implement your own permission system. :::

:::: tabs

::: tab πŸ”¨ Creating a Shell House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the Shell type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Category: Choose from available interior categories (Houses, Shacks, Manors, etc.)
    • Rooms Amount: Select the number of rooms for your chosen category
    • Interior: Pick a specific interior
    • Shell Location: Place the interior shell in the world
    • Front Door Location: Move the entrance marker
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set money and gold prices
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Visit Mode: Press the visit mode key (default: R) to preview the interior

  5. Validation: All required fields must be completed:

    • House name cannot be empty
    • Front door location must be set
    • Valid interior must be selected
    • Shell location must be placed
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  6. Press the "Create House" key when all requirements are met

[!TIP] πŸ’‘ Multiple houses on the same location If you create multiple houses with the same front door location, all will be merged into one location menu. This allows you to virtually create 'buildings' with multiple flats with only one entry!

:::

::: tab πŸ”¨ Creating a MLO House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the MLO type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Interior: Go inside the house you want to create and press Enter to set the interior
    • Add doors: While hovering this item, you can walk around the house and register the various doors. Each door you add will be treated as an access point for the property, showing the interaction prompt and marker (if enabled) when a player gets close. These registered doors are also used by the dedicated outside door menu and the quick open / close prompt.
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set money and gold prices
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Validation: All required fields must be completed:

    • House name cannot be empty
    • House interior must be set
    • At least one door must be registered
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  5. Press the "Create House" key when all requirements are met (you'll need to go outside the house first) :::

2. Usage > Create / Update / Delete houses

::: tab πŸ”¨ Creating a Zone House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the Zone type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Zone: Use the zone creator to add points and define the "inside" zone. This same zone is later reused for inside / outside checks, build mode safety and furniture placement validation.
    • Add doors: While hovering this item, you can walk around the house and register the various doors. Each door you add will be treated as an access point for the property, showing the interaction prompt and marker (if enabled) when a player gets close. These registered doors are also used by the dedicated outside door menu and the quick open / close prompt.
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set money and gold prices
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Validation: All required fields must be completed:

    • House name cannot be empty
    • House zone must be set
    • At least one door must be registered
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  5. Press the "Create House" key when all requirements are met (you'll need to go outside the house first) :::

::: tab ✏️ Updating a House Updating a House:

  1. Use the command /houseManager to open the housing management menu
  2. Select "Manage houses"
  3. Choose the house you want to modify
  4. Press the "Edit House" prompt key (default: E)
  5. Edit Mode Restrictions:
    • <Badge type="tip" text="Shell houses" /> : Category, rooms amount, interior type and house type cannot be changed
    • <Badge type="tip" text="MLO houses" /> : House type and interior cannot be changed
    • <Badge type="tip" text="Zone houses" /> : House type cannot be changed
    • All other properties can be modified
    • Visit mode is available to preview changes
  6. Modify any editable house details as needed
  7. Press "Update House" to save your changes (only saves if changes were made) :::

::: tab πŸ—‘οΈ Deleting a House Deleting a House:

  1. Use the command /houseManager to open the housing management menu
  2. Select "Manage houses"
  3. Choose the house you want to delete
  4. Press the "Delete House" prompt key (default: X)
  5. The house will be permanently deleted along with all associated data :::

::::

2. Usage > Buying a house

Players can purchase or rent available houses throughout your server.

:::: tabs

::: tab πŸ” Finding Available Houses Finding Available Houses:

  1. Approach a house door with the help of the map blips
  2. Press the front door prompt key (default: E) to open the contextual menu
  3. If several properties share the same entrance, first choose the unit you want to inspect :::

::: tab πŸ’° Purchasing a House

Purchasing a House:

  1. For Shell houses, the location menu lists the available units at that entrance
  2. For unowned MLO and Zone houses, the script opens a dedicated buy menu
  3. Available properties will show their sale or rent price
  4. Press the "Buy House" prompt key (default: ENTER) to purchase with money
  5. Alternatively, press the "Buy with Gold" key (default: G) if enabled
  6. For rental properties:
    • Select the number of days/weeks to rent using the slider
    • Press the appropriate key to pay with money or gold
  7. Once purchased, the house switches to its owner flow:
    • Shell: shell entry and interior menu flow
    • MLO / Zone: dedicated door menu and quick door prompt

:::

::: tab πŸ“… Rental Information Rental Information:

  • Rentals must be renewed before they expire
  • The house menu shows your current rental status:
    • 🟒 Green: Plenty of time remaining
    • 🟠 Orange: Rent due soon (warning period)
    • πŸ”΄ Red: Grace period (overdue but not yet evicted)
  • If rent expires beyond the grace period, the house will return to the market :::

::::

2. Usage > Using a house

Once you own a house, you can use its features and customize its interior.

:::: tabs

::: tab πŸšͺ Entering Your House Entering Your House:

  1. Approach your house's entrance

For <Badge type="tip" text="Shell houses" /> :

  1. Press the "Enter House" prompt key (default: ENTER)
  2. You'll be teleported inside your private shell instance
  3. Inside the shell, use the entrance prompts to open the interior menu or leave the house

For <Badge type="tip" text="MLO houses" /> and <Badge type="tip" text="Zone houses" />:

  1. Press the front door menu key (default: E) to open the dedicated door menu
  2. Depending on your access, this menu can show actions such as:
    • Open / Close door
    • Manage my house
    • Enter build mode
    • Knock on house
  3. If you are close to an eligible registered door, you can also use the quick door prompt (default: ENTER) to directly open or close the nearest door without opening the menu
  4. The quick door prompt label automatically switches between "Open door" and "Close door"
  5. Door states are synchronized between players
  6. Access to MLO / Zone doors depends on ownership, accessibility mode, access list permissions and Config.enableKeyMode :::

::: tab πŸ› οΈ Build Mode Build Mode:

  1. Open the menu that matches your house type
    • Shell: use the interior menu near the entrance from inside the house
    • MLO / Zone: use the dedicated door menu
  2. Select "Enter build mode"
  3. For MLO and Zone houses, this option is only shown to eligible players, and it only becomes available while you are physically inside the house bounds
  4. In build mode, you can:
    • Press the "Add Furnitures" key (default: A) to purchase and place furniture
    • Press the "Edit Furnitures" key (default: E) to move, duplicate or delete existing furniture
    • Set locations for dressing rooms and storage if your house has these features
  5. Furniture placement is validated according to the house type:
    • Shell: around the spawned shell interior
    • MLO: inside the registered interior
    • Zone: inside the defined zone
  6. If you leave the bounds of an MLO or Zone house while building or editing, build mode closes automatically
  7. Zone houses display their zone boundaries during build mode to make placement clearer
  8. Press the "Leave Build Mode" key (default: R) to exit :::

::: tab πŸ” Managing Access Managing Access:

The access management options depend on your server's Config.enableKeyMode setting:

When Key Mode is Disabled (Config.enableKeyMode = false):

  1. Open the house menu for your property
  2. Select "Manage my house"
  3. Choose "Manage Access"
  4. Set your access type:
    • "Only me": Only you can enter
    • "List": Only players you've added to the access list can enter or use MLO / Zone door interactions
    • "Everyone": Any player can enter your house and use MLO / Zone door interactions
  5. If using the "List" option, you can add or remove players from your access list
  6. After adding a player, you can select them to manage their specific permissions:
    • Can access storage
    • Can access wardrobe
    • Can add furniture
    • Can edit furniture
  7. Access and permission updates are synchronized immediately for online players, even if they are currently outside the house

When Key Mode is Enabled (Config.enableKeyMode = true):

  1. Open the house menu for your property
  2. Select "Manage my house"
  3. Choose "Manage Access"
  4. Set your access type:
    • "Anyone with the key": Anyone who has a key to your house can enter
    • "Everyone": Any player can enter your house and toggle MLO / Zone doors
  5. Additional key management options:
    • Buy new key: Purchase additional keys to give to other players
    • Change lock: Change your house lock, making all existing keys obsolete (you'll receive a new key)
  6. For owned MLO and Zone houses, door toggling follows the same key rules: a valid key is required unless the house is set to Everyone

If an access or permission change is invalid or not allowed, the player receives an error notification. :::

::: tab 🏠 Using House Features Using House Features:

  • Storage: Approach your storage location and press the prompt key (default: E)
  • Dressing Room* : Approach your dressing location and press the prompt key (default: E)
  • Stable* : Approach the stable marker outside your house to access your horses
  • Wagon* : Approach the wagon marker outside your house to access your wagons

* Integration Required <br> This script does not have built-in solutions for dressing, stable and wagon functionality. You need to integrate with your existing resources using the external handler functions.

:::

::: tab πŸ‘₯ Inviting Players Inviting Players:

  1. Visitors can knock on an owned house from the front door or from the MLO / Zone door menu
  2. For Shell houses, eligible players inside receive a notification and can directly welcome the visitor in
  3. For MLO and Zone houses, eligible players are notified that someone is at the door and should go to the door to let them in
  4. Alternatively, add the visitor to your access list for permanent access or give them a key if Config.enableKeyMode is true :::

::: tab πŸ“€ Transferring Ownership Transferring Ownership:

  1. Open the house menu for your property
  2. Select "Manage my house"
  3. Choose "Transfer House"
  4. Enter the player's server ID
  5. The house ownership will transfer to that player :::

::::

:::tip πŸ’‘ Key Mode Configuration The Config.enableKeyMode setting fundamentally changes how house access is managed. Choose the mode that best fits your server's desired gameplay style.

πŸ”‘ Key Mode Enabled

When Config.enableKeyMode = true, house access is tied to a physical key item, creating a more immersive experience.

2. Usage > Using a house

  • Ownership: Players receive a key in their inventory upon purchasing a house.
  • Sharing: Owners can buy additional keys to give to friends or other players.
  • Security: An owner can change the locks at any time. This action makes all previously issued keys for that house obsolete and provides the owner with a new one.
  • Requirement: A player must have the corresponding key in their inventory to enter the house. For owned MLO and Zone houses, the same rule also applies to door toggling unless the house accessibility is set to Everyone.

πŸ‘€ Access List Mode

When Config.enableKeyMode = false, access is managed digitally through an in-game menu, offering a simpler, hassle-free system.

  • No Items: This mode does not use physical key items.
  • Management: Owners manage a digital access list for their property via the house menu.
  • Permissions: The owner can instantly grant or revoke access to any player.
  • Granular Controls: For each player on the list, the owner can set specific permissions:
    • Can access storage
    • Can access wardrobe
    • Can add furniture
    • Can edit furniture
  • Requirement: Players on the access list can enter without needing any specific item. On MLO and Zone houses, the same access rules also govern the door menu and quick door prompt. :::

3. Interiors Shells Gallery

Browse through all available interior options for the Shell houses. This gallery showcases the complete collection of interiors you can choose from when creating houses of type Shell using the /houseManager command.

:::details The gallery <InteriorGallery /> :::

4. Script Configuration

<ScriptConfig scriptPath="redm/housing" />

5. FAQ

:::details It's raining in my house ! Weather effects (rain, snow) can appear inside interiors due to your framework's weather synchronization system. To fix this, you need to disable weather sync when players enter houses and re-enable it when they leave.

Solution: Use the housing script's server actions to control your framework's weather sync:

-- Disable weather sync when entering a house
exports.jo_housing:registerAction('houseEntered', function(source, house, isVisiting)
    -- Weather sync from kibook
    TriggerClientEvent('weathersync:setSyncEnabled', source, false)
end)

-- Re-enable weather sync when leaving a house  
exports.jo_housing:registerAction('houseLeft', function(source, house)
    -- Weather sync from kibook
    TriggerClientEvent('weathersync:setSyncEnabled', source, true)
end)

Reference: houseEntered action and houseLeft action :::

:::details Can I add custom interiors ? Yes! You can add your own custom interiors using the addInteriors filter. This allows you to integrate custom MLO interiors or propsets into the housing system.

How to add custom interiors:

-- In your server-side resource or in jo_housing itself
exports.jo_housing:registerFilter('addInteriors', function(customInteriors)
    -- Add your custom interiors
    customInteriors.my_custom_cabin = {
        entries = {
            vec4(2.022125, -0.556265, 1.696404, 91.094)  -- Main door spawn point (relative to interior origin)
        },
        propset = "my_custom_cabin",     -- Interior propset/MLO name
        category = "shack",              -- Interior category (house, shack, manor, flat, rock_shack, worker)
        numberRoom = 2,                  -- Number of rooms in the interior
        insideDoors = {                  -- Optional: Interior working doors configuration
            { 
                model = "p_door04x", 
                position = vec3(0.062589, -4.6552873, 0.738253) 
            }
        }
    }
    
    customInteriors.luxury_penthouse = {
        entries = {
            vec4(0.0, 0.0, 2.0, 180.0)  -- Main door spawn point
        },
        propset = "luxury_penthouse_mlo",
        category = "manor",
        numberRoom = 8
        -- insideDoors is optional - omit if no working interior doors needed
    }
    
    return customInteriors
end)

Configuration Tips:

  • Set furniture limits for your custom interiors in Config.interiorsMaxFurnitures
  • Use /houseManager visit mode to test and preview your interior placement
  • Interior categories affect available furniture limits and UI organization

Reference: addInteriors filter in the Developer section :::

:::details Can I add custom furnitures (props) ? Yes! You can add custom furniture categories and props using the addFurnitures filter. This allows you to expand the available furniture options beyond the default categories.

How to add custom furniture:

-- In your server-side resource or in jo_housing itself
exports.jo_housing:registerFilter('addFurnitures', function(customFurnitures)
    -- Add your custom furniture categories
    customFurnitures.custom_electronics = {
        "p_tv01x",
        "p_radio01x", 
        "p_lamp_handheld01x",
        "p_phonograph01x"
    }
    
    customFurnitures.outdoor_furniture = {
        "p_bench05x",
        "p_campfire01x",
        "p_tent01x",
        "p_waterbarrel01x"
    }
    
    customFurnitures.western_decor = {
        "p_spittoon01x",
        "p_horseshoe01x",
        "p_banjo01x"
    }
    
    return customFurnitures
end)

Don't forget to configure pricing: Add pricing for your custom categories in your overwriteConfig.lua:

-- Configure pricing for custom furniture categories
Config.furnituresCategoriesPrices.custom_electronics = {
    money = 250,
    gold = 3
}

Config.furnituresCategoriesPrices.outdoor_furniture = {
    money = 75,
    gold = 1
}

-- You can also set specific prices for individual props
Config.furnituresPrices.p_tv01x = {
    money = 500,
    gold = 5
}

Reference: addFurnitures filter and Furniture Configuration in the Developer section :::

:::details Can I disable the Build Mode ? If you set the interior’s max furniture to 0 (zero), players won’t be able to add any furniture inside, effectively disabling build mode for that interior.

Example:

-- Available interior categories: shack, rock_shack, house, flat, manor, worker
-- Default furniture limit for interior categories
Config.interiorsCategoriesMaxFurnitures = {
    default = 0,      -- Disable build mode for all interiors
    manor = 200         -- Except for the manor category
}

-- Specific furniture limits per interior ID
Config.interiorsMaxFurnitures = {
    jo_pai_house = 0, -- Disable build mode only for jo_pai_house
}

:::

:::details The prompts are not working ! If prompts aren't appearing or responding, this is usually related to key binding or keyboard layout issues. Here are the most common solutions:

Primary Solution - Keyboard Layout: This script uses the raw keys module. If you have problems with prompts, set the keyboard layout properly.

Additional Troubleshooting Steps:

  1. Check for Key Conflicts:

    • Other resources might be using the same keys
    • Review your Config.keys settings in overwriteConfig.lua
    • Try changing conflicting keys to different bindings
  2. Verify Distance Settings:

    • Make sure you're close enough to interact with objects
    • Check Config.distanceShowHousePrompt and related distance settings
    • Default house prompt distance is 2.5 meters

5. FAQ

  1. Framework Compatibility:
    • Ensure your framework is compatible with jo_libs
    • Check that jo_libs is properly installed and started before jo_housing :::

6. For Developers > Actions

Actions are one of the two types of Hooks. They provide a way for running a function at a specific point in the execution of scripts. Callback functions for an Action do not return anything back to the calling Action hook. They are the counterpart to Filters.

Below is a complete list of all available actions in the jo_housing script.

6. For Developers > Actions > <Badge type="server" text="Server" /> accessibilityChanged

Triggered when house accessibility settings are changed.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param accessibilityType - new accessibility type ("everyone", "list", or "onlyMe")
exports.jo_housing:registerAction('accessibilityChanged', function(source, house, accessibilityType)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> buildModeEntered

Triggered when a player enters build mode in their house.

-- @param source - serverID of the player
exports.jo_housing:registerAction('buildModeEntered', function(source)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> dressingLocationSet

Triggered when a dressing room location is set in a house.

-- @param source - serverID of the player
-- @param house - the house object
-- @param coords - coordinates of the dressing location
exports.jo_housing:registerAction('dressingLocationSet', function(source, house, coords)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> furnitureBought

Triggered when a player purchases furniture for their house.

-- @param source - serverID of the player
-- @param house - the house object where furniture was bought
-- @param furniture - the furniture object that was purchased
exports.jo_housing:registerAction('furnitureBought', function(source, house, furniture)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> furnitureDeleted

Triggered when furniture is deleted from a house.

-- @param source - serverID of the player deleting the furniture
-- @param house - the house object containing the furniture
-- @param furnitureId - ID of the furniture that was deleted
exports.jo_housing:registerAction('furnitureDeleted', function(source, house, furnitureId)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> furnitureMoved

Triggered when furniture is moved within a house.

-- @param source - serverID of the player
-- @param house - the house object containing the furniture
-- @param furnitureId - ID of the furniture that was moved
exports.jo_housing:registerAction('furnitureMoved', function(source, house, furnitureId)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseBought

Triggered when a player successfully purchases a house.

-- @param source - serverID of the buyer
-- @param house - the house object that was purchased
exports.jo_housing:registerAction('houseBought', function(source, house)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseCreated

Triggered when a new house is created .

-- @param source - serverID of the player who created the house
-- @param house - the newly created house object
exports.jo_housing:registerAction('houseCreated', function(source, house)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseDeleted

Triggered when a house is deleted .

-- @param source - serverID of the player who deleted the house
-- @param house - the house object that was deleted
exports.jo_housing:registerAction('houseDeleted', function(source, house)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseEntered

Triggered when a player enters a house.

-- @param source - serverID of the player
-- @param house - the house object being entered
-- @param isVisiting - boolean indicating if the player is visiting
exports.jo_housing:registerAction('houseEntered', function(source, house, isVisiting)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseKeyBought

Triggered when a new house key is purchased.

-- @param source - serverID of the player buying the key
-- @param house - the house object
exports.jo_housing:registerAction('houseKeyBought', function(source, house)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseLeft

Triggered when a player leaves a house.

-- @param source - serverID of the player
-- @param house - the house object being left
exports.jo_housing:registerAction('houseLeft', function(source, house)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseLocationMenuOpened

Triggered when a player opens the house location menu.

-- @param source - serverID of the player
-- @param houses - array of nearby houses
exports.jo_housing:registerAction('houseLocationMenuOpened', function(source, houses)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseLockChanged

Triggered when a house lock is changed.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param success - boolean indicating if lock change was successful
exports.jo_housing:registerAction('houseLockChanged', function(source, house, success)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseStorageOpened

Triggered when a player opens house storage.

-- @param source - serverID of the player
-- @param house - the house object
-- @param invId - inventory ID that was opened
exports.jo_housing:registerAction('houseStorageOpened', function(source, house, invId)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseTransfered

Triggered when house ownership is transferred to another player.

-- @param source - serverID of the current owner
-- @param toPlayerSrc - serverID of the new owner
-- @param house - the house object being transferred
-- @param transferDone - boolean indicating if transfer was successful
exports.jo_housing:registerAction('houseTransfered', function(source, toPlayerSrc, house, transferDone)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseUpdated

Triggered when an existing house is updated .

-- @param source - serverID of the player who updated the house
-- @param house - the updated house object
-- @param changeset - table containing the changes made
exports.jo_housing:registerAction('houseUpdated', function(source, house, changeset)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

Triggered when a player opens house wardrobe.

-- @param source - serverID of the player
-- @param house - the house object
exports.jo_housing:registerAction('houseWardrobeOpened', function(source, house)
    -- Your code here
end)

#### <Badge type="server" text="Server" /> knockedOnHouse
Triggered when someone knocks on a house door.

```lua
-- @param source - serverID of the player knocking
-- @param house - the house object being knocked on
-- @param foundSources - array of player sources who were notified (propset: eligible players inside the house, MLO/Zone: notified targets excluding the knocker)
exports.jo_housing:registerAction('knockedOnHouse', function(source, house, foundSources)
    -- Your code here
end)
Rental & Keys Actions

<Badge type="server" text="Server" /> playerAddedToHouse

Triggered when a player is added to a house's access list.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param playerSrc - serverID of the player being added
-- @param playerName - name of the player being added
exports.jo_housing:registerAction('playerAddedToHouse', function(source, house, playerSrc, playerName)
    -- Your code here
end)

<Badge type="server" text="Server" /> playerPermissionsChanged

Triggered when the permissions for a player on the access list are changed.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param accessibilityId - ID of the access entry being changed
-- @param permissions - table containing the new permissions
exports.jo_housing:registerAction('playerPermissionsChanged', function(source, house, accessibilityId, permissions)
    -- Your code here
end)

<Badge type="server" text="Server" /> playerRemovedFromHouse

Triggered when a player is removed from a house's access list.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param accessibilityId - ID of the access entry that was removed
-- @param success - boolean indicating if removal was successful
exports.jo_housing:registerAction('playerRemovedFromHouse', function(source, house, accessibilityId, success)
    -- Your code here
end)

<Badge type="server" text="Server" /> storageLocationSet

Triggered when a storage location is set in a house.

-- @param source - serverID of the player
-- @param house - the house object
-- @param coords - coordinates of the storage location
exports.jo_housing:registerAction('storageLocationSet', function(source, house, coords)
    -- Your code here
end)
Access Management Actions

<Badge type="server" text="Server" /> rentPaid

Triggered when rent is paid for a house.

-- @param source - serverID of the player paying rent
-- @param house - the house object
-- @param numPeriods - number of periods paid for
-- @param isDaily - boolean indicating if it's daily or weekly rent
-- @param totalPrice - total amount paid
-- @param moneyType - payment type (0 for money, 1 for gold)
-- @param success - boolean indicating if payment was successful
exports.jo_housing:registerAction('rentPaid', function(source, house, numPeriods, isDaily, totalPrice, moneyType, success)
    -- Your code here
end)

Filters

Filters allow you to modify data or control permissions during script execution. Introduced in v1.2.0, filters provide a synchronous way to intercept and modify script behavior at specific points, unlike events which are asynchronous.

Below is a complete list of all available filters in the jo_housing script.

<Badge type="server" text="Server" /> addFurnitures

Allows you to add custom furniture categories and items to the housing system.

-- @param customFurnitures - empty table by default
-- @return table - table with custom furniture categories
exports.jo_housing:registerFilter('addFurnitures', function(customFurnitures)
    -- Add your custom furniture categories
    customFurnitures.custom_electronics = {
        "p_tv01x",
        "p_radio01x", 
        "p_lamp_handheld01x"
    }
    
    customFurnitures.outdoor_furniture = {
        "p_bench05x",
        "p_campfire01x",
        "p_tent01x"
    }
    
    return customFurnitures
end)

:::tip πŸ’‘ Custom Furniture Pricing Don't forget to configure pricing for your custom categories in Config.furnituresCategoriesPrices or specific items in Config.furnituresPrices.

-- In your overwriteConfig.lua
Config.furnituresCategoriesPrices.custom_electronics = {
    money = 250,
    gold = 3
}

Config.furnituresCategoriesPrices.outdoor_furniture = {
    money = 75,
    gold = 1
}

:::

<Badge type="server" text="Server" /> addInteriors

Allows you to add custom interior configurations to the housing system.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param customInteriors - empty table by default
-- @return table - table with custom interior configurations
exports.jo_housing:registerFilter('addInteriors', function(customInteriors)
    -- Add your custom interiors
    customInteriors.my_custom_cabin = {
        entries = {
            vec4(2.022125, -0.556265, 1.696404, 91.094)  -- Main door spawn point (relative to interior origin)
        },
        propset = "my_custom_cabin",     -- Interior propset/MLO name
        category = "shack",              -- Interior category (house, shack, manor, flat, rock_shack, worker)
        numberRoom = 2,                  -- Number of rooms in the interior
        insideDoors = {                  -- Optional: Interior working doors configuration
            { 
                model = "p_door04x", 
                position = vec3(0.062589, -4.6552873, 0.738253) 
            },
            { 
                model = "p_door04x", 
                position = vec3(-1.473662, -4.661302, 0.738253) 
            },
        }
    }
    
    customInteriors.luxury_penthouse = {
        entries = {
            vec4(0.0, 0.0, 2.0, 180.0)  -- Main door spawn point (relative to interior origin)
        },
        propset = "luxury_penthouse_mlo",
        category = "manor",
        numberRoom = 8
        -- insideDoors is optional - omit if no working interior doors needed
    }
    
    return customInteriors
end)

Interior Properties:

Property Type Required Description
entries table βœ… Array containing the main door spawn point (vec4 with heading). Only the first entry is used, coordinates are relative to interior origin
propset string βœ… Interior propset/MLO resource name
category string βœ… Interior category: house, shack, manor, flat, rock_shack, worker
numberRoom number βœ… Number of rooms in the interior
insideDoors table ❌ Optional array of interior doors with model and position properties

:::tip πŸ’‘ Interior Configuration

-- In your overwriteConfig.lua
Config.interiorsMaxFurnitures.my_custom_cabin = 150
Config.interiorsCategoriesMaxFurnitures.luxury = 300  -- If you add a custom category

:::

<Badge type="server" text="Server" /> canAddPlayerToHouse

Controls who can add players to a house's access list.

-- @param canAdd - boolean indicating if the action is allowed by default
-- @param source - serverID of the player adding someone
-- @param houseId - ID of the house
-- @param playerSrc - serverID of the player being added
-- @param playerName - name of the player being added
exports.jo_housing:registerFilter('canAddPlayerToHouse', function(canAdd, source, houseId, playerSrc, playerName)
    return canAdd
end)

<Badge type="server" text="Server" /> canBuyFurniture

Controls who can buy furniture.

-- @param canBuy - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furniture - furniture model/object data
-- @param houseId - ID of the house
-- @param moneyType - payment type (0 for money, 1 for gold)
-- @param categoryKey - category key of the furniture
exports.jo_housing:registerFilter('canBuyFurniture', function(canBuy, source, furniture, houseId, moneyType, categoryKey)
    return canBuy
end)

<Badge type="server" text="Server" /> canBuyHouse

Controls who can buy a house.

-- @param canBuy - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house being purchased
-- @param moneyType - payment type (0 for money, 1 for gold)
-- @param period - for rentals, number of periods the player is paying for
exports.jo_housing:registerFilter('canBuyHouse', function(canBuy, source, houseId, moneyType, period)
    return canBuy
end)

<Badge type="server" text="Server" /> canDeleteFurniture

Controls who can delete furniture from a house.

-- @param canDelete - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furnitureId - ID of the furniture
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canDeleteFurniture', function(canDelete, source, furnitureId, houseId)
    return canDelete
end)

<Badge type="server" text="Server" /> canDeleteHouse

Controls who can delete houses.

-- @param canDelete - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseData - table containing house data
exports.jo_housing:registerFilter('canDeleteHouse', function(canDelete, source, houseData)
    return canDelete
end)

<Badge type="server" text="Server" /> canEnterBuildMode

Controls who can enter build mode in a house.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param canEnter - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canEnterBuildMode', function(canEnter, source, houseId)
    return canEnter
end)

<Badge type="server" text="Server" /> canEnterHouse

Controls who can enter a house. Door toggling for owned MLO and Zone houses is handled separately by canToggleHouseDoor.

-- @param canEnter - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house being entered
exports.jo_housing:registerFilter('canEnterHouse', function(canEnter, source, houseId)
    return canEnter
end)

<Badge type="server" text="Server" /> canGetPlayersAccessibilityList

Controls who can retrieve the list of players with access to a house.

-- @param canGet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player requesting the list
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canGetPlayersAccessibilityList', function(canGet, source, houseId)
    return canGet
end)

<Badge type="server" text="Server" /> canMoveFurniture

Controls who can move furniture in a house.

-- @param canMove - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furnitureId - ID of the furniture
-- @param houseId - ID of the house
-- @param relPos - new relative position for the furniture
-- @param relRot - new relative rotation for the furniture
exports.jo_housing:registerFilter('canMoveFurniture', function(canMove, source, furnitureId, houseId, relPos, relRot)
    return canMove
end)

<Badge type="server" text="Server" /> canOpenHouseLocationMenu

Controls who can open the house location menu.

-- @param canOpen - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houses - array of nearby houses
exports.jo_housing:registerFilter('canOpenHouseLocationMenu', function(canOpen, source, houses)
    return canOpen
end)

<Badge type="server" text="Server" /> canOpenHouseStorage

Controls who can open a house's storage.

-- @param canOpenStorage - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canOpenHouseStorage', function(canOpenStorage, source, houseId)
    return canOpenStorage
end)

<Badge type="server" text="Server" /> canOpenHouseWardrobe

Controls who can open a house's wardrobe.

-- @param canOpen - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canOpenHouseWardrobe', function(canOpen, source, houseId)
    return canOpen
end)

<Badge type="server" text="Server" /> canPlaceFurniture

Controls who can place furniture in a house.

-- @param canPlace - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furniture - furniture model/object data
-- @param houseId - ID of the house
-- @param relPos - relative position where furniture will be placed
-- @param relRot - relative rotation of the furniture
-- @param moneyType - payment type (0 for money, 1 for gold)
-- @param categoryKey - category key of the furniture
exports.jo_housing:registerFilter('canPlaceFurniture', function(canPlace, source, furniture, houseId, relPos, relRot, moneyType, categoryKey)
    return canPlace
end)

<Badge type="server" text="Server" /> canRemovePlayerFromHouse

Controls who can remove players from a house's access list.

-- @param canRemove - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param accessibilityId - ID of the access entry to remove
exports.jo_housing:registerFilter('canRemovePlayerFromHouse', function(canRemove, source, houseId, accessibilityId)
    return canRemove
end)

<Badge type="server" text="Server" /> canSetDressingLocation

Controls who can set a dressing room location inside a house.

-- @param canSet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param coords - coordinates for the dressing location
exports.jo_housing:registerFilter('canSetDressingLocation', function(canSet, source, houseId, coords)
    return canSet
end)

<Badge type="server" text="Server" /> canSetStorageLocation

Controls who can set a storage location inside a house.

-- @param canSet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param coords - coordinates for the storage location
exports.jo_housing:registerFilter('canSetStorageLocation', function(canSet, source, houseId, coords)
    return canSet
end)

<Badge type="server" text="Server" /> canToggleHouseDoor

Controls who can open or close an owned MLO or Zone house door. This filter is evaluated by the dedicated server door-toggle callback, separately from canEnterHouse.

-- @param canToggle - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canToggleHouseDoor', function(canToggle, source, houseId)
    return canToggle
end)

<Badge type="server" text="Server" /> canTransferHouse

Controls who can transfer house ownership.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param canTransfer - boolean indicating if the action is allowed by default
-- @param source - serverID of the current owner
-- @param toPlayerSrc - serverID of the player receiving the house
-- @param houseId - ID of the house being transferred
exports.jo_housing:registerFilter('canTransferHouse', function(canTransfer, source, toPlayerSrc, houseId)
    return canTransfer
end)

<Badge type="server" text="Server" /> canUpdateAccessibility

Controls who can update a house's accessibility settings.

-- @param canUpdate - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param accessibilityType - type of access ("everyone", "list", or "onlyMe")
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canUpdateAccessibility', function(canUpdate, source, accessibilityType, houseId)
    return canUpdate
end)

<Badge type="server" text="Server" /> canUpdatePlayerPermissions

Controls who can update permissions for a player on a house's access list.

-- @param canUpdate - boolean indicating if the action is allowed by default
-- @param source - serverID of the player making the changes
-- @param houseId - ID of the house
-- @param accessibilityId - ID of the access entry being changed
-- @param permissions - table of the new permissions
exports.jo_housing:registerFilter('canUpdatePlayerPermissions', function(canUpdate, source, houseId, accessibilityId, permissions)
    return canUpdate
end)

<Badge type="server" text="Server" /> canUpsertHouse

Controls who can create or update houses.

-- @param canUpsert - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house (nil for creation)
-- @param changesetOrHouseData - changes to apply or full house data for creation
exports.jo_housing:registerFilter('canUpsertHouse', function(canUpsert, source, houseId, changesetOrHouseData)
    return canUpsert
end)

<Badge type="server" text="Server" /> canUseHouseManagerCommand

Controls who can use the /houseManager command to create and manage houses.

-- @param canUse - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
exports.jo_housing:registerFilter('canUseHouseManagerCommand', function(canUse, source)
    -- Example: Only allow admins
    local isAdmin = exports.your_permission_system:isAdmin(source)
    return isAdmin
end)

:::details Example of job lock for VORP

AllowedJobs = {
    houser = true,
    houseManager = true,
}

exports.jo_housing:registerFilter('canUseHouseManagerCommand', function(canUse, source)
    local job = jo.framework:getJob(source)
    if AllowedJobs[job] then
      return canUse
    else
      jo.notif.rightError("You don't have the right job to use the house manager")
      return false
    end
end)

:::

2. Usage > Create / Update / Delete houses

The housing system provides an admin interface to manage properties on your server.

[!NOTE] jo_housing supports three distinct types of properties: MLOs, Zones and Shells. MLO and Zone houses are physically part of the game world, so they now share the same outside flow for buying, door interactions, inside/outside detection and build mode. In contrast, Shell houses use pre-defined propset interiors that exist separately from the main map. When a player enters a Shell house, they are teleported from the front door to an instance of the interior, which the script spawns on demand.

:::tip πŸ”’ Permission Control (By job, grade,etc.) By default, any player can use the /houseManager command to create and manage houses.
You can restrict access by using the canUseHouseManagerCommand filter to implement your own permission system. :::

:::: tabs

::: tab πŸ”¨ Creating a Shell House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the Shell type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Category: Choose from available interior categories (Houses, Shacks, Manors, etc.)
    • Rooms Amount: Select the number of rooms for your chosen category
    • Interior: Pick a specific interior
    • Shell Location: Place the interior shell in the world
    • Front Door Location: Move the entrance marker
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set a jo.pricing price group. Payment options can use money, gold, role currency or items, and multiple alternatives can be combined with OR
    • House Tax: For sale houses only, enable recurring tax and configure its price group and period in days
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Visit Mode: Press the visit mode key (default: R) to preview the interior

  5. Validation: All required fields must be completed:

    • House name cannot be empty
    • Front door location must be set
    • Valid interior must be selected
    • Shell location must be placed
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  6. Press the "Create House" key when all requirements are met

[!TIP] πŸ’‘ Multiple houses on the same location If you create multiple houses with the same front door location, all will be merged into one location menu. This allows you to virtually create 'buildings' with multiple flats with only one entry!

:::

::: tab πŸ”¨ Creating a MLO House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the MLO type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Interior: Go inside the house you want to create and press Enter to set the interior
    • Add doors: While hovering this item, you can walk around the house and register the various doors. Each door you add will be treated as an access point for the property, showing the interaction prompt and marker (if enabled) when a player gets close. These registered doors are also used by the dedicated outside door menu and the quick open / close prompt.
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set a jo.pricing price group. Payment options can use money, gold, role currency or items, and multiple alternatives can be combined with OR
    • House Tax: For sale houses only, enable recurring tax and configure its price group and period in days
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Validation: All required fields must be completed:

    • House name cannot be empty
    • House interior must be set
    • At least one door must be registered
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  5. Press the "Create House" key when all requirements are met (you'll need to go outside the house first) :::

2. Usage > Create / Update / Delete houses

::: tab πŸ”¨ Creating a Zone House

  1. Use the command /houseManager to open the housing management menu

  2. Select Create a new house, then choose the Zone type

  3. Fill in the house details :

    • Name: Give your house a descriptive name
    • Zone: Use the zone creator to add points and define the "inside" zone. This same zone is later reused for inside / outside checks, build mode safety and furniture placement validation.
    • Add doors: While hovering this item, you can walk around the house and register the various doors. Each door you add will be treated as an access point for the property, showing the interaction prompt and marker (if enabled) when a player gets close. These registered doors are also used by the dedicated outside door menu and the quick open / close prompt.
    • Contract Type: Choose between one-time sale or rent
      • If rent: Select daily or weekly rent periods
    • Price: Set a jo.pricing price group. Payment options can use money, gold, role currency or items, and multiple alternatives can be combined with OR
    • House Tax: For sale houses only, enable recurring tax and configure its price group and period in days
    • Features:
      • Stable: Enable/disable horse storage
        • Stable Location: Place the stable interaction marker
        • Stable Spawn Location: Place where horses will spawn
      • Storage: Enable/disable item storage with weight/slot limits
      • Wagon: Enable/disable wagon storage
        • Wagon Location: Place the wagon interaction marker
        • Wagon Spawn Location: Place where wagons will spawn
      • Dressing Room: Enable/disable wardrobe functionality
  4. Validation: All required fields must be completed:

    • House name cannot be empty
    • House zone must be set
    • At least one door must be registered
    • If stable enabled: Both stable location and spawn location required
    • If wagon enabled: Both wagon location and spawn location required
    • Prices must be non-negative
  5. Press the "Create House" key when all requirements are met (you'll need to go outside the house first) :::

::: tab ✏️ Updating a House Updating a House:

  1. Use the command /houseManager to open the housing management menu
  2. Select "Manage houses"
  3. Choose the house you want to modify
  4. Press the "Edit House" prompt key (default: E)
  5. Edit Mode Restrictions:
    • <Badge type="tip" text="Shell houses" /> : Category, rooms amount, interior type and house type cannot be changed
    • <Badge type="tip" text="MLO houses" /> : House type and interior cannot be changed
    • <Badge type="tip" text="Zone houses" /> : House type cannot be changed
    • All other properties can be modified
    • Visit mode is available to preview changes
  6. Modify any editable house details as needed
  7. Press "Update House" to save your changes (only saves if changes were made) :::

::: tab πŸ—‘οΈ Deleting a House Deleting a House:

  1. Use the command /houseManager to open the housing management menu
  2. Select "Manage houses"
  3. Choose the house you want to delete
  4. Press the "Delete House" prompt key (default: X)
  5. The house will be permanently deleted along with all associated data :::

::::

2. Usage > Buying a house

Players can purchase or rent available houses throughout your server.

:::: tabs

::: tab πŸ” Finding Available Houses Finding Available Houses:

  1. Approach a house door with the help of the map blips
  2. Press the front door prompt key (default: E) to open the contextual menu
  3. If several properties share the same entrance, first choose the unit you want to inspect :::

::: tab πŸ’° Purchasing a House

Purchasing a House:

  1. For Shell houses, the location menu lists the available units at that entrance
  2. For unowned MLO and Zone houses, the script opens a dedicated buy menu
  3. Available properties will show their sale or rent price
  4. Press the "Buy House" prompt key (default: ENTER) to purchase with money
  5. Alternatively, press the "Buy with Gold" key (default: G) if enabled
  6. For rental properties:
    • Select the number of days/weeks to rent using the slider
    • Press the appropriate key to pay with money or gold
  7. Once purchased, the house switches to its owner flow:
    • Shell: shell entry and interior menu flow
    • MLO / Zone: dedicated door menu and quick door prompt

:::

::: tab πŸ“… Rental Information Rental Information:

  • Rentals must be renewed before they expire
  • The house menu shows your current rental status:
    • 🟒 Green: Plenty of time remaining
    • 🟠 Orange: Rent due soon (warning period)
    • πŸ”΄ Red: Grace period (overdue but not yet evicted)
  • If rent expires beyond the grace period, the house will return to the market :::

::: tab 🧾 House Tax House Tax:

House tax is an optional recurring payment for houses configured as for sale. Rental houses are never taxed because they already use the rent lifecycle.

Tax is configured by an administrator when creating or editing a house:

  • House Tax: Enable or disable the tax for the sale house
  • Tax Price: Price charged for one tax period; multiple payment alternatives are supported
  • Tax Period: Number of days covered by one payment

Once a taxed house is owned:

  1. The owner can open the house menu and select Pay house tax.
  2. The menu displays the date until which the tax is paid (Tax Paid Until).
  3. The owner can pay one or more periods at once, up to the configured advance limit.
  4. Early payments extend the existing paid period. Late payments start from the current date, so missed periods are not charged twice.

The default tax settings are:

  • 50 money per period
  • 7 days per period
  • 24 hours warning period
  • 3 days grace period after the due date
  • 14 days maximum paid-ahead balance

The house menu displays the current tax status:

  • 🟒 Green: Tax is paid and the due date is not approaching
  • 🟠 Orange: Tax is due within the warning period
  • πŸ”΄ Red: The due date has passed and the owner is within the grace period
  • Eviction suspended: An external filter temporarily prevents eviction
  • Eviction processed: The grace period has expired and the house has returned to the market

Tax status is enforced when the resource starts and when relevant house menus are opened. If the grace period expires, the owner is notified when online, occupants are removed, furniture and resale listings are cleared, and the house becomes available again. A new buyer or transferee receives a fresh paid period and never inherits the previous owner's tax debt.

For example, a server can temporarily suspend tax evictions for protected characters or during a server event:

exports.jo_housing:registerFilter('canEvictForUnpaidTax', function(canEvict, house, ownerIdentifier, ownerCharId)
    if exports.your_staff_system:isTaxProtected(ownerIdentifier, ownerCharId) then
        return false
    end

    return canEvict
end)

:::

::::

5. FAQ

:::details It's raining in my house ! Weather effects (rain, snow) can appear inside interiors due to your framework's weather synchronization system. To fix this, you need to disable weather sync when players enter houses and re-enable it when they leave.

Solution: Use the housing script's server actions to control your framework's weather sync:

-- Disable weather sync when entering a house
exports.jo_housing:registerAction('houseEntered', function(source, house, isVisiting)
    -- Weather sync from kibook
    TriggerClientEvent('weathersync:setSyncEnabled', source, false)
end)

-- Re-enable weather sync when leaving a house  
exports.jo_housing:registerAction('houseLeft', function(source, house)
    -- Weather sync from kibook
    TriggerClientEvent('weathersync:setSyncEnabled', source, true)
end)

Reference: houseEntered action and houseLeft action :::

:::details Can I add custom interiors ? Yes! You can add your own custom interiors using the addInteriors filter. This allows you to integrate custom MLO interiors or propsets into the housing system.

How to add custom interiors:

-- In your server-side resource or in jo_housing itself
exports.jo_housing:registerFilter('addInteriors', function(customInteriors)
    -- Add your custom interiors
    customInteriors.my_custom_cabin = {
        entries = {
            vec4(2.022125, -0.556265, 1.696404, 91.094)  -- Main door spawn point (relative to interior origin)
        },
        propset = "my_custom_cabin",     -- Interior propset/MLO name
        category = "shack",              -- Interior category (house, shack, manor, flat, rock_shack, worker)
        numberRoom = 2,                  -- Number of rooms in the interior
        insideDoors = {                  -- Optional: Interior working doors configuration
            { 
                model = "p_door04x", 
                position = vec3(0.062589, -4.6552873, 0.738253) 
            }
        }
    }
    
    customInteriors.luxury_penthouse = {
        entries = {
            vec4(0.0, 0.0, 2.0, 180.0)  -- Main door spawn point
        },
        propset = "luxury_penthouse_mlo",
        category = "manor",
        numberRoom = 8,
        -- insideDoors is optional - omit if no working interior doors needed
    }
    
    return customInteriors
end)

Configuration Tips:

  • Set furniture limits for your custom interiors in Config.interiorsMaxFurnitures
  • Use /houseManager visit mode to test and preview your interior placement
  • Interior categories affect available furniture limits and UI organization

Reference: addInteriors filter in the Developer section :::

:::details Can I add custom furnitures (props) ? Yes! You can add custom furniture categories and props using the addFurnitures filter. This allows you to expand the available furniture options beyond the default categories.

How to add custom furniture:

-- In your server-side resource or in jo_housing itself
exports.jo_housing:registerFilter('addFurnitures', function(customFurnitures)
    -- Add your custom furniture categories
    customFurnitures.custom_electronics = {
        "p_tv01x",
        "p_radio01x", 
        "p_lamp_handheld01x",
        "p_phonograph01x"
    }
    
    customFurnitures.outdoor_furniture = {
        "p_bench05x",
        "p_campfire01x",
        "p_tent01x",
        "p_waterbarrel01x"
    }
    
    customFurnitures.western_decor = {
        "p_spittoon01x",
        "p_horseshoe01x",
        "p_banjo01x"
    }
    
    return customFurnitures
end)

Don't forget to configure pricing: Add pricing for your custom categories in your overwriteConfig.lua:

-- Configure pricing for custom furniture categories
Config.furnituresCategoriesPrices.custom_electronics = {
    money = 250,
    gold = 3
}

Config.furnituresCategoriesPrices.outdoor_furniture = {
    money = 75,
    gold = 1
}

-- You can also set specific prices for individual props
Config.furnituresPrices.p_tv01x = {
    money = 500,
    gold = 5
}

Reference: addFurnitures filter and Furniture Configuration in the Developer section :::

:::details Can I disable the Build Mode ? If you set the interior’s max furniture to 0 (zero), players won’t be able to add any furniture inside, effectively disabling build mode for that interior.

Example:

-- Available interior categories: shack, rock_shack, house, flat, manor, worker
-- Default furniture limit for interior categories
Config.interiorsCategoriesMaxFurnitures = {
    default = 0,      -- Disable build mode for all interiors
    manor = 200         -- Except for the manor category
}

-- Specific furniture limits per interior ID
Config.interiorsMaxFurnitures = {
    jo_pai_house = 0, -- Disable build mode only for jo_pai_house
}

:::

:::details The prompts are not working ! If prompts aren't appearing or responding, this is usually related to key binding or keyboard layout issues. Here are the most common solutions:

Primary Solution - Keyboard Layout: This script uses the raw keys module. If you have problems with prompts, set the keyboard layout properly.

Additional Troubleshooting Steps:

  1. Check for Key Conflicts:

    • Other resources might be using the same keys
    • Review your Config.keys settings in overwriteConfig.lua
    • Try changing conflicting keys to different bindings
  2. Verify Distance Settings:

    • Make sure you're close enough to interact with objects
    • Check Config.distanceShowHousePrompt and related distance settings
    • Default house prompt distance is 2.0 meters

6. For Developers > Actions > <Badge type="server" text="Server" /> furnitureBought

Triggered when a player purchases furniture for their house.

-- @param source - serverID of the player
-- @param house - the house object where furniture was bought
-- @param furniture - the furniture object that was purchased
-- @param price - resolved price paid for the furniture
-- @param priceIndex - selected price option index
exports.jo_housing:registerAction('furnitureBought', function(source, house, furniture, price, priceIndex)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseBought

Triggered when a player successfully purchases a house.

-- @param source - serverID of the buyer
-- @param house - the house object that was purchased
-- @param price - resolved price paid for the house
-- @param priceIndex - selected price option index
exports.jo_housing:registerAction('houseBought', function(source, house, price, priceIndex)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseKeyBought

Triggered when a new house key is purchased.

-- @param source - serverID of the player buying the key
-- @param house - the house object
-- @param price - resolved price paid for the key
-- @param priceIndex - selected price option index
exports.jo_housing:registerAction('houseKeyBought', function(source, house, price, priceIndex)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseListedForResale

Triggered when an owned house is listed for resale.

-- @param source - serverID of the player listing the house
-- @param house - the house object being listed
-- @param groupOrReason - resale price group or the reason for the listing
exports.jo_housing:registerAction('houseListedForResale', function(source, house, groupOrReason)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseLockChanged

Triggered when a house lock is changed.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param price - resolved price paid for the lock change
-- @param priceIndex - selected price option index
-- @param success - boolean indicating if lock change was successful
exports.jo_housing:registerAction('houseLockChanged', function(source, house, price, priceIndex, success)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseOwnerRemoved

Triggered when an owner is removed from a house.

-- @param source - serverID of the player or script removing the owner
-- @param house - the house object
-- @param ownerRemoved - boolean indicating if the owner was successfully removed
exports.jo_housing:registerAction('houseOwnerRemoved', function(source, house, ownerRemoved)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseSold

Triggered when an owner sells or leaves a house.

-- @param source - serverID of the player selling the house
-- @param house - the house object that was sold
-- @param refundPrice - price refunded to the previous owner
exports.jo_housing:registerAction('houseSold', function(source, house, refundPrice)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseTaxEvicted

Triggered after an owner loses a house because its tax remained unpaid beyond the grace period.

-- @param house - the house object that was evicted
-- @param ownerIdentifier - identifier of the previous owner
-- @param ownerCharId - character ID of the previous owner
-- @param ownerSource - serverID of the previous owner, or false if offline
exports.jo_housing:registerAction('houseTaxEvicted', function(house, ownerIdentifier, ownerCharId, ownerSource)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseTaxPaid

Triggered after a house-tax payment has been processed.

-- @param source - serverID of the player paying the tax
-- @param house - the taxed house
-- @param price - resolved total price for the selected number of periods
-- @param priceIndex - selected price option index
-- @param numPeriods - number of tax periods selected
-- @param success - boolean indicating if the payment was recorded successfully
exports.jo_housing:registerAction('houseTaxPaid', function(source, house, price, priceIndex, numPeriods, success)
    -- Your code here
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

Triggered when a player opens house wardrobe.

-- @param source - serverID of the player
-- @param house - the house object
exports.jo_housing:registerAction('houseWardrobeOpened', function(source, house)
    -- Your code here
end)

#### <Badge type="server" text="Server" /> knockedOnHouse
Triggered when someone knocks on a house door.

```lua
-- @param source - serverID of the player knocking
-- @param house - the house object being knocked on
-- @param foundSources - array of player sources who were notified (propset: eligible players inside the house, MLO/Zone: notified targets excluding the knocker)
exports.jo_housing:registerAction('knockedOnHouse', function(source, house, foundSources)
    -- Your code here
end)
Rental & Keys Actions

<Badge type="server" text="Server" /> playerAddedToHouse

Triggered when a player is added to a house's access list.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param playerSrc - serverID of the player being added
-- @param playerName - name of the player being added
exports.jo_housing:registerAction('playerAddedToHouse', function(source, house, playerSrc, playerName)
    -- Your code here
end)

<Badge type="server" text="Server" /> playerPermissionsChanged

Triggered when the permissions for a player on the access list are changed.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param accessibilityId - ID of the access entry being changed
-- @param permissions - table containing the new permissions
exports.jo_housing:registerAction('playerPermissionsChanged', function(source, house, accessibilityId, permissions)
    -- Your code here
end)

<Badge type="server" text="Server" /> playerRemovedFromHouse

Triggered when a player is removed from a house's access list.

-- @param source - serverID of the house owner
-- @param house - the house object
-- @param accessibilityId - ID of the access entry that was removed
-- @param success - boolean indicating if removal was successful
exports.jo_housing:registerAction('playerRemovedFromHouse', function(source, house, accessibilityId, success)
    -- Your code here
end)

<Badge type="server" text="Server" /> storageLocationSet

Triggered when a storage location is set in a house.

-- @param source - serverID of the player
-- @param house - the house object
-- @param coords - coordinates of the storage location
exports.jo_housing:registerAction('storageLocationSet', function(source, house, coords)
    -- Your code here
end)
Access Management Actions

<Badge type="server" text="Server" /> rentPaid

Triggered when rent is paid for a house.

-- @param source - serverID of the player paying rent
-- @param house - the house object
-- @param numPeriods - number of periods paid for
-- @param isDaily - boolean indicating if it's daily or weekly rent
-- @param totalPrice - total amount paid
-- @param priceIndex - selected price option index
-- @param success - boolean indicating if payment was successful
exports.jo_housing:registerAction('rentPaid', function(source, house, numPeriods, isDaily, totalPrice, priceIndex, success)
    -- Your code here
end)

<Badge type="client" text="Client" /> interiorsInitialized

Triggered after the shared interior list has been initialized and custom interiors have been added.

jo.hook.registerAction('interiorsInitialized', function()
    -- Your code here
end)

Filters

Filters allow you to modify data or control permissions during script execution. Introduced in v1.2.0, filters provide a synchronous way to intercept and modify script behavior at specific points, unlike events which are asynchronous.

Below is a complete list of all available filters in the jo_housing script.

<Badge type="server" text="Server" /> addFurnitures

Allows you to add custom furniture categories and items to the housing system.

-- @param customFurnitures - empty table by default
-- @return table - table with custom furniture categories
exports.jo_housing:registerFilter('addFurnitures', function(customFurnitures)
    -- Add your custom furniture categories
    customFurnitures.custom_electronics = {
        "p_tv01x",
        "p_radio01x", 
        "p_lamp_handheld01x"
    }
    
    customFurnitures.outdoor_furniture = {
        "p_bench05x",
        "p_campfire01x",
        "p_tent01x"
    }
    
    return customFurnitures
end)

:::tip πŸ’‘ Custom Furniture Pricing Don't forget to configure pricing for your custom categories in Config.furnituresCategoriesPrices or specific items in Config.furnituresPrices.

-- In your overwriteConfig.lua
Config.furnituresCategoriesPrices.custom_electronics = {
    money = 250,
    gold = 3
}

Config.furnituresCategoriesPrices.outdoor_furniture = {
    money = 75,
    gold = 1
}

:::

<Badge type="server" text="Server" /> addInteriors

Allows you to add custom interior configurations to the housing system.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param customInteriors - empty table by default
-- @return table - table with custom interior configurations
exports.jo_housing:registerFilter('addInteriors', function(customInteriors)
    -- Add your custom interiors
    customInteriors.my_custom_cabin = {
        entries = {
            vec4(2.022125, -0.556265, 1.696404, 91.094)  -- Main door spawn point (relative to interior origin)
        },
        propset = "my_custom_cabin",     -- Interior propset/MLO name
        category = "shack",              -- Interior category (house, shack, manor, flat, rock_shack, worker)
        numberRoom = 2,                  -- Number of rooms in the interior
        insideDoors = {                  -- Optional: Interior working doors configuration
            { 
                model = "p_door04x", 
                position = vec3(0.062589, -4.6552873, 0.738253) 
            },
            { 
                model = "p_door04x", 
                position = vec3(-1.473662, -4.661302, 0.738253) 
            },
        }
    }
    
    customInteriors.luxury_penthouse = {
        entries = {
            vec4(0.0, 0.0, 2.0, 180.0)  -- Main door spawn point (relative to interior origin)
        },
        propset = "luxury_penthouse_mlo",
        category = "manor",
        numberRoom = 8,
        -- insideDoors is optional - omit if no working interior doors needed
    }
    
    return customInteriors
end)

Interior Properties:

Property Type Required Description
entries table βœ… Array containing the main door spawn point (vec4 with heading). Only the first entry is used, coordinates are relative to interior origin
propset string βœ… Interior propset/MLO resource name
category string βœ… Interior category: house, shack, manor, flat, rock_shack, worker
numberRoom number βœ… Number of rooms in the interior
insideDoors table ❌ Optional array of interior doors with model and position properties

:::tip πŸ’‘ Interior Configuration

-- In your overwriteConfig.lua
Config.interiorsMaxFurnitures.my_custom_cabin = 150
Config.interiorsCategoriesMaxFurnitures.luxury = 300  -- If you add a custom category

:::

<Badge type="server" text="Server" /> canAddPlayerToHouse

Controls who can add players to a house's access list.

-- @param canAdd - boolean indicating if the action is allowed by default
-- @param source - serverID of the player adding someone
-- @param houseId - ID of the house
-- @param playerSrc - serverID of the player being added
-- @param playerName - name of the player being added
exports.jo_housing:registerFilter('canAddPlayerToHouse', function(canAdd, source, houseId, playerSrc, playerName)
    return canAdd
end)

<Badge type="server" text="Server" /> canBuyFurniture

Controls who can buy furniture.

-- @param canBuy - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furniture - furniture model/object data
-- @param houseId - ID of the house
-- @param price - resolved price for the furniture
-- @param priceIndex - selected price option index
-- @param categoryKey - category key of the furniture
exports.jo_housing:registerFilter('canBuyFurniture', function(canBuy, source, furniture, houseId, price, priceIndex, categoryKey)
    return canBuy
end)

<Badge type="server" text="Server" /> canBuyHouse

Controls who can buy a house.

-- @param canBuy - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house being purchased
-- @param price - resolved price for the house
-- @param priceIndex - selected price option index
-- @param period - for rentals, number of periods the player is paying for
exports.jo_housing:registerFilter('canBuyHouse', function(canBuy, source, houseId, price, priceIndex, period)
    return canBuy
end)

<Badge type="server" text="Server" /> canDeleteFurniture

Controls who can delete furniture from a house.

-- @param canDelete - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furnitureId - ID of the furniture
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canDeleteFurniture', function(canDelete, source, furnitureId, houseId)
    return canDelete
end)

<Badge type="server" text="Server" /> canDeleteHouse

Controls who can delete houses.

-- @param canDelete - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseData - table containing house data
exports.jo_housing:registerFilter('canDeleteHouse', function(canDelete, source, houseData)
    return canDelete
end)

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

<Badge type="server" text="Server" /> canEnterBuildMode

Controls who can enter build mode in a house.

-- @param canEnter - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canEnterBuildMode', function(canEnter, source, houseId)
    return canEnter
end)

<Badge type="server" text="Server" /> canEnterHouse

Controls who can enter a house. Door toggling for owned MLO and Zone houses is handled separately by canToggleHouseDoor.

-- @param canEnter - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house being entered
exports.jo_housing:registerFilter('canEnterHouse', function(canEnter, source, houseId)
    return canEnter
end)

<Badge type="server" text="Server" /> canEvictForUnpaidTax

Controls whether an owner can be evicted when a house tax remains unpaid beyond the grace period. Return false to suspend the eviction; the owner keeps the house and the client receives an eviction-suspended status.

-- @param canEvict - boolean indicating if the eviction is allowed by default
-- @param house - the house object that is overdue
-- @param ownerIdentifier - identifier of the owner
-- @param ownerCharId - character ID of the owner
exports.jo_housing:registerFilter('canEvictForUnpaidTax', function(canEvict, house, ownerIdentifier, ownerCharId)
    return canEvict
end)

<Badge type="server" text="Server" /> canGetPlayersAccessibilityList

Controls who can retrieve the list of players with access to a house.

-- @param canGet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player requesting the list
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canGetPlayersAccessibilityList', function(canGet, source, houseId)
    return canGet
end)

<Badge type="server" text="Server" /> canListHouseForResale

Controls who can list a house for resale.

-- @param canList - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canListHouseForResale', function(canList, source, houseId)
    return canList
end)

<Badge type="server" text="Server" /> canMoveFurniture

Controls who can move furniture in a house.

-- @param canMove - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furnitureId - ID of the furniture
-- @param houseId - ID of the house
-- @param relPos - new relative position for the furniture
-- @param relRot - new relative rotation for the furniture
exports.jo_housing:registerFilter('canMoveFurniture', function(canMove, source, furnitureId, houseId, relPos, relRot)
    return canMove
end)

<Badge type="server" text="Server" /> canOpenHouseLocationMenu

Controls who can open the house location menu.

-- @param canOpen - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houses - array of nearby houses
exports.jo_housing:registerFilter('canOpenHouseLocationMenu', function(canOpen, source, houses)
    return canOpen
end)

<Badge type="server" text="Server" /> canOpenHouseStorage

Controls who can open a house's storage.

-- @param canOpenStorage - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canOpenHouseStorage', function(canOpenStorage, source, houseId)
    return canOpenStorage
end)

<Badge type="server" text="Server" /> canOpenHouseWardrobe

Controls who can open a house's wardrobe.

-- @param canOpen - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canOpenHouseWardrobe', function(canOpen, source, houseId)
    return canOpen
end)

<Badge type="server" text="Server" /> canPayHouseTax

Controls whether an owner can pay the tax of a house after the payment has been validated and the price has been resolved.

-- @param canPay - boolean indicating if the payment is allowed by default
-- @param source - serverID of the player paying the tax
-- @param houseId - ID of the house
-- @param price - resolved total price for the selected number of periods
-- @param priceIndex - selected price option index
-- @param numPeriods - number of tax periods selected
exports.jo_housing:registerFilter('canPayHouseTax', function(canPay, source, houseId, price, priceIndex, numPeriods)
    return canPay
end)

<Badge type="server" text="Server" /> canPlaceFurniture

Controls who can place furniture in a house.

-- @param canPlace - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param furniture - furniture model/object data
-- @param houseId - ID of the house
-- @param relPos - relative position where furniture will be placed
-- @param relRot - relative rotation of the furniture
-- @param price - resolved price for the furniture
-- @param priceIndex - selected price option index
-- @param categoryKey - category key of the furniture
exports.jo_housing:registerFilter('canPlaceFurniture', function(canPlace, source, furniture, houseId, relPos, relRot, price, priceIndex, categoryKey)
    return canPlace
end)

<Badge type="server" text="Server" /> canRemoveHouseOwner

Controls who can remove the owner from a house.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param canRemove - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canRemoveHouseOwner', function(canRemove, source, houseId)
    return canRemove
end)

<Badge type="server" text="Server" /> canRemovePlayerFromHouse

Controls who can remove players from a house's access list.

-- @param canRemove - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param accessibilityId - ID of the access entry to remove
exports.jo_housing:registerFilter('canRemovePlayerFromHouse', function(canRemove, source, houseId, accessibilityId)
    return canRemove
end)

<Badge type="client" text="Client" /> canSeeBlipForHouse

Controls if a blip should be created for a specific house on the client. This filter is evaluated locally for each house after the global canSeeHousesBlips filter has allowed blips.

-- @param canSee - boolean indicating if the blip is visible by default
-- @param house - house object
jo.hook.registerFilter('canSeeBlipForHouse', function(canSee, house)
    if not canSee then return false end

    -- Example: only show blips for houses owned by the current player
    return house:isOwner()
end)

<Badge type="server" text="Server" /> canSeeHousesBlips

Controls if the player can see housing blips at all. This filter is called once when the player's housing data is initialized, before houses are sent to the client.

-- @param canSee - boolean indicating if housing blips are visible by default
-- @param source - serverID of the player
exports.jo_housing:registerFilter('canSeeHousesBlips', function(canSee, source)
    return canSee
end)

<Badge type="server" text="Server" /> canSellHouse

Controls who can sell a house.

-- @param canSell - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canSellHouse', function(canSell, source, houseId)
    return canSell
end)

<Badge type="server" text="Server" /> canSetDressingLocation

Controls who can set a dressing room location inside a house.

-- @param canSet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param coords - coordinates for the dressing location
exports.jo_housing:registerFilter('canSetDressingLocation', function(canSet, source, houseId, coords)
    return canSet
end)

<Badge type="server" text="Server" /> canSetStorageLocation

Controls who can set a storage location inside a house.

-- @param canSet - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
-- @param coords - coordinates for the storage location
exports.jo_housing:registerFilter('canSetStorageLocation', function(canSet, source, houseId, coords)
    return canSet
end)

<Badge type="server" text="Server" /> canToggleHouseDoor

Controls who can open or close an owned MLO or Zone house door. This filter is evaluated by the dedicated server door-toggle callback, separately from canEnterHouse.

-- @param canToggle - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canToggleHouseDoor', function(canToggle, source, houseId)
    return canToggle
end)

<Badge type="server" text="Server" /> canTransferHouse

Controls who can transfer house ownership.

-- @param canTransfer - boolean indicating if the action is allowed by default
-- @param source - serverID of the current owner
-- @param toPlayerSrc - serverID of the player receiving the house
-- @param houseId - ID of the house being transferred
exports.jo_housing:registerFilter('canTransferHouse', function(canTransfer, source, toPlayerSrc, houseId)
    return canTransfer
end)

<Badge type="server" text="Server" /> canUpdateAccessibility

Controls who can update a house's accessibility settings.

-- @param canUpdate - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param accessibilityType - type of access ("everyone", "list", or "onlyMe")
-- @param houseId - ID of the house
exports.jo_housing:registerFilter('canUpdateAccessibility', function(canUpdate, source, accessibilityType, houseId)
    return canUpdate
end)

<Badge type="server" text="Server" /> canUpdatePlayerPermissions

Controls who can update permissions for a player on a house's access list.

-- @param canUpdate - boolean indicating if the action is allowed by default
-- @param source - serverID of the player making the changes
-- @param houseId - ID of the house
-- @param accessibilityId - ID of the access entry being changed
-- @param permissions - table of the new permissions
exports.jo_housing:registerFilter('canUpdatePlayerPermissions', function(canUpdate, source, houseId, accessibilityId, permissions)
    return canUpdate
end)

<Badge type="server" text="Server" /> canUpsertHouse

Controls who can create or update houses.

-- @param canUpsert - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
-- @param houseId - ID of the house (nil for creation)
-- @param changesetOrHouseData - changes to apply or full house data for creation
exports.jo_housing:registerFilter('canUpsertHouse', function(canUpsert, source, houseId, changesetOrHouseData)
    return canUpsert
end)

<Badge type="server" text="Server" /> canUseHouseManagerCommand

Controls who can use the /houseManager command to create and manage houses.

6. For Developers > Actions > <Badge type="server" text="Server" /> houseWardrobeOpened

-- @param canUse - boolean indicating if the action is allowed by default
-- @param source - serverID of the player
exports.jo_housing:registerFilter('canUseHouseManagerCommand', function(canUse, source)
    -- Example: Only allow admins
    local isAdmin = exports.your_permission_system:isAdmin(source)
    return isAdmin
end)

<Badge type="server" text="Server" /> houseTaxPrice

Allows you to adjust the total price charged for a house-tax payment, for example to apply discounts or tax-free events. The returned value is validated again before the payment is taken.

-- @param price - resolved total price for the selected number of periods
-- @param source - serverID of the player paying the tax
-- @param house - the taxed house
-- @param numPeriods - number of tax periods selected
-- @param priceIndex - selected price option index
exports.jo_housing:registerFilter('houseTaxPrice', function(price, source, house, numPeriods, priceIndex)
    return price
end)

<Badge type="server" text="Server" /> resalePaymentAmount

Allows you to modify the amount paid to the previous owner when a resale listing is bought.

-- @param amount - default resale payment amount
-- @param previousOwnerSource - serverID of the previous owner, or false if offline
-- @param previousOwnerIdentifier - identifier of the previous owner
-- @param previousOwnerCharId - character ID of the previous owner
-- @param buyerSource - serverID of the buyer
-- @param house - house object
-- @param priceIndex - selected price option index
exports.jo_housing:registerFilter('resalePaymentAmount', function(amount, previousOwnerSource, previousOwnerIdentifier, previousOwnerCharId, buyerSource, house, priceIndex)
    return amount
end)

:::details Example of job lock for VORP

AllowedJobs = {
    houser = true,
    houseManager = true,
}

exports.jo_housing:registerFilter('canUseHouseManagerCommand', function(canUse, source)
    local job = jo.framework:getJob(source)
    if AllowedJobs[job] then
      return canUse
    else
      jo.notif.rightError("You don't have the right job to use the house manager")
      return false
    end
end)

:::

Back to documentation