config/customer/08_integrations.lua = ชื่อ resource / export / option
config/functions/ = Customer hook / override ที่ตั้งใจให้แก้
internal/functions/ = Default adapter logic ของระบบExternal Notification#
Toast ปกติของ nX Police สามารถเลือกใช้ Provider ภายในหรือ External Notify ได้จาก Config
1. เลือก Provider#
Config.Notification = {
Enabled = true,
Provider = 'external',
Duration = 4500,
External = {
Resource = 'my_notify',
FallbackToInternal = true,
TypeMap = {
info = 'info',
success = 'success',
error = 'error',
warning = 'warning'
}
}
}Resource คือชื่อ resource แจ้งเตือนภายนอก หากการเชื่อมของคุณไม่ต้องระบุชื่อ resource สามารถปล่อยค่าว่างได้
2. เชื่อม Adapter จุดเดียว#
แก้เฉพาะ Functions.Client.ExternalNotify() ใน config/functions/client.lua ฟังก์ชันต้องคืน true เมื่อแสดงสำเร็จ หรือ false, reason เมื่อเรียกไม่ได้
-- ตัวอย่าง Export
function Functions.Client.ExternalNotify(message, kind, duration, settings)
exports['my_notify']:Notify(message, kind, duration)
return true
end
-- ตัวอย่าง Event
-- function Functions.Client.ExternalNotify(message, kind, duration, settings)
-- TriggerEvent('my_notify:show', message, kind, duration)
-- return true
-- end3. TypeMap#
nX Police ใช้ชนิดแจ้งเตือนมาตรฐาน info, success, error, warning หาก Notify ปลายทางใช้ชื่ออื่นให้เปลี่ยนเฉพาะค่าฝั่งขวา เช่น warning = 'warn' หรือ info = 'inform'
4. Fallback ที่แนะนำ#
ตั้ง FallbackToInternal = true เพื่อให้ Toast ภายในทำงานแทนเมื่อ resource ภายนอกหยุด, restart, export error หรือ Adapter คืน false ถ้าปิด fallback ข้อความนั้นจะไม่แสดงเมื่อ External Provider ล้มเหลว
5. อะไรถูกส่งไป External Notify#
ส่งออกเฉพาะ Toast ปกติ เท่านั้น ส่วน Dispatch Card, Jail HUD, Police Command, Report Chat, Mailbox, Countdown และ overlay ที่มี layout เฉพาะยังใช้ UI ของ nX Police. สำหรับประกาศหน่วยงาน/ประกาศภายในใช้ Adapter แยกด้านล่าง.
6. ใช้ Notification จาก resource อื่น#
-- Client
exports['nX_police']:Notify('บันทึกสำเร็จ', 'success', 4500)
-- Server
exports['nX_police']:NotifyPlayer(playerSource, 'คุณได้รับข้อความจากหน่วยงาน', 'info', 4500)Public API ทั้งสองแบบใช้การตั้งค่า Notification Provider และ TypeMap ชุดเดียวกับ nX Police
External Department Announcement UI#
หากเซิร์ฟเวอร์มี UI ประกาศของตัวเอง สามารถแทน UI ประกาศของ nX Police ได้จาก config/functions/client.lua โดยไม่ต้องแก้ Core.
Functions.Client.DepartmentAnnouncement = function(data)
exports['my_announcement']:Show({
title = data.title,
message = data.message,
duration = data.duration,
type = data.kind
})
return true
endreturn true หมายถึง Resource ภายนอกจัดการ UI แล้วและ nX Police จะไม่แสดง Announcement UI เดิมซ้ำ. ถ้า Adapter คืน false, reason หรือเกิด error ระบบจะ fallback กลับ UI ประกาศเดิมของ nX Police.
ข้อมูลที่ส่งให้ Adapter ได้แก่ kind, scope, audience, internal, title, message, duration, announcementId, caseId, suspectName, minutes และ charges. สำหรับประกาศภายใน audience ใช้ค่า all, on_duty หรือ off_duty.
-- ตัวอย่าง Event
Functions.Client.DepartmentAnnouncement = function(data)
TriggerEvent('my_announcement:show', data.title, data.message, data.duration, data)
return true
endDuty Lifecycle Hooks#
สำหรับ resource ภายนอกที่ต้องทำงานเมื่อเจ้าหน้าที่ กดเข้าเวร/ออกเวรสำเร็จ ให้ override hook ที่ config/functions/ แทนการดัก esx:setJob ทั่วทั้งเซิร์ฟเวอร์.
-- config/functions/client.lua
Functions.Client.OnDutyChanged = function(onDuty, data)
if onDuty then
TriggerEvent('my_policehud:setDuty', true)
else
TriggerEvent('my_policehud:setDuty', false)
end
end
-- config/functions/server.lua
Functions.Server.OnDutyChanged = function(source, xPlayer, onDuty, data)
TriggerEvent('my_resource:policeDuty', source, onDuty)
endHook ทั้ง Client และ Server จะถูกเรียกหลัง nX_police:toggleDuty ผ่านการตรวจสิทธิ์/ระยะ/คุมขัง/อุปกรณ์, เปลี่ยน Job และ Server verify สถานะสำเร็จแล้วเท่านั้น. เข้าเวรหนึ่งครั้งเรียกหนึ่งครั้ง และออกเวรหนึ่งครั้งเรียกหนึ่งครั้ง.
data มีสถานะประกอบ เช่น onDuty, previousOnDuty, trigger (ui/point), mode, job และข้อมูล loadout ที่เกี่ยวข้อง.
External UI + Cuff Whitelist#
UI ภายนอกที่เปิดด้วยปุ่มให้ส่งชื่อปุ่มหรือ FiveM Control ID ที่ใช้เปิด UI เข้า nX Police ก่อนเปิดผ่าน CanOpenUi API นี้ตรวจทั้ง Config.UI.AllowWhileDead และ Cuff Whitelist ในจุดเดียว
-- ตัวอย่าง: phone ใช้ F1
local openKey = 'F1'
local uiOk = exports['nX_police']:CanOpenUi('phone', openKey)
if not uiOk then return end
local active = exports['nX_police']:SetCuffUiActive('phone', true, openKey)
if not active then return end
SetNuiFocus(true, true)
-- เปิด UI ตาม API ของ resource คุณ
-- ตอนปิด UI
SetNuiFocus(false, false)
exports['nX_police']:SetCuffUiActive('phone', false)เมื่อ Config.UI.AllowWhileDead=false ผู้เล่นที่ตายจะไม่ได้รับอนุญาตให้เปิด UI ที่เชื่อม policy นี้ ส่วนตอนถูกล็อคมือ ปุ่มหรือ Control ID ที่ใช้เปิด UI ต้องอยู่ใน Config.Cuff.Whitelist หากเปิด UI จาก export/event โดยไม่มีปุ่ม ให้ resource ต้นทางกำหนดและส่ง input ที่ใช้เป็นสิทธิ์ให้ชัดเจน
Inventory#
ค่า Default ใช้ Driver='auto' และรองรับ nc_inventory ที่ตรวจพบ หากใช้ Inventory อื่นให้ตั้ง Driver='generic', ชื่อ Resource/Export ให้ตรง และแก้เฉพาะ Adapter ที่เปิดให้เชื่อม
Config.Integrations.Inventory = {
Enabled = true,
Driver = 'generic',
Resource = 'my_inventory',
SearchExport = 'OpenPlayerInventory',
AddExport = 'AddItem',
RemoveExport = 'RemoveItem',
UseExportForMutations = true
}NC Driver - Manual fxmanifest integration#
nX Police ใช้ Compatibility Layer สำหรับ nc_inventory และ nc_vehiclestorage โดยลูกค้าติดตั้งเองใน fxmanifest.lua ของ NC เท่านั้น ไม่ต้องแก้ Customer Config ของ NC และไม่มีโปรแกรมติดตั้งอัตโนมัติ
nc_inventory — เพิ่ม 2 บรรทัด#
-- nc_inventory/fxmanifest.lua
server_scripts {
'@oxmysql/lib/MySQL.lua',
'config/config.database.lua',
'config/functions/config.framework.server.lua',
'config/functions/config.functions.server.lua',
'@nX_police/compat/nc_inventory/server.lua',
'server/main.lua'
}
client_scripts {
'config/config.locale.lua',
'config/config.rarity.lua',
'config/config.images.lua',
'config/config.descriptions.lua',
'config/functions/config.functions.client.lua',
'@nX_police/compat/nc_inventory/compat.lua',
'client/main.lua',
}nc_vehiclestorage — เพิ่ม 2 บรรทัด#
-- nc_vehiclestorage/fxmanifest.lua
shared_scripts {
-- config/storage entries ของ NC ตามเดิม
'config/config.extensions.lua',
'@nX_police/compat/nc_vehiclestorage/storage.lua'
}
server_scripts {
'@oxmysql/lib/MySQL.lua',
'config/config.database.lua',
'config/functions/config.framework.server.lua',
'config/functions/config.functions.server.lua',
'@nX_police/compat/nc_vehiclestorage/server.lua',
'server/main.lua'
}หลังแก้ให้ตรวจว่าทั้ง 4 path มีอย่างละหนึ่งครั้ง แล้ว restart resource ที่เกี่ยวข้องและรัน nxpolice_doctor
nc_inventory 1.8.7b+#
Built-in driver ของ nX Police รองรับ Search, Police Safe, account config และ secondary-inventory action ผ่าน Compatibility Hook ที่ลูกค้าเพิ่มใน fxmanifest.lua ตามคู่มือด้านบน
Server adapter อ่าน Config.Accounts และ Config.AccountsActionList จาก nc_inventory จริง เพื่อให้ account เช่น money, cash, black_money, dirty_money ใช้ policy ของเซิร์ฟเวอร์ลูกค้าโดยไม่ hardcode ใน nX Police
สำหรับ Generic Inventory สามารถเปิด UseExportForMutations=true และ override Functions.Server.InventoryMutation(), SafeSnapshot(), SafeMutation() หรือ UpdateWeaponAmmo() ให้ตรง signature ของ inventory ที่ใช้จริง
nc_vehiclestorage - Vehicle Storage#
Vehicle Storage ยังคงเป็นเจ้าของข้อมูล trunk ตามปกติ ส่วน nX Police ทำ server authorization/seizure redirect ผ่าน update-safe hook โดยตำรวจต้องผ่าน policy ฝั่ง server ก่อนเสมอ
ensure nX_police
ensure nc_inventory
ensure nc_vehiclestorage| การใช้งาน | ผลลัพธ์ |
|---|---|
| เจ้าของรถหยิบ Item / Weapon / เงินจากรถตัวเอง | ทำงานตาม nc_vehiclestorage ปกติ ของเข้ากระเป๋าเจ้าของรถ |
| ตำรวจหยิบ Item จากรถคนอื่น | ต้องเป็น police ที่กำลังเข้าเวร แล้วส่งของกลางเข้า Police Safe |
| ตำรวจหยิบ Weapon จากรถคนอื่น | ต้องเป็น police ที่กำลังเข้าเวร แล้วส่งอาวุธของกลางเข้า Police Safe พร้อมข้อมูลที่ระบบรองรับ |
| ตำรวจหยิบ Money / Account จากรถคนอื่น | ชื่อ Account และสิทธิ์ search อ่านจาก nc_inventory จริง; รายการที่ NC อนุญาตจะถูกส่งเข้าบัญชีหน่วยงาน |
| ตำรวจออกเวร / ผู้เล่นทั่วไป | เปิดท้ายรถของคนอื่นผ่านสิทธิ์ตำรวจไม่ได้ |
ถ้าการส่งของกลางเข้าหน่วยงานไม่สำเร็จ ระบบจะใช้เส้นทางคืนรายการตามสถานะที่ตรวจสอบได้เพื่อลดความเสี่ยงของหาย และแจ้งผู้ใช้งานเมื่อรายการไม่สมบูรณ์
Return value ของ Adapter สำหรับ Inventory อื่น#
ถ้าใช้ Generic Inventory และเปิด UseExportForMutations=true Export ปลายทางต้องคืนผลสำเร็จให้ชัดเจนตามรูปแบบด้านล่าง ค่าอื่นจะถือว่ารายการไม่สำเร็จ
-- รูปแบบ success ที่ยอมรับ
return true
-- หรือ
return { success = true }
return { ok = true }
return { changed = true }จุด Adapter หลักฝั่ง Server คือ Functions.Server.InventoryMutation(), SafeSnapshot(), SafeMutation() และ UpdateWeaponAmmo() ถ้า signature ของ Inventory ต่างจากตัวอย่าง ให้แก้เฉพาะ Adapter จุดนี้ให้ตรงกับ API ของ Inventory
Adapter Boundaries อื่น#
Functions.Server.RequestScreenshot()— ปรับให้ตรงกับ screenshot resource ที่ใช้Functions.Server.CallPlaypass()— ปรับให้ตรงกับ Playpass ที่เซิร์ฟเวอร์ใช้Functions.Client.OpenSearchInventory()/OpenPoliceSafe()— ปรับวิธีเปิด Inventory/Search/Safe เมื่อ API ต่างจากค่าเริ่มต้นFunctions.Client.RunQuickIntegration()— จุดเชื่อม Action ของ Quick Menu กับ event/export/command ของ resource อื่น
ยึดของเข้าตู้หน่วยงาน#
เมื่อ Inventory รองรับ Search hooks สามารถให้ของที่ยึดเข้า Agency Safe โดยตรง:
exports['nX_police']:ShouldRouteSearchSeizure(officerSource, targetSource, itemName, itemCount, itemType, searchType)
exports['nX_police']:DepositSearchSeizure(officerSource, targetSource, itemName, itemCount, itemType, searchType)บันทึกหลักฐานหลัง Remove สำเร็จ#
exports['nX_police']:RecordSeizedEvidence(
officerSource, suspectSource, itemName, amount, itemType, itemLabel, suspectName
)Screenshot / Safe / Playpass#
Integrations.Screenshot— ตั้ง Enabled, Resource และ Export สำหรับการจับรูปIntegrations.Safe— ตั้ง Resource และชื่อ Export ที่ใช้เปิด/อัปเดต Secondary InventoryIntegrations.Playpass— ตั้ง Enabled, Resource และชื่อตารางที่ระบบ Playpass ใช้
Profile Avatar#
Config.Integrations.ProfileAvatar เลือก Source='discord' หรือ 'steam', ขนาด Discord avatar และรูปแบบ Steam avatar ได้
Credential ให้ตั้งฝั่ง Server เท่านั้น ตัวอย่าง server.cfg:
set nX_police_discord_bot_token "YOUR_BOT_TOKEN"
set nX_police_steam_web_api_key "YOUR_STEAM_WEB_API_KEY"Optional Resources#
screenshot-basic— ใช้แนบ Screenshot ใน Discord Log เมื่อเปิด Event ที่รองรับ- Playpass / Profile Avatar — เปิดใช้ตาม Integration ที่เซิร์ฟเวอร์มีจริง