FiveM TriggerEvent คืออะไร? ใช้อย่างไร และต่างจาก TriggerServerEvent อย่างไร

 FiveM TriggerEvent() คือ Function สำหรับเรียก Event ภายใน Execution Context เดียวกัน เช่น Client เรียก Event ที่ทำงานฝั่ง Client หรือ Server เรียก Event ที่ทำงานฝั่ง Server

ตัวอย่างง่ายที่สุดคือ

TriggerEvent('example:hello')

แล้วมี Event Handler รอรับ

AddEventHandler('example:hello', function()
    print('Hello FiveM')
end)

สิ่งที่ FiveM Developer ต้องจำให้แม่นคือ TriggerEvent() ไม่ได้ส่ง Event จาก Client ไป Server โดยอัตโนมัติ

ถ้าต้องการ

Client → Server

ต้องใช้

TriggerServerEvent()

และถ้าต้องการ

Server → Client

โดยทั่วไปใช้

TriggerClientEvent()

บทความนี้จะเจาะเฉพาะ TriggerEvent() ตั้งแต่พื้นฐาน การส่ง Parameter การใช้ระหว่าง Resource ไปจนถึงความแตกต่างด้าน Security และ Performance

① TriggerEvent FiveM คืออะไร

TriggerEvent() ใช้สำหรับ Trigger Event ที่มี Handler รอฟังอยู่

Syntax คือ

TriggerEvent('eventName')

สามารถส่ง Parameter ต่อท้ายได้

TriggerEvent(
    'example:player',
    'John',
    5000
)

จากนั้น Handler รับข้อมูล

AddEventHandler(
    'example:player',
    function(name, money)
        print(name)
        print(money)
    end
)

Flow คือ

TriggerEvent
↓
Event Name
↓
AddEventHandler
↓
Callback Function

② TriggerEvent เป็น Local Event

คำว่า Local Event หมายถึง Event ที่ทำงานอยู่ใน ฝั่งเดียวกัน

เช่น

Client
↓
TriggerEvent
↓
Client Event Handler

หรือ

Server
↓
TriggerEvent
↓
Server Event Handler

ไม่ได้หมายถึง Local เฉพาะ Resource เดียวเสมอไป เพราะ Event สามารถใช้สื่อสารกับ Resource อื่นใน Context เดียวกันได้ด้วย

จุดสำคัญคือ มันไม่ข้าม Client/Server Network Boundary ด้วยตัวเอง

③ TriggerEvent ฝั่ง Client ทำงานอย่างไร

ตัวอย่างใน client.lua

AddEventHandler(
    'example:hello',
    function()
        print('Hello from client')
    end
)

จากนั้น

TriggerEvent('example:hello')

Handler ที่อยู่ Client-side จะทำงาน

Flow คือ

client.lua
↓
TriggerEvent
↓
Client Event Runtime
↓
Client Handler

Server ไม่ได้รับ Event นี้เพียงเพราะชื่อ Event เหมือนกัน

④ TriggerEvent ฝั่ง Server ทำงานอย่างไร

Server ก็ใช้ TriggerEvent() ได้

ตัวอย่างใน server.lua

AddEventHandler(
    'example:serverTask',
    function()
        print('Server event running')
    end
)

แล้ว Trigger

TriggerEvent(
    'example:serverTask'
)

Event จะทำงานใน Server Context

Client ไม่ได้รับ Event นี้โดยอัตโนมัติ

⑤ TriggerEvent ส่ง Client ไป Server ได้ไหม

ไม่ได้

ตัวอย่าง Client

TriggerEvent(
    'example:serverEvent'
)

ถึง Server จะมี

AddEventHandler(
    'example:serverEvent',
    function()
        print('Server')
    end
)

ก็ไม่ควรคาดหวังว่า Client TriggerEvent() จะวิ่งข้ามไปเรียก Server Handler

ถ้าต้องการ Client → Server ให้ใช้

TriggerServerEvent(
    'example:serverEvent'
)

⑥ TriggerServerEvent ต่างจาก TriggerEvent อย่างไร

จำง่ายที่สุดดังนี้

TriggerEvent
→ Local Context

TriggerServerEvent
→ Client ไป Server

ตัวอย่าง

Local Client

TriggerEvent(
    'garage:open'
)

Client ส่งไป Server

TriggerServerEvent(
    'garage:requestVehicles'
)

สอง Function มีชื่อคล้ายกัน แต่มี Network Behavior ต่างกันมาก

⑦ TriggerClientEvent ต่างจาก TriggerEvent อย่างไร

