FiveM AddEventHandler คืออะไร? วิธีสร้าง Event Listener ใน Lua FiveM

 FiveM AddEventHandler() คือ Function สำหรับเพิ่ม Event Handler หรือ Listener ให้ Script ทำงานเมื่อ Event ที่กำหนดถูก Trigger

ตัวอย่างพื้นฐานที่สุดคือ

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

จากนั้น Trigger Event

TriggerEvent('example:hello')

เมื่อ example:hello ถูกเรียก Function ที่อยู่ใน AddEventHandler() จะทำงาน

Flow คือ

TriggerEvent
↓
Event Name
↓
AddEventHandler
↓
Callback Function

AddEventHandler() ใช้ได้ทั้ง Client และ Server และสำคัญมากสำหรับ Local Events, Built-in Events, Resource Events และการสื่อสารระหว่าง Resource ที่อยู่ใน Execution Context เดียวกัน

สิ่งสำคัญคือ ถ้า Event ต้องข้าม Network ระหว่าง Client กับ Server จะต้องใช้ระบบ Network Event เช่น RegisterNetEvent() ร่วมกับ TriggerServerEvent() หรือ TriggerClientEvent() แทน

① AddEventHandler FiveM คืออะไร

AddEventHandler() ใช้เพิ่ม Function ที่รอรับ Event ชื่อหนึ่ง

Syntax พื้นฐานคือ

AddEventHandler(
    'eventName',
    function(...)
        -- logic
    end
)

ตัวอย่าง

AddEventHandler(
    'garage:opened',
    function()
        print('Garage opened')
    end
)

จากนั้น

TriggerEvent(
    'garage:opened'
)

Handler จะถูกเรียก

② Event Handler คืออะไร

Event Handler คือ Function ที่ตอบสนองเมื่อ Event เกิดขึ้น

สามารถคิดเป็น

เหตุการณ์เกิด
↓
Event ถูก Trigger
↓
Handler ที่รอฟัง Event นี้
↓
ทำงาน

ตัวอย่าง

รถถูกเก็บ
↓
garage:vehicleStored
↓
AddEventHandler
↓
เขียน Log

Event-driven Architecture จึงช่วยให้ระบบหนึ่งแจ้งอีกระบบได้โดยไม่ต้องผูก Function กันโดยตรงทุกกรณี

③ AddEventHandler ใช้กับ TriggerEvent อย่างไร

ตัวอย่าง Client หรือ Server Context เดียวกัน

AddEventHandler(
    'example:test',
    function()
        print('Event executed')
    end
)

TriggerEvent(
    'example:test'
)

เมื่อถึง TriggerEvent() FiveM จะเรียก Handler ที่ลงทะเบียนไว้สำหรับชื่อ example:test

นี่เป็น Pattern พื้นฐานที่สุดของ Local Event

④ AddEventHandler ใช้ฝั่ง Client ได้ไหม

ได้

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

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

แล้ว Client Script ส่วนอื่นเรียก

TriggerEvent(
    'menu:open'
)

เกิด Flow

Client
↓
TriggerEvent
↓
Client AddEventHandler

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

⑤ AddEventHandler ใช้ฝั่ง Server ได้ไหม

ได้เช่นกัน

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

AddEventHandler(
    'garage:vehicleStored',
    function(plate)
        print(
            'Stored:',
            plate
        )
    end
)

Server Logic เรียก

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

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

⑥ AddEventHandler เป็น Network Event หรือไม่

ไม่โดยตัวมันเอง

ถ้าเขียน

AddEventHandler(
    'example:test',
    function()
    end
)

และไม่ได้ Register Event สำหรับ Network Event นั้นจะใช้ใน Context เดียวกัน

เช่น

Client → Client

หรือ

Server → Server

นี่เป็นข้อแตกต่างสำคัญจาก RegisterNetEvent()

⑦ AddEventHandler ต่างจาก RegisterNetEvent อย่างไร

จำง่าย ๆ ว่า

AddEventHandler
→ เพิ่ม Listener/Handler

RegisterNetEvent
→ อนุญาต Event ให้ใช้ข้าม Network

