FiveM Event และ RegisterNetEvent คืออะไร
FiveM Event คือระบบสำหรับให้ Code ส่วนต่าง ๆ ส่งเหตุการณ์และสื่อสารกัน ไม่ว่าจะเป็น Resource เดียวกัน, Resource คนละตัว หรือการสื่อสารระหว่าง Client กับ Server
ตัวอย่างเช่น เมื่อผู้เล่นกดปุ่มทำงาน อาจเกิด Flow แบบนี้
ผู้เล่นกดเริ่มงาน
↓
Client
↓
TriggerServerEvent
↓
Server
↓
ตรวจ Job / ตำแหน่ง / เงื่อนไข
↓
บันทึกสถานะ
↓
TriggerClientEvent
↓
Client แสดง Notification
คำสั่งสำคัญที่ควรรู้คือ
AddEventHandler
RegisterNetEvent
TriggerEvent
TriggerServerEvent
TriggerClientEvent
สรุปให้จำง่ายก่อนว่า
TriggerEvent
= Event ภายใน Context เดียวกัน
RegisterNetEvent
= ลงทะเบียน Event ที่ต้องรับผ่าน Network ได้
TriggerServerEvent
= Client → Server
TriggerClientEvent
= Server → Client
สิ่งสำคัญที่สุดคือ Network Event ไม่ใช่ระบบรักษาความปลอดภัย หาก Client สามารถ Trigger Event ไปยัง Server ได้ Server ต้องตรวจข้อมูลเองเสมอ เช่น เงิน, Item, Job, Permission และตำแหน่งของผู้เล่น
① FiveM Event คืออะไร
Event คือกลไกที่ทำให้ Script สามารถบอกว่า
“มีเหตุการณ์บางอย่างเกิดขึ้นแล้ว ให้ Code ที่รอฟังเหตุการณ์นี้ทำงาน”
ตัวอย่าง
AddEventHandler('myScript:hello', function()
print('Hello FiveM')
end)
จากนั้น Trigger
TriggerEvent('myScript:hello')
ผลคือ Handler ที่รอ Event ชื่อ
myScript:hello
จะทำงาน
แนวคิดคือ
Trigger
↓
Event Name
↓
Handler
↓
Execute Code
② Event ช่วยอะไรในการเขียน FiveM Script
ถ้าไม่มี Event Resource ต่าง ๆ จะเชื่อมต่อกันได้ยากขึ้น
ตัวอย่างระบบ Job อาจประกอบด้วย
Client Interaction
↓
Server Validation
↓
Inventory
↓
Database
↓
Notification
Event ช่วยเชื่อมขั้นตอนเหล่านี้โดยไม่ต้องให้ Code ทุกส่วนอยู่ในไฟล์เดียวกัน
จึงสามารถออกแบบ Script แบบแยกหน้าที่ เช่น
client.lua
server.lua
inventory integration
framework integration
แล้วให้แต่ละส่วนสื่อสารกันผ่าน Event หรือ API ที่เหมาะสม
③ Local Event คืออะไร
Local Event หมายถึง Event ที่ทำงานภายใน Context เดียวกัน
ตัวอย่าง
Client → Client
หรือ
Server → Server
สำหรับ Lua สามารถใช้
AddEventHandler('myScript:test', function()
print('Event received')
end)
TriggerEvent('myScript:test')
หาก Code นี้อยู่ฝั่ง Server ก็เป็น Server-local Event
หากอยู่ฝั่ง Client ก็เป็น Client-local Event
Cfx.re แนะนำให้ใช้ AddEventHandler สำหรับ Event ที่ตั้งใจใช้งานภายใน Context เดียว เพื่อไม่เปิด Event นั้นเข้าสู่ Network โดยไม่จำเป็น.
④ TriggerEvent คืออะไร
TriggerEvent ใช้ Trigger Event ภายใน Context ปัจจุบัน
รูปแบบ
TriggerEvent('eventName')
ส่ง Parameter ได้ เช่น
TriggerEvent('myScript:message', 'Hello')
Handler
AddEventHandler('myScript:message', function(message)
print(message)
end)
ถ้าทำใน Client
Client Resource A
↓
TriggerEvent
↓
Client Event
ถ้าทำใน Server
Server Resource A
↓
TriggerEvent
↓
Server Event
TriggerEvent ไม่ใช่คำสั่ง Client → Server โดยตรง.
⑤ RegisterNetEvent คืออะไร
RegisterNetEvent ใช้ลงทะเบียน Event ให้สามารถใช้งานใน Network Context ได้ เช่น
Client → Server
หรือ
Server → Client
ตัวอย่าง
RegisterNetEvent('myScript:hello', function(message)
print(message)
end)
อีกรูปแบบหนึ่งคือ
RegisterNetEvent('myScript:hello')
AddEventHandler('myScript:hello', function(message)
print(message)
end)
สำหรับ Lua รูปแบบแรกสั้นและอ่านง่ายกว่า
เอกสาร Cfx.re อธิบายว่า RegisterNetEvent ใช้เมื่อ Event จำเป็นต้องถูก Trigger ข้าม Context ผ่าน Network และสามารถใส่ Handler Function ในคำสั่งเดียวได้.
⑥ RegisterNetEvent กับ AddEventHandler ต่างกันอย่างไร
จำง่าย ๆ ว่า
AddEventHandler
เหมาะกับ Event ภายใน Context เดียว
AddEventHandler('myScript:localEvent', function()
print('Local event')
end)
RegisterNetEvent
ใช้เมื่อ Event ต้องรับผ่าน Network
RegisterNetEvent('myScript:networkEvent', function()
print('Network event')
end)
ดังนั้นไม่ควรเปลี่ยน Event ทุกตัวให้เป็น RegisterNetEvent โดยไม่มีเหตุผล
ถ้า Event ใช้เฉพาะ
Server → Server
ให้ใช้ Local Event จะจำกัดพื้นผิวการโจมตีได้ดีกว่า
⑦ RegisterNetEvent ไม่ได้หมายความว่าปลอดภัย
นี่เป็นเรื่องที่สำคัญมาก
การเขียน
RegisterNetEvent('job:reward', function()
-- code
end)
ไม่ได้หมายความว่าเฉพาะ Client Script ของเราที่สามารถเรียก Event นี้ได้
Cfx.re เตือนว่า Client ที่ถูกดัดแปลงสามารถพยายาม Trigger Network Events ได้ ดังนั้น Server-side Event ต้องมี Validation ของตัวเอง.
หลักควรเป็น
Client ส่ง Request
↓
Server ไม่เชื่อทันที
↓
Server ตรวจข้อมูล
↓
ผ่าน
↓
ทำ Action
⑧ TriggerServerEvent คืออะไร
TriggerServerEvent ใช้จาก Client เพื่อ Trigger Event ฝั่ง Server
ตัวอย่าง Client
TriggerServerEvent('myJob:start')
Server
RegisterNetEvent('myJob:start', function()
print('Player started the job')
end)
Flow คือ
Client
↓
TriggerServerEvent
↓
Network
↓
Server
↓
RegisterNetEvent Handler
นี่เป็นรูปแบบพื้นฐานของ Client → Server communication ใน FiveM.
⑨ TriggerServerEvent ส่งข้อมูลได้ไหม
ได้
ตัวอย่าง Client
TriggerServerEvent('myScript:selectJob', 'police')
Server
RegisterNetEvent('myScript:selectJob', function(jobName)
print(jobName)
end)
แต่ต้องจำว่า
ข้อมูลจาก Client
=
Untrusted Input
ดังนั้นไม่ควรนำ jobName ไปเปลี่ยน Job ของผู้เล่นทันทีโดยไม่มี Validation
⑩ ตัวอย่าง Event Client → Server แบบไม่ปลอดภัย
ตัวอย่างที่ไม่ควรออกแบบ
Client
TriggerServerEvent('job:reward', 100000)
Server
RegisterNetEvent('job:reward', function(amount)
AddMoney(source, amount)
end)
ปัญหาคือ Client เป็นคนส่ง
amount
Server เชื่อทันที
ผู้โจมตีอาจพยายามส่ง
99999999
แทนค่าปกติ
นี่คือ Error ด้าน Architecture ไม่ใช่ Error ของ FiveM
⑪ ตัวอย่าง Reward Event ที่ดีกว่า
Client ส่งเพียง
TriggerServerEvent('job:complete')
Server
RegisterNetEvent('job:complete', function()
local src = source
-- ตรวจ Job
-- ตรวจสถานะงาน
-- ตรวจตำแหน่ง
-- ตรวจ Cooldown
local reward = 500
-- Server เป็นผู้กำหนด reward
-- AddMoney(src, reward)
end)
แนวคิดคือ
Client
→ ขอ Complete
Server
→ ตรวจว่า Complete จริงหรือไม่
→ คำนวณ Reward เอง
→ จ่ายเงิน
Cfx.re แนะนำโดยตรงให้ตรวจข้อมูล Server-side เช่น เงิน, Inventory, Player State, Position, Level และ Permissions แทนการเชื่อค่าที่ Client ส่งมา.
⑫ source ใน Server Event คืออะไร
เมื่อ Client Trigger Server Event
TriggerServerEvent('myScript:test')
ใน Server Handler สามารถใช้
source
เพื่ออ้างถึงผู้เล่นที่ Trigger Event
ตัวอย่าง
RegisterNetEvent('myScript:test', function()
local src = source
print(('Player %s triggered event'):format(src))
end)
ในระบบจริงมักนำ src ไปใช้ดึง
Player Object
Identifier
Job
Inventory
Permissions
จาก Framework หรือ Server API
⑬ อย่าให้ Client ส่ง source เองโดยไม่จำเป็น
ตัวอย่างที่ไม่ดี
Client
TriggerServerEvent('job:reward', GetPlayerServerId(PlayerId()))
Server
RegisterNetEvent('job:reward', function(playerId)
GiveMoney(playerId)
end)
กรณีทั่วไปไม่จำเป็น เพราะ Server มี
source
ของผู้ที่ Trigger Event อยู่แล้ว
แนวคิดที่ดีกว่าคือ
RegisterNetEvent('job:reward', function()
local src = source
end)
ช่วยลดข้อมูลที่ Client สามารถปลอมแปลงได้
⑭ TriggerClientEvent คืออะไร
TriggerClientEvent ใช้จาก Server เพื่อ Trigger Event ไปยัง Client
รูปแบบ
TriggerClientEvent('eventName', playerId)
ตัวอย่าง
Server
TriggerClientEvent('myScript:notify', source, 'งานเสร็จแล้ว')
Client
RegisterNetEvent('myScript:notify', function(message)
print(message)
end)
Flow คือ
Server
↓
TriggerClientEvent
↓
Network
↓
Client
↓
RegisterNetEvent
TriggerClientEvent ต้องระบุ Player ID เป้าหมาย และสามารถใช้ -1 เพื่อส่งไปยัง Clients ที่เชื่อมต่อทั้งหมดได้.
⑮ ส่ง Client Event ให้ผู้เล่นคนเดียว
ตัวอย่าง
local src = source
TriggerClientEvent('myScript:notify', src, 'Hello')
เฉพาะ Client ของ Player นั้นจะได้รับ Event
เหมาะกับ
Notification
UI
Player-specific Data
Animation
Menu
Personal State
ไม่ควร Broadcast ทุกอย่างไปทุกคนหากไม่มีความจำเป็น
⑯ ส่ง Event ให้ผู้เล่นทุกคน
ใช้
TriggerClientEvent('myScript:announcement', -1, 'Server restart soon')
Client
RegisterNetEvent('myScript:announcement', function(message)
print(message)
end)
-1 หมายถึงส่งไปยัง Clients ทั้งหมดที่เชื่อมต่ออยู่ในบริบทนี้.
แต่ไม่ควรใช้ Broadcast สำหรับข้อมูลส่วนตัว เช่น
Bank Balance
Private Inventory
Secret Data
Player-specific Information
เพราะไม่มีเหตุผลต้องส่งให้ทุกคน
⑰ TriggerClientEvent ส่ง Parameter หลายตัวได้ไหม
ได้
Server
TriggerClientEvent(
'myScript:updateJob',
source,
'police',
3,
true
)
Client
RegisterNetEvent('myScript:updateJob', function(job, grade, onDuty)
print(job, grade, onDuty)
end)
แต่ไม่ควรส่งข้อมูลจำนวนมากเกินความจำเป็น
FiveM ใช้ Serialization สำหรับข้อมูล Event และ Cfx.re แนะนำให้ส่งข้อมูลให้เล็กและเรียบง่ายเท่าที่จำเป็น.
⑱ Event สามารถส่ง Table ได้ไหม
สามารถส่งข้อมูลที่ Runtime รองรับ เช่น Table
ตัวอย่าง
TriggerClientEvent('myScript:data', source, {
job = 'police',
grade = 3
})
Client
RegisterNetEvent('myScript:data', function(data)
print(data.job)
print(data.grade)
end)
แต่ไม่ควรสร้าง Table ใหญ่มากแล้วส่งซ้ำถี่ ๆ หากไม่จำเป็น
โดยเฉพาะข้อมูลหลาย KB ขึ้นไป ควรพิจารณากลไก Latent Event หรือ Architecture อื่นตามประเภทข้อมูล
⑲ TriggerLatentServerEvent คืออะไร
FiveM มี Latent Event สำหรับข้อมูลขนาดใหญ่
Client → Server ใช้
TriggerLatentServerEvent(
'myScript:largeData',
bps,
data
)
Cfx.re อธิบายว่า Latent Event เหมาะกับ Transfer ขนาดใหญ่ เพราะช่วยจำกัดอัตราการส่งข้อมูลและหลีกเลี่ยงการ Block Network Channel แบบการส่งชุดข้อมูลใหญ่ใน Event ปกติ.
สำหรับ Event ทั่วไปขนาดเล็ก ไม่จำเป็นต้องใช้ Latent Event
⑳ TriggerLatentClientEvent คืออะไร
แนวคิดเดียวกันแต่เป็น
Server → Client
ใช้
TriggerLatentClientEvent(...)
เหมาะเมื่อ Server ต้องส่งข้อมูลขนาดใหญ่ไปยัง Client
ไม่ควรใช้ Latent Event เพียงเพราะคิดว่า “ใหม่กว่า” หรือ “เร็วกว่า”
ให้เลือกตามขนาดและลักษณะข้อมูล
㉑ Event Name ควรตั้งอย่างไร
ชื่อควรอ่านแล้วรู้ว่าเป็น Resource ไหนและทำอะไร
ตัวอย่างดี
mygarage:server:storeVehicle
mygarage:client:openMenu
police:server:setDuty
หลีกเลี่ยงชื่อกว้าง เช่น
event1
test
doThing
give
เพราะเมื่อ Server มี Script หลายร้อย Resource จะ Debug ยากมาก
รูปแบบหนึ่งที่นิยมใช้คือ
resource:side:action
เช่น
myjob:server:startJob
㉒ Event Name ต้องไม่ซ้ำหรือไม่
Event System ใช้ String เป็นชื่อ Identifier
ถ้า Resource หลายตัวใช้ชื่อกว้างเหมือนกัน เช่น
update
อาจสร้างความสับสนและ Collision เชิง Logic ได้
จึงควร Namespace ด้วยชื่อ Resource
เช่น
com_job:update
แทน
update
ช่วยให้ค้นหา Code และ Error ได้ง่ายกว่า
㉓ RegisterNetEvent แบบสั้นกับแบบแยกต่างกันอย่างไร
แบบสั้น
RegisterNetEvent('myScript:test', function(value)
print(value)
end)
แบบแยก
RegisterNetEvent('myScript:test')
AddEventHandler('myScript:test', function(value)
print(value)
end)
ทั้งสองรูปแบบใช้ได้
สำหรับ Lua Code ใหม่ รูปแบบแรกมักอ่านง่ายกว่าเมื่อ Event มี Handler เดียว
Cfx.re ระบุว่า RegisterNetEvent สามารถรับ Callback Function เพื่อ Register และเพิ่ม Handler ในขั้นตอนเดียวได้.
㉔ AddEventHandler ใช้กับ RegisterNetEvent ได้ไหม
ได้
ตัวอย่าง
RegisterNetEvent('myScript:test')
AddEventHandler('myScript:test', function()
print('received')
end)
แต่ถ้า Event ไม่จำเป็นต้องรับจาก Network ตั้งแต่แรก ก็ไม่ควร Register เป็น Net Event
ตัวอย่าง Server-only
AddEventHandler('myScript:internalCleanup', function()
-- server local
end)
จากนั้น
TriggerEvent('myScript:internalCleanup')
เพียงพอ
㉕ ทำไม Event ภายใน Server ไม่ควรใช้ RegisterNetEvent ทุกตัว
เพราะ RegisterNetEvent ทำให้ Event ถูก Register สำหรับ Network Context
ถ้า Event มีหน้าที่ภายใน Server เช่น
Internal Database Update
Internal Cache Refresh
Internal Reward Calculation
และไม่มีเหตุผลให้ Client เรียกได้ ให้ใช้
AddEventHandler(...)
แทน
Cfx.re ระบุว่านี่เป็นแนวทางสำคัญในการลดการเปิด Server Events สู่ Client โดยไม่จำเป็น.
㉖ ตัวอย่าง Server-only Event
AddEventHandler('myScript:internalSave', function(playerId)
print(('Saving player %s'):format(playerId))
end)
เรียกจาก Server
TriggerEvent('myScript:internalSave', source)
เพราะ Event นี้ไม่มีเหตุผลต้องเรียกจาก Client จึงไม่ต้องใช้
RegisterNetEvent
การเลือก Event Type ให้ถูกเป็นส่วนหนึ่งของ Security Architecture
㉗ Event ที่ให้ Item ต้องตรวจอะไร
สมมติ
RegisterNetEvent('job:collectItem', function()
Server ควรตรวจข้อมูล เช่น
ผู้เล่นกำลังทำ Job จริงหรือไม่
อยู่ตำแหน่งที่ถูกต้องหรือไม่
Cooldown ผ่านหรือไม่
จำนวน Item เกิน Limit หรือไม่
Inventory รองรับหรือไม่
จากนั้น Server เป็นผู้กำหนด
Item Name
Item Count
ไม่ควรให้ Client ส่ง
item = weapon
count = 9999
แล้ว Server เชื่อทันที
㉘ Event ซื้อของต้องตรวจอะไร
Client
TriggerServerEvent('shop:buy', 'water')
Server ควรทำ
ตรวจ Item มีขายจริง
↓
หาราคาจาก Server Config
↓
ตรวจเงิน
↓
หักเงิน
↓
เพิ่ม Item
อย่าออกแบบเป็น
TriggerServerEvent('shop:buy', 'water', 1)
แล้ว Server เชื่อว่าราคา 1 เป็นราคาจริง
ราคา Authority ต้องอยู่ Server
㉙ Event Job Reward ต้องตรวจตำแหน่งไหม
ถ้า Gameplay กำหนดว่าผู้เล่นต้องอยู่จุดส่งของ Server สามารถตรวจ Player Position ตามความเหมาะสมก่อน Reward
Flow
Client แจ้งส่งงาน
↓
Server ตรวจ Player State
↓
Server ตรวจตำแหน่ง
↓
Server ตรวจ Job
↓
Server ตรวจจำนวนงานที่สะสม
↓
Reward
นี่ตรงกับแนวทาง Cfx.re ที่แนะนำให้ใช้ Server-side Data เช่น Position, Inventory และ State ตรวจ Event สำคัญ.
㉚ Event Admin ต้องตรวจ Permission ฝั่ง Server
การซ่อน Admin Menu ฝั่ง Clientไม่เพียงพอ
สมมติ Client แสดงปุ่มเฉพาะ Admin
Admin
→ เห็นปุ่ม Give Money
แต่ Server Event
RegisterNetEvent('admin:giveMoney', function(...)
ต้องตรวจ Permission ซ้ำ
เพราะผู้โจมตีอาจพยายาม Trigger Event โดยตรงโดยไม่เปิด Menu
หลักคือ
Client UI Check
= UX
Server Permission Check
= Security
㉛ Client Validation มีประโยชน์ไหม
มี
Client สามารถตรวจ
อยู่ใน Zone หรือไม่
กดปุ่มได้หรือไม่
Job ที่แสดงใน UI
Cooldown สำหรับ UX
เพื่อลด Request ที่ไม่จำเป็น
แต่ Client Validation ไม่ควรเป็น Check สุดท้ายของระบบสำคัญ
Cfx.re ระบุชัดว่า Client Checks สามารถถูก Override ได้ และค่าที่สำคัญควรถูกตรวจด้วย Server-side Methods.
㉜ TriggerServerEvent จำนวนมากทำ Server หนักไหม
Event ทุกครั้งมีต้นทุนด้าน Network และ Serialization
Script ที่ Trigger Event ทุก Frame โดยไม่มีเหตุผลสามารถสร้าง Traffic และภาระเพิ่มขึ้นได้
ตัวอย่างที่ไม่ควรทำ
CreateThread(function()
while true do
TriggerServerEvent('player:update')
Wait(0)
end
end)
ควรออกแบบให้ Trigger เมื่อ
State เปลี่ยน
Player ทำ Action
Interval ที่เหมาะสม
Server จำเป็นต้องรู้จริง
ไม่ใช่ส่งทุก Frame
㉝ ไม่ควรส่งข้อมูลที่ Server หาเองได้
สมมติ Server สามารถหา Job จาก Player Object ได้
ไม่จำเป็นให้ Client ส่ง
TriggerServerEvent(
'job:reward',
'police',
4,
1000,
playerCoords
)
Server ควรใช้
source
↓
Player Object
↓
Job
Grade
Position
Server State
ตามข้อมูลที่ Server สามารถตรวจสอบเองได้
ยิ่ง Client ควบคุมข้อมูลสำคัญน้อยเท่าไร การ Validate ก็ง่ายขึ้น
㉞ Event Loop คืออะไร
ต้องระวัง Event ที่ Trigger กลับกันไม่รู้จบ
ตัวอย่าง
Event A
↓
Trigger Event B
↓
Event B
↓
Trigger Event A
↓
วนซ้ำ
อาจทำให้ Script ใช้ CPU สูงหรือเกิดปัญหารุนแรง
จึงต้องเข้าใจ Flow ก่อนเชื่อม Event หลายตัวเข้าด้วยกัน
การตั้งชื่อ Event ให้ชัดจะช่วย Debug Loop ได้ง่ายขึ้น
㉟ Event Error ดูตรงไหน
Client Event Error
ดู
F8
เช่น Error ใน
client/main.lua
Server Event Error
ดู Server Console หรือ txAdmin Console
เช่น
server/main.lua
หาก Event “กดแล้วไม่มีอะไรเกิดขึ้น” ให้เปิดทั้ง F8 และ Server Console แล้ว Trigger Action อีกครั้ง
จะช่วยเห็นว่า Flow หยุดอยู่ตรง Client หรือ Server
㊱ วิธี Debug Event แบบง่าย
สมมติ Client
print('Sending event')
TriggerServerEvent('job:start')
Server
RegisterNetEvent('job:start', function()
print('Received job:start')
end)
ถ้า F8 เห็น
Sending event
แต่ Server Console ไม่เห็น
Received job:start
ให้ตรวจ
Event Name
Server Script ถูกโหลดหรือไม่
RegisterNetEvent มีจริงหรือไม่
Resource Error หรือไม่
การเพิ่ม print() ชั่วคราวช่วยตาม Flow ได้เร็วมาก
㊲ Event Name ไม่ตรงจะเกิดอะไร
Client
TriggerServerEvent('job:startJob')
Server
RegisterNetEvent('job:startjob', function()
ชื่อไม่ตรงกัน
จึงไม่มี Handler ที่ตรงกับ Event ที่ Client Trigger
ควร Copy Event Name ให้ตรงทุกตัว
รูปแบบ Namespace ชัด ๆ ช่วยลด Error เช่น
myjob:server:startJob
㊳ Resource Restart แล้ว Event ไม่ทำงาน
ให้ตรวจว่า
Client File โหลดใหม่หรือไม่
Server File โหลดใหม่หรือไม่
Event Registration ถูก Execute หรือไม่
Dependency พร้อมหรือไม่
Player State ถูก Initialize ใหม่หรือไม่
บาง Script ผูก Event Registration ไว้หลัง Condition ที่ไม่ถูก Execute หลัง Hot Restart
ถ้า Restart Server แล้วได้ แต่ Restart Resource แล้วไม่ได้ อาจต้องตรวจ Lifecycle ของ Resource
㊴ RegisterNetEvent ซ้ำหลายไฟล์ได้ไหม
สามารถมี Handler มากกว่าหนึ่งตัวสำหรับ Event ตาม Architecture ได้ แต่ต้องระวังไม่ให้ Action ถูกทำซ้ำโดยไม่ได้ตั้งใจ
ตัวอย่าง
Event: player:reward
↓
Handler A ให้เงิน
Handler B ให้เงินอีกครั้ง
อาจทำให้ Reward ซ้ำ
ควร Search Event Name ทั้ง Project เมื่อพบพฤติกรรมที่ถูก Execute หลายครั้ง
㊵ Event กับ Export เลือกอะไรดี
ใช้ Export เมื่อ
Resource A
ต้องการเรียก Function จาก Resource B
และต้องการ Return ค่าโดยตรง
ใช้ Event เมื่อ
เกิดเหตุการณ์บางอย่าง
แล้วต้องการแจ้ง Handler ให้ทำงาน
ตัวอย่าง Export
local count = exports['inventory']:GetItemCount('water')
ตัวอย่าง Event
TriggerEvent('myScript:playerReady')
สำหรับ Client ↔ Server ให้ใช้ Network Event หรือ Callback ตามรูปแบบการสื่อสารที่ต้องการ
㊶ Event กับ Callback ต่างกันอย่างไร
Event มักเป็นลักษณะ
ส่งข้อความ/เหตุการณ์
→ Handler ทำงาน
Callback มักเป็น
Request
↓
รอ Response
↓
ได้รับข้อมูล
ตัวอย่าง
Client ขอจำนวนเงิน
↓
Server Query
↓
ส่งจำนวนเงินกลับ
อาจเหมาะกับ Callback มากกว่า Trigger Event ไปแล้วสร้าง Event อีกตัวเพื่อส่งคำตอบกลับ
รายละเอียด Callback จะอยู่ในหัวข้อถัดไปของชุดนี้
㊷ RegisterNetEvent ใช้กับ Client ได้ไหม
ได้
ตัวอย่าง Server ส่ง
TriggerClientEvent('myScript:openMenu', source)
Client รับ
RegisterNetEvent('myScript:openMenu', function()
-- เปิด Menu
end)
ดังนั้น RegisterNetEvent ไม่ได้หมายถึง “Server Event” เท่านั้น
ใช้ได้ทั้ง Client และ Server เมื่อ Handler ต้องรับ Network Event.
㊸ RegisterNetEvent ใช้กับ Server ได้ไหม
ได้เช่นกัน
Client
TriggerServerEvent('myScript:buyItem', 'water')
Server
RegisterNetEvent('myScript:buyItem', function(item)
local src = source
-- validate
end)
นี่เป็นหนึ่งในรูปแบบที่ใช้บ่อยที่สุดใน FiveM Lua
㊹ TriggerEvent ใช้ข้าม Resource ได้ไหม
ภายใน Context เดียวกัน TriggerEvent สามารถใช้สื่อสารกับ Handler ที่ Register อยู่ใน Context นั้น รวมถึง Resource อื่นได้
ตัวอย่าง Server Resource A
TriggerEvent('sharedSystem:ready')
Server Resource B
AddEventHandler('sharedSystem:ready', function()
print('Ready event received')
end)
แต่ถ้าต้องการ Server → Client หรือ Client → Server ต้องใช้ Network Event ที่เหมาะสม
㊺ อย่า Register ทุก Event เป็น Net Event
วิธีคิดที่ดีคือถามว่า
Event นี้จำเป็นต้องข้าม Client/Server จริงหรือไม่?
ถ้าไม่จำเป็น ใช้
AddEventHandler
+
TriggerEvent
ถ้าจำเป็นต้องข้าม Network จึงใช้
RegisterNetEvent
ร่วมกับ
TriggerServerEvent
หรือ
TriggerClientEvent
นี่ช่วยให้ Architecture อ่านง่ายและลด Network-facing Events โดยไม่จำเป็น
㊻ ตัวอย่าง Job Event ที่ปลอดภัยกว่า
Client
RegisterCommand('finishjob', function()
TriggerServerEvent('myjob:server:finish')
end)
Server
RegisterNetEvent('myjob:server:finish', function()
local src = source
-- ตรวจ Player
-- ตรวจว่าเริ่มงานจริง
-- ตรวจ Job
-- ตรวจ Position
-- ตรวจ Cooldown
-- ตรวจจำนวนงานที่ Server เก็บไว้
local reward = 500
-- จ่าย reward จากค่าที่ Server ควบคุม
end)
จุดสำคัญคือ Client ไม่ส่ง
reward
item count
job grade
permission
ถ้า Server สามารถหาและตรวจข้อมูลเหล่านี้เองได้
㊼ ตัวอย่าง Server แจ้ง Client
หลังทำรายการสำเร็จ
TriggerClientEvent(
'myjob:client:completed',
src,
500
)
Client
RegisterNetEvent('myjob:client:completed', function(reward)
print(('You received %s'):format(reward))
end)
ในกรณีนี้ Server เป็นคนกำหนด Reward ก่อน แล้วส่งไป Client เพื่อแสดงผล
Client จึงเป็น
Presentation
ส่วน Server เป็น
Authority
㊽ Checklist FiveM Event
Local Event
ใช้
AddEventHandler
TriggerEvent
เมื่อไม่ต้องข้าม Client/Server
Client → Server
ใช้
TriggerServerEvent
RegisterNetEvent
Server → Client
ใช้
TriggerClientEvent
RegisterNetEvent
Security
Server ต้องตรวจ
Player
Job
Grade
Permission
Money
Item
Position
State
Cooldown
Quantity
Reward
Performance
ไม่ Trigger ทุก Frame
ส่งข้อมูลเท่าที่จำเป็น
หลีกเลี่ยง Payload ใหญ่
ใช้ Latent Events เมื่อเหมาะกับข้อมูลขนาดใหญ่
Debug
ตรวจ Event Name
ตรวจ Client F8
ตรวจ Server Console
ใช้
print()ตาม Flowตรวจ Resource/Dependency
ตรวจ Registration
สรุป FiveM Event และ RegisterNetEvent คืออะไร
FiveM Event คือระบบที่ช่วยให้ Code และ Resource ต่าง ๆ สื่อสารกันผ่านเหตุการณ์
จำคำสั่งหลักได้ดังนี้
TriggerEvent
= Trigger Event ภายใน Context เดียวกัน
AddEventHandler
= รับ Event ภายใน Context
RegisterNetEvent
= Register Event ที่ต้องรับผ่าน Network
TriggerServerEvent
= Client → Server
TriggerClientEvent
= Server → Client
ถ้า Event ใช้เฉพาะ Server → Server หรือ Client → Client และไม่จำเป็นต้องเปิดผ่าน Network ควรใช้ Local Event แทนการใช้ RegisterNetEvent ทุกอย่าง.
ส่วน Network Event ต้องถือว่า Client สามารถส่งข้อมูลที่ไม่น่าเชื่อถือเข้ามาได้ ดังนั้น Server ต้อง Validate ข้อมูลสำคัญด้วยตัวเองเสมอ ไม่ว่าจะเป็นเงิน, Item, Job, Permission, Position หรือ Reward.
แนวทางของ comsiam คือให้ Client ส่งเพียงข้อมูลขั้นต่ำที่ Server จำเป็นต้องรับ แล้วให้ Server ดึงและตรวจข้อมูลสำคัญจาก Server-side State เอง วิธีนี้ช่วยลดช่องโหว่และทำให้ Event API เข้าใจง่ายขึ้น
สำหรับ Server RP ขนาดใหญ่ comsiam แนะนำให้ตั้งชื่อ Event แบบมี Namespace เช่น resource:server:action และแยก Local Events ออกจาก Network Events ให้ชัดตั้งแต่ต้น เพราะเมื่อมี Scripts หลายร้อยตัว การตามหา Event Error และตรวจ Security จะง่ายกว่าการใช้ชื่อ Event สั้น ๆ ซ้ำกันทั่ว Server
Comments
Post a Comment