TriggerClientEvent() ใช้จาก Server เพื่อส่ง Network Event ไปยัง Client ที่กำหนด

ตัวอย่าง

TriggerClientEvent(
    'garage:open',
    source
)

ส่วน

TriggerEvent(
    'garage:open'
)

บน Server จะ Trigger Event ภายใน Server Context เท่านั้น

สรุปคือ

TriggerEvent
→ Client→Client หรือ Server→Server

TriggerServerEvent
→ Client→Server

TriggerClientEvent
→ Server→Client

⑧ AddEventHandler คือคู่พื้นฐานของ TriggerEvent

การ Trigger Event จะมีประโยชน์เมื่อมี Handler รอรับ

ตัวอย่าง

AddEventHandler(
    'vehicle:stored',
    function()
        print('Vehicle stored')
    end
)

จากนั้น

TriggerEvent(
    'vehicle:stored'
)

เมื่อ Event ถูก Trigger Callback จะทำงาน

AddEventHandler() จึงเป็น Function สำคัญที่ควรเรียนคู่กับ TriggerEvent()

⑨ ตัวอย่าง TriggerEvent แบบง่ายที่สุด

AddEventHandler(
    'hello',
    function()
        print('Hello World')
    end
)

TriggerEvent('hello')

ผลลัพธ์คือ

Hello World

Concept นี้ดูง่าย แต่เป็นพื้นฐานของระบบ Event-driven ที่ซับซ้อนกว่านี้มาก

⑩ TriggerEvent ส่ง String ได้ไหม

ได้

ตัวอย่าง

TriggerEvent(
    'example:name',
    'John'
)

รับด้วย

AddEventHandler(
    'example:name',
    function(name)
        print(
            'Player name:',
            name
        )
    end
)

Parameter จะถูกส่งไปยัง Callback ตามลำดับ

⑪ TriggerEvent ส่ง Number ได้ไหม

ได้

ตัวอย่าง

TriggerEvent(
    'example:money',
    5000
)

รับ

AddEventHandler(
    'example:money',
    function(money)
        print(
            'Money:',
            money
        )
    end
)

สามารถส่งข้อมูลหลายค่าใน Event เดียวได้เช่นกัน

⑫ TriggerEvent ส่งหลาย Parameter ได้อย่างไร

ตัวอย่าง

TriggerEvent(
    'example:player',
    'John',
    5000,
    'police'
)

Handler

AddEventHandler(
    'example:player',
    function(name, money, job)
        print(name)
        print(money)
        print(job)
    end
)

ลำดับ Parameter ควรตรงกับสิ่งที่ Handler คาดหวัง

Project ใหญ่ควรมี Event Contract ที่ชัดเจน

⑬ TriggerEvent ส่ง Table ได้ไหม

ได้ใน Use Case ที่ Runtime รองรับข้อมูลนั้น

ตัวอย่าง

local data = {
    name = 'John',
    job = 'police',
    grade = 3
}

TriggerEvent(
    'example:playerData',
    data
)

รับ

AddEventHandler(
    'example:playerData',
    function(data)
        print(data.name)
        print(data.job)
    end
)

แต่ไม่ควรส่ง Table ขนาดใหญ่เพียงเพราะสะดวก

ส่งเฉพาะข้อมูลที่ Consumer ต้องใช้จะอ่านง่ายและลด Overhead

⑭ TriggerEvent ใช้กับ Function เดิมได้ไหม

ได้

สมมติมี Function

local function openGarage()
    print('Opening garage')
end

สามารถใช้เป็น Handler

AddEventHandler(
    'garage:open',
    openGarage
)

จากนั้น

TriggerEvent('garage:open')

จะเรียก Function openGarage

วิธีนี้ช่วยลดการซ้อน Anonymous Function เมื่อมี Function อยู่แล้ว

⑮ Function กับ TriggerEvent เลือกใช้อะไรดี

ถ้า Code อยู่ใน Module เดียวกันและต้องการเรียก Function โดยตรง

แบบนี้ง่ายกว่า

openGarage()

ไม่จำเป็นต้องทำ

TriggerEvent('garage:open')

ทุกครั้ง

Event เหมาะเมื่อ

  • ต้องการลด Coupling

  • Resource อื่นต้องรับ Event

  • หลาย Handler ต้องตอบสนอง

  • ต้องการ Event-driven Flow

อย่าใช้ Event แทน Function ทุก Function เพราะจะทำให้ตาม Flow ของ Code ยากขึ้น

⑯ TriggerEvent ใช้ระหว่าง Resource ได้ไหม