สำหรับ Local Event

AddEventHandler(
    'example:local',
    function()
    end
)

เพียงพอ

สำหรับ Client → Server เช่น

RegisterNetEvent(
    'example:network',
    function()
    end
)

จึงเหมาะกว่า

⑧ Event Server-only ควรใช้ AddEventHandler หรือ RegisterNetEvent

หาก Event มีไว้สำหรับ

Server
↓
Server

และ Client ไม่จำเป็นต้องเรียก

ควรใช้

AddEventHandler()

ตามแนวทาง Security ของ Cfx.re

ตัวอย่าง

AddEventHandler(
    'inventory:itemRemoved',
    function(playerId, itemName)
        -- server-only logic
    end
)

ช่วยไม่เปิด Event ให้ฝั่ง Client เรียกผ่าน Network โดยไม่จำเป็น

⑨ Event Client-only ควรใช้ AddEventHandler หรือไม่

ได้

ตัวอย่าง

AddEventHandler(
    'ui:open',
    function()
        SetNuiFocus(
            true,
            true
        )
    end
)

แล้ว Client เรียก

TriggerEvent('ui:open')

ถ้า Server ไม่จำเป็นต้อง Trigger Event นี้ ก็ไม่จำเป็นต้องใช้ RegisterNetEvent()

⑩ ทำไมไม่ควรใช้ RegisterNetEvent ทุก Event

เพราะ Event บางตัวไม่จำเป็นต้องเปิด Network

ตัวอย่าง Server มี Event

salary:calculateInternal

ที่ใช้เฉพาะ Server Logic

ถ้าเปิด Network ให้ Client เรียกโดยไม่จำเป็น จะเพิ่ม Attack Surface

หลักคือ

Local communication
→ AddEventHandler

Network communication
→ RegisterNetEvent

⑪ AddEventHandler รับ Parameter ได้ไหม

ได้

ตัวอย่าง

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

Trigger

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

ค่าจะถูกส่งตามลำดับ

John
→ name

5000
→ money

⑫ ส่งหลาย Parameter ได้ไหม

ได้

TriggerEvent(
    'job:update',
    'police',
    3,
    true
)

Handler

AddEventHandler(
    'job:update',
    function(job, grade, onDuty)
        print(job)
        print(grade)
        print(onDuty)
    end
)

Project ใหญ่ควรตั้งชื่อ Parameter ให้ชัด

⑬ ส่ง Table ได้ไหม

ได้ใน Lua Event ที่รองรับ Data นั้น

ตัวอย่าง

local vehicle = {
    plate = 'ABC123',
    model = 'sultan'
}

TriggerEvent(
    'garage:stored',
    vehicle
)

รับด้วย

AddEventHandler(
    'garage:stored',
    function(vehicle)
        print(vehicle.plate)
        print(vehicle.model)
    end
)

แต่ถ้าต้องใช้ข้อมูลเพียงหนึ่งหรือสองค่า ไม่จำเป็นต้องสร้าง Table ใหญ่โดยไม่มีเหตุผล

⑭ AddEventHandler ใช้ Function ที่สร้างไว้แล้วได้ไหม

ได้

ตัวอย่าง

local function onVehicleStored(plate)
    print(
        'Vehicle stored:',
        plate
    )
end

จากนั้น

AddEventHandler(
    'garage:stored',
    onVehicleStored
)

ช่วยให้ Handler อ่านง่าย โดยเฉพาะเมื่อ Function มี Logic หลายบรรทัด

⑮ Anonymous Function กับ Named Function เลือกอะไร

ถ้า Handler สั้น

AddEventHandler(
    'example:test',
    function()
        print('test')
    end
)

อ่านง่าย

แต่ถ้า Logic ยาว ควรแยก

local function handleTest()
    -- logic
end

AddEventHandler(
    'example:test',
    handleTest
)

ช่วยให้

  • อ่านง่าย

  • Test ง่าย

  • Reuse ได้

  • Event Registration ไม่รก

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

ได้ หากอยู่ใน Execution Context เดียวกัน

Resource A

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

Resource B ฝั่งเดียวกัน

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

