sh-housing is a server-authoritative, database-backed housing system for RedM. It supports VORP and RSG from one codebase and provides residential properties, hotel rooms, buy/rent/finance contracts, furniture placement, property storage, access sharing, realtor workflows, and sh-doorlocks integration.
This page covers both routine configuration and source-level customization. Back up config.lua, the data/ directory, and all sh_housing_* database tables before changing a production installation.
System overview
Player housing
Realtor tools
Hotels
Content packs
Installation
Install dependencies
oxmysql, your framework core, and the matching inventory. Install sh-doorlocks and sh-society when using the default integrations.Keep the resource name unchanged
sh-housing. The resource stops itself when started under another name.Import the schema
sh-housing/sql/install.sql against the same database used by your framework and oxmysql.Configure the resource
config.lua, especially the framework, economy, realtor job, storage, door provider, interaction mode, and content synchronization sections.Configure administrator access
shhousing.admin ACE or add the relevant framework groups, jobs, or gangs to Config.AdminAccess.Use a deterministic start order
Verify startup output
Commands
All command names can be changed inConfig.Commands.
Configuration guide
Framework, locale, and debugging
Framework, locale, and debugging
Config.Framework accepts auto, vorp, or rsg. Explicit selection is useful when both framework resources exist in a development server.Security and distance validation
Security and distance validation
Config.Security is enforced server-side. These values are not only visual ranges.EventCooldownMsthrottles repeated event calls.MaxInteractDistanceis the general property/hotel action range.MaxPropertyCreateDistancelimits how far a realtor can create or update content.MaxBuildDistanceandMaxBuildHeightDeltaconstrain furniture placement.MaxBuildMoveDeltalimits one placement update.MaxRequestPerMinuteis the per-player RPC budget.
Realtor jobs and grades
Realtor jobs and grades
BrokeredContractsOnly = true prevents ordinary players from bypassing the realtor workflow. When RequireNearPropertyForContracts is enabled, the realtor and buyer must complete the contract near the property marker.Administrator access
Administrator access
Economy, rent, and finance
Economy, rent, and finance
Config.Economy.DefaultCurrency supports cash, gold, allbanks, or a valid framework-specific money key.Purchase tax applies to buy and finance contracts. Rent tax applies to each rent cycle. Hotel tax applies to the initial hotel charge.Offline billing
Offline billing
Storage and furniture
Storage and furniture
Config.Storage supplies defaults when a property or hotel data entry does not define its own values.Config.Furniture controls placement limits, movement precision, render batching, furniture-store locations, wallet resale, and animal roaming.Keep RenderSpawnPerTick conservative on servers with large catalogs or densely furnished properties. Increasing render distance makes more objects eligible to exist on each client and can materially increase client load.Door provider
Door provider
auto uses sh-doorlocks when the resource is started and otherwise selects the internal backend. Explicitly set sh-doorlocks if missing doorlocks should be treated as a deployment error rather than silently falling back.Enable RSGManagedLockMode when an RSG character identifier cannot be converted to the numeric character ID expected by the door resource. In managed mode, housing permission checks remain authoritative.Target and prompt modes
Target and prompt modes
Config.Target.Mode accepts target, ingame, text3d, or auto.targetuses the configured target resource.ingameuses native RedM prompt groups.text3ddraws world text and listens for configured keys.autoprefers the target resource and falls back toFallbackMode.
ox_target, update Config.Target.Resource and confirm the target resource exposes compatible zone functions.Content synchronization
Content synchronization
forceUpdateProperties = true means a changed content file can replace database-backed fields for matching property UIDs. Runtime-created storage marker metadata is preserved, but you should still treat forced synchronization as a write operation and keep a database backup.Realtor society ledger
Realtor society ledger
sh-society.SocietyId when the society identifier differs from the realtor job name. Prefer correct server.cfg ordering over StartIfMissing = true.Content loading and data ownership
The content service loads these files by default:fxmanifest.lua make files available to the resource, but they do not automatically make the content service parse every JSON file. Add new file paths to MANAGED_CONTENT_FILES when splitting content into multiple files.uid; furniture and interior presets use id.
Source files versus database rows
The JSON content files are seed and synchronization sources. Live ownership, contracts, access grants, placed furniture, and payment history are stored in MySQL.Editing property data
Editdata/properties/properties_data.json to define repeatable property templates.
Property field reference
buildZone can be used instead of propertyLine. The preferred normalized form is:
Editing doors
Door handling has three distinct layers:data/doors/door_index.luaidentifies known world doors for automatic discovery.- A property’s
doorDatastores which doors belong to that property. sh-doorlocksor the internal backend owns the active lock state after the property becomes occupied.
Property door lifecycle
- The in-game property creator attempts to discover up to 64 doors inside the property boundary.
- Discovered mappings are stored with the property.
- An unsold property has no active doorlock records attached.
- When a buy, rent, or finance contract assigns an owner, housing creates or updates active door records and grants the owner access.
- Access changes rebuild the door access payload.
- When ownership is removed, housing removes the active door records but retains reusable mapping data without database door IDs.
Add a custom MLO door to the index
Add an entry todata/doors/door_index.lua using this structure:
Define a single door mapping
Define multiple property doors
Use the door override file
data/properties/property_doors_overrides.json lets you keep generated door mappings separate from a large property pack. Entries match a property by uid.
Editing interior and shell data
data/interiors/interiors_data.json can contain reusable presets, property-style interior entries under properties, or both.
Reusable teleport preset
entry and exit are absolute teleport points.
Spawned shell property
Shell coordinate rules
originCoordsis where the shell object is spawned.entryCoordsis where the player appears inside the shell.exitCoordsis the in-shell interaction point that returns the player to propertycoords.spawnDepthis a fallback Z offset when no absolute origin is supplied.entryOffsetandexitOffsetare supported fallbacks relative to the shell origin.headingOffset,entryHeadingOffset, andexitHeadingOffsetcan adjust orientation.onlyInside = truerestricts build validation to the interior zone when one is configured.
Editing hotel data
Editdata/hotels/hotels_data.json.
cycleHours, while residential rent uses rentCycleDays. An enabled hotel with mapped door data synchronizes its active door through the hotel contract lifecycle.
Editing the furniture catalog
Editdata/furniture/furniture_data.json.
Metadata reference
metadata objects let content authors attach structured information without adding database columns or changing the base content schema. Metadata is JSON, so values can be strings, numbers, booleans, arrays, or nested objects.
Use metadata for optional presentation details, grouping, integration identifiers, and runtime hints. Keep required business data such as prices, coordinates, owner IDs, and contract status in their documented fields.
Furniture metadata and custom images
The furniture catalog UI recognizes these preview keys inside each item’smetadata object:
metadata.imageUrl rather than a top-level imageUrl. The furniture normalizer preserves the metadata object, while its normalized top-level output only includes id, model, label, category, price, tags, and metadata.
metadata.imageormetadata.imageUrl.- The automatic model image generated from the model name.
- A metadata fallback URL.
- A hardcoded fallback in
CATALOG_PREVIEW_FALLBACKSinsideui/app.js. - The configured default fallback image.
Hosting furniture images inside the resource
You can ship preview images withsh-housing instead of using an external CDN.
- Create
ui/images/furniture/. - Add the image files to the
filesblock infxmanifest.lua. - Reference them relative to
ui/index.html.
sh-housing after adding new files to fxmanifest.lua. Refreshing only the JSON does not add newly introduced image files to the resource manifest.
Furniture tags and custom grouping
Furniture supports a top-leveltags array and any custom fields inside metadata.
id, label, model, and category. Tags and arbitrary metadata do not change behavior until another script or a UI customization consumes them.
To include top-level tags in catalog searches, update the search haystack in server/services/content_service.lua:
sh-housing after modifying the Lua service.
Furniture runtime metadata
Some furniture metadata fields affect placed entities:category to animals; the server automatically applies entityType = 'ped' and animalFurniture = true to the placement. Do not manually set walletConsumed or storePriceCash in catalog metadata because those fields represent transaction state.
category and sourceSection; transaction and placement fields are then added separately. Copy additional catalog metadata explicitly in furniture_service.lua if your placement runtime needs it.Property metadata
Property metadata contains several recognized fields:GetProperty or GetProperties from your integration resource and enforce any additional rule server-side.
Interior metadata
Interior metadata supports shell-related aliases and presentation fields. Recognized keys include:shell.model,interiorModel,interior_model, orInterior_Modelshell.originCoords,originCoords, orspawnCoordsshell.entryCoordsorentryCoordsshell.exitCoordsorexitCoordsshell.entryOffsetorentryOffsetshell.exitOffsetorexitOffsetshell.spawnDepthorspawnDepthheadingOffset,entryHeadingOffset, andexitHeadingOffsetInterior_SubModelpage
metadata.shell structure for new content because it keeps shell-specific fields grouped together.
Access metadata
Access grants accept arbitrary metadata. Job-based door rules recognizeminGrade:
minGrade when building sh-doorlocks job-grade rules. Other custom access metadata is stored for integrations and auditing but does not grant additional permissions by itself.
Contract and door metadata
Contract metadata stores terms and system-generated finance/tax state. Door metadata sent tosh-doorlocks identifies the managing system, entity kind, property/hotel ID, and door index.
Treat these as system-owned fields. Extend them only after checking the corresponding contract or door bridge service, and do not replace the complete metadata object during a partial update unless you intend to remove existing system state.
Localization
Locale files are located underlocales/.
To add French:
- Copy
locales/en.luatolocales/fr.lua. - Keep every message key unchanged and translate only the string values.
- Add
locales/fr.luatoshared_scriptsinfxmanifest.lua. - Set
Config.Locale = 'fr'.
ui/app.js. Add matching translations there when introducing a language that must localize the full web interface.
Access grants
The implemented access types are:char, job, group, and gang.
expiresAt is a Unix timestamp in seconds. Expired grants are ignored and periodically cleaned from the database.
Server exports
Programmatic property example
Database tables
Use a staging database
Create an unpublished property
Check automatic door mappings
Test every contract path
Verify permissions
Restart all relevant resources
Troubleshooting
A property appears in JSON but not in game
A property appears in JSON but not in game
- Validate the JSON syntax.
- Confirm the file is listed in
MANAGED_CONTENT_FILES. - Confirm the property has a non-empty unique
uid. - Check console output for content parsing or database errors.
- Run Sync Content Packs from the admin panel or restart
sh-housing. - If the UID already exists, review
forceUpdatePropertiesbehavior.
A custom MLO door is not detected
A custom MLO door is not detected
- Add the door to
data/doors/door_index.luaor the correspondingsh-doorlocksindex. - Verify door hash, model hash, model name, and world coordinates.
- Confirm the door lies inside the property’s polygon and vertical range.
- Use Auto Door (Nearest) while standing at the door when polygon discovery is unsuitable.
- Use Set Door Manual when the MLO does not expose a discoverable indexed door.
The door maps but does not lock after sale
The door maps but does not lock after sale
- Confirm
Config.Doors.Providerresolves to the intended backend. - Confirm
sh-doorlocksstarts beforesh-housing. - Press Sync Door and record the exact error.
- Check for an existing
sh-doorlocksrow with the same hash and coordinates. - Confirm the housing property has
doorDataand receives a persisteddoorIdafter sale. - If the lock state changes in the panel but the object remains movable, re-check the MLO door hash/model/coordinates.
The owner cannot operate a door
The owner cannot operate a door
- Confirm the property owner character ID matches the current selected character.
- On RSG, verify the numeric door character ID mapping.
- Enable
RSGManagedLockModeif identifiers cannot be converted safely. - Re-sync the property door after ownership or access changes.
Furniture cannot be placed
Furniture cannot be placed
- Confirm build mode is active for the selected property.
- Check the exterior or interior build polygon and its Z limits.
- Check
MaxBuildDistance,MaxBuildHeightDelta, andMaxBuildMoveDelta. - Confirm the player has owner, manage, or build access.
- Verify the catalog model exists and streams correctly.
A shell is black or teleports incorrectly
A shell is black or teleports incorrectly
- Confirm the model name is valid and streamed.
- Verify
originCoords,entryCoords, andexitCoordsare in the same shell coordinate space. - Test the shell without offsets before adding heading offsets.
- Adjust
Config.Interiors.Lightingonly after the shell position is correct. - Do not use lighting intensity to hide a shell that spawned at the wrong coordinates.
Content changes overwrite in-game edits
Content changes overwrite in-game edits
forceUpdateProperties or forceUpdateHotels option, or make the JSON source authoritative and apply the change there as well.Door mappings edited in-game can be exported to property_doors_overrides.json so they become part of authoritative content.Recommended production workflow
- Define properties, hotels, and interiors in source-controlled JSON.
- Use unpublished listings while measuring markers, polygons, shell points, and doors.
- Use in-game auto mapping for doors, then export mappings to the override file.
- Validate JSON and JavaScript before deployment.
- Seed into a staging database and complete contract/access tests.
- Back up production tables.
- Deploy data and scripts together, then restart dependencies in order.
- Monitor initialization, content-sync, contract, and door-sync logs.