ได้เมื่อ Resource อยู่ใน Context เดียวกันและ Event ถูกออกแบบให้รับได้

ตัวอย่าง Resource A

TriggerEvent(
    'mygarage:vehicleStored',
    'ABC123'
)

Resource B

AddEventHandler(
    'mygarage:vehicleStored',
    function(plate)
        print(
            'Vehicle stored:',
            plate
        )
    end
)

นี่เป็นหนึ่งใน Use Case สำคัญของ TriggerEvent()

⑰ Event ระหว่าง Resource เหมาะกับอะไร

เหมาะกับการประกาศว่า “บางอย่างเกิดขึ้นแล้ว”

เช่น

player:loaded
vehicle:stored
job:changed
character:selected
garage:opened

Resource อื่นที่สนใจก็สามารถ Listen Event นั้นได้

Concept คือ

Resource A
↓
เหตุการณ์เกิดขึ้น
↓
TriggerEvent
↓
Resource B / C
↓
ตอบสนอง

ช่วยลดการผูก Resource เข้าหากันโดยตรงในบางระบบ

⑱ TriggerEvent กับ Export เลือกอะไร

ถ้าต้องการแจ้งว่าเหตุการณ์เกิดขึ้น

Event

มักเหมาะกว่า

ตัวอย่าง

vehicleStored

แต่ถ้าต้องการเรียก Function ของ Resource อื่นและต้องการผลลัพธ์ เช่น

GetVehiclePrice()

exports อาจเหมาะกว่า

จำง่าย ๆ ว่า

Event
→ Something happened

Export
→ Do/Get something

ไม่ใช่กฎตายตัว แต่ช่วยเลือก Architecture ได้ดี

⑲ TriggerEvent กับ Callback ต่างกันอย่างไร

TriggerEvent() ไม่ใช่ระบบ Request/Response โดยตรง

Flow หลักคือ

Trigger
↓
Handler ทำงาน

ส่วน Callback มักเป็น

Request
↓
ประมวลผล
↓
Return Result

เช่นต้องการรายการรถจาก Database

Callback มักเหมาะกว่า Event Local ธรรมดา หากต้องการ Result กลับอย่างชัดเจน

⑳ TriggerEvent ต้อง RegisterNetEvent ไหม

ถ้า Event ใช้เฉพาะใน Context เดียวกัน ไม่จำเป็นต้อง Register เป็น Network Event

สามารถใช้

AddEventHandler(
    'example:local',
    function()
        print('Local event')
    end
)

แล้ว

TriggerEvent(
    'example:local'
)

นี่เป็นแนวทางที่ Cfx.re แนะนำสำหรับ Event ที่ไม่จำเป็นต้องข้าม Network

㉑ ทำไม Local Event ไม่ควรใช้ RegisterNetEvent โดยไม่จำเป็น

เพราะ RegisterNetEvent() ทำให้ Event ถูกทำเครื่องหมายสำหรับ Network Use

ถ้า Event ต้องใช้เพียง

Server → Server

หรือ

Client → Client

การใช้ AddEventHandler() ก็เพียงพอ

การเปิด Event เป็น Networked โดยไม่จำเป็นเพิ่ม Surface ที่ต้องคิดเรื่อง Security

ดังนั้นควร Register เท่าที่ต้องใช้จริง

㉒ TriggerEvent ปลอดภัยหรือไม่

ต้องแยก Client กับ Server

Server-side Local Event

ถ้าใช้ AddEventHandler() และไม่ได้ Register Network Event ก็ช่วยจำกัด Event ให้อยู่ Context เดียวกัน

Client-side Local Event

อย่าถือว่า Client Event เป็น Security Boundary ที่เชื่อถือได้

Cfx.re เตือนว่า Client ที่ถูกดัดแปลงสามารถเรียก Event ระหว่าง Client Resources ผ่าน TriggerEvent() ได้

ดังนั้น Logic สำคัญไม่ควรป้องกันด้วย Client Local Event เพียงอย่างเดียว

㉓ Client TriggerEvent ใช้ตรวจ Admin ได้ไหม

ใช้เพื่อ UX ได้ แต่ไม่ควรเป็น Authority

ตัวอย่างที่ไม่ปลอดภัยคือ

AddEventHandler(
    'admin:giveMoney',
    function(amount)
        -- critical logic
    end
)

แล้วคิดว่าเพราะเป็น Local Client Event จึงไม่มีใครเรียกได้

Client Environment สามารถถูก Manipulate ได้

Permission สำคัญต้องตรวจฝั่ง Server

㉔ Event Name ลับช่วย Security ไหม

