[{"data":1,"prerenderedAt":96},["ShallowReactive",2],{"site":3,"config-slug-site":15,"config-product-en-x3d-vault":21,"config-public-x3d-vault":88},{"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":53,"latest_released_at":54,"published_at":55,"has_docs":56,"config":57,"rent_price_satang":58,"description_md":59,"docs_md":60,"seo_title":61,"seo_description":62,"media":63,"versions":77},"b642bb91-9f6d-4c3c-8830-e594c2a92fcb","x3d-vault","X3D Vault","Free ESX vaults on the x3d_inventory screen: player vaults with levels and sharing, job vaults per society, Thai and English.",0,"none","public\u002Fproducts\u002Fx3d-vault\u002Fcover\u002F72ea3a88a64e0034.jpg","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fcover\u002F72ea3a88a64e0034.jpg",[31,32],"inventory","jobs",[34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],"vault","stash","ตู้เซฟ","ตู้เก็บของ","ตู้เซฟ fivem","esx stash","job vault","ตู้หน่วยงาน","x3d_inventory","esx","ฟรี","free","ตู้เซฟแก๊ง","ตู้ส่วนตัว","แชร์ตู้","อัปเกรดตู้","ตู้เซฟตำรวจ","ตู้เซฟอาชีพ","ลิมิตตู้","1.0.2","2026-10-12T17:15:03.928293Z","2026-10-05T04:28:43.828514Z",true,"public",null,"Vaults that open beside the player's bag. Walk up to a vault, press **E**, pick a vault, and its storage opens next to the **x3d_inventory** bag. Drag items in and out right away.\n\nIncludes player vaults that players create, level up and share with friends, and job vaults open only to one job. Free, for ESX.\n\n---\n\n## Features\n\n- [+] Easy to set up: tune everything in the config, or change it from the web\n- [+] Players create their own vaults; set an item they must have to create one\n- [+] Name and rename vaults; each gets a code like #7WT6, with a length you set\n- [+] Level vaults up with upgrade cards; slots and weight are set per level\n- [+] Separate money, item and weapon limits per level\n- [+] Share a vault with friends and revoke access; cap vaults per player\n- [+] Job vaults, open only to people with that job\n- [+] Set an item needed to open a vault; leave empty and none is needed\n- [+] Allow and block lists per vault type, for items, money and weapons\n- [+] Set every location, safe model, blip and label yourself\n- [+] Opens beside the x3d_inventory bag, drag items in and out\n- [+] Hook your own scripts in at addons\u002F\n\n---\n\n## Player vaults\n\n- Players create their own vault (you choose the item it costs), name it, rename or delete it, each with a code such as `#7WT6`\n- Level it up with an upgrade card; every level sets slots, weight and what may be stored (money \u002F items \u002F weapons)\n- Share it with nearby players, see who has access and revoke it\n- Limit how many vaults each player can own\n\n## Job vaults\n\n- One vault per job, such as police or EMS, open only to that job\n- Set every location, safe model, blip and label yourself\n\n## Safe by design\n\n- Every deposit and withdrawal is checked on the server: the player must stand at the vault, amounts are whole numbers, access and bag space are verified\n- The vault is locked while items move, so two players can't double-spend at once\n- Allow and block lists per vault type\n\n## Easy to use\n\n- Amber panel with large, readable Thai text; Thai \u002F English chosen with `Config.Locale`\n- Labels above the vault use the bundled Thai font, nothing else to install\n- Exports let other scripts deposit items into a vault or list a player's vaults\n\n**Free** · edit the code as you like · just don't resell it or re-upload it under your own name\n","## Installation\n\n1. Put the `x3d_vault` folder in `resources\u002F`\n2. Run `install.sql` (creates `x3d_vault`, `x3d_vault_storage` and the `card_safe` item)\n3. In `server.cfg`, `ensure x3d_inventory` first, then `ensure x3d_vault`\n4. Set vault locations in `config\u002FVAULT\u002Fconfig.group.lua` (player vaults) and `config\u002FVAULT\u002Fconfig.society.lua` (job vaults)\n\n### Requirements\n\n| Needs | Notes |\n|---|---|\n| ESX Legacy (`es_extended`) | uses `@es_extended\u002Fimports.lua` |\n| `oxmysql` | |\n| `x3d_inventory` | vault storage uses this bag's screen (free in the X3D store) |\n\n## Main settings (`config\u002Fconfig.general.lua`)\n\n| Setting | What it does |\n|---|---|\n| `Config.Locale` | `'th'` or `'en'` |\n| `Config.VaultNameTagLength` | length of the vault code (3–6) |\n| `Config.CloseEvents` | client events from other scripts that close the vault at once |\n\n## Vault types\n\nEach vault type is one block in `Config.GroupVault` (player) or `Config.SocietyVault` (job).\n\n| Field | What it does |\n|---|---|\n| `name` | vault type name players see |\n| `max_level` \u002F `exp_per_lvl` | highest level and EXP per level (player vaults) |\n| `lvl_permission[level]` | `slot`, `weight`, `total_*_limit` (-1 = unlimited), `allow_accounts\u002Fitems\u002Fweapons`, `shareable` |\n| `allowed_list` | `accounts\u002Fitems\u002Fweapons`, each `type = 'whitelist' \\| 'blacklist'` + `list` |\n| `upgrade_items` | items that add EXP, e.g. `{ type = 'item', name = 'card_safe', exp = 1000 }` |\n| `open_vault_option` \u002F `create_vault_option` | items needed to open\u002Fcreate a vault (empty = none) |\n| `allowed_jobs` | jobs allowed into a job vault |\n| `zone.blip` \u002F `zone.text` | map blip and the label above the vault |\n| `zone.locations` | vault spots: `model`, `coords`, `heading`, `action_radius` (E reach) |\n| `max_vault_per_player` \u002F `can_remove` | vaults per player, and whether players may delete their own |\n\n## Connect your own systems\n\n- `addons\u002Fhooks\u002Fclient.lua` → `Hooks.OpenGuide(reopen, isOpen)` opens your guide from the `[?]` button (`nil` hides it)\n- `addons\u002Ftext3d\u002Fclient.lua` draws the labels (natives + Thai font); swap it for your own label system\n\n## Exports (server)\n\n- `GetPlayerVaults(identifier)` vaults a player owns or is shared into\n- `GetVaultData(group, nameTag, identifier)` \u002F `CanAccessVault(vault, identifier)`\n- `DepositItems(identifier, group, nameTag, items)` deposits items into a vault and returns what didn't fit\n","Free FiveM ESX Vault Storage Script","Free FiveM ESX vault script on the x3d_inventory screen. Player vaults with levels and sharing, job vaults per society, Thai and English.",[64,67,69,71,73,75],{"kind":65,"url":66},"image","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002F4c1cf3521abb7a6e.png",{"kind":65,"url":68},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002F1f04286a85e4d9a6.png",{"kind":65,"url":70},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002F5539f6160c43bcf2.png",{"kind":65,"url":72},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002Fcb0c86c0cf96d6ac.png",{"kind":65,"url":74},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002F0727a7e57bce58ec.png",{"kind":65,"url":76},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-vault\u002Fmedia\u002F2a3f18d449526b15.png",[78,80,84],{"version":53,"released_at":54,"changelog_md":79},"### Changed\n\n- Store page text and config comments updated (no gameplay change)",{"version":81,"released_at":82,"changelog_md":83},"1.0.1","2026-10-12T03:29:04.608824Z","### Changed\n\n- UI font is now Kanit (one family for Thai and English), bundled inside the script; the Thai font now really loads in game (FiveM's browser dropped the old font declaration and fell back to a system font)",{"version":85,"released_at":86,"changelog_md":87},"1.0.0","2026-10-05T04:28:42.05702Z","### Added\n- Player vaults (create, name, rename, delete, code tag), levels with upgrade items, per-level slots\u002Fweight\u002Fpermissions, sharing with nearby players and a friends list\n- Job vaults per society with allowed jobs\n- Vault storage on the x3d_inventory storage screen; server checks distance, whole-number amounts, access and bag space, with a per-vault lock while items move\n- Amber panel, Thai\u002FEnglish via `Config.Locale`, native 3D labels with the bundled Thai font\n- `Hooks.OpenGuide`, `Config.CloseEvents`; server exports `GetPlayerVaults`, `GetVaultData`, `CanAccessVault`, `DepositItems`",{"slug":23,"version":53,"latest_version":53,"files":89,"options":58},[90,93],{"path":91,"content":92},"config\u002Fconfig.database.lua","VaultDB = {}\n\n---------------------------------------------------\n-------------------- Database ---------------------\n---------------------------------------------------\n\n-- Load vault storage by vault ID\n-- BUG FIX: MySQL.prepare.await returns the first COLUMN of the first row (scalar),\n-- not the row as a table. SELECT * for a single row must use MySQL.single.await.\n-- Previously this always returned nil → vault loaded as empty → next save wiped prior data.\nVaultDB.LoadVaultStorageByVaultId = function(id)\n\tif not Config then return nil end\n\tlocal storage = MySQL.single.await('SELECT * FROM `x3d_vault_storage` WHERE `vault_id` = ?', { id })\n\tif type(storage) == 'table' then\n\t\treturn {\n\t\t\tvault_id = storage.vault_id,\n\t\t\taccounts = storage.accounts or '{}',\n\t\t\titems = storage.items or '[]',\n\t\t\tweapons = storage.weapons or '[]'\n\t\t}\n\tend\n\n\treturn nil\nend\n\n-- Create new vault\nVaultDB.CreateVault = function(vaultData)\n\tif not Config then return nil end\n\n\t-- Use pcall for error handling\n\tlocal success, insertId = pcall(function()\n\t\tlocal query = 'INSERT INTO `x3d_vault` (owner, `group`, name, name_tag, exp, shared) VALUES (?, ?, ?, ?, ?, ?)'\n\t\tlocal values = {\n\t\t\tvaultData.owner,\n\t\t\tvaultData.vault_type,\n\t\t\tvaultData.name,\n\t\t\tvaultData.name_tag,\n\t\t\tvaultData.exp,\n\t\t\tvaultData.shared\n\t\t}\n\t\treturn MySQL.prepare.await(query, values)\n\tend)\n\n\tif not success then\n\t\tprintDebug('^1[X3D Vault DB]^7 Error creating vault: ' .. tostring(insertId), 'database')\n\t\treturn nil\n\tend\n\n\tif insertId then\n\t\t-- Create vault storage entry\n\t\tlocal storageSuccess, storageResult = pcall(function()\n\t\t\tlocal storageQuery = 'INSERT INTO `x3d_vault_storage` (vault_id, accounts, items, weapons) VALUES (?, ?, ?, ?)'\n\t\t\tlocal storageValues = {\n\t\t\t\tinsertId,\n\t\t\t\t'{}',\n\t\t\t\t'[]',\n\t\t\t\t'[]'\n\t\t\t}\n\t\t\treturn MySQL.prepare.await(storageQuery, storageValues)\n\t\tend)\n\n\t\tif not storageSuccess then\n\t\t\tprintDebug('^1[X3D Vault DB]^7 Error creating vault storage: ' .. tostring(storageResult), 'database')\n\t\tend\n\n\t\t-- Return the vault ID regardless of storage result\n\t\treturn insertId\n\tend\n\treturn nil\nend\n\n-- Check if name tag exists\nVaultDB.CheckNameTagExists = function(nameTag)\n\tif not Config then return false end\n\tlocal result = MySQL.prepare.await('SELECT COUNT(*) as count FROM `x3d_vault` WHERE `name_tag` = ?', { nameTag })\n\n\t-- Handle both table format {count = X} and direct number format\n\tif result then\n\t\tif type(result) == 'table' then\n\t\t\treturn result.count and result.count > 0\n\t\telse\n\t\t\treturn result > 0\n\t\tend\n\tend\n\n\treturn false\nend\n\n-- Get player vault count\nVaultDB.GetPlayerVaultCount = function(playerId)\n\tif not Config then return 0 end\n\tlocal result = MySQL.prepare.await('SELECT COUNT(*) as count FROM `x3d_vault` WHERE `owner` = ?', { playerId })\n\n\t-- Handle both table format {count = X} and direct number format\n\tif result then\n\t\tif type(result) == 'table' then\n\t\t\treturn result.count or 0\n\t\telse\n\t\t\treturn result\n\t\tend\n\tend\n\n\treturn 0\nend\n\n-- Save vault data by vault ID\nVaultDB.SaveVaultDataByVaultId = function(id, owner, name, name_tag, exp, shared)\n\tif not Config then return false end\n\t-- SECURITY: use .await so a DB failure surfaces to the caller instead of being\n\t-- silently swallowed (fire-and-forget previously masked dupe-causing save errors).\n\tlocal ok, result = pcall(MySQL.prepare.await, 'UPDATE `x3d_vault` SET `owner` = ?, `name` = ?, `name_tag` = ?, `exp` = ?, `shared` = ? WHERE `_id` = ?', { owner, name, name_tag, exp, json.encode(shared), id })\n\tif not ok then\n\t\tprintDebug('^1[X3D Vault DB]^7 SaveVaultData failed: ' .. tostring(result), 'database')\n\t\treturn false\n\tend\n\treturn true\nend\n\n-- Save vault storage by vault ID\nVaultDB.SaveVaultStorageByVaultId = function(id, accounts, items, weapons)\n\tif not Config then return false end\n\taccounts, items, weapons = json.encode(accounts), json.encode(items), json.encode(weapons)\n\tlocal ok, result = pcall(MySQL.prepare.await, 'INSERT INTO `x3d_vault_storage` (`vault_id`, `accounts`, `items`, `weapons`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE `accounts` = ?, `items` = ?, `weapons` = ?', { id, accounts, items, weapons, accounts, items, weapons })\n\tif not ok then\n\t\tprintDebug('^1[X3D Vault DB]^7 SaveVaultStorage failed: ' .. tostring(result), 'database')\n\t\treturn false\n\tend\n\treturn true\nend\n\n-- Remove vault by vault ID\nVaultDB.RemoveVaultByVaultId = function(id)\n\tif not Config then return false end\n\treturn MySQL.transaction.await({ 'DELETE FROM `x3d_vault` WHERE `_id` = ?', 'DELETE FROM `x3d_vault_storage` WHERE `vault_id` = ?' }, { id })\t\t\t\t-- return true when database is updated\nend\n\nVaultDB.GetPlayerVaults = function(playerId)\n\tif not Config then return {} end\n\n\t-- Use pcall for error handling\n\tlocal success, result = pcall(function()\n\t\t-- Use MySQL.query.await for multi-row results (not MySQL.prepare.await)\n\t\treturn MySQL.query.await('SELECT * FROM `x3d_vault` WHERE `owner` = ? OR JSON_CONTAINS(`shared`, JSON_QUOTE(?))', { playerId, playerId })\n\tend)\n\n\tif not success then\n\t\tprintDebug('^1[X3D Vault DB]^7 Error getting player vaults: ' .. tostring(result), 'database')\n\t\treturn {}\n\tend\n\n\t-- Handle result format\n\tif result then\n\t\tif type(result) == 'table' then\n\t\t\treturn result  -- MySQL.query.await returns array directly\n\t\tend\n\tend\n\n\treturn {}\nend",{"path":94,"content":95},"config\u002Fconfig.general.lua","-- x3d_vault · X3D-Developer · https:\u002F\u002Fx3d-developer.com\r\n-- ตู้แต่ละแบบ (ส่วนตัว\u002Fหน่วยงาน) ตั้งใน config\u002FVAULT\u002F*.lua \u002F each vault type lives in config\u002FVAULT\u002F*.lua\r\n\r\nConfig = {}\r\n\r\n-- ===== ตั้งค่าหลัก \u002F Main settings =====\r\n\r\n-- ภาษาของสคริปต์นี้: 'th' | 'en'\r\n-- Language of this script: 'th' | 'en'\r\nConfig.Locale = 'th'\r\n\r\n-- พิมพ์ log ลงคอนโซลไว้ไล่ปัญหา\r\n-- Print debug lines to the console\r\nConfig.Debug = false\r\n\r\n-- จำนวนตัวอักษรรหัสตู้ (3–6) เช่น #7WT6\r\n-- Length of the vault code (3–6), e.g. #7WT6\r\nConfig.VaultNameTagLength = 4\r\n\r\n-- อีเวนต์ฝั่ง client ของสคริปต์อื่นที่ให้ปิดตู้ทันที เช่น { 'myserver:restartLock' }\r\n-- Client events from other scripts that close the vault at once, e.g. { 'myserver:restartLock' }\r\nConfig.CloseEvents = {}\r\n\r\n-- พิมพ์ debug เมื่อ Config.Debug = true \u002F prints only when Config.Debug = true\r\nfunction printDebug(message)\r\n    if Config.Debug then print(message) end\r\nend\r\n",1792316314962]