FiveM Callback คืออะไร ใช้งานอย่างไร
FiveM Callback คือรูปแบบการสื่อสารแบบ Request → Response ที่ Script ฝั่งหนึ่งส่งคำขอไปยังอีกฝั่ง แล้วรอรับข้อมูลกลับมาเพื่อนำไปใช้งานต่อ
ตัวอย่างเช่น Client ต้องการรู้ว่าผู้เล่นมีเงินเท่าไร
Client
↓
ขอข้อมูลเงิน
↓
Server
↓
อ่าน Player Data
↓
ส่งจำนวนเงินกลับ
↓
Client
↓
เปิด Menu
นี่แตกต่างจาก Event ทั่วไปที่มักเป็นลักษณะ
ส่ง Event
↓
ให้ Handler ทำงาน
Callback เหมาะกับกรณีที่ผู้เรียก ต้องการคำตอบกลับ
ตัวอย่างเช่น
ผู้เล่นมี Item นี้ไหม?
มีตำรวจออนไลน์กี่คน?
รถคันนี้เป็นของใคร?
ผู้เล่นมีเงินพอหรือไม่?
มีข้อมูล Garage อะไรบ้าง?
อย่างไรก็ตาม FiveM Core ไม่ได้มีระบบ Server Callback รูปแบบเดียวที่ทุก Framework ต้องใช้เหมือนกัน ระบบอย่าง QBCore, ESX และ ox_lib สามารถมี Callback API ของตัวเอง ซึ่งสร้าง Request/Response Pattern ขึ้นมาบนระบบสื่อสารของ FiveM
① Callback คืออะไรแบบง่ายที่สุด
ลองเปรียบเทียบกับการถามคำถาม
Client:
มีตำรวจออนไลน์กี่คน?
Server:
ตรวจข้อมูล...
Server:
ตอนนี้มี 5 คน
Client:
รับค่า 5 แล้วแสดงใน UI
นี่คือ Callback
ในเชิง Code แนวคิดคือ
Request
↓
Function ทำงาน
↓
Response
ต่างจาก Event ที่เราอาจส่ง
playerDied
แล้วไม่จำเป็นต้องรอค่าอะไรกลับมา
② Callback ต่างจาก Event อย่างไร
Event
เหมาะกับการบอกว่า
“เหตุการณ์นี้เกิดขึ้นแล้ว”
ตัวอย่าง
TriggerServerEvent('job:server:start')
Server รับแล้วทำงาน
RegisterNetEvent('job:server:start', function()
-- ทำงาน
end)
ไม่จำเป็นต้องส่ง Response กลับโดยตรง
Callback
เหมาะกับคำถามประเภท
“ช่วยหาข้อมูลนี้แล้วส่งคำตอบกลับมา”
ตัวอย่าง
Client
↓
ขอ Item Count
↓
Server
↓
ตรวจ Inventory
↓
return 5
↓
Client
ดังนั้นจำง่าย ๆ ว่า
Event
= แจ้งเหตุการณ์
Callback
= ขอข้อมูลและรอคำตอบ
③ FiveM มี Callback มาตรฐานเดียวหรือไม่
ต้องแยกให้ออกตรงนี้
FiveM มีระบบพื้นฐานอย่าง
Events
Network Events
NUI Callbacks
แต่ Callback แบบ
Client → Server → Response
ที่เห็นใน Framework ต่าง ๆ มักเป็น Abstraction ที่ Framework หรือ Library สร้างขึ้น
ตัวอย่างเช่น QBCore มี
QBCore.Functions.TriggerCallback
QBCore.Functions.CreateCallback
ส่วน ox_lib มี
lib.callback
lib.callback.await
lib.callback.register
ดังนั้นอย่าเห็น Code Callback จาก Framework หนึ่งแล้ว Copy ไปใช้อีก Framework โดยตรง เพราะ API อาจไม่เหมือนกัน
④ QBCore Callback ทำงานอย่างไร
QBCore มีระบบ Server Callback สำหรับ Client ขอข้อมูลจาก Server
ฝั่ง Server Register Callback เช่น
QBCore.Functions.CreateCallback('myScript:getData', function(source, cb)
local data = 'Hello'
cb(data)
end)
ฝั่ง Client เรียก
QBCore.Functions.TriggerCallback('myScript:getData', function(data)
print(data)
end)
Flow คือ
Client
↓
TriggerCallback
↓
Server
↓
CreateCallback
↓
cb(data)
↓
Client Callback Function
เอกสาร QBCore ปัจจุบันระบุว่า TriggerCallback ใช้ Client เรียก Server และรับค่ากลับ ส่วน CreateCallback ใช้ Register Server-side Callback.
⑤ ตัวอย่าง QBCore Callback แบบง่าย
Server
QBCore.Functions.CreateCallback('shop:getPrice', function(source, cb, item)
local prices = {
water = 10,
bread = 15
}
cb(prices[item])
end)
Client
QBCore.Functions.TriggerCallback('shop:getPrice', function(price)
print(('Price: %s'):format(price))
end, 'water')
ถ้า Server พบ
water = 10
Client ก็ได้รับ
10
กลับมา
⑥ source ใน Server Callback คืออะไร
Callback ที่มาจาก Client มักมีข้อมูลผู้เล่นที่เรียก Request
ตัวอย่าง QBCore
QBCore.Functions.CreateCallback('myScript:getJob', function(source, cb)
local Player = QBCore.Functions.GetPlayer(source)
if not Player then
cb(nil)
return
end
cb(Player.PlayerData.job.name)
end)
source คือ Server ID ของ Client ที่เรียก Callback
สามารถใช้ไปหา
Player
Job
Money
Inventory
Permission
Metadata
จาก Server-side API
⑦ Callback ควร return หรือ cb ทุกเส้นทาง
นี่เป็นนิสัยสำคัญในการเขียน Callback
ตัวอย่าง
QBCore.Functions.CreateCallback('myScript:getPlayer', function(source, cb)
local Player = QBCore.Functions.GetPlayer(source)
if not Player then
cb(nil)
return
end
cb(Player.PlayerData)
end)
ถ้า Player ไม่มี ให้ Response กลับอย่างชัดเจน
ไม่ควรปล่อยบาง Branch ให้ Request ค้างโดยไม่มี Response หาก Callback Framework คาดหวังคำตอบ
แนวคิดคือ
Success
→ Response
Fail
→ Response เช่น nil / false / error object
⑧ ox_lib Callback คืออะไร
ox_lib มี Callback API ที่ใช้ได้ทั้ง
Client → Server
และ
Server → Client
ฝั่ง Client สามารถเรียก Server Callback ด้วย
lib.callback(...)
หรือ
lib.callback.await(...)
และฝั่ง Server Register ด้วย
lib.callback.register(...)
เอกสาร ox_lib ปัจจุบันยังแสดง API เหล่านี้โดยตรง.
⑨ ตัวอย่าง ox_lib Client → Server Callback
Server
lib.callback.register('myScript:getItemCount', function(source, item)
-- ตัวอย่าง
local count = 5
return count
end)
Client
lib.callback('myScript:getItemCount', false, function(count)
print(('Count: %s'):format(count))
end, 'water')
Flow คือ
Client
↓
lib.callback
↓
Server callback
↓
return count
↓
Client function
⑩ lib.callback.await คืออะไร
await ช่วยให้ Code ดูเหมือนเรียก Function แล้วรอค่ากลับโดยตรง
ตัวอย่าง
local count = lib.callback.await(
'myScript:getItemCount',
false,
'water'
)
print(count)
แทนรูปแบบ
lib.callback('myScript:getItemCount', false, function(count)
print(count)
end, 'water')
เอกสาร ox_lib ระบุว่า lib.callback.await จะ Yield Coroutine ปัจจุบันจนกว่าจะได้รับ Response.
⑪ callback กับ callback.await ต่างกันอย่างไร
callback
ใช้ Function รับค่ากลับ
lib.callback('test', false, function(result)
print(result)
end)
callback.await
รอแล้วคืนค่าออกมา
local result = lib.callback.await('test', false)
print(result)
ดังนั้น Code แบบ await มักอ่านง่ายเมื่อต้องใช้ Response ต่อทันที
แต่ต้องเข้าใจว่ามันกำลังรอ Network Response ไม่ใช่ Function Local ที่ตอบทันที
⑫ Server เรียก Callback ไป Client ได้หรือไม่
ระบบ Callback บาง Library/Framework รองรับ Server → Client ด้วย
ตัวอย่าง ox_lib ฝั่ง Server สามารถเรียก
lib.callback(...)
หรือ
lib.callback.await(...)
ไปยัง Player ID
และ Client Register
lib.callback.register(...)
ได้.
QBCore ปัจจุบันก็มี TriggerClientCallback ฝั่ง Server เช่นกัน.
แต่มีข้อควรระวังสำคัญเรื่อง Security
⑬ Server ไม่ควรเชื่อ Response จาก Client
สมมติ Server ถาม Client ว่า
คุณมีเงินเท่าไร?
แล้ว Clientตอบ
99999999
Serverไม่ควรนำค่านั้นมาเป็น Source of Truth
ข้อมูลสำคัญ เช่น
Money
Inventory
Job
Permission
Rewards
Database State
ควรหาและตรวจจาก Server-side Data
Cfx.re ระบุชัดว่าข้อมูลจาก Client สามารถถูกดัดแปลงได้ และควรตรวจเงิน, Inventory, Player State, Position และ Permission ด้วยข้อมูลฝั่ง Server.
⑭ Server → Client Callback เหมาะกับอะไร
เหมาะกับข้อมูลที่ Client มี Authority หรือเข้าถึง Game World ได้สะดวกกว่า
เช่น
Nearby Entities
บาง Client State
Camera/Game Information
Local World Data
แต่ Server ต้องไม่ใช้ Client Response เพื่อยืนยันธุรกรรมสำคัญแบบไม่มี Validation
ตัวอย่าง
Client บอก:
ฉันอยู่ที่จุดส่งของแล้ว
ไม่ควรเป็นหลักฐานเพียงอย่างเดียวในการจ่ายเงิน
Server ควรตรวจ Position หรือ State ที่ Server ตรวจสอบได้ด้วย
⑮ Callback ต่างจาก Export อย่างไร
Export เหมาะกับ
Resource A
↓
เรียก Function
↓
Resource B
ใน Runtime เดียวกัน
ตัวอย่าง
local count = exports['inventory']:GetItemCount(...)
Callback มักใช้เมื่อมี Request/Response ที่ข้าม
Client ↔ Server
หรือผ่านระบบ Callback ของ Framework/Library
จำง่าย ๆ ว่า
Export
= Resource-to-Resource API
Callback
= Request → Response
แต่บาง Framework อาจ Implement ระบบภายในต่างกัน จึงต้องดู Documentation ของ API จริง
⑯ Callback ต่างจาก TriggerServerEvent อย่างไร
Event
TriggerServerEvent('myScript:getData')
เพียงส่ง Event ไป Server
ถ้าต้องการส่งคำตอบกลับ อาจต้องสร้าง Event อีกตัว
Client
↓
Event Request
↓
Server
↓
Event Response
↓
Client
Callback รวม Pattern นี้ให้ใช้ง่ายขึ้น
Client
↓
Callback Request
↓
Server
↓
Response
↓
Callback Function
จึงเหมาะกับการขอข้อมูลมากกว่า
⑰ Callback เหมาะกับข้อมูลประเภทไหน
ตัวอย่างที่เหมาะ ได้แก่
จำนวนตำรวจออนไลน์
ยอดเงิน
ข้อมูล Garage
ข้อมูล House
จำนวน Item
ข้อมูล Character
Job Data
Permission Result
Database Query Result
ตัวอย่าง
เปิด Garage
↓
Client ขอรถ
↓
Server Query Database
↓
ส่งรายการรถ
↓
Client เปิด Menu
นี่เป็น Use Case ของ Callback ที่ชัดเจน
⑱ ตัวอย่าง Garage Callback
Server
lib.callback.register('garage:getVehicles', function(source)
-- Query Database ตาม source
-- ตัวอย่างข้อมูล
return {
{
plate = 'ABC123',
model = 'sultan'
}
}
end)
Client
local vehicles = lib.callback.await(
'garage:getVehicles',
false
)
if not vehicles then
return
end
for i = 1, #vehicles do
print(vehicles[i].plate)
end
Flow คือ
เปิด Garage
↓
Callback
↓
Server Query
↓
Return Vehicles
↓
Client Render Menu
⑲ ตัวอย่าง HasItem Callback
Server
lib.callback.register('myJob:hasItem', function(source, item)
-- ตรวจ Inventory Server-side
local hasItem = true
return hasItem
end)
Client
local hasItem = lib.callback.await(
'myJob:hasItem',
false,
'repairkit'
)
if hasItem then
print('สามารถทำงานต่อได้')
end
การตรวจ Item จริงควรเกิดฝั่ง Server
Client รับผลไปใช้แสดง UX
⑳ Callback สำหรับซื้อของต้องระวัง
สมมติ Client ขอ
ซื้อ water
Server Callback สามารถตรวจ
Item มีจริง?
Price เท่าไร?
Player มีเงินไหม?
Inventory รับ Item ได้ไหม?
แต่ต้องระวังไม่สร้าง Logic เช่น
Client ส่ง
item = water
price = 1
แล้ว Server เชื่อ Price นั้น
Server ต้องหา Price จาก Server-controlled Data เอง
㉑ Callback ไม่ได้ทำให้ Client Input ปลอดภัย
แม้ API จะชื่อ Callback แทน Event แต่ Client Request ยังเป็นข้อมูลจาก Client
ตัวอย่าง
lib.callback.register('shop:getPrice', function(source, item)
item มาจาก Client
จึงควรตรวจว่า item
เป็น String หรือไม่
อยู่ใน Shop จริงหรือไม่
Player ใช้ Shop นี้ได้หรือไม่
Callback ไม่ได้เป็น Security Layer
มันเป็น Communication Pattern
㉒ อย่าส่งข้อมูล Secret กลับ Client
Callback Server → Client ไม่ควร Return
Database Password
API Secret
Private Token
Server-only Configuration
Internal Security Data
เพราะ Response ถูกส่งไป Client
ถ้า Client ไม่จำเป็นต้องรู้ อย่าส่งออกไป
หลักคือ
ส่งข้อมูลขั้นต่ำ
ที่ Client จำเป็นต้องใช้
㉓ อย่าส่ง Player Object ทั้งก้อนโดยไม่จำเป็น
สมมติ Client ต้องการเพียง
job name
ไม่ควร Return Player Object ขนาดใหญ่ทั้งหมดหากไม่จำเป็น
ควร Return
return {
job = jobName
}
หรือ
return jobName
ช่วยลด
Network Payload
Serialization
Coupling
ข้อมูลที่เปิดเผยให้ Client
Cfx.re แนะนำให้ส่งข้อมูล Network ให้น้อยและเรียบง่ายเท่าที่จำเป็น เนื่องจากมีต้นทุน Serialization.
㉔ Callback เรียกทุก Frame ได้ไหม
ไม่ควร
ตัวอย่างที่แย่
CreateThread(function()
while true do
local money = lib.callback.await(
'player:getMoney',
false
)
Wait(0)
end
end)
นี่อาจสร้าง Network Requests จำนวนมาก
ควรเรียกเมื่อ
เปิด Menu
State เปลี่ยน
กด Action
ต้อง Refresh จริง
หรือ Cache ข้อมูลที่เหมาะสม
อย่าใช้ Server Callback เป็นแทน Local Variable ทุก Frame
㉕ Callback อาจทำให้ Menu เปิดช้าได้
ตัวอย่าง
เปิด Menu
↓
Callback Server
↓
Database Query ช้า
↓
Response ช้า
↓
Menu รอ
ผู้เล่นจะรู้สึกว่า UI Lag
ดังนั้นถ้า Callback ช้า ให้ตรวจ
Database Query
Network
Server Load
Callback Logic
Dependency
จำนวนข้อมูลที่ Return
ไม่ควรโทษ NUI หรือ Client FPS อย่างเดียว
㉖ Database Query ใน Callback ควร Optimize
ตัวอย่าง
เปิด Inventory
↓
Callback
↓
SELECT ข้อมูลจำนวนมาก
↓
Response
หาก Query ทำทุกครั้งและไม่มี Index ที่เหมาะสม อาจสร้าง Performance Problem
แนวทางที่ดี เช่น
Query เฉพาะ Column ที่ต้องใช้
ใช้ Index
Cache ข้อมูลที่เหมาะสม
ไม่ Query ซ้ำโดยไม่จำเป็น
ไม่ Return Dataset ใหญ่เกินไป
Callback ที่ออกแบบดีต้องคิดทั้ง Correctness และ Performance
㉗ Callback ซ้อน Callback ควรระวัง
ตัวอย่าง
Callback A
↓
รอ Callback B
↓
รอ Callback C
↓
Query Database
ระบบอาจทำงานได้ แต่ Dependency Chain ยาวขึ้น
หาก C ช้า
C ช้า
↓
B รอ
↓
A รอ
↓
UI รอ
จึงควรออกแบบ Request Flow ให้เรียบง่าย
หาก Server หา Data เองได้หลายส่วน ควรรวม Logic ที่เหมาะสมฝั่ง Server มากกว่าทำ Network Round Trip หลายครั้ง
㉘ await ใช้เยอะเกินไปมีปัญหาไหม
await ทำให้ Code อ่านง่าย แต่ไม่ได้ทำให้ Network Response เป็น Instant
ตัวอย่าง
local a = lib.callback.await('getA', false)
local b = lib.callback.await('getB', false)
local c = lib.callback.await('getC', false)
หากแต่ละคำขอต้องรอทีละชุด อาจเพิ่ม Latency
บางกรณีควรออกแบบ Callback เดียวให้ Server Return ข้อมูลที่เกี่ยวข้องพร้อมกัน
เช่น
local data = lib.callback.await(
'dashboard:getData',
false
)
แล้ว Return
{
money = ...,
job = ...,
level = ...
}
หากข้อมูลเหล่านั้นควรถูกรวมกันจริง
㉙ Callback Timeout เกิดจากอะไร
อาการเช่น
Callback ไม่ตอบ
UI ค้าง
Code รอ
อาจเกิดจาก
Callback Name ไม่ตรง
Server Callback ไม่ได้ Register
Resource ไม่ Start
Server Error ก่อน Return
Dependency Error
Database Query ค้าง
บาง Code Path ไม่ Return
Resource Restart กลาง Request
ให้ตรวจทั้ง
F8
Server Console
พร้อมกัน
㉚ Callback Name ไม่ตรง
Client
lib.callback.await('garage:getVehicles', false)
Server
lib.callback.register('garage:getVehicle', function(source)
ต่างกันเพียง
getVehicles
vs
getVehicle
ก็เป็นคนละชื่อ
ควรใช้ Naming Convention เช่น
resource:action
ตัวอย่าง
mygarage:getVehicles
mygarage:getVehicleOwner
mygarage:canStoreVehicle
㉛ Callback ชื่อซ้ำต้องระวัง
ถ้า Resource หลายตัว Register ชื่อกว้างอย่าง
getData
อาจสร้างความสับสน
ควร Namespace
mygarage:getData
myphone:getData
myjob:getData
หรือให้ละเอียดกว่า
mygarage:getOwnedVehicles
ช่วย Debug และดูแล Server ในระยะยาวง่ายขึ้น
㉜ Resource Restart แล้ว Callback ใช้ไม่ได้
ตรวจ
Client Resource โหลดใหม่?
Server Callback Register ใหม่?
Dependency พร้อม?
Framework พร้อม?
Player State Initialize แล้ว?
ถ้า Restart Server แล้วใช้ได้ แต่ Hot Restart Resource แล้ว Callback พัง อาจเป็น Lifecycle Problem
โดยเฉพาะ Script ที่สร้าง Callback หลัง Framework Ready Event บางประเภท
㉝ Callback ใช้ได้เมื่อ ensure ใหม่ แต่ Boot ไม่ได้
กรณีนี้ควรตรวจ Start Order
ตัวอย่าง
ensure my-garage
ensure ox_lib
แต่ my-garage Register หรือเรียก ox_lib Callback ตอน Start
อาจเกิด Error
ควรจัด
ensure ox_lib
ensure my-garage
ถ้า Resource นั้นพึ่ง ox_lib
หลักคือ Dependency ต้องพร้อมก่อน Resource ลูก
㉞ QBCore Callback ขึ้น nil ต้องตรวจอะไร
ถ้า
QBCore.Functions.TriggerCallback('myCallback', function(result)
แล้ว result เป็น nil
ให้ตรวจ
Server
CreateCallbackมีไหมCallback Name ตรงไหม
Player Object มีไหม
Server เรียก
cb(...)หรือไม่Data ที่หาไม่มีจริงหรือไม่
Database Query สำเร็จไหม
Server Console มี Error หรือไม่
อย่าแก้ด้วยการกำหนดค่า Default ก่อนหาสาเหตุ เพราะอาจซ่อน Server Error
㉟ ox_lib callback ใช้ delay ทำอะไร
ฝั่ง Client ของ ox_lib มี Parameter
delay
ใน
lib.callback(name, delay, cb, ...)
และ
lib.callback.await(name, delay, ...)
เอกสารระบุว่า Delay ใช้กำหนดช่วงเวลาก่อน Callback ชื่อดังกล่าวจะ Trigger ได้อีกครั้ง.
ตัวอย่าง
lib.callback(
'myScript:getData',
1000,
function(data)
print(data)
end
)
แนวคิดคือช่วยป้องกันการ Trigger Callback ฝั่ง Client ถี่เกินช่วงที่กำหนด
แต่ไม่ควรใช้เป็น Security Validation ฝั่ง Serverเพียงอย่างเดียว
㊱ Client Rate Limit ไม่แทน Server Validation
แม้ Client Callback มี Delay เช่น
1000 ms
ผู้โจมตีสามารถดัดแปลง Client ได้
ดังนั้น Server ยังควรตรวจ
Cooldown
Player State
Permission
Job
Position
ตามความจำเป็น
Cfx.re ย้ำว่า Client-side checks สามารถถูก Override ได้.
㊲ Callback ควรใช้สำหรับ Reward หรือไม่
Callback สามารถใช้ใน Flow Reward ได้ แต่ Architecture ต้องถูก
ตัวอย่างไม่ดี
Client:
Reward เท่าไร?
Server:
500
Client:
โอเค เดี๋ยวฉันเพิ่มเงินเอง
เงินต้องถูกแก้ฝั่ง Server
Flow ที่ดีกว่า
Client
↓
ขอ Complete Job
↓
Server
↓
Validate
↓
Server เพิ่มเงิน
↓
Response
↓
{ success = true, reward = 500 }
↓
Client แสดง Notification
Response มีไว้บอกผล ไม่ใช่ให้ Client เป็นผู้แก้ Economy
㊳ ตัวอย่าง Callback Response แบบมี Status
แทนการ Return ค่าเดียว สามารถใช้ Table
Server
lib.callback.register('shop:buyItem', function(source, item)
if not item then
return {
success = false,
error = 'invalid_item'
}
end
-- validate / purchase
return {
success = true,
item = item
}
end)
Client
local response = lib.callback.await(
'shop:buyItem',
false,
'water'
)
if not response.success then
print(response.error)
return
end
รูปแบบนี้ช่วยให้ API ชัดขึ้นเมื่อมีหลายกรณี
㊴ Callback Error ควร Return อย่างไร
ออกแบบ Response ให้คาดเดาได้
ตัวอย่าง
Success
→ { success = true, data = ... }
Fail
→ { success = false, error = ... }
ดีกว่าบางครั้ง Return
false
nil
string
table
สลับกันโดยไม่มีมาตรฐาน
เมื่อ Script ใหญ่ขึ้น Consistent API จะช่วยลด Bug ได้มาก
㊵ NUI Callback คือ Callback แบบเดียวกันไหม
แนวคิด Request/Response คล้ายกัน แต่เป็นระบบคนละบริบท
FiveM มี NUI Callbacks สำหรับการสื่อสารระหว่าง Browser UI กับ Game Client
ตัวอย่าง Lua
RegisterNUICallback('getItemInfo', function(data, cb)
cb({
name = 'Water'
})
end)
Browser-side JavaScript สามารถ fetch() ไปยัง Resource แล้วรับ Response กลับ
เอกสาร Cfx.re ระบุว่า NUI Callback ควรเรียก cb ตอบกลับเสมอ มิฉะนั้น Request อาจค้างและเกิด Error ในฝั่ง fetch.
㊶ NUI Callback กับ Server Callback ต่างกันอย่างไร
NUI Callback
Browser UI
↕
FiveM Client
Framework/Library Server Callback
FiveM Client
↕
FiveM Server
ตัวอย่าง Flow ของ UI ที่ต้องใช้ Server Data อาจเป็น
NUI
↓
RegisterNUICallback
↓
Client
↓
Server Callback
↓
Server
↓
Database
↓
Response
↓
Client
↓
NUI cb()
นี่เป็น Flow ที่พบได้บ่อยใน Phone, Garage, MDT และ Inventory UI
㊷ NUI Callback ต้องตอบ cb ทุกครั้ง
ตัวอย่าง
RegisterNUICallback('getData', function(data, cb)
if not data.id then
cb({
success = false
})
return
end
cb({
success = true
})
end)
อย่ามี Path แบบ
if not data.id then
return
end
โดยไม่มี cb
เพราะ Browser Request สามารถค้างได้
Cfx.re ระบุเรื่องนี้โดยตรงใน NUI Callback Documentation.
㊸ Callback ไม่ทำงานต้องตรวจอะไร
ตรวจตามนี้
Callback Name
↓
Resource Started
↓
Dependency
↓
Client Registration
↓
Server Registration
↓
Framework/Library Version
↓
Server Console
↓
F8
↓
Response Path
หากใช้ QBCore ตรวจ
CreateCallback
TriggerCallback
หากใช้ ox_lib ตรวจ
lib.callback.register
lib.callback
lib.callback.await
อย่าผสม Syntax จากคนละ Framework
㊹ อย่าใช้ QBCore Callback บน ESX โดยตรง
ตัวอย่าง
QBCore.Functions.TriggerCallback(...)
ต้องมี QBCore API
ถ้า Server ใช้ ESX จะไม่สามารถ Copy Syntax นี้มาใช้โดยตรง
เช่นเดียวกัน Code
ESX.TriggerServerCallback(...)
เป็น API ของ ESX ecosystem ไม่ใช่ QBCore
Script ที่รองรับหลาย Framework จึงมักสร้าง Bridge เช่น
bridge/esx.lua
bridge/qb.lua
bridge/qbox.lua
แล้ว Code หลักเรียก Interface กลาง
㊺ Bridge Callback ช่วยอะไร
สมมติ Code หลักต้องการ
GetPlayerJob
แทนที่จะกระจาย
QBCore Callback
ESX Callback
Qbox API
ทั่ว Script สามารถสร้าง
Framework.GetPlayerJob()
แล้ว Bridge เป็นผู้เลือกวิธีเรียกตาม Framework
ข้อดีคือ
เปลี่ยน Framework ง่าย
Debug ง่าย
Code หลักสะอาด
ลด Framework-specific Code
เหมาะกับ Script ที่ต้องรองรับหลายระบบ
㊻ Callback กับ Promise เกี่ยวกันอย่างไร
ระบบ Callback สมัยใหม่บางตัวสามารถใช้ Promise/Coroutine เพื่อทำ Code แบบ Await
ตัวอย่าง QBCore Server Function ปัจจุบันระบุว่า TriggerClientCallback รองรับทั้งรูปแบบ Callback Function และ Promise-style response handling.
แนวคิดคือ
Request
↓
Promise Pending
↓
Response
↓
Resolve
↓
Code ทำต่อ
ช่วยให้ Code ที่มี Async Operations อ่านง่ายขึ้น
แต่ Network และ Database ยังคงมี Latency จริงอยู่
㊼ Callback ไม่ควรกลายเป็น Database API ทุกอย่าง
ถ้าทุกการกดปุ่มเล็ก ๆ ต้อง
Client
↓
Callback
↓
Database
Server จะมี Query จำนวนมากโดยไม่จำเป็น
ควรพิจารณา
Server-side Cache
Player State
Framework Player Data
State Bags
Data ที่โหลดไว้แล้ว
Local Client Cache สำหรับข้อมูลที่ไม่สำคัญ
Database ควรถูกเรียกเมื่อจำเป็นจริง
㊽ Checklist FiveM Callback
ก่อนสร้าง Callback
ต้องการ Response จริงหรือไม่
Event ธรรมดาพอหรือไม่
Data อยู่ Client หรือ Server
Framework/Library ไหนรับผิดชอบ
Server Callback
ตรวจ
sourcePlayer มีจริง
Input Type
Job
Permission
Money
Item
Position
Cooldown
Response
Return ทุก Code Path
ใช้รูปแบบข้อมูลสม่ำเสมอ
ไม่ Return Secret
ส่งข้อมูลเท่าที่จำเป็น
Performance
ไม่เรียกทุก Frame
ลด Database Query
ลด Payload
หลีกเลี่ยง Callback Chain ยาว
ใช้ Cache เมื่อเหมาะสม
Debug
Callback Name ตรง
Resource Start
Dependency พร้อม
ตรวจ F8
ตรวจ Server Console
ตรวจ Version ของ Framework/Library
วิธีเลือก Event หรือ Callback
เลือกตามคำถามง่าย ๆ
ต้องการแค่บอกว่าเกิดอะไรขึ้น?
ใช้ Event
ผู้เล่นเริ่มงานแล้ว
ต้องการคำตอบกลับ?
ใช้ Callback
ผู้เล่นมี Item นี้กี่ชิ้น?
ตัวอย่าง
Event
→ Start Job
Callback
→ Get Job Data
ไม่จำเป็นต้องเปลี่ยน Event ทุกตัวเป็น Callback
และไม่จำเป็นต้องใช้ Callback หากไม่มี Response ที่ต้องรอ
สรุป FiveM Callback คืออะไร ใช้งานอย่างไร
FiveM Callback คือรูปแบบ Request → Response ที่เหมาะกับการขอข้อมูลระหว่าง Client และ Server เช่น
Client
↓
ขอข้อมูล
↓
Server
↓
ตรวจ/Query
↓
Return
↓
Client
ตัวอย่างที่พบบ่อย ได้แก่
Get Item Count
Get Player Job
Get Money
Get Vehicles
Get House Data
Get Online Police
Check Permission
สิ่งสำคัญคือต้องเข้าใจว่า Callback Client/Server ที่ใช้ใน FiveM ecosystem ไม่ได้มี Syntax เดียวสำหรับทุก Framework
ตัวอย่าง QBCore ใช้แนวทาง
TriggerCallback
CreateCallback
ขณะที่ ox_lib ใช้
lib.callback
lib.callback.await
lib.callback.register
ดังนั้นต้องใช้ Documentation ให้ตรงกับ Framework และ Library ที่ติดตั้งอยู่.
ด้าน Security ต้องใช้หลักเดียวกับ Network Events คือ อย่าเชื่อข้อมูลจาก Client โดยตรง Server ควรตรวจเงิน, Item, Job, Permission, Position และ State ด้วย Server-side Data ก่อนทำธุรกรรมสำคัญ.
แนวทางของ comsiam คือใช้ Callback เฉพาะตอนต้องการ Response จริง และให้ Server เป็น Source of Truth สำหรับข้อมูลสำคัญ ไม่ควรสร้าง Callback ทุก Frameหรือ Query Database ซ้ำเพียงเพื่ออัปเดต UI เล็กน้อย
สำหรับ FiveM Server ขนาดใหญ่ comsiam แนะนำให้กำหนด Callback Naming และ Response Format ให้เป็นมาตรฐานตั้งแต่ต้น เช่น resource:action และ { success, data, error } เพราะเมื่อมี Script จำนวนมาก การ Debug, เปลี่ยน Framework และตรวจ Security จะทำได้ง่ายขึ้นมาก
Comments
Post a Comment