ไม่ควรใช้เป็น Security Mechanism

เช่นตั้งชื่อ

a9x7z_secret_event_42

ไม่ได้ทำให้ Event ปลอดภัย

Event Security ต้องมาจาก

Context ถูกต้อง
Server Validation
Permission
State
Rate Limit
Authority

ไม่ใช่ความหวังว่าไม่มีใครรู้ชื่อ Event

㉕ ตัวอย่าง Local Client Event ที่เหมาะสม

เช่นเปิด UI

AddEventHandler(
    'garage:openUi',
    function()
        SetNuiFocus(true, true)

        SendNUIMessage({
            action = 'open'
        })
    end
)

แล้วส่วนอื่นของ Client Resource เรียก

TriggerEvent(
    'garage:openUi'
)

นี่เป็น Client Presentation Logic ที่เหมาะกับ Local Event

㉖ ตัวอย่าง Local Server Event ที่เหมาะสม

เช่นแจ้งระบบ Logging ภายใน Server

AddEventHandler(
    'garage:vehicleStored',
    function(src, plate)
        print(
            ('Player %s stored %s'):format(
                src,
                plate
            )
        )
    end
)

เมื่อ Logic Garage สำเร็จ

TriggerEvent(
    'garage:vehicleStored',
    src,
    plate
)

ไม่จำเป็นต้องเปิด Client ให้ Trigger Event นี้ หากไม่มี Use Case

㉗ TriggerEvent ใช้กับ ESX ได้ไหม

ได้

Resource ESX สามารถมีทั้ง Custom Local Events และ Network Events

แต่ต้องแยกให้ชัดว่า Event ที่กำลังเรียกเป็น

Client Event
Server Event
Framework Event
Network Event

อย่าเห็น Event ของ ESX แล้วเปลี่ยนทุกอย่างเป็น TriggerServerEvent() หรือ TriggerEvent() แบบเดา

ต้องดู Context ของ Event นั้น

㉘ TriggerEvent ใช้กับ QBCore ได้ไหม

ได้

QBCore Resource จำนวนมากใช้ Event-driven Architecture

คุณอาจพบ Pattern เช่น

Local Event
Framework Event
Network Event
Callback

หากเข้าใจ TriggerEvent() พื้นฐาน จะตาม Flow ของ QBCore Resource ได้ง่ายขึ้น

㉙ TriggerEvent ใช้กับ Qbox ได้ไหม

ได้เหมือนกัน

Framework ไม่ได้เปลี่ยนหลัก Event Runtime ของ FiveM

สิ่งที่ต้องรู้คือ

Event นี้อยู่ฝั่งไหน?
ต้องข้าม Network หรือไม่?
ใครเป็นคน Trigger?
ใครเป็น Handler?

เมื่อหาคำตอบสี่ข้อนี้ได้ การ Debug Framework จะง่ายขึ้นมาก

㉚ TriggerEvent ใช้เปิด NUI ได้ไหม

ได้ใน Client Context

ตัวอย่าง

AddEventHandler(
    'phone:open',
    function()
        SetNuiFocus(true, true)

        SendNUIMessage({
            action = 'open'
        })
    end
)

แล้ว Trigger

TriggerEvent(
    'phone:open'
)

เหมาะกับการแยก Logic การเปิด UI ออกจากส่วนที่ตรวจ Input

㉛ TriggerEvent ใช้กับ Command ได้ไหม

ได้

ตัวอย่าง

RegisterCommand(
    'openmenu',
    function()
        TriggerEvent(
            'menu:open'
        )
    end,
    false
)

แล้ว

AddEventHandler(
    'menu:open',
    function()
        print('Opening menu')
    end
)

นี่เป็น Project ฝึก Event ที่ดีสำหรับมือใหม่

㉜ TriggerEvent ใช้ใน Thread ได้ไหม

ได้

ตัวอย่าง

CreateThread(function()
    Wait(5000)

    TriggerEvent(
        'example:ready'
    )
end)

หลังรอ 5 วินาที Event จะถูก Trigger

แต่ต้องระวังไม่สร้าง Event Spam จาก Loop

㉝ อย่า TriggerEvent ทุก Tick ถ้าไม่จำเป็น

ตัวอย่าง

CreateThread(function()
    while true do
        Wait(0)

        TriggerEvent(
            'player:update'
        )
    end
end)

Event นี้อาจถูก Trigger ทุก Game Tick

ถ้า Handler มี Logic หนักจะสร้าง Overhead โดยไม่จำเป็น

ควรถามว่า

ข้อมูลเปลี่ยนทุก Tickจริงหรือ?

