[{"data":1,"prerenderedAt":84},["ShallowReactive",2],{"site":3,"config-slug-site":15,"config-product-en-x3d-vehiclestorage":21,"config-public-x3d-vehiclestorage":76},{"site":4,"contact":10,"seo":13,"payment":14},{"name":5,"host":6,"default_locale":7,"locales":8},"X3D Developer","fivem.x3d-developer.com","th",[7,9],"en",{"discord_invite":11,"email":12,"line":12},"https:\u002F\u002Fdiscord.gg\u002FNA5kPVynFv","",{"google_site_verification":12,"bing_site_verification":12},{"bank_name":12,"account_name":12,"account_no":12,"promptpay_id":12},{"site":16,"contact":18,"seo":19,"payment":20},{"name":5,"host":6,"default_locale":7,"locales":17},[7,9],{"discord_invite":11,"email":12,"line":12},{"google_site_verification":12,"bing_site_verification":12},{"bank_name":12,"account_name":12,"account_no":12,"promptpay_id":12},{"id":22,"slug":23,"name":24,"summary":25,"price_thb":26,"license_mode":27,"cover_key":28,"cover_url":29,"category_slugs":30,"tags":33,"latest_version":51,"latest_released_at":52,"published_at":53,"has_docs":54,"config":55,"rent_price_satang":56,"description_md":57,"docs_md":58,"seo_title":59,"seo_description":60,"media":61,"versions":69},"93def22a-9501-4b66-bdf0-158cc6a0093c","x3d-vehiclestorage","X3D Vehicle Storage","Free ESX trunk storage on the x3d_inventory screen: capacity by vehicle class and model, owner-only access, job cars and an admin view.",0,"none","public\u002Fproducts\u002Fx3d-vehiclestorage\u002Fcover\u002F2c2d97cb47678e1b.jpg","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vehiclestorage\u002Fcover\u002F2c2d97cb47678e1b.jpg",[31,32],"inventory","garage",[34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50],"trunk","ท้ายรถ","ท้ายรถ fivem","vehicle storage","trunk inventory","esx trunk","เก็บของในรถ","x3d_inventory","esx","ฟรี","free","ตู้ท้ายรถ","รถงาน","กล่องเก็บของหน้ารถ","น้ำหนักท้ายรถ","ล็อกรถ","เปิดท้ายรถ","1.0.2","2026-10-12T17:14:46.845123Z","2026-10-05T04:28:52.749188Z",true,"public",null,"# Trunk storage on the same screen as the player's bag\n\nPress **H** at the back of a car and the trunk opens beside the **x3d_inventory** bag. Drag items in and out and pick the amount, with nothing new to learn.\n\n---\n\n## Features\n\n- [+] Easy to set up: tune everything in config, or edit it from the web builder\n- [+] Press H at the trunk, with the key changeable in config\n- [+] Same x3d_inventory screen: drag items in and out\n- [+] Capacity by GTA vehicle class, all 22 classes\n- [+] Per-model capacity override, 0 = no trunk, whole classes can be switched off\n- [+] Owner-only access, with the check switchable in config\n- [+] Locked cars stay closed, except for jobs you allow\n- [+] Job vehicles open for the jobs you list\n- [+] Your own list of items that cannot be stored\n- [+] Optional front-of-car access to the same storage (glovebox)\n- [+] Admins open any trunk by plate with \u002Ftrunkadmin\n- [+] Connect other scripts through addons\u002F\n\n---\n\n## Realistic capacity\n\n- Capacity per GTA vehicle class, all 22 classes (sedans, SUVs, vans, motorcycles, …)\n- Override any model, or set it to 0 so that model has no trunk\n- Turn storage off for whole classes (cycles, trains)\n\n## Nothing lost, nothing stolen\n\n- Only the owner can open it (checked server-side against the vehicles table)\n- Locked cars stay closed, except for jobs you allow\n- Job vehicles open for everyone with that job (police \u002F ambulance \u002F mechanic, configurable)\n- Items that cannot be dropped are blocked automatically, plus your own blacklist\n- Every move is checked on the server: distance to the car, whole-number amounts, trunk weight and bag space\n\n## For admins and developers\n\n- `\u002Ftrunkadmin \u003Cplate>` opens any trunk\n- `exports.x3d_vehiclestorage:OpenRemote(plate)` opens a trunk from another menu, such as a garage\n- Hooks for your vehicle-key script, gang cars and logs in `addons\u002Fhooks\u002Fserver.lua`\n- Storage of vehicles that no longer exist is cleaned up automatically\n\nThai \u002F English, chosen with `Config.Locale`\n\n**Free** · edit the code as you like · just don't resell it or re-upload it under your own name\n","## Installation\r\n\r\n1. Put the `x3d_vehiclestorage` folder in `resources\u002F`\r\n2. Run `install.sql` (creates the `x3d_vehiclestorage` table)\r\n3. In `server.cfg`, `ensure x3d_inventory` first, then `ensure x3d_vehiclestorage`\r\n4. Your vehicles table isn't `owned_vehicles`? Set `Config.OwnedVehiclesTable`\r\n\r\n### Requirements\r\n\r\n| Needs | Notes |\r\n|---|---|\r\n| ESX Legacy (`es_extended`) | uses `@es_extended\u002Fimports.lua` |\r\n| `oxmysql` | |\r\n| `x3d_inventory` | the trunk uses this bag's storage screen (free in the X3D store) |\r\n\r\n## Main settings (`config.lua`)\r\n\r\n| Setting | What it does |\r\n|---|---|\r\n| `Config.Locale` | `'th'` or `'en'` |\r\n| `Config.OpenTrunkKey` | key that opens the trunk (default `h`) |\r\n| `Config.VehicleWeightByClass` \u002F `Config.VehicleWeightByModel` | capacity (kg) by class\u002Fmodel · 0 = no trunk |\r\n| `Config.CheckOwnership` | owner-only access |\r\n| `Config.JobVehicleAccess` | jobs that can open job vehicles |\r\n| `Config.CheckLockStatus` \u002F `Config.LockBypassJobs` | locked cars stay closed, except listed jobs |\r\n| `Config.ItemBlacklist` | items that can't go in a car |\r\n| `Config.EnableGlovebox` | also open the same storage from the front of the car |\r\n| `Config.AdminGroups` | groups allowed to use `\u002Ftrunkadmin \u003Cplate>` |\r\n\r\n## Connect your own systems (`addons\u002Fhooks\u002Fserver.lua`)\r\n\r\n| Hook | What it does |\r\n|---|---|\r\n| `Hooks.HasVehicleKey(source, plate)` | does the player hold a key for this car (your key script) |\r\n| `Hooks.HasSharedAccess(identifier, row)` | shared access such as gang cars (`row` = the vehicle's DB row) |\r\n| `Hooks.Log(source, kind, data)` | log admin actions |\r\n\r\n## Exports\r\n\r\n- client: `OpenRemote(plate)`\r\n- server: `GetVehicleStorageData(plate)`, `IsStorageOpen(source)`\r\n","Free FiveM ESX Trunk Storage Script","Free FiveM ESX trunk storage on the x3d_inventory screen. Capacity by class and model, owner-only access, job vehicles, admin view, Thai and English.",[62,65,67],{"kind":63,"url":64},"image","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vehiclestorage\u002Fmedia\u002F37a89b010c003240.png",{"kind":63,"url":66},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vehiclestorage\u002Fmedia\u002F5b51a3f37cffac21.png",{"kind":63,"url":68},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vehiclestorage\u002Fmedia\u002Fde12536fa1cd4186.png",[70,72],{"version":51,"released_at":52,"changelog_md":71},"### Changed\n\n- Store images now carry English captions for the English store page",{"version":73,"released_at":74,"changelog_md":75},"1.0.0","2026-10-05T04:28:51.223981Z","### Added\n- Trunk storage on the x3d_inventory storage screen (H at the back of the car), capacity by vehicle class and per model\n- Owner-only access, locked-car check with job bypass, job vehicles, item blacklist, admin `\u002Ftrunkadmin \u003Cplate>`\n- Hooks `HasVehicleKey`, `HasSharedAccess`, `Log` (server) and `OnReady` (client); `OpenRemote` (client), `GetVehicleStorageData` and `IsStorageOpen(source)` (server) exports\n- `Config.Locale` th\u002Fen, `Config.OwnedVehiclesTable`, optional front-of-car access to the same storage (`Config.EnableGlovebox`)",{"slug":23,"version":51,"latest_version":51,"files":77,"options":56},[78,81],{"path":79,"content":80},"config.lua","-- ═══════════════════════════════════════════════════════════════\r\n--  x3d_vehiclestorage — ตั้งค่า \u002F settings\r\n--  X3D-Developer · https:\u002F\u002Fx3d-developer.com\r\n--  ท้ายรถใช้หน้าจอคลังของ x3d_inventory \u002F the trunk uses x3d_inventory's storage screen\r\n-- ═══════════════════════════════════════════════════════════════\r\n\r\nConfig = {}\r\n\r\n-- ===== ทั่วไป \u002F General =====\r\n\r\n-- พิมพ์ log ไล่ปัญหา และวาดวงระยะเปิดท้ายรถ \u002F debug prints and draw the trunk-reach circle\r\nConfig.Debug = false\r\n\r\n-- ภาษาของสคริปต์นี้: 'th' | 'en' \u002F language of this script: 'th' | 'en'\r\nConfig.Locale = 'th'\r\n\r\n-- ตารางรถของผู้เล่นใน DB (ESX ปกติคือ owned_vehicles) \u002F players' vehicles table in the DB (stock ESX: owned_vehicles)\r\nConfig.OwnedVehiclesTable = 'owned_vehicles'\r\n\r\n-- ===== การเปิดท้ายรถ \u002F Opening the trunk =====\r\n\r\n-- ปุ่มเปิดท้ายรถ (ผู้เล่นเปลี่ยนเองได้ใน Settings > Key Bindings) \u002F key that opens the trunk (players can rebind it)\r\nConfig.OpenTrunkKey = 'h'\r\n\r\n-- ระยะ (เมตร) จากท้ายรถที่เปิดได้ \u002F reach (meters) from the trunk\r\nConfig.TrunkInteractionDistance = 2.5\r\n\r\n-- ระยะค้นหารถรอบตัว (เมตร) — ระยะจริงคุมด้วย TrunkInteractionDistance \u002F search radius for a nearby car (meters)\r\nConfig.VehicleSearchDistance = 8.0\r\n\r\n-- ความถี่เช็กรถใกล้ตัว (ms) \u002F how often to look for a nearby car (ms)\r\nConfig.CheckInterval = 500\r\n\r\n-- ===== หน้ารถ \u002F Front of the car =====\r\n\r\n-- เปิดคลังเดียวกันจากหน้ารถได้ด้วย (ไม่ใช่ช่องแยก — ใช้ของชุดเดียวกับท้ายรถ)\r\n-- Also open the same storage from the front of the car (not a separate compartment — same contents as the trunk)\r\nConfig.EnableGlovebox = false\r\n-- ระยะ (เมตร) จากหน้ารถ \u002F reach (meters) from the front\r\nConfig.GloveboxInteractionDistance = 1.5\r\n\r\n-- ===== ความจุ \u002F Capacity =====\r\n\r\n-- น้ำหนักท้ายรถ (kg) ตามประเภทรถ GTA (0 = เก็บของไม่ได้)\r\n-- Trunk capacity (kg) by GTA vehicle class (0 = no storage)\r\n-- 0 Compacts · 1 Sedans · 2 SUVs · 3 Coupes · 4 Muscle · 5 Sports Classics · 6 Sports · 7 Super · 8 Motorcycles\r\n-- 9 Off-road · 10 Industrial · 11 Utility · 12 Vans · 13 Cycles · 14 Boats · 15 Helicopters · 16 Planes\r\n-- 17 Service · 18 Emergency · 19 Military · 20 Commercial · 21 Trains\r\nConfig.VehicleWeightByClass = {\r\n  [0] = 50,   -- Compacts (50kg)\r\n  [1] = 75,   -- Sedans (75kg)\r\n  [2] = 100,  -- SUVs (100kg)\r\n  [3] = 60,   -- Coupes (60kg)\r\n  [4] = 80,   -- Muscle (80kg)\r\n  [5] = 70,   -- Sports Classics (70kg)\r\n  [6] = 65,   -- Sports (65kg)\r\n  [7] = 60,   -- Super (60kg)\r\n  [8] = 25,   -- Motorcycles (25kg)\r\n  [9] = 120,  -- Off-road (120kg)\r\n  [10] = 200, -- Industrial (200kg)\r\n  [11] = 150, -- Utility (150kg)\r\n  [12] = 180, -- Vans (180kg)\r\n  [13] = 10,  -- Cycles (10kg)\r\n  [14] = 150, -- Boats (150kg)\r\n  [15] = 100, -- Helicopters (100kg)\r\n  [16] = 150, -- Planes (150kg)\r\n  [17] = 120, -- Service (120kg)\r\n  [18] = 100, -- Emergency (100kg)\r\n  [19] = 200, -- Military (200kg)\r\n  [20] = 250, -- Commercial (250kg)\r\n  [21] = 0    -- Trains (0kg - no storage)\r\n}\r\n\r\n-- น้ำหนักเมื่อไม่รู้ประเภทรถ (kg) \u002F capacity when the class is unknown (kg)\r\nConfig.DefaultTrunkWeight = 75\r\n\r\n-- น้ำหนักรายรุ่น (ชื่อ model ตัวเล็ก) ชนะค่าตามประเภท — 0 = ไม่มีท้ายรถ\r\n-- Per-model capacity (lowercase model name), beats the class table — 0 = no trunk\r\nConfig.VehicleWeightByModel = {\r\n  bmx = 0,\r\n  cruiser = 0,\r\n  faggio = 0,\r\n  bati = 0,\r\n  stretch = 21,\r\n  guardian = 201,\r\n}\r\n\r\n-- ===== สิทธิ์การเปิด \u002F Access =====\r\n\r\n-- เฉพาะเจ้าของ (หรือคนมีกุญแจ\u002Fสิทธิ์ร่วมผ่าน hook) ถึงเปิดได้ \u002F only the owner (or key\u002Fshared access via hooks) can open\r\nConfig.CheckOwnership = true\r\n\r\n-- รถงาน: ใครมี job ตามรายการเปิดได้ \u002F job vehicles: anyone with a listed job can open them\r\nConfig.AllowJobVehicles = true\r\nConfig.JobVehicleAccess = {\r\n  ['police'] = true,\r\n  ['ambulance'] = true,\r\n  ['mechanic'] = true,\r\n}\r\n\r\n-- กลุ่มแอดมิน: ใช้ \u002Ftrunkadmin \u003Cทะเบียน> และเปิดได้ทุกคัน \u002F admin groups: \u002Ftrunkadmin \u003Cplate> and open any car\r\nConfig.AdminGroups = {\r\n  ['superadmin'] = true,\r\n  ['admin'] = true,\r\n}\r\n\r\n-- ===== ของที่ห้ามเก็บ \u002F Blocked items =====\r\n\r\n-- ไอเทม\u002Fอาวุธที่ห้ามเก็บในรถ (ไอเทม can_remove = 0 ถูกห้ามอยู่แล้ว)\r\n-- Items\u002Fweapons that can never go in a car (items with can_remove = 0 are blocked anyway)\r\nConfig.ItemBlacklist = {\r\n  -- ['item_name'] = true,\r\n  -- ['WEAPON_RPG'] = true,\r\n}\r\n\r\n-- ===== ล้างข้อมูลเก่า \u002F Cleanup =====\r\n\r\n-- ลบคลังของทะเบียนที่ไม่มีในตารางรถแล้ว เมื่อเก่ากว่ากี่วัน (เผื่อรถเช่า)\r\n-- Delete storage of plates gone from the vehicles table after this many days (grace for rentals)\r\nConfig.OrphanSweepDays = 7\r\n\r\n-- ===== ล็อกรถ \u002F Locked cars =====\r\n\r\n-- รถล็อกอยู่ = เปิดไม่ได้ ยกเว้น job ในรายการ \u002F locked car = no access, except listed jobs\r\nConfig.CheckLockStatus = true\r\nConfig.LockBypassJobs = {\r\n  ['police'] = true,\r\n}\r\n\r\n-- ===== รถที่ไม่มีท้ายรถ \u002F Vehicles without storage =====\r\n\r\n-- รุ่นรถที่ไม่มีท้ายรถ (hash) \u002F models without storage (hash)\r\nConfig.BlacklistedVehicles = {\r\n  -- `bmx`,\r\n}\r\n\r\n-- ประเภทรถที่ไม่มีท้ายรถ \u002F classes without storage\r\nConfig.BlacklistedClasses = {\r\n  [13] = true, -- Cycles\r\n  [21] = true, -- Trains\r\n}\r\n\r\n-- ===== ดีบัก \u002F Debug =====\r\n\r\n-- สีวงระยะตอน Config.Debug \u002F colour of the debug reach circle\r\nConfig.MarkerColor = { r = 245, g = 162, b = 74, a = 100 }\r\n",{"path":82,"content":83},"config\u002Fconfig.database.lua","-- =====================================================\n-- X3D VEHICLE STORAGE - DATABASE FUNCTIONS\n-- =====================================================\n-- Following ESX Framework & oxmysql Best Practices\n-- https:\u002F\u002Fcoxdocs.dev\u002Foxmysql\n-- =====================================================\n\n-- =====================================================\n-- HELPER FUNCTIONS\n-- =====================================================\n\nlocal function safeJsonDecode(data)\n  if not data or data == '' then\n    return {}\n  end\n\n  local success, result = pcall(json.decode, data)\n  if success then\n    return result or {}\n  end\n\n  return {}\nend\n\nlocal function safeJsonEncode(data)\n  if not data then\n    return '{}'\n  end\n\n  local success, result = pcall(json.encode, data)\n  if success then\n    return result or '{}'\n  end\n\n  return '{}'\nend\n\n-- =====================================================\n-- GET VEHICLE STORAGE DATA\n-- =====================================================\n\nfunction GetVehicleStorageData(plate)\n  if not plate or plate == '' then\n    return nil\n  end\n\n  local result = MySQL.single.await('SELECT * FROM x3d_vehiclestorage WHERE plate = ?', {plate})\n\n  if not result then\n    return nil\n  end\n\n  return {\n    plate = result.plate,\n    owner = result.owner,\n    items = safeJsonDecode(result.items),\n    weapons = safeJsonDecode(result.weapons),\n    accounts = safeJsonDecode(result.accounts),\n    weight = result.weight or 0,\n    maxWeight = result.max_weight or Config.DefaultTrunkWeight\n  }\nend\n\n-- =====================================================\n-- CREATE VEHICLE STORAGE\n-- =====================================================\n\nfunction CreateVehicleStorage(plate, owner, maxWeight)\n  if not plate or plate == '' then\n    return false\n  end\n\n  -- owner ต้องถูก assign หลังพวก IF() — MySQL ประเมิน ON DUPLICATE ซ้ายไปขวา\n  -- เจ้าของเปลี่ยน (ขายรถ\u002Fทะเบียนถูก reuse) = ล้างของเก่าทิ้ง กันของรั่วข้ามเจ้าของ\n  local mw = math.floor(tonumber(maxWeight) or 0)\n  local affectedRows = MySQL.insert.await([[\n    INSERT INTO x3d_vehiclestorage (plate, owner, items, weapons, accounts, weight, max_weight)\n    VALUES (?, ?, '[]', '[]', '[]', 0, IF(? > 0, ?, ?))\n    ON DUPLICATE KEY UPDATE\n      items = IF(owner \u003C> VALUES(owner), '[]', items),\n      weapons = IF(owner \u003C> VALUES(owner), '[]', weapons),\n      accounts = IF(owner \u003C> VALUES(owner), '[]', accounts),\n      weight = IF(owner \u003C> VALUES(owner), 0, weight),\n      owner = VALUES(owner),\n      max_weight = IF(? > 0, ?, max_weight)\n  ]], {\n    plate,\n    owner,\n    mw, mw, Config.DefaultTrunkWeight,\n    mw, mw\n  })\n\n  return affectedRows > 0\nend\n\n-- =====================================================\n-- SAVE VEHICLE STORAGE DATA\n-- =====================================================\n\nfunction SaveVehicleStorageData(plate, storageData)\n  if not plate or plate == '' then\n    return false\n  end\n\n  local items = safeJsonEncode(storageData.items or {})\n  local weapons = safeJsonEncode(storageData.weapons or {})\n  local accounts = safeJsonEncode(storageData.accounts or {})\n  local weight = storageData.weight or 0\n\n  local affectedRows = MySQL.update.await([[\n    UPDATE x3d_vehiclestorage\n    SET items = ?, weapons = ?, accounts = ?, weight = ?\n    WHERE plate = ?\n  ]], {\n    items,\n    weapons,\n    accounts,\n    weight,\n    plate\n  })\n\n  return affectedRows > 0\nend\n\n-- =====================================================\n-- CHECK IF VEHICLE STORAGE EXISTS\n-- =====================================================\n\nfunction VehicleStorageExists(plate)\n  if not plate or plate == '' then\n    return false\n  end\n\n  local result = MySQL.scalar.await('SELECT COUNT(*) FROM x3d_vehiclestorage WHERE plate = ?', {plate})\n  return result and result > 0\nend\n\n-- =====================================================\n-- GET VEHICLE OWNER FROM ESX owned_vehicles\n-- =====================================================\n\nfunction GetVehicleOwner(plate)\n  if not plate or plate == '' then\n    return nil\n  end\n\n  local result = MySQL.scalar.await('SELECT owner FROM `' .. Config.OwnedVehiclesTable .. '` WHERE plate = ?', {plate})\n  return result\nend\n\n-- =====================================================\n-- DELETE VEHICLE STORAGE (cleanup)\n-- =====================================================\n\nfunction DeleteVehicleStorage(plate)\n  if not plate or plate == '' then\n    return false\n  end\n\n  local affectedRows = MySQL.query.await('DELETE FROM x3d_vehiclestorage WHERE plate = ?', {plate})\n  return affectedRows > 0\nend\n\n-- =====================================================\n-- GET ALL VEHICLE STORAGE BY OWNER\n-- =====================================================\n\nfunction GetPlayerVehicleStorages(identifier)\n  if not identifier or identifier == '' then\n    return {}\n  end\n\n  local results = MySQL.query.await('SELECT * FROM x3d_vehiclestorage WHERE owner = ?', {identifier})\n\n  if not results then\n    return {}\n  end\n\n  local storages = {}\n  for i = 1, #results do\n    local result = results[i]\n    storages[#storages + 1] = {\n      plate = result.plate,\n      owner = result.owner,\n      items = safeJsonDecode(result.items),\n      weapons = safeJsonDecode(result.weapons),\n      accounts = safeJsonDecode(result.accounts),\n      weight = result.weight or 0,\n      maxWeight = result.max_weight or Config.DefaultTrunkWeight\n    }\n  end\n\n  return storages\nend\n\n-- =====================================================\n-- SWEEP ORPHAN STORAGES (plate gone from the owned-vehicles table)\n-- =====================================================\n\nfunction SweepOrphanStorages(days)\n  local affected = MySQL.update.await(([[\n    DELETE vs FROM x3d_vehiclestorage vs\n    LEFT JOIN `%s` ov ON ov.plate = vs.plate\n    WHERE ov.plate IS NULL AND vs.last_updated \u003C NOW() - INTERVAL ? DAY\n  ]]):format(Config.OwnedVehiclesTable), { days })\n\n  return affected or 0\nend\n\n-- =====================================================\n-- EXPORTS\n-- =====================================================\n\nexports('GetVehicleStorageData', GetVehicleStorageData)\nexports('CreateVehicleStorage', CreateVehicleStorage)\nexports('SaveVehicleStorageData', SaveVehicleStorageData)\nexports('VehicleStorageExists', VehicleStorageExists)\nexports('GetVehicleOwner', GetVehicleOwner)\nexports('DeleteVehicleStorage', DeleteVehicleStorage)\nexports('GetPlayerVehicleStorages', GetPlayerVehicleStorages)\n",1792316314944]