สามารถใช้ Event เป็นวิธีแจ้ง Resource อื่นว่าเหตุการณ์บางอย่างเกิดขึ้นแล้ว

⑰ Client Resource A ส่ง Event ไป Client Resource B ได้ไหม

ได้ผ่าน Local Client Event

Client Resource A
↓
TriggerEvent
↓
Client Event Runtime
↓
Client Resource B
↓
AddEventHandler

แต่ต้องจำเรื่อง Security:

Client Environment อยู่บนเครื่อง Player

ดังนั้น Event Client-side ไม่ควรถูกใช้เป็น Authority สำหรับข้อมูลสำคัญ

⑱ Server Resource A ส่ง Event ไป Server Resource B ได้ไหม

ได้

ตัวอย่าง

Garage Resource
↓
vehicle stored
↓
TriggerEvent
↓
Logging Resource
↓
AddEventHandler

เหมาะกับ Event อย่าง

vehicleStored
playerLoaded
jobChanged
transactionCompleted

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

⑲ AddEventHandler กับ Export ต่างกันอย่างไร

Event เหมาะกับแนวคิด

Something happened

เช่น

vehicleStored

Export เหมาะกับ

เรียก Function

เช่น

GetVehiclePrice()

ตัวอย่าง

Event
→ แจ้งหลาย Listener ได้

Export
→ เรียก Function ของ Resource โดยตรง

เลือกตาม Architecture

⑳ AddEventHandler กับ Callback ต่างกันอย่างไร

Event Handler ไม่ได้ออกแบบมาเป็น Request → Return Value โดยตรง

Flow คือ

Trigger
↓
Handler

Callback เหมาะกว่าเมื่อต้องการ

Request
↓
Process
↓
Response

เช่น Client ขอรายการรถจาก Server แล้วต้องรอข้อมูลกลับ

㉑ Event หนึ่งมีหลาย Handler ได้ไหม

ได้

ตัวอย่าง

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

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

Trigger

TriggerEvent(
    'player:ready'
)

Handler ที่ Listen Event เดียวกันสามารถถูกเรียกได้

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

㉒ หลาย Handler มีข้อเสียอะไร

ถ้ามี Handler จำนวนมากเกินไป Event Flow จะตามยาก

เช่น

playerLoaded
↓
Handler 1
↓
Handler 2
↓
Handler 3
↓
Handler 4
...
↓
Handler 30

ยิ่งแต่ละ Handler Trigger Event ต่ออีก ยิ่ง Debug ยาก

ดังนั้นไม่ควรใช้ Event เป็นคำตอบของทุกการสื่อสารใน Project

㉓ Handler ทำงานตามลำดับที่เพิ่มไว้แน่นอนไหม

ไม่ควรออกแบบ Business Logic สำคัญโดยพึ่งลำดับของ Event Handlers หลายตัวแบบเปราะบาง

ถ้ากระบวนการต้องเกิดตามลำดับ เช่น

Validate
↓
หักเงิน
↓
เพิ่ม Item
↓
บันทึก Transaction

ควรควบคุม Flow ด้วย Function/Service ที่ชัดเจน

ไม่ควรหวังว่า Handler หลายตัวจะทำ Transaction สำคัญต่อกันอย่างปลอดภัยเอง

㉔ AddEventHandler คืนค่าอะไร

AddEventHandler() คืน Event Handler Data/Reference

สามารถเก็บไว้ได้ เช่น

local handler =
    AddEventHandler(
        'example:test',
        function()
            print('test')
        end
    )

Reference นี้สามารถใช้กับ

RemoveEventHandler()

เพื่อถอด Listener ออกในภายหลัง

㉕ RemoveEventHandler คืออะไร

ใช้ลบ Handler ที่เพิ่มไว้ก่อนหน้า

ตัวอย่าง

local handler =
    AddEventHandler(
        'example:test',
        function()
            print('Hello')
        end
    )

Trigger

TriggerEvent(
    'example:test'
)

จากนั้นลบ

RemoveEventHandler(
    handler
)