ถ้าไม่ ให้ Trigger เฉพาะเมื่อข้อมูลเปลี่ยน

㉞ TriggerEvent ทำให้ FPS ตกได้ไหม

ตัว TriggerEvent() เพียงครั้งเดียวไม่ได้หมายความว่าจะทำให้ FPS ตก

แต่ปัญหาเกิดได้เมื่อ

  • Trigger ถี่มาก

  • มี Handler หลายตัว

  • Handler ทำงานหนัก

  • Trigger อยู่ใน Loop ทุก Frame

  • Event Chain ยาวมาก

ดังนั้น Performance ต้องดูทั้ง Producer และ Consumer ของ Event

㉟ Handler หลายตัวรับ Event เดียวกันได้ไหม

ได้

ตัวอย่าง

AddEventHandler(
    'player:ready',
    function()
        print('HUD ready')
    end
)

AddEventHandler(
    'player:ready',
    function()
        print('Phone ready')
    end
)

จากนั้น

TriggerEvent(
    'player:ready'
)

Handler ที่ Register ไว้สำหรับ Event เดียวกันสามารถตอบสนองได้

นี่เป็นข้อดีของ Event-driven Architecture

㊱ แต่ Handler เยอะเกินไปมีข้อเสียไหม

มี

ถ้า Event เดียว Trigger ระบบจำนวนมาก จะตาม Flow ยาก

เช่น

player:loaded
↓
20 Handlers
↓
บาง Handler Trigger Event ต่อ
↓
อีก 15 Handlers

เมื่อเกิด Error จะ Debug ยากมาก

Project ใหญ่ควร Document Event Flow และหลีกเลี่ยง Event Chain ที่ซับซ้อนโดยไม่มีเหตุผล

㊲ Event Chain คืออะไร

ตัวอย่าง

Event A
↓
Handler A
↓
Trigger Event B
↓
Handler B
↓
Trigger Event C

สามารถใช้ได้

แต่ถ้า Chain ยาวมากจะทำให้เข้าใจลำดับยาก

สำหรับ Business Logic ที่ต้องทำเรียงลำดับแน่นอน การใช้ Function Call ตรง ๆ บางครั้งเหมาะกว่า

㊳ ระวัง Event Loop

ตัวอย่าง

Event A
↓
Trigger B

Event B
↓
Trigger A

จะเกิดการเรียกกลับไปกลับมา

อาจสร้าง

  • CPU Load

  • Script Error

  • Infinite Logic

  • Server/Client Hang

ก่อนเชื่อม Event หลายตัวควรวาด Flow ให้รู้ว่า Event สิ้นสุดตรงไหน

㊴ TriggerEvent ทำงานแบบ Return Value ไหม

ไม่ควรออกแบบ TriggerEvent() ให้เป็น Function Call ที่คาดหวัง Return Value โดยตรงแบบนี้

result = TriggerEvent(...)

Event ถูกออกแบบเพื่อ Trigger Handler

ถ้าต้องการค่ากลับอย่างเป็นระบบ ควรพิจารณา

  • Function

  • Export

  • Callback

  • Promise/Async Pattern

ตาม Context

㊵ TriggerEvent เหมาะกับ Notification ไหม

ได้

สมมติ Client Resource มีระบบ Notification กลาง

AddEventHandler(
    'notify:show',
    function(message)
        print(message)
    end
)

Resource อื่นฝั่ง Clientอาจเรียก

TriggerEvent(
    'notify:show',
    'Vehicle stored'
)

นี่เป็น Use Case ที่เข้าใจง่าย

แต่หากเป็น Notification System ที่ Resource อื่นต้องเรียก Function และมี API ชัดเจน Export ก็เป็นอีกทางเลือกหนึ่ง

㊶ TriggerEvent เหมาะกับ Logging ไหม

ได้สำหรับ Event-based Logging ภายใน Server

เช่น

TriggerEvent(
    'audit:transaction',
    src,
    'vehicle_purchase',
    50000
)

Logging Resource รับ

AddEventHandler(
    'audit:transaction',
    function(src, action, amount)
        -- log
    end
)

แต่ข้อมูลสำคัญควรมาจาก Server Logic ที่เชื่อถือได้ ไม่ใช่ Client ส่งมาโดยตรงแล้ว Trigger Log ต่อ

㊷ TriggerEvent ใช้กับ Database ได้ไหม

สามารถ Trigger Local Server Event ที่นำไปสู่ Database Logic ได้

ตัวอย่าง

Server Business Logic
↓
TriggerEvent
↓
Server Handler
↓
Database/Logging

