legacy-garages
Troubleshooting
Known gotchas and pitfalls when running or configuring legacy-garages.
Troubleshooting
Resource conflicts
jg-advancedgaragesmust be disabled. legacy-garagesprovide()s thejg-advancedgaragesresource name; two resources cannot provide the same name at once. Move the original toresources-disabled.- Do not rename this resource in
fxmanifest— third-party scripts (vms_housing,rcore_police,lb-phone) rely on thejg-advancedgaragesname alias to keep working.
Database
- This resource depends on JG's exact
player_vehiclescolumns (in_garage,garage_id,impound,impound_data,nickname,job_vehicle,gang_vehicle,damage, etc.). Schema changes to that table will break compatibility — there is no migration path in the other direction. sql/loadouts.sqlusesCREATE TABLE IF NOT EXISTSand is not auto-applied — import it manually before first launch, orlegacy-garages:saveLoadoutwill error on a missing table.legacy_garages_loadoutshas a composite primary key (citizenid,garage,vehicle) — re-saving a loadout overwrites the previous row instead of inserting a new one.configis stored as unvalidated LONGTEXT JSON, andcitizenidhas no foreign key constraint (orphaned rows can persist after character deletion).
Config gotchas
- Renaming a
Config.Garageskey changes thegarage_idvalue expected in the database — existing rows referencing the old key will point at a garage that no longer exists unless you run a manualUPDATE. Config.Financeonly works ifjg-dealershipsis running and exposesmakeFinancePayment; enabling it without that resource will error when a financed vehicle is opened.jg-dealershipsexposes no single payoff export, sofinancePayoffloops payments (max 60) and can leave partial progress if funds run out mid-loop.trunkLoadout/gloveboxLoadoutitem ids are validated againstox_inventoryat boot — an unknown item id is a startup error, not a silent no-op.Config.CheckVehicleModel = truewill silently refuse to spawn vehicles whose stored DB model doesn't match — bulk-imported vehicles with mismatched model columns will fail to spawn until fixed or the check is disabled.Config.AllowInfiniteSpawns = falsecan leave a vehicle permanently "stuck out" if a previous session never marked it returned (e.g. a crash).Config.ImpoundReleaseExternalbeing set to a resource name changes the release UX to just show a message — if that external resource isn't actually running impound releases, players will have no way to release vehicles.Config.UseTarget = truerequires the resource inConfig.Target(defaultox_target) to be started before legacy-garages.- Mixing up
vehiclesType('owned'vs'spawner') on a job garage entry changes its entire interaction flow. Config.ClassTypesonly maps a subset of vehicle class IDs — unmapped classes have no defined garage type, affecting where such vehicles can be stored.
Runtime / restart behaviour
- On every resource start (including a manual
restart legacy-garages), all vehicles within_garage = 0andimpound = 0are force-set back toin_garage = 1after a short delay — any vehicle legitimately left outside (e.g. tracked via valet) is silently pulled back in, with no return fee charged despite referencing JG's fee behavior in comments. - The
jg-advancedgarages:server:register-vehicle-outsidecompat handler only updatesin_garage— it does not despawn the world entity or validategarage_id, and combined with the restart-recovery behavior above, a vehicle "checked out via valet" will be silently pulled back on next restart. reserveUniquePlate/used-plates tracking is in-memory only and resets on restart — plate collisions are only prevented within a single server session (plus a DB check against stored vehicles).getMileageUnit()caches lazily on first successful call becausejg-vehiclemileageis expected to start after legacy-garages; reversing load order can set (or permanently fail) the cache at the wrong time.GetPlayerGangNameand job-grade label lookups callqbx_coreexports directly, bypassinglegacy-lib's framework bridge — swapping frameworks requires patching these two functions insv-main.luaspecifically.
Client-side gotchas
- The showroom preview vehicle is not frozen and keeps physics (only collision is disabled) — this is required for extras (e.g. lightbars) to re-attach correctly. Don't "fix" apparent drift by freezing it; the camera loop already pins its position every frame.
- Toggling a showroom extra fully re-spawns the preview vehicle (a new one is created before the old is deleted) rather than mutating it in place, because
SetVehicleExtradoesn't reliably re-attach some extras live. SpawnJobVehicle'sentry.platemust be the full, server-reserved, session-unique plate — appending anything to it can cause two vehicles to collide and share a trunk/glovebox loadout server-side.'legacy-garages:client:despawn-plate'only trims trailing whitespace when matching plates — differently padded/cased plates elsewhere won't match, and the vehicle will silently fail to despawn.StoreVehicle()and the impound form both prefer arealPlatestatebag (forlegacy-pettycrimefake-plate compatibility) over the visible plate — if you set fake plates via another mechanism, mirror the real plate intoEntity(vehicle).state.realPlateor garage integrations will store/impound the wrong plate.removeBlips()must be used to clear blips (it stores{handle, coords, group}tables, not raw handles) — passing one of these directly toRemoveBlipis a silent no-op and leaves blips stranded after a restart.- Storing a vehicle whose type (car/air/sea, from
Config.ClassTypes) doesn't match the garage's configured type is rejected client-side with an error notification and never reaches the server.
UI build gotchas
web/distis what the game actually serves — the Vite dev server (port 3006) is for local preview only. Runnpm run buildafter every UI change.vite.config.jssetsbuild.emptyOutDir: true— every production build wipesweb/distfirst; don't manually place files there expecting them to persist.- Tailwind only scans
./index.htmland./src/**/*.{js,jsx}for classes — new UI source files outsidesrc/won't be scanned for class purging. - The Vite
baseoption must remain'./'(relative) for assets to load inside the FiveM NUI (CEF); an absolute path will break asset loading in-game.