หลัง Handler ถูกถอดออกแล้ว จะไม่รับ Event ในรูปเดิมอีก

㉖ ทำไม RemoveEventHandler มีประโยชน์

เหมาะกับ Dynamic Listener เช่น

  • Temporary Event

  • Mission

  • Session

  • UI Lifecycle

  • Temporary Feature

ตัวอย่าง

Mission Start
↓
Add Handler
↓
Mission Running
↓
Mission End
↓
Remove Handler

ช่วยไม่ให้ Listener ที่ไม่ใช้แล้วค้างอยู่โดยไม่จำเป็น

㉗ ต้อง RemoveEventHandler ทุกตัวไหม

ไม่จำเป็น

Handler ที่ควรอยู่ตลอดอายุ Resource สามารถอยู่ได้จน Resource Stop

เช่น

playerDropped
resourceStarted
garage:vehicleStored

ที่เป็น Core Listener ของ Resource

RemoveEventHandler() มีประโยชน์เมื่อ Listener มี Lifecycle แบบชั่วคราว

㉘ AddEventHandler กับ Built-in Event

FiveM มี Built-in Events ที่ Developer สามารถ Listen ได้

ตัวอย่างเช่น Resource หรือ Player Lifecycle Events

รูปแบบคือ

AddEventHandler(
    'builtInEventName',
    function(...)
        -- logic
    end
)

จึงไม่ได้ใช้เฉพาะ Custom Event ที่ Developer สร้างเอง

㉙ ตัวอย่าง onClientGameTypeStart

เอกสาร Lua แรกของ Cfx.re มีตัวอย่าง

AddEventHandler(
    'onClientGameTypeStart',
    function()
        -- client logic
    end
)

นี่แสดงว่า AddEventHandler() สามารถ Listen Built-in Client Event ได้

เมื่อ Event นั้นเกิด Handler จะทำงาน

㉚ playerDropped ใช้ AddEventHandler ได้ไหม

ได้ฝั่ง Server

ตัวอย่าง Concept

AddEventHandler(
    'playerDropped',
    function(reason)
        local src = source

        print(
            'Player left:',
            src,
            reason
        )
    end
)

นิยมใช้ Cleanup เช่น

  • Player Cache

  • Active Job

  • Cooldown

  • Temporary Session

  • Runtime Data

㉛ source ใน AddEventHandler คืออะไร

ใน Event Context บางประเภท โดยเฉพาะ Server Events ที่เกี่ยวข้องกับ Player จะมี Global source

ตัวอย่าง

AddEventHandler(
    'playerDropped',
    function()
        local src = source

        print(src)
    end
)

แต่ source ไม่ใช่ค่าที่ควรถือว่าใช้ได้ตลอดเวลาใน Async Logic

㉜ ทำไมต้องเก็บ source เป็น local ก่อน Wait

นี่เป็นรายละเอียดสำคัญมาก

เอกสาร Cfx.re ระบุว่า Global source รับประกันค่าได้สำหรับ Event Call เริ่มต้น

ถ้าคุณทำ

Wait(...)

หรือเข้าสู่ Asynchronous Scope ค่า Global source อาจถูก Reset/เปลี่ยนกลับ

ดังนั้นควรทำ

local src = source

ก่อน

㉝ ตัวอย่าง source ที่ถูกต้อง

AddEventHandler(
    'example:test',
    function()
        local src = source

        Wait(1000)

        print(src)
    end
)

ไม่ควรพึ่ง

AddEventHandler(
    'example:test',
    function()
        Wait(1000)

        print(source)
    end
)

หากต้องการ Player Source เดิมหลัง Yield

หลักเดียวกันสำคัญกับ Network Event Handlers ด้วย

㉞ AddEventHandler ใช้ Wait ได้ไหม

ได้ใน Context/Coroutine ที่ Runtime รองรับ

แต่ถ้าต้องใช้ source ภายหลังให้ Capture เป็น Local ก่อน

local src = source

และต้องระวัง Handler ที่ใช้เวลานาน เพราะ Event สามารถถูก Trigger อีกครั้งในระหว่างที่ Logic อื่นกำลังดำเนินการได้ตาม Architecture