แต่ไม่ควรสร้าง Architecture ที่ Query Database ผ่าน Event หลายชั้นโดยไม่มีเหตุผล

Database Function หรือ Service ที่เรียกตรง ๆ มักอ่านง่ายกว่าในหลายกรณี

㊸ TriggerEvent กับ Resource Dependency

ถ้า Resource A Trigger Event ที่ Resource B ต้องรับ ต้องแน่ใจว่า Resource B พร้อมทำงาน

ตัวอย่าง server.cfg

ensure resource_b
ensure resource_a

หรือกำหนด Dependency ใน Manifest ตาม Architecture ที่ต้องการ

ไม่เช่นนั้น Resource A อาจ Trigger Event ตอนที่ Consumer ยังไม่พร้อม

㊹ Event Name ควรตั้ง Prefix หรือไม่

ควร

ตัวอย่าง Resource ชื่อ mygarage

ใช้

mygarage:open
mygarage:close
mygarage:stored
mygarage:vehicleSpawned

ดีกว่า

open
close
update

เพราะ

  • ลดชื่อชน

  • Search ง่าย

  • Debug ง่าย

  • อ่าน Log รู้ Resource

㊺ ควรใช้ชื่อ Event แบบไหน

ชื่อที่ดีควรสื่อว่าเกิดอะไรขึ้น

ตัวอย่าง

garage:vehicleStored
job:started
job:completed
phone:opened
player:characterLoaded

ชื่อที่อ่านยาก เช่น

doThing
event1
test2
abc

จะสร้างปัญหาเมื่อ Project ใหญ่ขึ้น

㊻ Event Parameter ควรตั้งชื่อให้ชัด

แทน

AddEventHandler(
    'garage:stored',
    function(a, b, c)
    end
)

ใช้

AddEventHandler(
    'garage:stored',
    function(playerId, plate, garageId)
    end
)

จะอ่านง่ายกว่า

Event Contract ที่ดีช่วยลด Bug ที่ส่ง Parameter สลับตำแหน่ง

㊼ Event Contract ควรเขียนอย่างไร

ตัวอย่าง

Event:
mygarage:vehicleStored

Context:
Server → Server

Parameters:
playerId: number
plate: string
garageId: string

Purpose:
แจ้งว่า Vehicle ถูกบันทึกเรียบร้อยแล้ว

ทีม Developer สามารถรู้ได้ทันทีว่า Event ใช้อย่างไร

Project ใหญ่ควร Document Custom Events สำคัญ

㊽ TriggerEvent ไม่ทำงาน ตรวจอะไรเป็นอันดับแรก

ตรวจ

① Event Name ตรงกันไหม
② Trigger กับ Handler อยู่ Context เดียวกันไหม
③ Resource Start แล้วไหม
④ Handler Register ก่อน Event ถูก Trigger หรือยัง
⑤ มี Error ก่อนถึง Trigger หรือไม่
⑥ Parameter ถูกต้องไหม
⑦ ดู Console แล้วหรือยัง

หลายครั้งปัญหาไม่ได้เกิดจาก TriggerEvent() เอง แต่เกิดจาก Resource หรือ Handler

㊾ Event Name ผิดหนึ่งตัวมีผลไหม

มี

ตัวอย่าง Trigger

TriggerEvent(
    'garage:open'
)

แต่ Handler

AddEventHandler(
    'garage:Open',
    function()
    end
)

ควรใช้ชื่อเดียวกันอย่างสม่ำเสมอ

หลีกเลี่ยงการใช้ตัวพิมพ์ใหญ่เล็กปะปนโดยไม่มี Standard

㊿ Handler ต้องถูก Register ก่อน Trigger ไหม

ถ้า Event ถูก Trigger ก่อนที่ Listener ที่ต้องการจะถูกติดตั้ง Handler นั้นอาจไม่ได้รับ Event ที่ผ่านไปแล้ว

Event ไม่ใช่ Database ที่เก็บเหตุการณ์ย้อนหลังให้ Handler ใหม่โดยอัตโนมัติ

ดังนั้น Resource Lifecycle และ Load Order มีความสำคัญเมื่อ Event ถูก Trigger ตั้งแต่ช่วง Initialize

51 TriggerEvent กับ Resource Start

ตัวอย่าง Resource อาจต้อง Initialize เมื่อ Resource เริ่ม

แต่ก่อน Trigger Custom Event ควรแน่ใจว่า Handler หรือ Dependency ที่ต้องใช้พร้อมแล้ว

