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
Post a Comment