㉟ Handler ยาวมากควรทำอย่างไร

ไม่ควรใส่ Business Logic หลายร้อยบรรทัดไว้ใน Handler หากแยกได้

แทน

AddEventHandler(
    'garage:stored',
    function(...)
        -- 300 lines
    end
)

สร้าง

local function handleVehicleStored(
    playerId,
    plate
)
    -- logic
end

แล้ว

AddEventHandler(
    'garage:stored',
    handleVehicleStored
)

ช่วยให้ Code Maintain ง่ายกว่า

㊱ AddEventHandler ใช้กับ RegisterNetEvent อย่างไร

รูปแบบดั้งเดิมคือ

RegisterNetEvent(
    'shop:buy'
)

AddEventHandler(
    'shop:buy',
    function(item, amount)
        -- logic
    end
)

ปัจจุบันสามารถย่อเป็น

RegisterNetEvent(
    'shop:buy',
    function(item, amount)
        -- logic
    end
)

ทั้งสองแบบถูกต้อง

㊲ RegisterNetEvent จริง ๆ เกี่ยวข้องกับ AddEventHandler อย่างไร

เอกสาร Security ของ Cfx.re อธิบายว่าในแนวคิด RegisterNetEvent() เป็นการทำให้ Event ใช้ผ่าน Network และผูก Handler ได้

ดังนั้น Pattern

RegisterNetEvent(
    'eventName'
)

AddEventHandler(
    'eventName',
    function()
    end
)

และ

RegisterNetEvent(
    'eventName',
    function()
    end
)

ให้ผลในแนวทางเดียวกันสำหรับ Use Case ทั่วไป

㊳ Event Network ควรใช้ AddEventHandler อย่างเดียวไหม

ไม่

ถ้าต้องการให้ Event รับจากอีก Network Context เช่น

Client → Server

ต้อง Register Event สำหรับ Network ด้วย

การมีเพียง

AddEventHandler(
    'shop:buy',
    function()
    end
)

ทำให้ Event เป็น Local Handler ไม่ใช่ Network Event โดยอัตโนมัติ

㊴ Local Event ปลอดภัยกว่า Network Event หรือไม่

ในแง่ Attack Surface ฝั่ง Server Local Event ที่ไม่ได้ Register Network จะไม่เปิดให้ Client เรียกผ่าน Network Context แบบเดียวกับ Net Event

ดังนั้น Server-internal Event ควรอยู่ Local หากไม่มีเหตุผลให้ Client เรียก

แต่ยังต้องเขียน Logic ให้ถูกต้อง ไม่ใช่ว่า Local Event แล้วไม่มี Bug ได้เลย

㊵ Client Local Event ปลอดภัยจาก Cheat ไหม

ไม่ควรใช้ Client Local Event เป็น Security Boundary

Cfx.re ระบุว่า Cheat อาจสามารถ Trigger Event ใน Client Context ได้

ดังนั้นระบบอย่าง

Money
Reward
Admin Permission
Inventory

ไม่ควรถูกป้องกันเพียง

AddEventHandler()

บน Client

Authority สำคัญต้องอยู่ Server

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

เช่นเปิด Menu

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

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

นี่เป็น Presentation Logic

ถ้า Client Manipulate UI ได้ ผลเสียด้าน Authority ควรถูกจำกัด เพราะ Transaction จริงยังตรวจ Server-side

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

เช่น Logging

AddEventHandler(
    'bank:transactionCompleted',
    function(src, amount)
        print(
            src,
            amount
        )
    end
)

Banking Logic ฝั่ง Server Trigger

TriggerEvent(
    'bank:transactionCompleted',
    src,
    amount
)

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

㊸ AddEventHandler ใช้กับ Database ได้ไหม

ได้ใน Server Logic

เช่น Transaction เสร็จแล้ว Trigger Server-local Event เพื่อ Log

แต่ถ้า Function เดียวสามารถเรียก Database Service โดยตรงได้ง่ายกว่า ก็ไม่จำเป็นต้องเพิ่ม Event Layer

อย่าสร้าง Architecture

Event
↓
Event
↓
Event
↓
Database

หลายชั้นโดยไม่มีประโยชน์

㊹ AddEventHandler กับ NUI

Client สามารถใช้ Local Event เปิดหรือปิด NUI

เช่น

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

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

แล้ว Command หรือ Client Logic เรียก

TriggerEvent(
    'phone:open'
)

เป็นการแยก Input Logic กับ UI Logic ออกจากกัน

㊺ AddEventHandler กับ ESX

ESX Resource จำนวนมากใช้ Events

บาง Event เป็น

Local Event

บาง Event เป็น

Network Event

อย่าเห็นชื่อ Event ของ Framework แล้วเลือก AddEventHandler หรือ RegisterNetEvent แบบเดา

ต้องตรวจว่า Event ถูก Trigger มาจาก Context ไหน

㊻ AddEventHandler กับ QBCore

หลักเหมือนกัน

QBCore มี Framework Events และ Custom Resource Events

Developer ต้องแยกว่า

Client → Client
Server → Server
Client → Server
Server → Client

ให้ได้ก่อนเลือก Event API

㊼ AddEventHandler กับ Qbox

Qbox ก็ยังทำงานบน FiveM Event Runtime

ดังนั้นการเข้าใจ AddEventHandler() ช่วยให้อ่าน Resource ได้ง่ายขึ้น ไม่ว่าจะใช้ Framework ใด

Framework เป็นเพียง Layer เหนือ Event System ของ FiveM

㊽ JavaScript ใช้อะไรแทน AddEventHandler

ใน JavaScript FiveM ใช้

on(
    'example:test',
    (data) => {
        console.log(data);
    }
);

สำหรับ Local Event Listener

Trigger ด้วย

emit(
    'example:test',
    data
);

Concept จึงเป็น

Lua
AddEventHandler + TriggerEvent

JavaScript
on + emit

㊾ JavaScript Network Event ใช้อะไร

ถ้าเป็น Network Event JavaScript ใช้

onNet()

ร่วมกับ emitNet() ตามทิศทางและ Runtime

จึงคล้ายกับความแตกต่าง

Lua:
AddEventHandler
vs
RegisterNetEvent

ในเชิง Local vs Network Listener

㊿ C# ใช้ Event Handler อย่างไร

C# FiveM ใช้ Event Handler System เช่น

EventHandlers["eventName"]

ตัวอย่าง Concept

EventHandlers["eventName"] +=
    new Action(
        () =>
        {
            Debug.WriteLine(
                "Event called"
            );
        }
    );

Syntax ต่างจาก Lua แต่ Concept Event Listener เหมือนกัน

51 AddEventHandler มีผลต่อ Performance ไหม

การมี Handler ไม่ได้หมายความว่าจะมีปัญหาทันที

Performance ขึ้นกับ

  • Event Trigger บ่อยแค่ไหน

  • Handler ทำงานหนักแค่ไหน

  • มี Listener กี่ตัว

  • Handler Trigger Event ต่อหรือไม่

  • มี Loop/Database/Native หนักหรือไม่

Event ที่ Trigger วันละไม่กี่ครั้งแทบไม่เหมือน Event ที่ Trigger ทุก Frame

52 Trigger Event ทุก Frame ควรไหม

โดยทั่วไปควรหลีกเลี่ยงถ้าไม่จำเป็น

ตัวอย่าง

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

        TriggerEvent(
            'player:update'
        )
    end
end)

ถ้า Handler ทำงานหนักจะสร้าง Overhead ทุก Tick

ควร Trigger เมื่อข้อมูลเปลี่ยนหรือใช้ Interval ที่เหมาะสม

53 AddEventHandler ทำให้ Memory Leak ได้ไหม

Handler ที่อยู่ตลอด Resource Lifecycle เป็นเรื่องปกติ

แต่ถ้า Script สร้าง Dynamic Handlers ซ้ำเรื่อย ๆ แล้วไม่ลบเมื่อหมดอายุ อาจทำให้มี Listener สะสมและ Behavior ซ้ำซ้อน

เช่นทุกครั้งที่เปิด Mission สร้าง Handler ใหม่

Mission 1
→ Handler 1

Mission 2
→ Handler 2

Mission 3
→ Handler 3

โดยไม่ Remove ตัวเก่า

จึงควรใช้ RemoveEventHandler() สำหรับ Dynamic Lifecycle เมื่อเหมาะสม

54 Event ถูกเรียกซ้ำหลายครั้งเกิดจากอะไร

หนึ่งในสาเหตุคือมี Handler ถูก Add ซ้ำ

ตัวอย่าง Function Initialize ถูกเรียกหลายครั้ง และทุกครั้งทำ

AddEventHandler(
    'example:test',
    function()
    end
)

เมื่อ Trigger Event หนึ่งครั้ง Handler หลายตัวจึงตอบสนอง

ถ้าเห็น Event ทำงานซ้ำ ให้ตรวจว่า Registration ถูกเรียกกี่ครั้ง

55 วิธี Debug AddEventHandler

เริ่มจาก

print(
    'registering handler'
)

แล้วใน Handler

AddEventHandler(
    'example:test',
    function(data)
        print(
            'event received:',
            data
        )
    end
)

จุดที่ Trigger

print(
    'triggering event'
)

TriggerEvent(
    'example:test',
    'hello'
)

จะเห็นว่า Flow หายตรงไหน

56 AddEventHandler ไม่ทำงาน ตรวจอะไรบ้าง

ตรวจ

① Event Name ตรงกันหรือไม่
② Trigger กับ Handler อยู่ Context เดียวกันหรือไม่
③ Resource Start หรือยัง
④ Handler ถูก Register ก่อน Trigger หรือไม่
⑤ client.lua/server.lua ถูกโหลดหรือไม่
⑥ มี Error ก่อน AddEventHandler หรือไม่
⑦ Parameter ถูกต้องหรือไม่
⑧ ดู F8 Console หรือยัง
⑨ ดู Server Console หรือยัง

ส่วนใหญ่ปัญหาอยู่ที่ชื่อ Event หรือ Context ผิด

57 Event Name ควรตั้งอย่างไร

ใช้ Prefix ตาม Resource

ตัวอย่าง

mygarage:vehicleStored
mygarage:opened
mygarage:closed

ดีกว่า

stored
open
close

ช่วยลดชื่อชนและ Search Code ง่ายขึ้น

58 Checklist ก่อนใช้ AddEventHandler

ก่อนสร้าง Handler ถามว่า

① Event นี้ Client หรือ Server?
② เป็น Local Event หรือ Network Event?
③ ถ้า Network ต้อง RegisterNetEvent หรือไม่?
④ Parameter มีอะไร?
⑤ Event ถูก Trigger จาก Resource ไหน?
⑥ Event Trigger บ่อยแค่ไหน?
⑦ Handler ทำงานหนักหรือไม่?
⑧ ต้องการหลาย Listener จริงไหม?
⑨ Handler เป็น Temporary หรือ Permanent?
⑩ ถ้า Temporary ต้อง RemoveEventHandler หรือไม่?
⑪ ใช้ Function ตรงง่ายกว่าหรือไม่?
⑫ ถ้าใช้ source หลัง Wait เก็บเป็น local แล้วหรือยัง?

Checklist นี้ช่วยลด Error ได้มาก

59 มือใหม่ควรฝึก AddEventHandler อย่างไร

เริ่มจาก Local Client Event

client.lua

AddEventHandler(
    'training:hello',
    function(name)
        print(
            'Hello',
            name
        )
    end
)

RegisterCommand(
    'eventhello',
    function()
        TriggerEvent(
            'training:hello',
            'FiveM'
        )
    end,
    false
)

เมื่อใช้

/eventhello

Flow คือ

Command
↓
TriggerEvent
↓
training:hello
↓
AddEventHandler
↓
Callback

เมื่อเข้าใจ Pattern นี้แล้วค่อยต่อ Network Events

60 AddEventHandler ต้องจำอะไรที่สุด

จำหลักสั้น ๆ

AddEventHandler
=
ฟัง Event

และ

TriggerEvent
=
เรียก Local Event