Project ที่มี Resource หลายตัวควรใช้ Resource Dependency และ Lifecycle Event ให้เหมาะสม

อย่าพึ่ง Delay แบบสุ่มเพียงเพื่อหวังว่า Resource อื่นจะ Start ทัน

52 TriggerEvent กับ Shared Script

Shared Script สามารถประกาศ Function หรือ Event Handler ที่ถูกโหลดทั้ง Client และ Serverได้

แต่ต้องจำว่า

Client Context
≠
Server Context

ดังนั้น Event ชื่อเดียวกันใน Shared Code อาจมี Instance ของ Handler แยกอยู่คนละ Runtime

TriggerEvent() ยังไม่ข้าม Network

53 TriggerEvent กับ State Bags ต่างกันอย่างไร

TriggerEvent() คือการแจ้งว่าเหตุการณ์เกิดขึ้น

State Bag คือการเก็บ/Replicate State

ตัวอย่าง

TriggerEvent:
vehicle:locked
→ แจ้งว่า Action เกิดขึ้น

State Bag:
locked = true
→ บอก State ปัจจุบัน

สองระบบสามารถใช้ร่วมกันได้ แต่ไม่ใช่สิ่งเดียวกัน

54 TriggerEvent กับ OneSync เกี่ยวข้องไหม

TriggerEvent() Local Event ไม่ได้กลายเป็น OneSync Network Message เพียงเพราะ Server เปิด OneSync

OneSync เกี่ยวข้องกับ Player/Entity Networking และ State

ส่วน TriggerEvent() Local ยังคงทำงานใน Context เดียวกัน

หากต้อง Client ↔ Server ต้องใช้ Network Event API ที่เหมาะสม

55 TriggerEvent กับ JavaScript ใช้อะไร

ใน JavaScript Runtime แนวคิด Local Event ใช้

emit('example:hello');

รับด้วย

on('example:hello', () => {
    console.log('Hello');
});

Concept เหมือน Lua

Lua:
TriggerEvent

JavaScript:
emit

แต่ Syntax ของ Runtime ต่างกัน

56 TriggerEvent กับ C# ใช้ได้ไหม

C# Runtime ก็มีระบบ Trigger Event ของ CitizenFX ตาม API ของ Runtime

Concept เดียวกันคือ

Event Name
+
Arguments
+
Handler

ดังนั้นความรู้เรื่อง Local vs Network Context สามารถย้ายข้าม Lua, JavaScript และ C# ได้

57 TriggerEvent และ Security ต้องจำอะไร

จำสามข้อ

1. TriggerEvent ไม่ได้ข้าม Client/Server

2. Server-only Local Event ใช้ AddEventHandler
   หากไม่จำเป็นต้อง Network

3. Client-side Local Event ไม่ใช่ Security Boundary

โดยเฉพาะข้อสามสำคัญมาก

Client Script สามารถถูก Manipulate ได้ ดังนั้นอย่าเก็บ Money, Permission หรือ Reward Authority ไว้ใน Client Event Flow

58 TriggerEvent และ Performance ต้องจำอะไร

ใช้ Event เมื่อมีเหตุการณ์จริง

ไม่ใช่

ทุก Frame
↓
TriggerEvent
↓
Heavy Handler

ตลอดเวลา

ควรพิจารณา

  • Event Frequency

  • Handler Cost

  • จำนวน Handler

  • Parameter Size

  • Event Chain

โดยเฉพาะ Resource ใหญ่

59 Checklist ก่อนใช้ TriggerEvent

ถามตัวเองว่า

① Event นี้อยู่ Client หรือ Server?
② Consumer อยู่ Context เดียวกันไหม?
③ ต้องข้าม Network หรือไม่?
④ ถ้าข้าม Networkควรใช้ TriggerServerEvent/TriggerClientEvent หรือไม่?
⑤ ต้องใช้ Event หรือเรียก Function ตรงง่ายกว่า?
⑥ Resource อื่นต้องรับ Event ไหม?
⑦ Parameter มีอะไร?
⑧ Event ถูก Trigger ถี่ไหม?
⑨ Event มี Security-sensitive Logic หรือไม่?
⑩ Event Name มี Prefix ชัดเจนไหม?

ถ้าตอบได้ครบ การเลือกใช้ TriggerEvent() จะผิดพลาดน้อยลงมาก

60 ตัวอย่าง Project ฝึก TriggerEvent

ลองสร้างระบบ Menu ง่าย ๆ

client.lua

AddEventHandler(
    'training:openMenu',
    function(title)
        print(
            'Opening:',
            title
        )
    end
)

RegisterCommand(
    'trainingmenu',
    function()
        TriggerEvent(
            'training:openMenu',
            'FiveM Training'
        )
    end,
    false
)

เมื่อใช้

/trainingmenu

Flow คือ

Command
↓
TriggerEvent
↓
training:openMenu
↓
AddEventHandler
↓
แสดงข้อความ

นี่เป็นตัวอย่างพื้นฐานที่ดีสำหรับมือใหม่ก่อนเข้าสู่ Network Event

คำถามที่พบบ่อยเกี่ยวกับ FiveM TriggerEvent

FiveM TriggerEvent คืออะไร

คือ Function สำหรับ Trigger Event ภายใน Execution Context เดียวกัน เช่น Client → Client หรือ Server → Server

TriggerEvent ใช้จาก Client ไป Server ได้ไหม

ไม่ได้โดยตรง ต้องใช้ TriggerServerEvent()

Server ส่ง Event ไป Client ใช้อะไร

ใช้ TriggerClientEvent() ตาม Target ที่ต้องการ

TriggerEvent ใช้คู่กับอะไร

โดยทั่วไปใช้คู่กับ AddEventHandler() สำหรับ Local Event

TriggerEvent ต้องใช้ RegisterNetEvent ไหม

ไม่ หาก Event ต้องทำงานภายใน Context เดียวกันเท่านั้น

TriggerEvent ส่ง Parameter ได้ไหม

ได้ สามารถส่งค่าต่อท้ายชื่อ Event ได้

TriggerEvent ส่ง Table ได้ไหม

ได้ในข้อมูลที่ Runtime รองรับ แต่ควรส่งเฉพาะข้อมูลที่จำเป็น

TriggerEvent ใช้ระหว่าง Resource ได้ไหม

ได้ ถ้า Resource อยู่ Context เดียวกันและ Handler สำหรับ Event นั้นทำงานอยู่

TriggerEvent คืนค่าได้ไหม

ไม่ควรใช้เป็น Function Return Mechanism โดยตรง หากต้องการ Result ควรพิจารณา Function, Export หรือ Callback

TriggerEvent ปลอดภัยไหม

Server-side Local Event ที่ไม่ Networked ลด Exposure ได้ แต่ Client-side Events ไม่ควรถูกใช้เป็น Security Boundary

TriggerEvent ทำให้ Script กิน CPU ไหม

ตัว Event เองไม่ใช่ปัญหาหลัก แต่การ Trigger ถี่มากหรือ Handler ทำงานหนักสามารถเพิ่ม CPU Usage ได้

TriggerEvent กับ TriggerServerEvent ต่างกันอย่างไร

TriggerEvent() ทำงานใน Context เดียวกัน ส่วน TriggerServerEvent() ส่ง Network Event จาก Client ไป Server

สรุป FiveM TriggerEvent คืออะไร

FiveM TriggerEvent() คือ Function สำหรับ Trigger Event ภายใน Client หรือ Server Context เดียวกัน

หลักที่ต้องจำคือ

CLIENT

TriggerEvent
↓
Client Handler

และ

SERVER

TriggerEvent
↓
Server Handler

แต่ถ้าต้องการ

Client
↓
Server

ต้องใช้

TriggerServerEvent()

และถ้าต้องการ

Server
↓
Client

ใช้

TriggerClientEvent()

สำหรับ Local Event ที่ไม่ต้องข้าม Network ควรใช้ AddEventHandler() และไม่จำเป็นต้องเปิด Event เป็น Network Event ด้วย RegisterNetEvent() โดยไม่มีเหตุผล

อีกเรื่องที่สำคัญคือ Client-side TriggerEvent() ไม่ใช่ระบบ Security เพราะ Client ที่ถูกดัดแปลงสามารถพยายามเรียก Client Events ได้ ดังนั้น Money, Items, Rewards, Permission และ Business Logic สำคัญยังต้องตรวจ Server-side

สำหรับคนที่กำลังเรียน FiveM Developer กับ comsiam ให้จำประโยคสั้น ๆ ว่า TriggerEvent = เรียก Event ฝั่งเดิม จะช่วยแยก Function ในกลุ่ม Event ได้ง่ายมาก

เมื่อเข้าใจ TriggerEvent() แล้ว ขั้นต่อไปบน comsiam คือ TriggerServerEvent() ซึ่งเป็นจุดเริ่มต้นของการส่ง Network Event จาก Client ไป Server และเป็นหัวข้อที่ต้องให้ความสำคัญกับ Server-side Validation และ Security มากเป็นพิเศษ

Comments

Popular posts from this blog

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

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

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