[{"data":1,"prerenderedAt":91},["ShallowReactive",2],{"site":3,"config-slug-site":15,"config-product-th-x3d-dailylogin":21,"config-public-x3d-dailylogin":86},{"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":32,"latest_version":49,"latest_released_at":50,"published_at":51,"has_docs":52,"config":53,"rent_price_satang":54,"description_md":55,"docs_md":56,"seo_title":57,"seo_description":58,"media":59,"versions":71},"7a9dd386-11c0-4f21-8c2e-cfa7dc24d15d","x3d-dailylogin","X3D Daily Login (เช็คอินรายวัน)","เช็คอินรายวัน สตรีค + ตารางรางวัลแก้เองได้ทั้งหมด ฟรี ไม่ต้องลงฐานข้อมูล กระเป๋าเต็มไม่เสียของ ต่อกับเฟรมเวิร์กไหนก็ได้",0,"none","public\u002Fproducts\u002Fx3d-dailylogin\u002Fcover\u002Fffcad7901926aa67.jpg","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fcover\u002Fffcad7901926aa67.jpg",[31],"gameplay",[33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"esx","standalone","เช็คอินรายวัน","daily login","daily reward","สตรีค","รางวัลรายวัน","ฟรี","free","ล็อคอินรายวัน","รางวัลค้างรับ","รับย้อนหลัง","โบนัสเวลาเล่น","รางวัลเข้าเกม","เช็คชื่อรายวัน","ไม่ต้องใช้ฐานข้อมูล","1.4.3","2026-10-12T17:21:47.885233Z","2026-09-20T10:37:48.155794Z",true,"public",null,"# เช็คอินรายวัน ที่ผู้เล่นไม่มีวันเสียของ\n\nเข้าเกมมากดรับรางวัล สตรีคเดินต่อทุกวัน ขาดวันแล้วจะรีเซ็ตหรือนับต่อก็เลือกได้ · ตารางรางวัลแก้เองทั้งหมด · **ไม่ต้องลงอะไรเพิ่มเลย** ไม่ต้องใช้ฐานข้อมูล ทำงานกับ ESX ได้ทันที และทำงานแบบ standalone ได้ถ้าไม่มีเฟรมเวิร์ก (ต่อ QBCore เองได้ผ่าน adapter ที่เตรียมไว้ให้)\n\n---\n\n## ฟีเจอร์\n\n- [+] ตั้งค่าง่าย ปรับได้หมดใน config หรือตั้งผ่านเว็บได้เลย\n- [+] ตารางรางวัลกี่วันก็ได้ จะวนตารางใหม่หรือจบก็ได้\n- [+] รางวัลผสมได้ ไอเทม เงินเขียว\u002Fธนาคาร\u002Fเงินแดง อาวุธ โทเคน\n- [+] ขาดวันแล้วสตรีครีเซ็ตหรือนับต่อ เลือกได้\n- [+] เปิดรับย้อนหลังได้ ตั้งจำนวนวันที่ตามเก็บเอง\n- [+] โบนัสเล่นครบ N ชั่วโมง ปลดล็อกรางวัลหรือรับวันถัดไปทันที\n- [+] หลายแทร็กพร้อมกัน ตั้งช่วงเวลาเปิดปิดกิจกรรม หรือจบในตัวได้\n- [+] กดรับเอง รับอัตโนมัติ หรือเปิดหน้าต่างให้เองตอนเข้าเกม\n- [+] กระเป๋าเต็มรางวัลไม่หาย เก็บเป็นรางวัลค้างรับ ตั้งลิมิตได้\n- [+] กันกดรับซ้ำ จ่ายครั้งเดียว ไม่กินวันเปล่า\n- [+] ส่งรางวัลเข้ากล่องจดหมาย x3d_mailbox (ตู้นิรภัย) แทนกระเป๋าได้\n- [+] เขียนเชื่อมกับสคริปต์อื่นได้ที่ addons\u002F\n\n---\n\n## กระเป๋าเต็มก็ไม่เสียรางวัล\n\nกระเป๋าเต็มตอนกดรับ ระบบเก็บรางวัลไว้ให้เป็น **รางวัลค้างรับ** ของคนนั้นเอง วันยังถูกนับ สตรีคเดินต่อ แล้วค่อยมากดรับทีหลังตอนมีที่ว่างจากหน้าต่างเดิม — ไม่ต้องพึ่งระบบอื่น ไม่มีทางที่รางวัลจะหายไปเฉย ๆ\n\n## ปลอดภัยแบบเดียวกับระบบเงิน\n\n- กดรับซ้ำจ่ายครั้งเดียว — ตัดวันก่อนจ่ายเสมอ และมีล็อกกันกดซ้ำในจังหวะเดียวกัน\n- จ่ายไม่สำเร็จเลย = คืนวันให้ผู้เล่นกดใหม่ ไม่ใช่กินวันแล้วเงียบ\n- จำนวนของถูกปัดเป็นจำนวนเต็มและรวมแถวซ้ำก่อนเช็กกระเป๋าเสมอ\n- จำกัด 1 ครั้ง\u002Fวินาที\u002Fคน ทั้งการเปิดหน้าต่างและการกดรับ\n- เส้นแบ่งวันคิดจากเวลาเซิร์ฟเวอร์ตาม `Config.TimezoneOffset` ไม่ใช่เวลาเครื่องผู้เล่น\n\n## ต่อกับ inventory ของคุณได้จริง\n\nเซิร์ฟที่เปลี่ยน inventory (x3d_inventory \u002F ox_inventory \u002F NC) ตัวเลขน้ำหนักของ ESX จะเพี้ยนจนรางวัลถูกปฏิเสธว่า \"กระเป๋าเต็ม\" ทั้งที่ยังว่าง — ตั้ง `Config.CanCarryExport` ชี้ไปที่ export ของ inventory จริง จบ และใส่ path รูปไอเทมของคุณเองได้ด้วย\n\n## ปรับได้ทุกอย่าง\n\nตารางรางวัลกี่วันก็ได้ วนตารางหรือจบก็ได้ · รับเองหรือรับอัตโนมัติตอนเข้าเกม · ชื่อไอเทม\u002Fชื่อบัญชีภาษาไทย · เก็บข้อมูลเป็นไฟล์ JSON ในตัว หรือสลับไปใช้ oxmysql ด้วยการคัดลอกบล็อกที่เตรียมไว้ให้แล้ว\n\n**ฟรี** · แก้โค้ดได้ตามใจ · ขอแค่อย่านำไปขายต่อและอย่าอัปโหลดใหม่ในชื่อตัวเอง\n\n- **ถ้ามี X3D Admin Panel (ไม่บังคับ):** แก้ปฏิทินรางวัลแยกตามแทร็ก เพิ่ม\u002Fปิด\u002Fตั้งเวลาแทร็กสำหรับเทศกาล แก้ระดับโบนัสเวลาเล่น และดูประวัติการรับ มีผลทันทีไม่ต้องรีสตาร์ท ไม่มีแผงก็ทำงานเหมือนเดิม\n- **แทร็กแบบจบในตัว:** ตั้ง `loop` แยกต่อแทร็ก เช่นปฏิทิน 31 วันของผู้เล่นใหม่ที่จบหลังวันสุดท้าย ส่วนปฏิทินหลักวนต่อตามปกติ\n- **ส่งรางวัลเข้ากล่องจดหมาย:** ส่งไอเทมและเงินเข้าตู้นิรภัยของ X3D Mailbox แทนใส่กระเป๋าได้\n- **เปิดเองและแจ้งเตือน:** เปิดหน้าต่างครั้งเดียวต่อการเชื่อมต่อหลังหน่วงเวลา และแจ้งรางวัลที่รับให้อัตโนมัติทุกชิ้น\n","## การติดตั้ง\n\n1. แตกไฟล์แล้ว **เปลี่ยนชื่อโฟลเดอร์เป็น `x3d_dailylogin`** (สคริปต์อื่นเรียก export ด้วยชื่อนี้)\n2. วางใน `resources\u002F`\n3. เพิ่มใน `server.cfg`:\n\n```\nensure x3d_dailylogin\n```\n\nไม่ต้องใช้ฐานข้อมูล ไม่มีไลเซนส์ · ทำงานกับ ESX ได้ทันที และทำงานแบบ standalone ได้ถ้าไม่มีเฟรมเวิร์ก (QBCore ต้องต่อ adapter เองก่อน)\n\nเปิดหน้าต่างด้วย **F6** หรือพิมพ์ `\u002Fdailylogin` — หรือปล่อยให้เปิดเอง (`Config.AutoOpen = true` ค่าเริ่มต้น) ตอนมีอะไรให้กด\n\nตัวเลือกเปิดหน้าต่างอัตโนมัติใน `config.lua`:\n\n- `Config.AutoOpen = 'always'` — เปิดครั้งเดียวต่อการเชื่อมต่อ (หลัง `Config.AutoDelay`) ไม่เปิดซ้ำทุกครั้งที่เกิดใหม่\n- `Config.AutoDelay` — เวลาหน่วงก่อนเปิด\n- `Config.AutoNotify` — แจ้งรางวัลที่รับอัตโนมัติทุกชิ้น (ชื่อรางวัล + แทร็กและวันที่)\n\n## ตารางรางวัล\n\nแก้ที่ `config.lua` — จะกี่วันก็ได้ ใส่ `items`\u002F`money`\u002F`weapon`\u002F`token` ผสมกันได้\n\n```lua\nConfig.Rewards = {\n    [1] = { money = { { account = 'money', amount = 500 } } },\n    [2] = { items = { { item = 'bread', count = 2 } } },\n    [3] = { weapon = { { name = 'WEAPON_PISTOL', ammo = 30 } } },\n    [7] = { money = { { account = 'bank', amount = 5000 } }, label = 'รางวัลครบสัปดาห์' },\n}\n\nConfig.LoopRewards = true   -- ครบตารางแล้ววนกลับไปรับของวันแรกอีกครั้ง (key ไม่ต้องเรียงติดกันก็ได้ — ตัวอย่างนี้มีแค่ 3 วัน วันที่ 7 จะมาถึงตอนกดรับครั้งที่ 3)\nConfig.ResetOnMiss = true   -- ขาดวันแล้วสตรีคกลับไปเริ่มที่ 1\nConfig.AutoClaim   = false  -- true = รับให้อัตโนมัติตอนเข้าเกม\n```\n\nอยากมีหลายชุดพร้อมกัน (ปฏิทินปกติ + กิจกรรมเปิดตัวจำกัดเวลา) ตั้ง `Config.Tracks` — แต่ละแทร็กมีสตรีค\u002Fรางวัลค้างรับเป็นของตัวเอง มี `from`\u002F`to` (unix) กำหนดช่วงเวลาเปิดได้ ไม่ตั้งเลย = ทำงานแทร็กเดียวเหมือนเดิม\n\n- `loop` แยกต่อแทร็กใน `Config.Tracks` (ไม่ตั้ง = ใช้ `Config.LoopRewards`): ตั้ง `loop = false` ได้แทร็กแบบครั้งเดียว เช่นปฏิทิน 31 วันของผู้เล่นใหม่ที่จบหลังวันสุดท้าย\n- `Config.TokenLabels` — ชื่อที่อ่านง่ายสำหรับรางวัลโทเคน\n- `Config.Mailbox` + `Reward.ToMailbox` — ส่งไอเทม\u002Fเงินของรางวัลนั้นเข้ากล่องจดหมาย (ค่าเริ่มต้นคือตู้นิรภัยของ x3d_mailbox) แทนใส่กระเป๋า\n- **ถ้าใช้ X3D Admin Panel:** ที่แก้ในแผงเก็บไว้ใน storage key `__admin` และทับ `Config.Rewards` \u002F `Config.Tracks` \u002F `Config.PlaytimeBonusHours` ตอนเริ่มเซิร์ฟ — config ใช้เป็นค่าตั้งต้นครั้งแรกเท่านั้น ส่วน `Daily.Rebuild()` สร้างตารางรางวัลใหม่จาก `Config` ตอนรันไทม์\n\n## โบนัสเวลาเล่น\n\nเล่นครบ N ชั่วโมงในวันนี้ปลดล็อกรางวัลอีกชุดให้กดเอง (แยกจากปฏิทิน, นับต่อได้ข้าม reconnect):\n\n```lua\nConfig.PlaytimeBonusHours = {\n    [2] = { money = { { account = 'money', amount = 300 } } },\n    [5] = { money = { { account = 'bank', amount = 800 } } },\n}\n```\n\nว่างเปล่า = ปิดฟีเจอร์นี้ ไม่โชว์ในหน้าต่างเลย\n\n`Config.PlaytimeMode = 'extra_day'` — แต่ละระดับของโบนัสเวลาเล่นจะรับวันถัดไปของทุกแทร็กที่เปิดอยู่พร้อมกัน แทนที่จะเป็นรางวัลแยก\n\n## เส้นแบ่งวัน\n\n```lua\nConfig.TimezoneOffset = 7 * 3600   -- ไทย GMT+7\n```\n\nคิดจากเวลาเซิร์ฟเวอร์ ไม่ใช่เวลาเครื่องผู้เล่น — ผู้เล่นหมุนนาฬิกาเครื่องตัวเองไม่ช่วยอะไร\n\n## รับย้อนหลัง (ปิดไว้เป็นค่าเริ่มต้น)\n\nขาดไปหลายวันแล้วอยากให้ตามเก็บรางวัลวันที่พลาดได้ เปิดใน `config.lua`:\n\n```lua\nConfig.BackClaim = {\n    enabled = true,\n    maxDays = 3,     -- ตามเก็บย้อนหลังได้ไกลสุด 3 วัน\n}\n```\n\n| สถานการณ์ | ปิด (ค่าเริ่มต้น) | เปิด maxDays = 3 |\n|---|---|---|\n| ขาด 3 วันแล้วกลับมา | กด 1 ครั้ง ได้รางวัลของวันนี้ วันที่ขาดหายไปเลย | กดได้ 4 ครั้ง ไล่เก็บวันที่ขาดทีละวันจนถึงวันนี้ |\n| ขาด 9 วันแล้วกลับมา | เหมือนเดิม กดครั้งเดียว | ตามเก็บได้แค่ 3 วันหลังสุด + วันนี้ |\n| ผู้เล่นใหม่ที่เพิ่งกดครั้งแรก | เริ่มวันนี้ | เริ่มวันนี้เหมือนกัน ไม่แจกวันย้อนหลังที่ไม่เคยเล่น |\n\nตามเก็บทีละวันแปลว่า **สตรีคไม่ขาด** เพราะวันถูกตัดต่อเนื่องกัน · หน้าต่างจะบอกว่าเหลือให้ตามเก็บอีกกี่วัน\n\n## กระเป๋าเต็มตอนกดรับ\n\nรางวัลไม่หาย และไม่เสียวัน — ระบบเก็บไว้เป็น **รางวัลค้างรับ** ของผู้เล่นคนนั้น ไปกดรับทีหลังตอนมีที่ว่างได้จากหน้าต่างเดิม\n\n| ค่า | ผลลัพธ์ |\n|---|---|\n| `Config.HoldWhenFull = true` (ค่าเริ่มต้น) | เก็บเป็นรางวัลค้างรับ ตัดวันตามปกติ สตรีคเดินต่อ |\n| `Config.HoldWhenFull = false` | ไม่ตัดวัน ผู้เล่นเคลียร์กระเป๋าแล้วมากดใหม่ |\n| `Config.MaxPending` | เก็บค้างได้สูงสุดกี่ชุดต่อคน (ค่าเริ่มต้น 10) ถึงเพดานแล้วจะไม่ตัดวันให้ |\n\nรางวัลค้างรับเก็บอยู่ในตัว x3d_dailylogin เอง **ไม่ยุ่งกับกล่องจดหมาย** (กล่องจดหมายเป็นของทีมงานและโค้ดรับของ)\n\n```lua\nexports['x3d_dailylogin']:GetPending(src)            --> รายการค้างรับ\nexports['x3d_dailylogin']:ClaimPending(src, 1)       --> ok, reason\n\nAddEventHandler('x3d_dailylogin:onPending', function(src, entry, ok, trackId) end)\n```\n\n## เซิร์ฟที่ใช้ inventory ของตัวเอง\n\n| ค่า | ใช้ทำอะไร |\n|---|---|\n| `Config.CanCarryExport` | export ของ inventory จริงที่ใช้เช็กว่ากระเป๋ารับไหว — **ขึ้น \"กระเป๋าเต็ม\" ทั้งที่ยังว่าง ให้ตั้งค่านี้** (ตัวเลขน้ำหนักของ ESX จะเพี้ยนเมื่อ inventory อื่นเป็นคนคุม) |\n| `Config.ItemImage` | path รูปไอเทมของ inventory ที่ใช้ (`{item}` = ชื่อไอเทม), ว่าง = ไม่โชว์รูป |\n| `Config.ItemLabels` \u002F `Config.AccountLabels` | ชื่อไทยของไอเทมและบัญชีเงิน |\n\nตัวอย่างที่เตรียมไว้ให้แล้วใน `config.lua`: x3d_inventory, ox_inventory, qb-inventory\n\n## เรียกจาก resource อื่น\n\n```lua\nexports['x3d_dailylogin']:GetStreak(src, trackId)   --> number — trackId ไม่ใส่ = 'default'\nexports['x3d_dailylogin']:CanClaim(src, trackId)    --> true \u002F false\nexports['x3d_dailylogin']:Claim(src, trackId)       --> ok, reason\nexports['x3d_dailylogin']:ClaimPlaytime(src, hours) --> ok, reason\n\nAddEventHandler('x3d_dailylogin:onClaim', function(src, streak, reward, ok, trackId)\n    -- ok = false แปลว่าจ่ายได้ไม่ครบ (บางชิ้นไม่เข้า) — จดหมายเหตุไว้เช็กได้ (listener เก่าที่ไม่รับ trackId ยังใช้ได้ปกติ)\n    print(('ผู้เล่น %s รับรางวัลวันที่ %d ของแทร็ก %s (ครบ: %s)'):format(src, streak, trackId, tostring(ok)))\nend)\n```\n\n## คำสั่งแอดมิน\n\n`\u002Fdailyreset \u003Cserver id>` — ล้างสตรีค วันล่าสุด และรางวัลค้างรับทั้งหมดของผู้เล่นคนนั้นทิ้ง ใช้ตอนต้องแก้ปัญหาให้ผู้เล่น ไม่ใช่ของเล่น เรียกจากคอนโซลได้เสมอ เรียกจากผู้เล่นในเกมต้องมี ace `Config.AdminAce` (ค่าเริ่มต้น `x3d_dailylogin.admin`) คำสั่งส่งข้อมูลที่ล้างแล้วไปอัปเดตหน้าจอเป้าหมายเฉย ๆ ไม่แย่งโฟกัส — ปฏิทินจะโชว์ข้อมูลใหม่ก็ต่อเมื่อผู้เล่นคนนั้นเปิดหน้าต่างอยู่ก่อนแล้วเท่านั้น ไม่ได้เปิดปฏิทินขึ้นมาให้เอง\n\n## เก็บข้อมูลด้วย oxmysql แทนไฟล์ JSON\n\nค่าเริ่มต้นเก็บใน `data\u002Fdailylogin.json` ไม่ต้องใช้ฐานข้อมูลเลย · อยากใช้ฐานข้อมูล: เปิด `addons\u002Fstorage\u002Fserver.lua` คัดลอกบล็อก oxmysql ท้ายไฟล์ทับสี่ฟังก์ชันด้านบน แล้วรัน `install.sql`\n\nการเซฟ: ทุก 30 วินาที (`Config.Storage.FlushSeconds`), ตอนผู้เล่นออก และตอนปิด resource แบบปกติ · เซิร์ฟดับกะทันหันจะเสียได้มากที่สุดแค่ช่วงที่ยังไม่ได้เซฟ\n\n## ช่วยเหลือ\n\nDiscord: discord.gg\u002Fx3d-developer · เว็บ: x3d-developer.com\n","สคริปต์เช็คอินรายวัน FiveM ฟรี พร้อมสตรีค","สคริปต์ FiveM ฟรี ระบบเช็คอินรายวันพร้อมสตรีคและตารางรางวัลแก้เองได้ ไม่ต้องลงฐานข้อมูล ต่อกับเฟรมเวิร์กไหนก็ได้ กระเป๋าเต็มไม่เสียรางวัล ทดสอบ 49 เคส",[60,63,65,67,69],{"kind":61,"url":62},"image","\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fmedia\u002Faf63b761b443b974.png",{"kind":61,"url":64},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fmedia\u002Fb5026093f5d59f28.png",{"kind":61,"url":66},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fmedia\u002F207d4f9572b33005.png",{"kind":61,"url":68},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fmedia\u002F4139bf7de3314fd7.png",{"kind":61,"url":70},"\u002Fapi\u002Fv1\u002Fmedia\u002Fpublic\u002Fproducts\u002Fx3d-dailylogin\u002Fmedia\u002Fa2cbf95b87dd92e7.png",[72,74,78,82],{"version":49,"released_at":50,"changelog_md":73},"### Changed\n\n- Store images now carry English captions for the English store page",{"version":75,"released_at":76,"changelog_md":77},"1.4.1","2026-10-12T03:25:11.338905Z","### 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":79,"released_at":80,"changelog_md":81},"1.4.0","2026-10-05T03:00:08.391317Z","### Added\r\n- Optional x3d_adminpanel menu (`admin.lua`): edit the reward calendar per track, add\u002Fdisable\u002Fschedule tracks for festivals, edit playtime bonus tiers and browse claim history, all applied live without a restart; runs the same as before when the panel is not installed\r\n- Panel edits are stored in the storage key `__admin` and override `Config.Rewards`\u002F`Config.Tracks`\u002F`Config.PlaytimeBonusHours` at boot (the config only seeds the first run)\r\n- `Daily.Rebuild()` rebuilds the reward tables from `Config` at runtime\r\n- Per-track `loop` in `Config.Tracks` (unset = `Config.LoopRewards`): a one-time track such as a 31-day new-player calendar ends after its last day while the regular calendar keeps repeating\r\n- `Config.TokenLabels` shows a readable name for token rewards\r\n- `Config.AutoOpen = 'always'`, `Config.AutoDelay`, `Config.AutoNotify`: open once per connection after a delay and notify each auto-claimed reward (reward name + track and day)\r\n- `Config.PlaytimeMode = 'extra_day'`: each playtime tier claims the next day of every active track at once\r\n- `Config.Mailbox` + `Reward.ToMailbox`: send items\u002Fmoney to a mailbox (x3d_mailbox safe by default) instead of the bag\r\n- Calendar footer shows the event period, where rewards go, today's auto-claimed days and playtime progress; tabs show progress against the track length and a check mark when finished\r\n### Fixed\r\n- A finished one-time track counted as claimable, so the panel opened on every spawn and the calendar jumped back to day 1 with no stamps\r\n- Auto-claim\u002Fauto-open ran again on every respawn; it now runs once per connection\r\n- Days claimed today now get the stamp animation when the calendar opens, not only after a manual click\r\n### Changed\r\n- Item names on the calendar fall back to the framework's item label when `Config.ItemLabels` has no entry, instead of showing the raw key",{"version":83,"released_at":84,"changelog_md":85},"1.2.1","2026-09-20T10:37:46.445431Z","### Fixed\r\n- Playtime bonus tiers could be claimed twice by sending the same tier once as an integer and once as a float (e.g. `2` then `2.0`) — the reward lookup, the claimed flag and the in-flight lock now all derive from one floored integer, and a non-integer or unconfigured tier is refused cleanly\r\n- `Daily.Claim`\u002F`Daily.ClaimPending` threw a script error (no toast) instead of answering when `trackId` was not a string, since it was concatenated into the lock key before being validated\r\n- `Config.PlaytimeBonusHours = nil` (documented as \"feature off\") produced a misleading `deliver_failed` instead of a clean refusal\r\n- The last few seconds of a session's playtime, between the last periodic tick and a disconnect, are now credited instead of lost\r\n### Changed\r\n- README\u002Fdocs corrected: `\u002Fdailyreset` only refreshes a target's calendar if it is already open, it does not force the panel open",{"slug":23,"version":49,"latest_version":49,"files":87,"options":54},[88],{"path":89,"content":90},"config.lua","-- ═══════════════════════════════════════════════════════════════\n--  x3d_dailylogin — ตั้งค่า\n--  X3D-Developer · https:\u002F\u002Fx3d-developer.com\n-- ═══════════════════════════════════════════════════════════════\n\nConfig = {}\n\n-- ===== ทั่วไป \u002F General =====\n\n-- เฟรมเวิร์กที่ใช้: 'auto' (ESX ถ้ามี ไม่มีก็ standalone) | 'esx' | 'standalone'\n-- Framework: 'auto' (ESX if present, otherwise standalone) | 'esx' | 'standalone'\nConfig.Framework = 'auto'\n\n-- ภาษาของหน้าต่างและข้อความ: 'th' | 'en' \u002F UI and message language: 'th' | 'en'\nConfig.Locale  = 'th'\n\n-- คำสั่งเปิดหน้าต่าง (\u002Fdailylogin) \u002F Command that opens the panel (\u002Fdailylogin)\nConfig.Command = 'dailylogin'\n\n-- ปุ่มเปิดหน้าต่าง ('' = ไม่ผูกปุ่ม) \u002F Key that opens the panel ('' = no key bound)\nConfig.Key     = 'F6'\n\n-- สิทธิ์ ACE ของแอดมิน (ใช้กับ \u002Fdailyreset) \u002F Admin ACE permission (used by \u002Fdailyreset)\nConfig.AdminAce = 'x3d_dailylogin.admin'\n\n-- ===== เวลาและสตรีค \u002F Time and streak =====\n\n-- เส้นแบ่งวัน — วินาทีที่บวกเพิ่มจากเวลา UTC (ไทย GMT+7 = 7 * 3600)\n-- Day boundary: seconds added to UTC time (Thailand GMT+7 = 7 * 3600)\nConfig.TimezoneOffset = 7 * 3600\n\n-- ขาดวันแล้วสตรีคกลับไปเริ่มที่ 1 ใหม่ไหม (false = นับต่อจากเดิมเรื่อย ๆ)\n-- Reset the streak to 1 after a missed day (false = keep counting)\nConfig.ResetOnMiss = true\n\n-- ===== รับอัตโนมัติ \u002F Auto claim and auto open =====\n\n-- รับรางวัลให้อัตโนมัติตอนเข้าเกม (true = ไม่ต้องเปิดหน้าต่าง แค่เด้งแจ้งเตือน)\n-- Claim automatically on join (true = no window needed, only a notification)\nConfig.AutoClaim = false\n\n-- เปิดหน้าต่างให้อัตโนมัติตอนมีรางวัลให้กด (แยกจาก AutoClaim — เปิดโชว์ปฏิทินเฉย ๆ ไม่ได้กดรับให้)\n-- ไม่เปิดถ้าผู้เล่นตายอยู่\u002Fค้าง pause menu (เช็คฝั่ง client ด้วย Framework.IsBusy)\n-- Open the panel automatically when there is something to claim (separate from AutoClaim, it only shows the calendar)\n-- Skipped while the player is dead or in the pause menu (client side check via Framework.IsBusy)\n-- 'always' = เปิดปฏิทินทุกครั้งที่เข้าเกม (ครั้งแรกของการเชื่อมต่อ) แม้ไม่มีอะไรให้รับ\n-- 'always' = open the calendar on every join (first spawn of the session) even with nothing to claim\nConfig.AutoOpen = true\n\n-- รอกี่มิลลิวินาทีหลังเกิดในเมืองก่อนรับ\u002Fเปิดอัตโนมัติ (ให้ HUD\u002Fกระเป๋าโหลดเสร็จก่อน)\n-- Milliseconds after spawning before the auto-claim\u002Fauto-open runs (lets the HUD and bag load first)\nConfig.AutoDelay = 3000\n\n-- แจ้งเตือนรางวัลที่รับอัตโนมัติทีละรายการ (ชื่อรางวัล + กิจกรรม วันที่ N)\n-- Notify each auto-claimed reward (reward name + track and day)\nConfig.AutoNotify = true\n\n-- ===== กล่องจดหมาย \u002F Mailbox delivery =====\n\n-- ส่งไอเทม\u002Fเงินเข้ากล่องจดหมายแทนกระเป๋า — กระเป๋าเต็มก็ไม่มีวันเสียของ\n--   resource = ชื่อสคริปต์กล่องจดหมาย ('' = เข้ากระเป๋าตรง) · ส่งยังไงดูที่ Reward.ToMailbox ใน addons\u002Freward\u002Fserver.lua\n--   box = กล่องปลายทาง ('safe' = ตู้นิรภัยไม่หมดอายุของ x3d_mailbox) · sender = ชื่อผู้ส่งบนจดหมาย\n-- Send items\u002Fmoney to a mailbox instead of the bag, so a full bag never loses a reward\n--   resource = the mailbox script ('' = straight to the bag) · see Reward.ToMailbox in addons\u002Freward\u002Fserver.lua\n--   box = target box ('safe' = x3d_mailbox's non-expiring safe) · sender = sender name on the mail\n-- เช่น \u002F e.g. { resource = 'x3d_mailbox', box = 'safe', sender = 'รางวัลเช็คชื่อ' }\nConfig.Mailbox = { resource = '', box = 'safe', sender = 'รางวัลเช็คชื่อ' }\n\n-- ===== รับย้อนหลัง \u002F Back-claim =====\n\n-- รับย้อนหลัง — ขาดไปหลายวันแล้วยังตามเก็บรางวัลวันที่พลาดได้\n--   enabled = false (ค่าเริ่มต้น) คือกดครั้งเดียวจบที่รางวัลของวันนี้ ของวันที่ขาดหายไปเลย\n--   enabled = true  คือกดได้ทีละวัน ไล่เก็บจากวันที่เก่าสุดที่ยังตามได้ มาจนถึงวันนี้\n--                   สตรีคเดินต่อเนื่องเพราะตัดวันทีละวัน ไม่กระโดด\n--   maxDays = ตามเก็บย้อนหลังได้ไกลสุดกี่วัน (3 = ขาดไป 5 วันก็ตามเก็บได้แค่ 3 วันหลังสุด)\n-- Back-claim: after missing several days the player can still collect the missed days\n--   enabled = false (default) means one press pays only today's reward, the missed days are gone\n--   enabled = true  means one day per press, oldest reachable day first, up to today\n--                   the streak stays continuous because days are consumed one at a time\n--   maxDays = how far back the player may collect (3 = after missing 5 days only the last 3 can be collected)\nConfig.BackClaim = {\n    enabled = false,\n    maxDays = 3,\n}\n\n-- ===== กระเป๋าเต็ม \u002F Full bag =====\n\n-- กระเป๋าเต็มตอนกดรับ: เก็บรางวัลไว้เป็น \"รางวัลค้างรับ\" ของผู้เล่นคนนั้น\n-- แล้วให้มากดรับทีหลังตอนมีที่ว่าง (สตรีคเดินต่อตามปกติ ไม่เสียวัน)\n-- false = ไม่ตัดวันให้เลย ผู้เล่นต้องเคลียร์กระเป๋าแล้วมากดใหม่\n-- Bag full at claim time: keep the reward as a \"pending reward\" for that player\n-- and let them claim it later when there is room (the streak continues, no day lost)\n-- false = the day is not consumed, the player clears the bag and claims again\nConfig.HoldWhenFull = true\n\n-- เก็บรางวัลค้างรับได้สูงสุดกี่ชุดต่อคน (เกินกว่านี้ต้องเคลียร์ของเก่าก่อน)\n-- Maximum pending rewards held per player (clear old ones before more are accepted)\nConfig.MaxPending = 10\n\n-- ===== ตารางรางวัล \u002F Reward table =====\n\n-- ตารางรางวัล — วันที่ 1..N ของสตรีค\n--   items  = { { item = 'ชื่อไอเทม', count = 1 } }\n--   money  = { { account = 'bank', amount = 500 } }\n--   weapon = { { name = 'WEAPON_PISTOL', ammo = 30 } }\n--   token  = { { kind = 'battle', amount = 5 } }   -- ต้องเซิร์ฟมีระบบเหรียญของตัวเอง ดู Reward.GiveToken\n--   label  = ชื่อรางวัลที่โชว์บนปฏิทิน (ไม่ใส่ก็ได้)\n-- Reward table: day 1..N of the streak\n--   items  = { { item = 'item name', count = 1 } }\n--   money  = { { account = 'bank', amount = 500 } }\n--   weapon = { { name = 'WEAPON_PISTOL', ammo = 30 } }\n--   token  = { { kind = 'battle', amount = 5 } }   -- needs your own token system, see Reward.GiveToken\n--   label  = reward name shown on the calendar (optional)\n-- เช่น \u002F e.g. [8] = { items = { { item = 'bread', count = 2 } } }\nConfig.Rewards = {\n    [1] = { money = { { account = 'money', amount = 500 } } },\n    [2] = { money = { { account = 'money', amount = 750 } } },\n    [3] = { money = { { account = 'bank', amount = 1000 } } },\n    [4] = { money = { { account = 'money', amount = 1250 } } },\n    [5] = { money = { { account = 'bank', amount = 1500 } } },\n    [6] = { money = { { account = 'money', amount = 2000 } } },\n    [7] = { money = { { account = 'bank', amount = 5000 } }, label = 'รางวัลครบสัปดาห์' },\n}\n\n-- วันที่ 8 วนกลับไปรับรางวัลวันที่ 1 ใหม่ (false = วันที่เกินตารางไม่มีรางวัล)\n-- Day 8 loops back to day 1's reward (false = days past the table give nothing)\nConfig.LoopRewards = true\n\n-- ===== แทร็กรางวัล \u002F Reward tracks =====\n\n-- แทร็กรางวัลหลายชุดพร้อมกัน (เช่น ปฏิทินปกติ + กิจกรรมเปิดตัวจำกัดเวลา) — แต่ละแทร็กมีสตรีค\u002Fปฏิทินของตัวเอง\n-- ปล่อยเป็น nil (ค่าเริ่มต้น) = ใช้ Config.Rewards ด้านบนเป็นแทร็กเดียวชื่อ 'default' เหมือนเดิมทุกอย่าง ไม่กระทบ config เดิม\n-- ตั้งเองแล้วอยากได้แทร็กเดิมด้วย ต้องใส่ id = 'default' รับ rewards = Config.Rewards เองในลิสต์\n--   id      = ชื่ออ้างอิง (unique)\n--   label   = ชื่อแท็บที่โชว์ในพาแนล (ไม่ใส่ก็โชว์ id)\n--   enabled = false = ปิดแทร็กนี้ไปเลย ไม่โชว์ไม่ให้กด\n--   loop    = true\u002Ffalse ครบตารางแล้ววนใหม่ไหม (ไม่ใส่ = ตาม Config.LoopRewards) — false = รับครั้งเดียวจบ เช่นแทร็กผู้เล่นใหม่\n--   from\u002Fto = os.time() ของช่วงเวลาที่แทร็กนี้เปิดอยู่ (unix, nil = ไม่จำกัด) — นอกช่วงนี้ = มองไม่เห็น กดไม่ได้\n--   rewards = ตารางรางวัลของแทร็กนี้ (รูปแบบเดียวกับ Config.Rewards)\n-- Several reward tracks at once (e.g. the normal calendar + a limited launch event), each with its own streak\u002Fcalendar\n-- nil (default) = Config.Rewards above is used as the single track 'default', nothing changes for old configs\n-- If you set this and still want the original track, add id = 'default' with rewards = Config.Rewards yourself\n--   id      = reference name (unique)\n--   label   = tab name shown in the panel (id is shown when omitted)\n--   enabled = false = track fully off, hidden and not claimable\n--   loop    = true\u002Ffalse whether the track loops after the last day (omitted = follows Config.LoopRewards), false = one time only, e.g. a new-player track\n--   from\u002Fto = os.time() window when the track is open (unix, nil = unlimited), outside it the track is hidden and locked\n--   rewards = reward table of this track (same format as Config.Rewards)\n-- Config.Tracks = {\n--     { id = 'default', label = 'เช็คอินรายวัน', enabled = true, rewards = Config.Rewards },\n--     { id = 'grand_open', label = 'เปิดเซิร์ฟใหม่', enabled = true, from = 1758000000, to = 1759000000, rewards = {\n--         [1] = { items = { { item = 'bread', count = 5 } } },\n--         [2] = { money = { { account = 'bank', amount = 3000 } } },\n--     } },\n-- }\nConfig.Tracks = nil\n\n-- ===== โบนัสเวลาเล่น \u002F Playtime bonus =====\n\n-- โบนัสเวลาเล่น — เล่นครบ N ชม. ในวันนี้ (นับตั้งแต่เข้าเกม ไม่รวมเวลาที่ออกจากเซิร์ฟ) ปลดล็อกรางวัลให้กด\n-- key = จำนวนชั่วโมง — ต้องกดรับเอง (ไม่ auto), รับได้ระดับละ 1 ครั้งต่อวัน, รีเซ็ตพร้อมเส้นแบ่งวันเดียวกับเช็คอิน\n-- Playtime bonus: playing N hours today (counted from join, offline time excluded) unlocks a reward to claim\n-- key = number of hours; claimed by hand (never automatic), once per tier per day, resets on the same day boundary as check-in\n-- เช่น \u002F e.g. [10] = { money = { { account = 'bank', amount = 2000 } }, label = 'เล่นครบ 10 ชม.' }\nConfig.PlaytimeBonusHours = {\n    [2] = { money = { { account = 'money', amount = 300 } }, label = 'เล่นครบ 2 ชม.' },\n    [5] = { money = { { account = 'bank', amount = 800 } }, label = 'เล่นครบ 5 ชม.' },\n}\n-- เช็ก\u002Fนับเวลาเล่นทุกกี่วินาที \u002F How often (seconds) playtime is checked and counted\nConfig.PlaytimeTickSeconds = 60\n\n-- โบนัสเวลาเล่นให้อะไร\n--   'reward'    = ครบชั่วโมงแล้วปลดล็อกของรางวัลในตาราง Config.PlaytimeBonusHours ให้กดรับเอง\n--   'extra_day' = ครบชั่วโมงแล้วรับวันถัดไปของทุกกิจกรรมให้ทันที (ไม่ต้องรอพรุ่งนี้) ไม่ใช้ของในตาราง\n-- What the playtime bonus gives\n--   'reward'    = each tier unlocks its Config.PlaytimeBonusHours reward to claim by hand\n--   'extra_day' = each tier instantly claims the next day of every active track (no waiting for tomorrow)\nConfig.PlaytimeMode = 'reward'\n\n-- ===== ชื่อที่โชว์ \u002F Display labels =====\n\n-- ชื่อไอเทมที่จะโชว์ (ไม่ใส่ก็ได้ — ไม่ใส่จะโชว์ชื่อ key ตรง ๆ)\n-- Item names to show (optional, the raw key is shown when missing)\n-- เช่น \u002F e.g. bread = 'ขนมปัง'\nConfig.ItemLabels = {\n    -- bread = 'ขนมปัง',\n}\n\n-- ชื่อบัญชีเงินที่จะโชว์ \u002F Money account names to show\nConfig.AccountLabels = {\n    money       = 'เงินสด',\n    bank        = 'ธนาคาร',\n    black_money = 'เงินดำ',\n}\n\n-- ชื่อเหรียญ\u002Fสกุลเงินพิเศษที่จะโชว์ (key = kind ใน token) ไม่ใส่ = โชว์ kind ตรง ๆ\n-- Token\u002Fcurrency names to show (key = kind in a token reward), the raw kind is shown when missing\n-- เช่น \u002F e.g. event = 'เหรียญกิจกรรม'\nConfig.TokenLabels = {\n    -- event = 'เหรียญกิจกรรม',\n}\n\n-- ชื่ออาวุธที่จะโชว์ (ไม่ใส่ก็ได้ — ไม่ใส่จะโชว์ชื่อ key ตรง ๆ)\n-- Weapon names to show (optional, the raw key is shown when missing)\n-- เช่น \u002F e.g. WEAPON_PISTOL = 'ปืนพก'\nConfig.WeaponLabels = {\n    -- WEAPON_PISTOL = 'ปืนพก',\n}\n\n-- ===== Inventory =====\n\n-- รูปไอเทมบนปฏิทิน — path รูปของ inventory ที่เซิร์ฟใช้ ({item} = ชื่อไอเทม)\n-- ปล่อยเป็น '' = ไม่โชว์รูป โชว์แค่ชื่อไอเทม\n-- Item image on the calendar: image path of your inventory ({item} = item name)\n-- '' = no image, only the item name is shown\nConfig.ItemImage = ''\n-- Config.ItemImage = 'nui:\u002F\u002Fx3d_inventory\u002Fweb\u002Fbuild\u002Fimg\u002Fitems\u002F{item}.png'\n-- Config.ItemImage = 'nui:\u002F\u002Fox_inventory\u002Fweb\u002Fimages\u002F{item}.png'\n-- Config.ItemImage = 'nui:\u002F\u002Fqb-inventory\u002Fhtml\u002Fimages\u002F{item}.png'\n-- ไม่มีในลิสต์นี้? เปิดโฟลเดอร์รูปไอเทมของ inventory resource ที่ใช้อยู่ แล้วคัดลอก path มาแทน\n-- Not in this list? Open your inventory's item image folder and copy the path\n\n-- เช็กว่ากระเป๋ารับของไหวก่อนจ่ายรางวัล — ใส่เฉพาะตอนเซิร์ฟใช้ inventory ของตัวเอง\n-- (x3d_inventory\u002Fox_inventory ฯลฯ) เพราะเลขน้ำหนักของ ESX เองจะเพี้ยนเมื่อ inventory\n-- ตัวอื่นเป็นคนคุม ทำให้รางวัลถูกปฏิเสธว่า \"กระเป๋าเต็ม\" ทั้งที่ยังว่าง\n-- ปล่อยว่าง = เช็กผ่าน ESX canCarryItem ถ้ามี ไม่มีก็ปล่อยผ่าน\n-- Check the bag can take the reward before paying; set it only when the server runs its own inventory\n-- (x3d_inventory\u002Fox_inventory etc.), because ESX's own weight numbers go stale when another inventory\n-- controls the bag and rewards get refused as \"bag full\" on an empty bag\n-- Empty = ESX canCarryItem if available, otherwise the check passes\nConfig.CanCarryExport = { resource = '', method = '' }\n-- Config.CanCarryExport = { resource = 'x3d_inventory', method = 'CanCarryItemSrc' }\n-- Config.CanCarryExport = { resource = 'ox_inventory', method = 'CanCarryItem' }\n\n-- ===== ที่เก็บข้อมูล \u002F Storage =====\n\n-- ไฟล์ JSON ที่เก็บข้อมูล และความถี่การเซฟ (วินาที)\n-- JSON file that holds the data, and how often it is saved (seconds)\nConfig.Storage = {\n    File         = 'data\u002Fdailylogin.json',\n    FlushSeconds = 30,\n}\n",1792316309324]