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

ตรวจ

  • source

  • Player มีจริง

  • 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

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด