[{"data":1,"prerenderedAt":96},["ShallowReactive",2],{"site":3,"config-slug-site":15,"config-product-th-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 (ตู้เซฟแก๊ง)","ตู้เซฟ ESX ใช้หน้าจอกระเป๋า x3d_inventory ตู้ส่วนตัวมีเลเวล แชร์ให้เพื่อน ตู้หน่วยงานตามอาชีพ ฟรี",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,"ตู้เซฟที่เปิดข้างกระเป๋าผู้เล่น เดินไปที่ตู้ กด **E** เลือกตู้ของตัวเอง แล้วคลังตู้เซฟเปิดข้างกระเป๋า **x3d_inventory** ลากของเข้า-ออกได้ทันที\n\nมีทั้งตู้ส่วนตัวที่ผู้เล่นสร้างเองอัปเกรดเลเวลและแชร์ให้เพื่อนได้ และตู้หน่วยงานที่เปิดได้เฉพาะอาชีพ ใช้กับ ESX ฟรี\n\n---\n\n## ฟีเจอร์\n\n- [+] ตั้งค่าง่าย ปรับได้หมดใน config หรือตั้งผ่านเว็บได้เลย\n- [+] ผู้เล่นสร้างตู้เองได้ ตั้งของที่ต้องมีเพื่อสร้างตู้ได้\n- [+] ตั้งชื่อ เปลี่ยนชื่อตู้ มีรหัสตู้ เช่น #7WT6 ปรับความยาวรหัสได้\n- [+] อัปเกรดเลเวลตู้ด้วยการ์ด กำหนดช่อง น้ำหนักต่อเลเวล\n- [+] ตั้งลิมิตการเก็บเงิน ไอเทม อาวุธแยกกันต่อเลเวลได้\n- [+] แชร์ตู้ให้เพื่อนและถอนสิทธิ์ได้ จำกัดจำนวนตู้ต่อคนได้\n- [+] ตู้หน่วยงานตามอาชีพ เปิดได้เฉพาะคนในอาชีพนั้น\n- [+] ตั้งไอเทมที่ต้องมีเพื่อเปิดตู้ ว่างไว้ = ไม่ต้องใช้\n- [+] ตั้งรายการไอเทมห้ามเก็บ \u002F อนุญาตแยกตามประเภทตู้\n- [+] ตั้งตำแหน่ง โมเดลตู้ blip และป้ายเองได้ทุกจุด\n- [+] เปิดข้างกระเป๋า x3d_inventory ลากของเข้า-ออกได้ทันที\n- [+] เขียนเชื่อมกับสคริปต์อื่นได้ที่ addons\u002F\n\n---\n\n## ตู้ส่วนตัว\n\n- ผู้เล่นสร้างตู้เองได้ (ตั้งของที่ต้องใช้สร้างได้) ตั้งชื่อ เปลี่ยนชื่อ ลบเองได้ มีรหัสตู้ เช่น `#7WT6`\n- อัปเกรดเลเวลด้วยการ์ดอัปเกรด แต่ละเลเวลกำหนดช่อง น้ำหนัก และสิ่งที่เก็บได้ (เงิน \u002F ไอเทม \u002F อาวุธ)\n- แชร์ให้ผู้เล่นใกล้ตัว ดูรายชื่อคนที่แชร์และถอนสิทธิ์ได้\n- จำกัดจำนวนตู้ต่อผู้เล่น\n\n## ตู้หน่วยงาน\n\n- ตู้ประจำอาชีพ เช่น ตำรวจ หมอ เปิดได้เฉพาะคนในอาชีพนั้น\n- ตั้งตำแหน่ง โมเดลตู้ blip และป้ายเองได้ทุกจุด\n\n## ปลอดภัย\n\n- ทุกการฝาก-ถอนตรวจที่เซิร์ฟเวอร์: ต้องยืนอยู่ที่ตู้จริง จำนวนเป็นจำนวนเต็ม สิทธิ์เข้าตู้ และพื้นที่กระเป๋า\n- ล็อกตู้ระหว่างย้ายของ กันกดซ้ำพร้อมกันสองคน\n- รายการห้ามเก็บ\u002Fอนุญาตแยกตามประเภทตู้\n\n## ใช้งานง่าย\n\n- หน้าจอสีอำพัน ตัวหนังสือไทยใหญ่อ่านง่าย ภาษาไทย \u002F อังกฤษ เลือกที่ `Config.Locale`\n- ป้ายเหนือตู้ใช้ฟอนต์ไทยที่มากับสคริปต์ ไม่ต้องลงอะไรเพิ่ม\n- exports ให้สคริปต์อื่นฝากของเข้าตู้ หรืออ่านรายชื่อตู้ของผู้เล่นได้\n\n**ฟรี** · แก้โค้ดได้ตามใจ · ขอแค่อย่านำไปขายต่อและอย่าอัปโหลดใหม่ในชื่อตัวเอง\n","## การติดตั้ง\n\n1. วางโฟลเดอร์ `x3d_vault` ใน `resources\u002F`\n2. รัน `install.sql` (สร้างตาราง `x3d_vault`, `x3d_vault_storage` และไอเทม `card_safe`)\n3. ใน `server.cfg` ให้ `ensure x3d_inventory` ก่อน แล้วค่อย `ensure x3d_vault`\n4. ตั้งตำแหน่งตู้ใน `config\u002FVAULT\u002Fconfig.group.lua` (ตู้ส่วนตัว) และ `config\u002FVAULT\u002Fconfig.society.lua` (ตู้หน่วยงาน)\n\n### ต้องมี\n\n| ต้องมี | หมายเหตุ |\n|---|---|\n| ESX Legacy (`es_extended`) | ใช้ `@es_extended\u002Fimports.lua` |\n| `oxmysql` | |\n| `x3d_inventory` | คลังตู้เซฟใช้หน้าจอของกระเป๋านี้ (ฟรีในร้าน X3D) |\n\n## ตั้งค่าหลัก (`config\u002Fconfig.general.lua`)\n\n| ค่า | ใช้ทำอะไร |\n|---|---|\n| `Config.Locale` | `'th'` หรือ `'en'` |\n| `Config.VaultNameTagLength` | ความยาวรหัสตู้ (3–6) |\n| `Config.CloseEvents` | อีเวนต์ฝั่ง client ของสคริปต์อื่นที่ให้ปิดตู้ทันที |\n\n## Vault types\n\nตู้แต่ละแบบเป็นหนึ่งก้อนใน `Config.GroupVault` (ส่วนตัว) หรือ `Config.SocietyVault` (หน่วยงาน)\n\n| ช่อง | ใช้ทำอะไร |\n|---|---|\n| `name` | ชื่อประเภทตู้ที่ผู้เล่นเห็น |\n| `max_level` \u002F `exp_per_lvl` | เลเวลสูงสุด และ EXP ต่อเลเวล (ตู้ส่วนตัว) |\n| `lvl_permission[level]` | `slot`, `weight`, `total_*_limit` (-1 = ไม่จำกัด), `allow_accounts\u002Fitems\u002Fweapons`, `shareable` |\n| `allowed_list` | `accounts\u002Fitems\u002Fweapons` แต่ละอัน `type = 'whitelist' \\| 'blacklist'` + `list` |\n| `upgrade_items` | ของที่ใช้เพิ่ม EXP เช่น `{ type = 'item', name = 'card_safe', exp = 1000 }` |\n| `open_vault_option` \u002F `create_vault_option` | ของที่ต้องมีเพื่อเปิด\u002Fสร้างตู้ (เว้นว่าง = ไม่ต้องใช้) |\n| `allowed_jobs` | อาชีพที่เปิดตู้หน่วยงานได้ |\n| `zone.blip` \u002F `zone.text` | blip บนแผนที่ และป้ายเหนือตู้ |\n| `zone.locations` | ตำแหน่งตู้: `model`, `coords`, `heading`, `action_radius` (ระยะกด E) |\n| `max_vault_per_player` \u002F `can_remove` | จำนวนตู้ต่อคน และให้ลบตู้เองได้ไหม |\n\n## ต่อกับระบบของคุณ\n\n- `addons\u002Fhooks\u002Fclient.lua` → `Hooks.OpenGuide(reopen, isOpen)` เปิดคู่มือจากปุ่ม `[?]` (ปล่อย `nil` = ซ่อนปุ่ม)\n- `addons\u002Ftext3d\u002Fclient.lua` ป้ายเหนือตู้ (native + ฟอนต์ไทย) แทนที่ด้วยระบบป้ายของคุณเองได้\n\n## exports (server)\n\n- `GetPlayerVaults(identifier)` รายชื่อตู้ที่ผู้เล่นเป็นเจ้าของหรือได้รับแชร์\n- `GetVaultData(group, nameTag, identifier)` \u002F `CanAccessVault(vault, identifier)`\n- `DepositItems(identifier, group, nameTag, items)` ฝากของเข้าตู้ คืนรายการที่ฝากไม่ได้\n","สคริปต์ตู้เซฟ FiveM ESX ฟรี","สคริปต์ตู้เซฟ FiveM ESX ฟรี ใช้หน้าจอกระเป๋า x3d_inventory ตู้ส่วนตัวอัปเกรดเลเวลได้ แชร์ให้เพื่อน ตู้หน่วยงานตามอาชีพ ภาษาไทยและอังกฤษ",[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",1792316306825]