วิธีสร้าง Command FiveM ด้วย RegisterCommand
RegisterCommand() คือ Native ของ FiveM สำหรับสร้าง Command ที่ผู้เล่นหรือ Server Console สามารถเรียกใช้งานได้ เช่น /car, /heal, /coords, /job หรือ Command สำหรับ Admin
ตัวอย่างพื้นฐานที่สุดใน Lua คือ
RegisterCommand('hello', function(source, args, rawCommand)
print('Hello FiveM')
end, false)
เมื่อผู้เล่นพิมพ์
/hello
Function ที่ลงทะเบียนไว้จะทำงาน
RegisterCommand() มีรูปแบบหลักคือ
RegisterCommand(
commandName,
handler,
restricted
)
โดยมี 3 ส่วนสำคัญคือ
commandName
→ ชื่อ Command
handler
→ Function ที่ทำงานเมื่อ Command ถูกเรียก
restricted
→ จำกัดด้วย ACE Permission หรือไม่
FiveM Developer ควรใช้ RegisterCommand() แทนระบบ Command แบบเก่าที่อาศัย Event อย่าง chatMessage หรือ rconCommand เพราะ Cfx.re แนะนำ RegisterCommand() และรองรับระบบ ACL/ACE รวมถึง Console และ Core Functionality อื่นของ FiveM
① RegisterCommand FiveM คืออะไร
RegisterCommand() ใช้ประกาศ Command ใหม่ให้ FiveM รู้จัก
ตัวอย่าง
RegisterCommand(
'test',
function()
print('Command executed')
end,
false
)
จากนั้นใช้
/test
Command จะเรียก Handler ที่กำหนดไว้
Concept คือ
Player / Console
↓
Command
↓
RegisterCommand
↓
Handler
↓
Script Logic
② Syntax ของ RegisterCommand
รูปแบบพื้นฐานคือ
RegisterCommand(
commandName,
handler,
restricted
)
ตัวอย่าง
RegisterCommand(
'hello',
function(source, args, rawCommand)
print('Hello')
end,
false
)
Parameter สำคัญคือ
source
args
rawCommand
ซึ่งควรเข้าใจให้ชัดก่อนสร้าง Command จริง
③ commandName คืออะไร
คือชื่อ Command
ตัวอย่าง
RegisterCommand(
'car',
function()
end,
false
)
ผู้เล่นใช้
/car
ถ้าสร้าง
RegisterCommand(
'coords',
function()
end,
false
)
ก็ใช้
/coords
โดยทั่วไปไม่ต้องใส่ / ใน RegisterCommand()
ใช้เพียง
coords
ไม่ใช่
/coords
④ Handler คืออะไร
Handler คือ Function ที่ทำงานเมื่อ Command ถูกเรียก
ตัวอย่าง
RegisterCommand(
'hello',
function(source, args, rawCommand)
print(
'Hello FiveM'
)
end,
false
)
Code ภายใน Function คือ Command Logic
สามารถเรียก
Native
Function
Event
Export
Framework API
Database Logic ฝั่ง Server
ได้ตาม Context
⑤ source คืออะไร
source มีความหมายสำคัญโดยเฉพาะฝั่ง Server
ตัวอย่าง
RegisterCommand(
'whoami',
function(source)
print(
'Source:',
source
)
end,
false
)
ถ้า Player เรียก Server Command
source
=
Player Server ID
จึงสามารถใช้หา Player Name หรือ Player Object ต่อได้
⑥ source ฝั่ง Server Console เป็นอะไร
สำหรับ Server-side RegisterCommand() ค่า source สามารถใช้แยก Player กับ Console ได้
Cfx.re ยก Pattern ว่า
if source > 0 then
print('Player command')
else
print('Console command')
end
ดังนั้น Concept คือ
source > 0
→ Player
source == 0
→ Server Console
มีประโยชน์มากสำหรับ Admin Command
⑦ ตัวอย่าง Command ที่ใช้ได้เฉพาะ Console
RegisterCommand(
'servertest',
function(source)
if source ~= 0 then
return
end
print(
'Command executed from console'
)
end,
false
)
Player พยายามใช้
/servertest
ก็จะถูก Return ออก
เหมาะกับ Server Maintenance Command บางประเภท
⑧ args คืออะไร
args เป็น Table ของ Arguments ที่ผู้ใช้ใส่หลังชื่อ Command
ตัวอย่าง
/car sultan
ใน Handler
args[1]
จะเป็นประมาณ
sultan
ตัวอย่าง
RegisterCommand(
'test',
function(source, args)
print(args[1])
end,
false
)
ใช้
/test hello
จะได้
hello
⑨ args หลายตัวทำอย่างไร
สมมติ Command
/pay 15 500
จะได้ประมาณ
args[1] = 15
args[2] = 500
ตัวอย่าง
RegisterCommand(
'pay',
function(source, args)
local target =
args[1]
local amount =
args[2]
print(
target,
amount
)
end,
false
)
แต่ค่าใน args ควรถูก Validate ก่อนใช้
⑩ args เป็น String หรือ Number
Arguments จาก Command ควรถือว่าเป็นข้อมูลข้อความที่ต้อง Parse ตาม Use Case
ถ้าต้องการ Number ใช้
local target =
tonumber(args[1])
local amount =
tonumber(args[2])
จากนั้นตรวจ
if not target then
return
end
if not amount then
return
end
อย่าเอา args[1] ไปใช้เป็น Number โดยไม่ตรวจ
⑪ rawCommand คืออะไร
rawCommand คือข้อความ Command ที่ผู้ใช้ป้อนในรูปแบบเต็ม
สมมติ
/message 15 hello world
args ถูกแยกเป็นแต่ละ Argument
แต่ rawCommand จะมีข้อมูล Command ในรูปแบบดิบที่ถูกป้อน
มีประโยชน์กับระบบที่ต้อง Parse ข้อความเอง เช่น
Announcement
Message
Reason
Admin Note
แต่สำหรับ Command ง่าย ๆ อาจไม่จำเป็นต้องใช้
⑫ ตัวอย่าง args พร้อม Default Value
ตัวอย่างจากแนวทาง Cfx.re สามารถใช้
local vehicleName =
args[1] or 'adder'
ดังนั้น
/car sultan
ได้
sultan
แต่ถ้าใช้เพียง
/car
จะใช้ Default
adder
นี่เป็น Pattern ที่ดีสำหรับ Optional Arguments
⑬ restricted คืออะไร
Parameter ตัวที่สามของ RegisterCommand() คือ Boolean
false
หรือ
true
ตัวอย่าง
RegisterCommand(
'test',
function()
end,
false
)
หมายถึงไม่ได้ใช้ Automatic ACE Restriction ของ Command นี้
แต่ถ้าเป็น
RegisterCommand(
'admin',
function()
end,
true
)
Command จะถูกจำกัดด้วย ACE Permission
⑭ restricted = false หมายถึงอะไร
ตัวอย่าง
RegisterCommand(
'coords',
function()
end,
false
)
Command ไม่ถูกบังคับด้วย ACE Object ที่ RegisterCommand() สร้างจากชื่อ Command
ไม่ได้หมายความว่า Command ต้องเปิดให้ทุกคนเสมอไป
Developer ยังสามารถเขียน Permission Check เองภายใน Handler ได้
แต่ถ้าต้องการใช้ ACE System โดยตรง restricted = true เป็นวิธีที่สะอาดกว่าในหลายกรณี
⑮ restricted = true หมายถึงอะไร
สมมติ
RegisterCommand(
'kick',
function()
-- logic
end,
true
)
Cfx.re ระบุว่า Restricted Command จะใช้ ACL Object ในรูปแบบ
command.kick
ผู้ใช้ต้องได้รับ Permission ของ Object นี้จึงจะ Execute Command ได้
ดังนั้น
Command:
kick
ACE Object:
command.kick
⑯ ACE Permission คืออะไร
ACE เป็น Permission System ของ FiveM Server
ใช้ควบคุมว่า Principal หรือ Identifier ใดมีสิทธิ์ใช้ Object อะไร
แนวคิดคือ
Player / Group
↓
ACE Permission
↓
command.xxx
↓
อนุญาต / ปฏิเสธ
เหมาะกับ Admin Commands ที่ไม่ควรเปิดให้ Player ทุกคน
⑰ ตัวอย่าง ACE สำหรับ Command
สมมติ Server มี
RegisterCommand(
'admincar',
function(source)
-- admin logic
end,
true
)
Command Object คือ
command.admincar
ใน server.cfg สามารถออกแบบ Permission เช่น
add_ace group.admin command.admincar allow
จากนั้นสมาชิก group.admin ที่ถูกกำหนดอย่างถูกต้องจึงสามารถใช้ Command ได้
⑱ ทำไม restricted ดีกว่าเช็กชื่อ Player
ไม่ควรเขียน Permission แบบ
if GetPlayerName(source) ==
'John' then
-- admin
end
เพราะชื่อ Player ไม่ใช่ระบบ Permission ที่เหมาะสม
ควรใช้
ACE
Framework Permission
Identifier/Role System
Admin Resource
ตาม Architecture
restricted = true ทำให้ใช้ ACE Integration ของ FiveM ได้ตรง ๆ
⑲ RegisterCommand ใช้ฝั่ง Client ได้ไหม
ได้
ตัวอย่าง client.lua
RegisterCommand(
'coords',
function()
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
print(coords)
end,
false
)
Player ใช้
/coords
แล้ว Client ของ Player คนนั้นจะอ่านตำแหน่ง Local Player
⑳ Client Command เหมาะกับอะไร
ตัวอย่าง
เปิด UI
Debug Client
Animation
Camera
Local Coordinates
Toggle HUD
Toggle Client Feature
ตัวอย่าง
RegisterCommand(
'togglehud',
function()
TriggerEvent(
'hud:toggle'
)
end,
false
)
ถ้าเป็น Presentation Logic แบบ Local Client ก็เหมาะกับ Client Command
㉑ Server Command เหมาะกับอะไร
Server Command เหมาะกับสิ่งที่ต้องการ Server Authority เช่น
Admin
Player Management
Money
Inventory
Job
Server Maintenance
Database Action
Global Announcement
ตัวอย่าง
RegisterCommand(
'playername',
function(source)
if source <= 0 then
return
end
local name =
GetPlayerName(source)
print(name)
end,
false
)
㉒ Command ที่เกี่ยวกับ Money ควรอยู่ฝั่งไหน
ควรตัดสินผลฝั่ง Server
ไม่ควรทำ
Client Command
↓
เพิ่มเงินจริงใน Client
แล้วถือว่าเป็น Economy Authority
ระบบ Money ต้องใช้ Server-side Framework/Economy Data
ถ้ามี Client Command เป็นตัว Trigger Request ก็ยังต้องให้ Server Validate ต่อ
㉓ ตัวอย่าง Client Command ส่ง Server Event
Client
RegisterCommand(
'finishjob',
function()
TriggerServerEvent(
'job:finish'
)
end,
false
)
Server
RegisterNetEvent(
'job:finish',
function()
local src =
source
-- validate job
-- validate position
-- calculate reward
end
)
Command เป็นเพียง Input Method
Server Event ยังต้อง Validate ตามปกติ
㉔ Command ไม่ใช่ Security Boundary
นี่สำคัญมาก
สมมติ Client Command /finishjob เรียก
TriggerServerEvent(
'job:finish'
)
ถึงคุณจะซ่อน Command หรือไม่บอก Player ว่ามี Command นี้อยู่ ก็ไม่ได้ทำให้ Server Event ปลอดภัย
Server Event ต้อง Validate เหมือนเดิม
ดังนั้น
Command Hidden
≠
Secure Event
㉕ ตัวอย่าง Command พร้อมตรวจ Argument
RegisterCommand(
'teleportid',
function(source, args)
local target =
tonumber(args[1])
if not target then
print(
'Invalid player ID'
)
return
end
print(
'Target:',
target
)
end,
false
)
ก่อนใช้ Argument ต้องตรวจว่ามีและ Convert สำเร็จ
㉖ ตรวจ Amount อย่างไร
ตัวอย่าง
local amount =
tonumber(args[2])
if not amount then
return
end
if amount <= 0 then
return
end
อาจเพิ่ม Maximum
if amount > 100000 then
return
end
ตาม Business Rule
อย่าเชื่อ Command Arguments เพียงเพราะผู้ใช้เป็น Player จริง
㉗ Command Message หลายคำทำอย่างไร
สมมติ
/announce Server restart in 10 minutes
args จะถูกแยกเป็น
Server
restart
in
10
minutes
สามารถรวมกลับ
local message =
table.concat(
args,
' '
)
ตัวอย่าง
RegisterCommand(
'announce',
function(source, args)
local message =
table.concat(
args,
' '
)
print(message)
end,
true
)
เหมาะกับ Announcement Command
㉘ ควรตรวจ Empty Message
ตัวอย่าง
if #args == 0 then
return
end
หรือ
local message =
table.concat(
args,
' '
)
if message == '' then
return
end
ป้องกัน Command ที่ไม่มี Argument ตาม Requirement
㉙ ตัวอย่าง Announcement Server Command
RegisterCommand(
'announce',
function(source, args)
local message =
table.concat(
args,
' '
)
if message == '' then
return
end
TriggerClientEvent(
'chat:addMessage',
-1,
{
args = {
'SERVER',
message
}
}
)
end,
true
)
เมื่อ restricted = true ควรกำหนด ACE ของ
command.announce
ให้ Admin Group ที่ต้องการ
㉚ ส่งหา Player คนเดียวด้วย Command
ตัวอย่าง Server Command
RegisterCommand(
'message',
function(source, args)
local target =
tonumber(args[1])
if not target then
return
end
local message =
table.concat(
args,
' ',
2
)
if message == '' then
return
end
TriggerClientEvent(
'chat:addMessage',
target,
{
args = {
'SERVER',
message
}
}
)
end,
true
)
ควรตรวจด้วยว่า Target Player มีอยู่จริงตามระบบก่อนใช้งานจริง
㉛ ตรวจ Player มีอยู่หรือไม่
ตัวอย่างหนึ่งคือ
local targetName =
GetPlayerName(target)
if not targetName then
return
end
จากนั้นจึงทำ Action
อย่าสมมติว่า Player ID ที่ Admin ใส่มาจะมีอยู่เสมอ
㉜ RegisterCommand ใช้จาก Server Console ได้ไหม
Server-side Command สามารถถูกเรียกจาก Server Console ได้
Cfx.re ระบุว่าหนึ่งในข้อดีของ RegisterCommand() คือรองรับ Console Usage และ Integrated ACL System
ดังนั้น Handler ควรออกแบบว่าต้องการรองรับ
Player
Console
หรือทั้งสอง
อย่างชัดเจน
㉝ Command ที่ Console ใช้ต้องระวัง source
หาก Console เรียก Command
source = 0
ดังนั้น Code อย่าง
GetPlayerName(source)
อาจไม่เหมาะหาก source คือ Console
ควรตรวจ
if source == 0 then
-- console path
else
-- player path
end
ก่อนใช้ Player-specific Logic
㉞ ตัวอย่างรองรับทั้ง Player และ Console
RegisterCommand(
'wherefrom',
function(source)
if source == 0 then
print(
'Executed from console'
)
return
end
print(
'Executed by player:',
source
)
end,
false
)
Flow ชัดเจนและลด Error
㉟ Client RegisterCommand มี source แบบ Server ไหม
Parameter Signature คล้ายกัน แต่ source มีความหมายสำคัญเป็น Player ID โดยเฉพาะบน Server
สำหรับ Client Command Developer มักไม่จำเป็นต้องใช้ source
จึงเห็นรูปแบบ
RegisterCommand(
'coords',
function()
-- client logic
end,
false
)
ได้บ่อย
㊱ RegisterCommand กับ RegisterKeyMapping
FiveM สามารถสร้าง Command แล้วผูกกับ Key Binding
ตัวอย่าง Concept
RegisterCommand(
'+handsup',
function()
handsUp = true
end,
false
)
RegisterCommand(
'-handsup',
function()
handsUp = false
end,
false
)
แล้วผูก
RegisterKeyMapping(
'+handsup',
'Hands Up',
'keyboard',
'i'
)
นี่ทำให้ผู้เล่นสามารถปรับ Key Binding ผ่าน Settings ได้ตามระบบ FiveM
㊲ ทำไม KeyMapping ใช้ Command
FiveM Input Mapping สามารถผูก Key กับ Command
Flow คือ
Keyboard
↓
RegisterKeyMapping
↓
Command
↓
RegisterCommand Handler
จึงไม่ต้อง Hardcode Keyboard Native สำหรับทุก Feature
เหมาะกับ
Hands Up
Push-to-talk Feature
Menu
Interaction
Toggle UI
ตาม Use Case
㊳ +command และ -command คืออะไร
Pattern เช่น
+handsup
-handsup
ใช้สร้าง Press/Release Behavior
ตัวอย่าง
RegisterCommand(
'+handsup',
function()
handsUp = true
end,
false
)
RegisterCommand(
'-handsup',
function()
handsUp = false
end,
false
)
กดปุ่ม
+handsup
ปล่อยปุ่ม
-handsup
เหมาะกับ Action ที่ต้องรู้ Press State
㊴ RegisterCommand กับ Chat
ผู้เล่นมักใช้ Command จาก Chat เช่น
/car
/coords
/help
แต่ RegisterCommand() ไม่ได้ผูกกับ chatMessage Event แบบเก่าโดยตรง
Cfx.re แนะนำใช้ RegisterCommand() เพราะระบบ Command ถูกจัดการใน Core และรองรับ ACL/Console ได้ดีกว่าแนวทางเก่า
㊵ ไม่ควรใช้ chatMessage สร้าง Command ใหม่
เอกสาร Cfx.re ระบุชัดว่าควรใช้ RegisterCommand() แทนการจับ chatMessage เพื่อ Parse Command เอง
เหตุผลรวมถึง
Integrated ACL
Console Usage
Core Functionality
Command Handling ที่เป็นมาตรฐาน
ดังนั้น Resource ใหม่ควรเริ่มด้วย RegisterCommand()
㊶ rconCommand ยังควรใช้ไหม
Cfx.re ระบุว่า Event
rconCommand
เป็น Deprecated และแนะนำให้ใช้ REGISTER_COMMAND พร้อม restricted Flag แทน
ดังนั้น Resource ใหม่ไม่ควรเริ่มจาก API เก่านี้หากไม่มีเหตุผลเรื่อง Compatibility
㊷ RegisterCommand กับ ESX
ESX Resource สามารถใช้ FiveM RegisterCommand() ได้ตามปกติ
หลังจาก Handler ทำงาน สามารถเรียก ESX API เช่น
GetPlayerFromId
Player Data
Permission
Job
ตาม Version ของ Framework
แต่ Command ที่เกี่ยวข้องกับ Money/Inventory ต้องยัง Validate ฝั่ง Server
㊸ RegisterCommand กับ QBCore
QBCore ก็สามารถใช้ RegisterCommand() ของ FiveM ได้
QBCore ยังมี Command Layer/API ของ Framework เองในบางระบบ
ถ้า Resource ต้องเป็น Framework-independent การใช้ FiveM Core RegisterCommand() แล้วเชื่อม Framework ภายในอาจช่วยลด Coupling
แต่เลือกตาม Architecture ของ Server
㊹ RegisterCommand กับ Qbox
หลักเดียวกัน
FiveM Core Command ยังใช้ได้ไม่ว่า Server จะเป็น
ESX
QBCore
Qbox
Standalone
Framework สามารถเพิ่ม Permission และ Player Data Layer เพิ่มเติมด้านบน
แต่พื้นฐาน Command ของ FiveM ยังเหมือนเดิม
㊺ Command สำหรับ Admin ควรใช้ Client หรือ Server
Server
โดยเฉพาะ Action เช่น
Kick
Ban
Give Money
Give Item
Set Job
Delete Character
Server Management
Permission Check และ Action สำคัญควรอยู่ Server-side
Client สามารถมี UI หรือ Input แต่ไม่ควรเป็น Authority
㊻ ตัวอย่าง Admin Command ที่ไม่ดี
RegisterCommand(
'givemoney',
function(source, args)
local amount =
tonumber(args[1])
TriggerServerEvent(
'admin:giveMoney',
amount
)
end,
false
)
ถ้า Server Event ไม่ตรวจ Admin Permission ผู้เล่นสามารถพยายามเรียก Event โดยตรงได้
Command ไม่ได้ป้องกัน Event
㊼ Admin Command ที่ดีกว่า
สร้าง Server Command
RegisterCommand(
'givemoney',
function(source, args)
local target =
tonumber(args[1])
local amount =
tonumber(args[2])
if not target
or not amount then
return
end
if amount <= 0 then
return
end
-- server-side economy action
end,
true
)
แล้วใช้ ACE Permission
command.givemoney
หรือ Permission Layer ของ Framework ตามระบบ
㊽ restricted=true เพียงพอทุกระบบไหม
สำหรับ ACE-protected Command มันช่วยควบคุมว่าใคร Execute Command ได้
แต่ Business Logic ยังควร Validate Input เช่น
Target
Amount
Player Exists
Range
Resource State
ACE ตอบคำถามว่า
ใครใช้ Command ได้?
ไม่ใช่
Arguments ทุกอย่างถูกต้องไหม?
จึงต้องทำทั้ง Permission และ Validation
㊾ Command Name ควรตั้งอย่างไร
ชื่อควร
สั้น
จำง่าย
สื่อความหมาย
ไม่ชนกับ Resource อื่นง่ายเกินไป
ตัวอย่าง
coords
car
repair
announce
setjob
สำหรับ Internal/Admin Command ที่เฉพาะ Resource อาจใช้ Prefix
garage_debug
inventory_reload
ตาม Convention ของ Server
㊿ ไม่ควรสร้าง Command Generic เกินไป
เช่น
/do
/run
/action
/x
แล้วใช้ Argument แรกตัดสิน Action จำนวนมาก
/do giveMoney
/do giveItem
/do kick
/do ban
Command แบบ Generic มากเกินไปทำให้
Permission ยาก
Audit ยาก
Help ยาก
Security Review ยาก
แยก Command ตาม Responsibility มักชัดกว่า
51 ควรมี Usage Message ไหม
ควรสำหรับ Command ที่มี Arguments
ตัวอย่าง
RegisterCommand(
'pay',
function(source, args)
if not args[1]
or not args[2] then
print(
'Usage: /pay [id] [amount]'
)
return
end
end,
false
)
สำหรับ Player Command อาจส่งข้อความผ่าน Chat/Notification แทน print()
UX จะดีกว่า
52 Command ควรมี Error Message ไหม
ควรมีข้อความที่ช่วยผู้ใช้แก้ไข
เช่น
Player ID ไม่ถูกต้อง
จำนวนเงินต้องมากกว่า 0
ไม่พบ Player
คุณไม่มีสิทธิ์ใช้ Command นี้
แต่ไม่ควรส่งข้อมูลภายในที่ Sensitive เช่น
SQL Error
Secret
Stack Trace
Token
ให้ Player โดยไม่จำเป็น
53 Command Logging สำคัญไหม
สำหรับ Admin Command สำคัญ ควรพิจารณา Log
ตัวอย่าง
Admin Source
Command
Target
Action
Amount
Timestamp
ช่วย Audit เมื่อเกิดปัญหา
แต่หลีกเลี่ยงการ Log Secret หรือข้อมูล Sensitive โดยไม่จำเป็น
54 Command Spam มีผลไหม
ได้
Player สามารถเรียก Command ซ้ำเร็ว ๆ ได้ในบางกรณี
ถ้า Handler ทำงานหนัก เช่น
Database Query
Entity Spawn
HTTP Request
Inventory Transaction
ควรพิจารณา Cooldown/Rate Limit ตาม Use Case
RegisterCommand() ไม่ได้ Rate Limit Business Logic ให้คุณทั้งหมดโดยอัตโนมัติ
55 Command Query Database ทุกครั้งได้ไหม
ได้ถ้าจำเป็น
แต่ต้องดู Frequency
Command Admin ที่ใช้วันละไม่กี่ครั้งต่างจาก Public Command ที่ Player หลายร้อยคน Spam ได้
ถ้าข้อมูล Cache ได้ ควรพิจารณา Cache
ถ้า Query ช้า ควร Optimize SQL
อย่าแก้ Performance ด้วยการเพิ่ม Wait() ใน Command แบบสุ่ม
56 RegisterCommand มีผลต่อ Performance ไหม
การ Register Command จำนวนหนึ่งไม่ใช่ปัญหาหลัก
Cost จะเกิดตอน Handler ทำงาน
เช่น
Command
↓
Loop 1000 Entities
↓
Database Query
↓
HTTP Request
↓
Broadcast Event
ถ้า Logic หนัก Command ก็หนัก
จึงต้อง Optimize Handler ไม่ใช่กลัวตัว RegisterCommand() เอง
57 วิธี Debug RegisterCommand
เริ่มจาก
RegisterCommand(
'test',
function(source, args, rawCommand)
print(
'Command called'
)
print(
'source:',
source
)
print(
'arg1:',
args[1]
)
print(
'raw:',
rawCommand
)
end,
false
)
จากนั้นลอง
/test hello
ดู F8 หากเป็น Client Command
ดู Server Console หากเป็น Server Command
58 RegisterCommand ไม่ทำงานตรวจอะไร
ตรวจตามลำดับ
① Resource Start หรือยัง
② client.lua/server.lua ถูกโหลดหรือไม่
③ Command Name ถูกไหม
④ มี Error ก่อน RegisterCommand หรือไม่
⑤ Command ชนกับ Resource อื่นหรือไม่
⑥ restricted=true แต่ไม่มี Permission หรือไม่
⑦ ใช้ Client/Server Context ถูกหรือไม่
⑧ ดู F8 หรือ Server Console แล้วหรือยัง
ถ้า Restricted Command ใช้ไม่ได้ ให้ตรวจ ACE Configuration เพิ่มด้วย
59 Checklist ก่อนสร้าง FiveM Command
ก่อนสร้าง Command ใหม่ ให้ถาม
① Command ใช้ Client หรือ Server?
② Command Name คืออะไร?
③ Player ใช้ได้หรือ Console เท่านั้น?
④ มี Arguments อะไร?
⑤ ต้อง tonumber หรือ Validate Type ไหม?
⑥ ต้องตรวจ Range ไหม?
⑦ Target Player มีอยู่จริงไหม?
⑧ เป็น Admin Command หรือไม่?
⑨ ใช้ restricted=true + ACE ได้ไหม?
⑩ ต้องใช้ Framework Permission หรือไม่?
⑪ Handler มี Database/HTTP งานหนักไหม?
⑫ ต้อง Rate Limit หรือไม่?
⑬ Action สำคัญอยู่ Server-side หรือยัง?
⑭ ต้อง Log หรือไม่?
⑮ ต้องมี Usage/Error Message หรือไม่?
Checklist นี้ช่วยให้ Command พร้อมใช้งานจริงมากกว่าเขียนแค่ให้ /command ทำงานได้
⑥⓪ ตัวอย่าง RegisterCommand สำหรับมือใหม่
client.lua
สร้าง /coords
RegisterCommand(
'coords',
function()
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
print(
('x: %.2f y: %.2f z: %.2f')
:format(
coords.x,
coords.y,
coords.z
)
)
end,
false
)
Command นี้เหมาะกับ Client เพราะ Coordinates ของ Local Player สามารถอ่านด้วย Client Native โดยตรง
server.lua
สร้าง /whoami
RegisterCommand(
'whoami',
function(source)
if source == 0 then
print(
'Server Console'
)
return
end
local playerName =
GetPlayerName(source)
print(
('Player %s: %s')
:format(
source,
playerName or 'unknown'
)
)
end,
false
)
ตัวอย่างนี้ช่วยให้เห็นความแตกต่างระหว่าง Client และ Server Commands
ตัวอย่าง Restricted Admin Command
server.lua
RegisterCommand(
'announce',
function(source, args)
local message =
table.concat(
args,
' '
)
if message == '' then
return
end
TriggerClientEvent(
'chat:addMessage',
-1,
{
args = {
'SERVER',
message
}
}
)
end,
true
)
เมื่อ Command คือ
announce
ACE Object คือ
command.announce
จากนั้นกำหนด Permission ให้ Group ที่ต้องการใน Server Configuration
Flow คือ
Admin
↓
/announce
↓
RegisterCommand
↓
ACE ตรวจสิทธิ์
↓
Handler
↓
TriggerClientEvent
↓
ข้อความถึงผู้เล่น
นี่เป็น Pattern ที่เหมาะกับ Command ที่ต้องจำกัดสิทธิ์
คำถามที่พบบ่อยเกี่ยวกับ FiveM RegisterCommand
RegisterCommand คืออะไร
คือ Native สำหรับสร้าง Player/Console Command ใน FiveM
Syntax คืออะไร
RegisterCommand(
commandName,
handler,
restricted
)
source คืออะไร
บน Server คือ Player ID ของผู้ที่เรียก Command และโดยทั่วไป 0 หมายถึง Server Console
args คืออะไร
Table ของ Arguments ที่ใส่ต่อท้าย Command
rawCommand คืออะไร
ข้อความ Command ที่ถูกป้อนในรูปแบบดิบ
restricted คืออะไร
Boolean ที่บอกว่า Command ต้องใช้ ACE Restriction หรือไม่
restricted=true ใช้ Permission อะไร
Cfx.re ใช้ Object รูปแบบ
command.commandName
เช่น /kick
command.kick
RegisterCommand ใช้ Client ได้ไหม
ได้
RegisterCommand ใช้ Server ได้ไหม
ได้
Command Client กับ Server ต่างกันอย่างไร
Client Command เหมาะกับ Local Gameplay/UI ส่วน Server Command เหมาะกับ Authority, Database, Admin และ Player Management
RegisterCommand ใช้จาก Console ได้ไหม
Server-side Command รองรับ Server Console ตามระบบ FiveM
ใช้ chatMessage สร้าง Command ดีไหม
Cfx.re แนะนำใช้ RegisterCommand() แทน เพราะรองรับ ACL และ Core Command Functionality ดีกว่า
rconCommand ยังควรใช้ไหม
เอกสารปัจจุบันระบุว่า rconCommand Deprecated และแนะนำ RegisterCommand() พร้อม restricted Flag แทน
Command Admin ควรอยู่ Client หรือ Server
Server เป็นหลักสำหรับ Permission และ Action สำคัญ
restricted=true ทำให้ Command ปลอดภัยทั้งหมดไหม
ช่วยควบคุม Permission แต่ยังต้อง Validate Arguments และ Business Logic
RegisterCommand ใช้กับ ESX ได้ไหม
ได้
RegisterCommand ใช้กับ QBCore ได้ไหม
ได้
RegisterCommand ใช้กับ Qbox ได้ไหม
ได้
RegisterCommand ผูก Key ได้ไหม
ได้ โดยใช้ RegisterKeyMapping() ร่วมกับ Command ตามระบบ Key Mapping ของ FiveM
สรุปวิธีสร้าง Command FiveM ด้วย RegisterCommand
RegisterCommand() คือวิธีมาตรฐานสำหรับสร้าง Command ใน FiveM
โครงสร้างพื้นฐานคือ
RegisterCommand(
'commandname',
function(source, args, rawCommand)
-- command logic
end,
false
)
สิ่งที่ต้องจำคือ
source
→ ใครเรียก Command โดยเฉพาะฝั่ง Server
args
→ Arguments ที่ผู้ใช้ส่งมา
rawCommand
→ Command ดิบ
restricted
→ ACE Restriction
ถ้าเป็น Command ทั่วไปใช้
false
ตาม Requirement
ถ้าเป็น Command ที่ต้องใช้ ACE Permission สามารถใช้
true
แล้วกำหนดสิทธิ์ผ่าน Object รูปแบบ
command.commandName
และหลักสำคัญที่สุดสำหรับ Admin Command คือ
Command Input
↓
Permission
↓
Validate Arguments
↓
Server-side Business Logic
↓
Action
↓
Log เมื่อจำเป็น
ไม่ใช่แค่สร้าง /givemoney แล้วเชื่อค่าที่ถูกส่งเข้ามาทั้งหมด
สำหรับคนที่เรียน FiveM Developer กับ comsiam การใช้ RegisterCommand() ให้คล่องช่วยได้มากกว่าการสร้างคำสั่ง Chat เพราะมันสามารถใช้เป็นเครื่องมือ Debug Resource, ทดสอบ Native, เรียก Event และสร้าง Admin Tool ได้อย่างรวดเร็ว
หลักจาก comsiam ที่ควรจำคือ Public Command ใช้ Validation ส่วน Privileged Command ต้องมีทั้ง Permission และ Validation เพราะสองอย่างทำหน้าที่คนละเรื่อง
Comments
Post a Comment