legacy-crafting
Troubleshooting
Known gotchas and pitfalls when running or extending legacy-crafting.
Troubleshooting
Resource won't start / errors immediately
- Load order.
legacy-lib,ox_lib, andoxmysqlmust start beforelegacy-crafting. In particular,client/benches.luacallsexports['legacy-lib']:Target()at file load time — iflegacy-libisn't started first, this fails immediately. - legacy-skills not started.
legacy-skillsis not a hard dependency, but ifConfig.UseSkillsResource = trueand it isn't running, XP calls will fail or behave unexpectedly.GetCraftingProgresssilently falls back to the local tier/XP system iflegacy-skills's resource state isn't'started'— this fails silently, so double-checkConfig.UseSkillsResourcematches what's actually running.
Benches not appearing
fetchAndSpawn()only runs once, 2000ms after resource start, and again onlegacy-crafting:syncBenches. There's no retry/backoff — ifoxmysqlis still connecting when that timer fires, benches simply won't show until the next sync event.- For
use_existing_propbenches,OpenCraftingresolves camera position from the live spawned entity first, falling back to storedbench.coords. Existing-prop benches never have a spawned entity, sobench.coordsmust be populated correctly in the DB or the camera will be positioned wrong. - If you insert bench rows directly into
legacy_crafting_benchesinstead of using the in-game placement UI (StartBenchPlacement/StartExistingPropSelection), you must manually populatecamera_angle,camera_height,camera_distance,prop_offset_x/y/z, andplayer_coords— otherwise the camera/prop preview silently falls back to defaults (0,0,0.85 offset,Config.CameraOffsetheight/distance,'front'angle) and the player will play the crafting animation wherever they're standing instead of at the bench. RegisterBenchStashes()only runs once, 1 second after resource start. Iflegacy-craftingitself restarts, every storage-enabled bench must still exist inlegacy_crafting_benchesfor its stash to re-register on boot.
Admin panel / command rejected
Config.AdminCommandand every admin export checkIsAdmin(), which combines the framework's own admin flag with ACE group membership (group.<name>) for each entry inConfig.AdminGroups. Players need the ACE group actually granted, not just an in-game admin job/permission level — this is the most common reason admins get rejected.- The admin command rejects console execution (
source == 0) entirely.
Crafting fails or behaves unexpectedly
- Storage-mode benches never see player inventory items. If
bench.storage_enabledis true, ingredient checks look only at the bench's stash, never the player's inventory. A player carrying the item but with an empty stash will always see "Missing". - ox_inventory is hard-required for crafting, even outside storage mode — the
craftcallback callsexports.ox_inventory:Search/RemoveItemdirectly rather than going through theInventorywrapper. Swapping inventory frameworks vialegacy-libdoes not change this. - required_jobs format differs between benches and recipes.
bench.required_jobsis a raw comma-separated string compared against the player's job/gang name.recipe.required_jobsonlegacy_crafting_recipesis stored/decoded as JSON. Writing the wrong format directly to the DB will silently break access checks for one or the other.
XP / tiers look wrong
- When
Config.UseSkillsResource = true,legacy_crafting_xp.xpstays at0for every player (onlytotal_craftsis tracked locally) — XP actually lives inlegacy-skills. Readinglegacy_crafting_xpdirectly in this mode will misleadingly show 0 XP / Novice even for high-level players. cachedTiersis a module-level cache populated on firstgetTiers()call, only invalidated bysaveTier/deleteTier. Editinglegacy_crafting_tiersdirectly in the database will not be reflected until a resource restart.- If
legacy_crafting_tiersis empty,LoadTiers()silently falls back toConfig.Tiersfromconfig.luainstead of erroring — seed the table or configureConfig.Tiersbefore relying on tiers. - Toggling
Config.UseSkillsResourcechanges whatrequired_levelmeans (1–5 vs 1–75) — recipes configured for one system will be misconfigured after switching without revisitingrequired_levelvalues on every recipe.
Database / seed script pitfalls
sql/schema.sqlstarts withDROP TABLE IF EXISTSon alllegacy_crafting_*tables — never run it against a live database unless you intend a full wipe.- Cascading deletes are in effect: deleting a category deletes all its recipes; deleting a recipe or bench deletes its ingredients/assignments. Using
deleteCategory/deleteRecipe/deleteBenchhas effects beyond the single row. sql/seed_gun_recipes.sqlandsql/seed_heists_and_weapons.sqlare idempotent via upfrontDELETE ... WHERE output_item IN (...)blocks — re-running them discards any manual edits you made to those same recipes in the admin panel.sql/seed_heists_and_weapons.sqluses raw weapon hash strings (e.g.WEAPON_MINIGUN) asoutput_item— your inventory system must recognize these as valid items or crafting will succeed server-side while producing an item your inventory can't handle. It also seeds some recipes withrequired_levelup to 72; if your tier/XP curve doesn't reach that high, those recipes are permanently uncraftable until adjusted.saveBenchlooks upprop_modelfrom alegacy_crafting_bench_typestable whenbench_type_idis given andprop_modelis blank. This table is not part of the documented schema — if it doesn't exist or isn't populated, the lookup silently returns nothing and falls back toprop_tool_bench02.
NUI / locale issues
- The NUI (
web/) must be built withnpm install && npm run buildinsideweb/to produceweb/dist— the Lua side loads the built output, not the rawsrc. Running commands from the resource root won't trigger this build. - Adding a new language requires a
locales/<locale>.jsonwith the exact same keys aslocales/en.json— there is no fallback for missing keys. web/vite.config.jsmust keepbase: './'; changing it to an absolute path breaks asset loading inside the FiveM CEF browser.tailwind.config.jsreferences CSS variables (--accent,--accent-dim,--accent-gradient) that must be defined in a root CSS file elsewhere, or accent color/gradient utility classes won't render correctly.