Overview
This modified qbx_properties works with drs_garages so owned properties can have garages. It keeps the existing buying, renting, interiors, stash, clothing, and realtor features. Garage access stays updated when owners, renters, or keyholders change.
Requirements
- Framework
- qbx_core
- Required
- ox_lib, oxmysql, ox_inventory, and illenium-appearance
- Garage
- drs_garages with qbx_vehicles and qbx_vehiclekeys
- Resource name
- Must remain qbx_properties
- Optional
- bob74_ipl for added IPL interior choices
Installation
Back up the properties table and existing resource folders, then install the live folder as qbx_properties with fxmanifest.lua directly inside it.
Stop lunar_garage, qbx_garages, and any other overlapping garage owner before starting the DRS stack.
Start the Qbox and OX dependencies first, then drs_garages, qbx_properties, and the optional drs_vehicleshop companion in that order.
Let qbx_properties check the properties.garage column and run property_garages.sql only when it is missing. The first upgrade needs permission to change the database and stops safely if preparation fails.
Test /createproperty, purchase or rental, owner and keyholder access, garage entry, parking, a DRS restart, and /myproperties before live use.
Configuration
Keep the exact runtime names
Install fxmanifest.lua directly inside a folder named qbx_properties and keep the garage folder named drs_garages. Do not use an extra wrapper directory. Companion calls and lifecycle detection depend on these exact resource names.
Confirm the complete runtime set
ox_lib, qbx_core, and oxmysql are declared in the manifest. The active stash and clothing actions call ox_inventory and illenium-appearance directly, so install and start both for the complete property flow. The DRS Qbox path also requires qbx_vehicles and qbx_vehiclekeys.
04Review interiors
Configure property interiors and garageInteriors in config/shared.lua. Start bob74_ipl first only when the added residential, premium, or business IPL entries are enabled.
Allow the first database update
On startup, the resource checks whether properties.garage exists and runs property_garages.sql only when that column is missing. The database account needs ALTER permission for the first upgrade. It checks again afterward and stops if the update failed instead of running with an incomplete database.
Preserve access rules
The /createproperty command checks for the exact realestate job. Server creation also validates the realtor's proximity and limits property-to-entry and entry-to-spawn distances to 125 meters.
How to use and test it
Create a property as a realtor
Choose an interior, enter the name, price, and optional rent interval, enable a garage if wanted, select its interior, then place the on-foot entry and vehicle parking points.
/createpropertyValidate the player workflow
Buy or rent at the door, enter the property, use stash and clothing actions, ring the doorbell, enter the garage interior when configured, then park and retrieve an authorized vehicle.
Test access changes and restarts
Test a purchase, rental, added keyholder, removed keyholder, ended or expired rental, qbx_properties restart, and drs_garages restart. The current owner and keyholders should have access. Other players and doorbell visitors should not, and removed access should disappear after the database update succeeds.
Check property discovery
Confirm available properties use their configured blips, owned and keyholder properties appear distinctly, and the property list can set a GPS route.
/mypropertiesExercise every storage mode you enable
Park and retrieve a property vehicle under the configured DRS global, garage, or property storage policy. Confirm inaccessible-property recovery behaves as configured, then re-park recovered vehicles so their current canonical assignment is saved.
How the property garage connection works
qbx_properties stores the property and access list. DRS Garages handles its vehicle storage while the server is running.
- Requested identity
- property_qbx_<database id>
- Display identity
- Editable property_name label
- Access authority
- Property owner and current keyholders
- Runtime authority
- Canonical ID returned by drs_garages
Stable identity
The requested garage ID is property_qbx_<properties.id>. It is derived from the immutable numeric row ID rather than the editable property name, so renames and duplicate display names do not change or collide with garage identity.
Use the ID returned by DRS Garages
Registration sends the label, car type, separate entry and spawn coordinates, optional interior, owner citizen ID, and keyholder citizen IDs to drs_garages. qbx_properties saves the non-empty ID returned by DRS and uses it for later updates, removal, and garage-interior entry.
Access refresh
Purchase, rental, and key changes commit to the database before qbx_properties registers the complete current record again. Re-registration updates DRS runtime access; the bridge does not call RefreshPropertyGarage directly.
Restart and removal behavior
Owned garage rows register after schema readiness. Starting or restarting DRS rebuilds registrations, rental end removes the garage, and stopping qbx_properties removes its cached DRS registrations. Failed removals retry with a bounded backoff.
Database and upgrade behavior
Portable garage migration
property_garages.sql contains one ALTER TABLE statement adding a nullable JSON garage column. qbx_properties executes it only after confirming the column is missing, making the runtime check compatible with MariaDB and MySQL 8 without stored procedures or multi-statement settings.
Existing rows remain intact
The migration does not replace the properties table or rewrite property rows. Legacy one-coordinate garage JSON is normalized at runtime into matching entry and spawn points.
Review old garage assignments
Older name-derived property garage IDs are not bulk-rewritten. Review DRS recovery behavior for the selected storage mode; when recovery is enabled, retrieve and re-park affected test vehicles to persist the new canonical property ID. When recovery is disabled, plan an administrator-reviewed reassignment.
Rental timers after restart
The upstream schema does not persist the next rental due timestamp. Restored active rentals wait one complete configured interval after qbx_properties starts instead of charging immediately on each restart.
Troubleshooting
Stash or clothing actions fail
Confirm ox_inventory and illenium-appearance start before qbx_properties. These feature paths call their exports directly even though the manifest does not declare them as dependencies.
A property garage does not appear
Confirm drs_garages reached database readiness before qbx_properties, both resources use their exact names, and the property row has an owner plus valid garage JSON. Run drsgarages:doctor, review both server logs, and verify the registering payload contains property_qbx_<id>, entry and spawn coordinates, owner, and current keyholders.
The garage schema migration stops the resource
Read the qbx_properties startup error, confirm the properties table exists, and grant the oxmysql account permission to inspect INFORMATION_SCHEMA and ALTER the table. With the resource stopped and a backup available, an administrator may run property_garages.sql once only when the garage column is confirmed absent.
Access is stale after a key or rental change
Confirm the guarded property update succeeded before the DRS re-registration. Restart drs_garages and then qbx_properties once to rebuild runtime registrations, and check that no legacy garage resource is competing for the same property.
Vehicles still reference an older property name ID
Review the selected DRS storage mode and RecoverInaccessibleProperties policy. Do not bulk-rewrite framework vehicle rows blindly. Test recovery with a backed-up vehicle, then re-park it at the current property so DRS records the stable canonical assignment.
An interior preview is empty or unsafe
Remove invalid shell entries, confirm bob74_ipl is running for its added IPLs, and test every configured exit, clothing, stash, and logout coordinate before publishing the property.
Reference
Property registration payload
The bridge builds this server-side DRS contract from the authoritative property row. The returned canonical ID must be used for subsequent runtime actions.
local requestedId = ('property_qbx_%d'):format(propertyId)
local canonicalId = exports.drs_garages:RegisterPropertyGarage(requestedId, {
id = requestedId,
label = propertyName,
vehicleType = 'car',
entryCoords = garage.entry,
spawnCoords = garage.spawn,
interior = garage.interior,
owner = ownerCitizenId,
keyholders = keyholderCitizenIds,
})Update and removal behavior
The current property bridge refreshes DRS by registering the full current record again with the cached canonical ID. When ownership ends or the property resource stops, it removes that same canonical registration.
canonicalId = exports.drs_garages:RegisterPropertyGarage(canonicalId, currentPropertyData)
exports.drs_garages:RemovePropertyGarage(canonicalId)Player and realtor commands
These are the public commands exposed by this build. Its namespaced callbacks and network events are internal implementation details rather than a third-party API.
/createproperty
/myproperties- Older garage JSON containing one coordinate remains supported and is normalized into matching entry and spawn positions.
- drs_garages is selected before the legacy lunar_garage transition path. qbx_garages is used only when neither is running and does not provide the equivalent interior or public unregister lifecycle.
- The public main branch reports version 0.0.1 and currently has no GitHub tag or packaged Release. Pin the tested commit used by your server.
- ox_inventory and illenium-appearance are used directly even though the manifest does not declare them as dependencies.
- This is a GPL-3.0 manual derivative of Qbox-project/qbx_properties; retain Qbox Project attribution.