ส่วนถ้าต้องข้าม Client/Server

RegisterNetEvent
+
TriggerServerEvent
หรือ
TriggerClientEvent

จึงเข้ามาเกี่ยวข้อง

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

AddEventHandler คืออะไร

คือ Function สำหรับเพิ่ม Event Listener/Handler ที่จะทำงานเมื่อ Event ที่ระบุถูก Trigger

AddEventHandler ใช้ฝั่ง Client ได้ไหม

ได้

AddEventHandler ใช้ฝั่ง Server ได้ไหม

ได้

AddEventHandler ใช้คู่กับ TriggerEvent ได้ไหม

ได้ และเป็น Pattern หลักของ Local Events

AddEventHandler เป็น Network Event ไหม

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

AddEventHandler ต่างจาก RegisterNetEvent อย่างไร

AddEventHandler() เพิ่ม Listener ส่วน RegisterNetEvent() ทำให้ Event สามารถถูกเรียกข้าม Network Context ได้

AddEventHandler รับ Parameter ได้ไหม

ได้

AddEventHandler รับ Table ได้ไหม

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

Event หนึ่งมีหลาย AddEventHandler ได้ไหม

ได้ แต่ควรระวัง Complexity และ Handler ที่ถูก Add ซ้ำโดยไม่ตั้งใจ

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

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

AddEventHandler คืนค่าอะไร

คืน Event Handler Data/Reference ซึ่งใช้กับ RemoveEventHandler() ได้

RemoveEventHandler คืออะไร

ใช้ลบ Handler ที่เคยสร้างด้วย AddEventHandler()

ต้อง Remove Handler ทุกตัวไหม

ไม่จำเป็น เฉพาะ Dynamic/Temporary Handler ที่ไม่ต้องการใช้อีกแล้วตาม Lifecycle

source ใช้หลัง Wait ได้ไหม

ควรเก็บ source ลง Local Variable ก่อน Wait หรือ Async Scope เพราะ Cfx.re ระบุว่า Global source รับประกันค่าเฉพาะ Event Call เริ่มต้น

AddEventHandler ปลอดภัยจาก Cheat ไหม

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

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

FiveM AddEventHandler() คือ Function สำหรับสร้าง Event Handler หรือ Listener ให้ Code ทำงานเมื่อ Event ที่ระบุถูก Trigger

ตัวอย่างพื้นฐานคือ

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

เรียกด้วย

TriggerEvent(
    'example:hello'
)

สำหรับ Event ใน Context เดียวกัน

Client → Client
หรือ
Server → Server

AddEventHandler() เหมาะมากและไม่จำเป็นต้องเปิด Network Event

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

Client → Server
หรือ
Server → Client

ต้องใช้ RegisterNetEvent() และ Network Trigger ที่เหมาะสม

อีกเรื่องสำคัญมากคือ AddEventHandler() คืน Handler Reference ซึ่งสามารถส่งให้ RemoveEventHandler() เพื่อลบ Listener ภายหลังได้ เหมาะกับระบบที่สร้าง Temporary Events

และสำหรับ Server Event ที่ใช้ source หาก Code จะ Wait() หรือเข้าสู่ Async Operation ควรเก็บ

local src = source

ก่อน เพราะค่า Global source ไม่ได้รับประกันว่าจะคงเดิมหลัง Event Call แรกจบหรือ Script Yield

สำหรับผู้ที่เรียน FiveM Developer กับ comsiam หากแยก AddEventHandler() กับ RegisterNetEvent() ได้ถูกต้อง จะช่วยให้ Event Architecture ทั้งอ่านง่ายและลด Network Exposure ที่ไม่จำเป็น

หลักที่ควรจำจาก comsiam คือ Local Event ใช้ Local Handler ส่วน Network Event เปิด Network เฉพาะเมื่อจำเป็น และหัวข้อถัดไปจะเข้าสู่ระบบ Callback FiveM ซึ่งใช้เมื่อต้องการ Request ข้อมูลแล้วรับ Response กลับอย่างเป็นระบบ

Comments

Popular posts from this blog

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

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

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