Shared Script FiveM คืออะไร
Shared Script FiveM คือ Script ที่ FiveM โหลดให้ทำงานทั้งฝั่ง Client และ Server ภายใน Resource เดียวกัน เหมาะกับ Code หรือข้อมูลที่ทั้งสองฝั่งต้องใช้งานร่วมกัน เช่น Config, Constants, Utility Functions, Shared Tables และ Data Definitions
ใน fxmanifest.lua สามารถกำหนด Shared Script ได้ด้วย
shared_script 'config.lua'
หรือหากมีหลายไฟล์
shared_scripts {
'shared/config.lua',
'shared/functions.lua'
}
สิ่งสำคัญมากคือ Shared Script ถูกโหลดฝั่ง Client ด้วย ดังนั้นห้ามเก็บ Password, Secret Key, Database Credential หรือข้อมูลลับที่ไม่ต้องการให้ Client เข้าถึงไว้ใน Shared Script
บทความนี้จะอธิบายว่า Shared Script FiveM คืออะไร ต่างจาก Client Script และ Server Script อย่างไร ใช้กับ config.lua แบบไหน และควรจัดโครงสร้าง Resource อย่างไรให้ถูกต้อง
① Shared Script FiveM คืออะไร
Resource FiveM สามารถแบ่ง Script ตาม Execution Context ได้หลัก ๆ เป็น
Client Script
Server Script
Shared Script
หน้าที่แต่ละแบบคือ
Client Script
→ โหลดฝั่ง Client
Server Script
→ โหลดฝั่ง Server
Shared Script
→ โหลดทั้ง Client และ Server
ตัวอย่าง Resource
my_resource/
├── fxmanifest.lua
├── config.lua
├── client.lua
└── server.lua
ถ้า config.lua ต้องใช้ทั้งสองฝั่ง สามารถกำหนด
shared_script 'config.lua'
จากนั้น Client และ Server จะสามารถใช้ข้อมูลในไฟล์นี้ได้ตาม Runtime และ Scope ของแต่ละฝั่ง
② shared_script ใช้เขียนตรงไหน
กำหนดใน
fxmanifest.lua
ตัวอย่าง
fx_version 'cerulean'
game 'gta5'
shared_script 'config.lua'
client_script 'client.lua'
server_script 'server.lua'
FiveM จะโหลด
config.lua
ทั้ง Client และ Server
ส่วน
client.lua
โหลดเฉพาะ Client
และ
server.lua
โหลดเฉพาะ Server
③ shared_script กับ shared_scripts ต่างกันอย่างไร
ถ้ามีไฟล์เดียวสามารถใช้
shared_script 'config.lua'
ถ้ามีหลายไฟล์ ใช้
shared_scripts {
'shared/config.lua',
'shared/functions.lua',
'shared/constants.lua'
}
ทั้งสอง Directive มีหน้าที่เหมือนกันในหลักการ
ต่างกันเพียงรูปแบบการกำหนดจำนวนไฟล์
④ Shared Script รองรับ Globbing ไหม
รองรับ
ตัวอย่าง
shared_scripts {
'shared/*.lua'
}
จะโหลดไฟล์ Lua ที่ตรงกับ Pattern ภายใน Folder shared
Resource ขนาดใหญ่จึงสามารถจัดเป็น
shared/
├── config.lua
├── constants.lua
├── items.lua
└── functions.lua
แล้วกำหนดเพียง
shared_scripts {
'shared/*.lua'
}
ได้
แต่ต้องระวัง ลำดับการโหลดไฟล์ หากไฟล์หนึ่งต้องพึ่ง Variable หรือ Function จากอีกไฟล์
⑤ Shared Script เหมาะกับอะไร
ตัวอย่างข้อมูลที่เหมาะกับ Shared Script ได้แก่
Config
Coordinates
Constants
Item Definitions ที่ Client และ Server ต้องรู้
Job Definitions
Vehicle Configuration
Shared Utility Functions
Enumeration
Data Mapping
-ข้อความทั่วไปResource Constants
ตัวอย่าง
Config = {}
Config.ShopPrice = 500
Config.ShopLocation = vector3(
25.7,
-1345.2,
29.5
)
หากทั้ง Client และ Server ต้องรู้ราคาและตำแหน่งนี้ ก็สามารถวางไว้ใน Shared Script ได้
แต่ต้องพิจารณาเรื่อง Security เพิ่มเติมด้วย
⑥ ตัวอย่าง config.lua แบบ Shared
สร้าง
config.lua
แล้วเขียน
Config = {}
Config.Job = 'police'
Config.MarkerDistance = 20.0
Config.Location = vector3(
441.2,
-981.9,
30.7
)
ใน fxmanifest.lua
shared_script 'config.lua'
client_script 'client.lua'
server_script 'server.lua'
จากนั้น Client สามารถอ่าน
print(Config.Location)
และ Server ก็สามารถอ่าน
print(Config.Job)
ได้
⑦ Shared Script ทำงานครั้งเดียวหรือสองครั้ง
ในทางแนวคิดให้มองว่า Script ถูกโหลดใน สอง Execution Context แยกกัน
คือ
Client Runtime
+
Server Runtime
ดังนั้น Variable ไม่ได้ถูกแชร์ Memory ตรง ๆ ระหว่าง Client กับ Server
ตัวอย่าง
Counter = 0
ถ้าอยู่ใน Shared Script
Client มี Counter ของ Client
Server ก็มี Counter ของ Server
การเปลี่ยน
Counter = 100
บน Client ไม่ได้ทำให้ Server Variable เปลี่ยนเป็น 100 อัตโนมัติ
นี่เป็นจุดที่มือใหม่สับสนบ่อยมาก
⑧ Shared Script ไม่ใช่ Shared Memory
คำว่า Shared หมายถึง
FiveM โหลด Script เดียวกันทั้ง Client และ Server
ไม่ได้หมายถึง
Client และ Server ใช้ Variable ตัวเดียวกันใน Memory
ตัวอย่าง
Config = {
Price = 500
}
ทั้งสองฝั่งเริ่มจากค่าเดียวกันเพราะโหลด Code เดียวกัน
แต่ถ้า Client ทำ
Config.Price = 1
ไม่ได้หมายความว่า Server จะเห็น
Price = 1
ตามไปด้วย
แต่ละ Runtime มี State ของตัวเอง
⑨ แล้ว Client กับ Server ส่งข้อมูลกันอย่างไร
ถ้าต้องการ Sync หรือสื่อสารข้อมูลจริงระหว่าง Client กับ Server ต้องใช้กลไก เช่น
Network Events
Callbacks
State Bags
Exports
ตาม Use Case
ตัวอย่าง
Client
↓
TriggerServerEvent
↓
Server
Shared Script ไม่ได้มาแทนระบบ Network Communication
⑩ Shared Script เหมาะกับ Config มากที่สุดหรือไม่
Config เป็นหนึ่งใน Use Case ที่พบมากที่สุด
ตัวอย่าง Resource ร้านค้า
shared/config.lua
อาจเก็บ
Config = {}
Config.Shops = {
{
coords = vector3(
25.7,
-1345.2,
29.5
),
label = 'Shop'
}
}
Client ใช้ Coordinates เพื่อสร้าง Marker
Server ใช้ข้อมูล Shop เพื่อ Validate Request
ทำให้ไม่ต้องประกาศ Coordinates ซ้ำในสองไฟล์
⑪ Shared Config ช่วยลด Code ซ้ำได้อย่างไร
ถ้าไม่ใช้ Shared Config อาจต้องมี
client.lua
local shopPrice = 500
และ
server.lua
local shopPrice = 500
ถ้าเปลี่ยนราคาต้องแก้สองจุด
แต่สามารถย้ายมา
Config.ShopPrice = 500
ใน Shared Config
แล้วทั้งสองฝั่งอ่านค่าเดียวกันจาก Source Code เดียว
ช่วยลดปัญหาค่าตั้งไม่ตรงกัน
⑫ แต่ราคา Item ควรเชื่อ Client หรือไม่
ไม่ควร
ถึงแม้
Config.ShopPrice = 500
จะอยู่ใน Shared Script และ Client มองเห็นราคาได้ Server ยังต้องเป็นผู้ใช้ค่าฝั่ง Server สำหรับ Transaction จริง
ตัวอย่าง Client อาจใช้ราคาเพื่อแสดง UI
Water
$500
แต่เมื่อ Player ซื้อของ
Client
↓
ส่ง Item ที่ต้องการซื้อ
↓
Server
↓
Server อ่านราคาเอง
↓
ตรวจเงิน
↓
หักเงิน
↓
ให้ Item
อย่าให้ Client ส่ง
price = 1
แล้ว Server เชื่อ
⑬ ตัวอย่าง Shop ที่ใช้ Shared Script ถูกวิธี
config.lua
Config = {}
Config.Items = {
water = {
label = 'Water',
price = 100
}
}
client.lua
Client อ่าน Config เพื่อแสดง UI
local item = Config.Items.water
print(
item.label,
item.price
)
server.lua
Server อ่าน Config ของตัวเองเพื่อคำนวณราคา
local item = Config.Items[itemName]
if not item then
return
end
local price = item.price
Client กับ Server จึงใช้ Definition เดียวกัน แต่ Server ไม่เชื่อราคาที่ Client ส่งเข้ามา
⑭ Shared Script ใส่ Database Password ได้ไหม
ไม่ได้ และไม่ควรทำเด็ดขาด
ตัวอย่างที่ผิด
Config.DatabasePassword = 'mypassword'
ถ้า config.lua ถูกโหลดด้วย
shared_script 'config.lua'
ไฟล์นั้นถูกเพิ่มเข้า Resource Packfile และโหลดฝั่ง Client ด้วย
ดังนั้นอย่าถือข้อมูลใน Shared Script ว่าเป็นความลับ
ข้อมูลเช่น
Database Password
API Secret
Private Key
Webhook Secret
Internal Token
ควรเก็บเฉพาะ Server-side
⑮ API Key ใส่ Shared Script ได้ไหม
ถ้าเป็น Secret API Key คำตอบคือ ไม่ควร
แยกเป็นไฟล์ Server-only เช่น
server/config.lua
แล้วใน fxmanifest.lua
server_scripts {
'server/config.lua',
'server/main.lua'
}
Client ไม่จำเป็นต้องรู้ Secret
หลักง่าย ๆ คือ
ข้อมูลต้องเป็นความลับ
→ Server-only
⑯ Shared Script ใส่ Admin List ได้ไหม
ต้องดูว่าข้อมูลนั้น Sensitive หรือไม่
ถ้าเป็นข้อมูลอย่าง
Admin Role Names
Permission Labels
ที่ Client ต้องใช้แสดง UI อาจ Shared ได้
แต่ข้อมูลที่ใช้เป็น Authority เช่น
ใครเป็น Admin จริง
Permission จริงของ Player
Security Token
ไม่ควรให้ Client เป็นผู้ตัดสิน
Server ต้องตรวจ Permission เอง
⑰ Shared Config ใส่ Coordinates ได้ไหม
ได้ และเป็น Use Case ที่ดี
ตัวอย่าง
Config.Garages = {
{
label = 'Legion Garage',
coords = vector3(
215.8,
-810.1,
30.7
)
}
}
Client ใช้สร้าง Marker
Server สามารถใช้ตำแหน่งเดียวกันตรวจ Distance หรือ Garage Logic
ช่วยให้ Coordinates ไม่ต้องเขียนซ้ำหลายจุด
⑱ Shared Script ใช้ Function ได้ไหม
ได้ หาก Function นั้นสามารถทำงานได้ทั้ง Client และ Server
ตัวอย่าง
function FormatMoney(amount)
return ('$%s'):format(amount)
end
ทั้งสองฝั่งสามารถเรียก
FormatMoney(5000)
ได้
แต่ Function ต้องไม่พึ่ง API ที่มีเฉพาะ Client หรือ Server
⑲ ตัวอย่าง Function ที่ไม่ควร Shared
เช่น
function GetMyCoords()
return GetEntityCoords(
PlayerPedId()
)
end
Function นี้ใช้
PlayerPedId()
ซึ่งเป็น Client-side Native
ถ้า Shared Script ถูก Server โหลดด้วย แล้ว Server เรียก Function นี้ อาจเกิดปัญหาเพราะ API ไม่ใช่ Server Context
ดังนั้น Function ใน Shared Script ควรเป็น Logic ที่ Runtime-neutral เท่าที่เป็นไปได้
⑳ Shared Function ที่เหมาะสม
ตัวอย่าง
function Clamp(value, min, max)
if value < min then
return min
end
if value > max then
return max
end
return value
end
Function นี้เป็น Pure Logic
ไม่ได้ใช้
Client Native
Server Native
Database
Player-specific Runtime
จึงเหมาะกับการ Share มากกว่า
㉑ Shared Script ตรวจว่าอยู่ Client หรือ Server ได้ไหม
ในบางกรณี Resource อาจมี Shared Code ที่ต้องแยก Behavior ตาม Context
แต่หาก Code แตกต่างกันมาก ควรแยกเป็น Client/Server File ตั้งแต่ต้นเพื่อให้อ่านง่ายกว่า
แนวคิดที่ดีกว่าคือ
Shared
→ สิ่งที่เหมือนกันจริง ๆ
Client
→ Client Logic
Server
→ Server Logic
อย่าพยายามยัดทุกอย่างลง Shared Script แล้วใช้ Condition แยก Context จำนวนมาก
㉒ Shared Script กับ Client Script ต่างกันอย่างไร
Shared Script
โหลด
Client + Server
Client Script
โหลด
Client เท่านั้น
ตัวอย่าง Client-specific Logic
local ped = PlayerPedId()
ควรอยู่ Client Script
ส่วน Configuration
Config.MarkerDistance = 20.0
ที่ทั้งสองฝั่งต้องใช้ สามารถอยู่ Shared Script
㉓ Shared Script กับ Server Script ต่างกันอย่างไร
Server Script ทำงานเฉพาะ FXServer
เหมาะกับ
Database
Permissions
Money
Inventory
Server Validation
Secret Config
Shared Script ถูกโหลดทั้งสองฝั่ง
ดังนั้นไม่ควรใช้ Shared Script กับ Logic ที่ต้องเป็น Server-only
㉔ เปรียบเทียบ Client Server Shared แบบง่าย
| ประเภท | Client | Server | เหมาะกับ |
|---|---|---|---|
| Client Script | ✅ | ❌ | Gameplay, NUI, Ped, Vehicle |
| Server Script | ❌ | ✅ | Database, Security, Authority |
| Shared Script | ✅ | ✅ | Config, Constants, Shared Logic |
ตารางนี้เป็นหลักพื้นฐานที่ควรจำก่อนออกแบบ Resource
㉕ Shared Script ใช้กับ JavaScript ได้ไหม
shared_script เลือก Script Loader จาก Extension เช่นเดียวกับ Client Script ตาม Resource Manifest
ดังนั้นสามารถกำหนดไฟล์ที่ Runtime รองรับได้ตาม Context
แต่ต้องระวังว่า JavaScript Environment ฝั่ง Client กับ Serverไม่เหมือนกันทั้งหมด
เช่น Node.js-specific Code ไม่ควรใส่ Shared JavaScript แล้วคาดว่า Client จะใช้ได้
หลักเดียวกับ Lua คือ Shared Code ต้องรองรับทั้งสอง Runtime Context ที่มันถูกโหลดเข้าไป
㉖ Shared Script ใช้กับ C# ได้ไหม
Resource Manifest สามารถเลือก Loader จากชนิดไฟล์ตาม Runtime ที่รองรับ
แต่ C# Project มักมี Client และ Server Assembly แยกกันเนื่องจาก Reference และ API Context ต่างกัน
ดังนั้นในงาน C# จริง การแชร์ Code มักจัดการผ่าน Shared Project/Library ใน Build System มากกว่าการรวมทุกอย่างเป็น Script เดียวอย่างง่ายแบบ config.lua
ต้องเลือก Architecture ให้เหมาะกับ Runtime
㉗ Shared Script ใช้กับ Lua ได้ดีไหม
ดีมาก และพบได้บ่อย
ตัวอย่าง
shared/
├── config.lua
├── constants.lua
└── functions.lua
Manifest
shared_scripts {
'shared/*.lua'
}
เป็น Structure ที่เข้าใจง่ายสำหรับ Lua Resource
โดยเฉพาะ Framework และ Standalone Resource จำนวนมาก
㉘ Shared Script ใช้กับ ESX ได้ไหม
ได้
Resource ESX จำนวนมากอาจมี Configuration หรือ Shared Data ที่ Client และ Server ต้องใช้
เช่น
Job Config
Shop Config
Coordinates
Item Mapping
สามารถ Shared ได้ตามความเหมาะสม
แต่ข้อมูลอย่างเงินจริงหรือ Permission จริงยังต้องตรวจฝั่ง Server
Framework ไม่เปลี่ยนกฎพื้นฐานเรื่อง Trust Boundary
㉙ Shared Script ใช้กับ QBCore ได้ไหม
ได้ในหลักเดียวกัน
ตัวอย่าง Resource อาจมี
shared/config.lua
client/main.lua
server/main.lua
Shared เก็บ Configuration
Client จัด Interaction
Server จัด Business Logic
นี่เป็น Structure ที่ค่อนข้างชัดเจนและ Maintain ง่าย
㉚ Shared Script ใช้กับ Qbox ได้ไหม
ได้
ไม่ว่าใช้ Framework ใด หลักการก็ยังเหมือนเดิม
Shared
→ Data/Logic ที่ต้องใช้ทั้งสองฝั่ง
Client
→ UX/Game Client
Server
→ Authority/Database/Security
Framework เป็น Layer เพิ่มเติมเหนือ FiveM Resource System
㉛ Shared Script ใช้ ox_lib ได้ไหม
Resource หลายตัวอาจกำหนด Shared Imports หรือ Library Files ตามวิธีที่ Library กำหนด
แต่ควรตรวจ Documentation ของ Version ที่ใช้งานจริงเสมอ
หลักสำคัญยังเหมือนเดิม:
ถ้าไฟล์ถูกโหลดทั้ง Client และ Server ต้องมั่นใจว่า Code ภายในรองรับทั้งสอง Context
อย่าใส่ Server-only Secret หรือ Database Logic ลงไปเพียงเพราะ Folder ชื่อ shared
㉜ Shared Script ใช้ oxmysql ได้ไหม
Database Access ควรอยู่ Server-side
oxmysql หรือ Database Layer ไม่ควรถูกนำไปใช้เป็น Client-accessible Shared Logic
Architecture ที่เหมาะคือ
Shared Config
↓
Server Script
↓
oxmysql
↓
Database
Client ติดต่อ Server ผ่าน Event หรือ Callback
ไม่ใช่
Client
↓
Shared Database Function
↓
MySQL
㉝ Shared Script ใช้กับ NUI ได้ไหม
Shared Script สามารถเก็บข้อมูลที่ Client ใช้กับ NUI ได้ เช่น
Config.MenuTitle = 'Garage'
แต่ NUI Browser ไม่ได้รัน Shared Lua โดยตรง
Flow คือ
Shared Config
↓
Client Script
↓
SendNUIMessage
↓
NUI JavaScript
ดังนั้นอย่าสับสนระหว่าง Shared Script กับ Front-end NUI Files
㉞ Shared Script เปลี่ยนค่า Runtime ได้ไหม
ได้ใน Runtime ของแต่ละฝั่ง
ตัวอย่าง
Config.Enabled = true
Client สามารถทำ
Config.Enabled = false
แต่จะเปลี่ยนเฉพาะ Client State นั้น
Server ไม่เปลี่ยนตาม
เช่นเดียวกัน ถ้า Server ทำ
Config.Enabled = false
ไม่ได้ส่งค่าใหม่ไป Client โดยอัตโนมัติ
ถ้าต้องการ Sync ต้องใช้ Network/State Mechanism
㉟ ถ้าต้องการ Config เปลี่ยนแบบ Real-time ทำอย่างไร
ถ้าค่าเปลี่ยนระหว่าง Runtime และต้อง Sync ให้ Client ควรใช้ระบบเหมาะสม เช่น
Server State
↓
Network Event
↓
Client Update
หรือ
State Bags
ตาม Use Case
Shared Script เหมาะกับ Initial Definition
ไม่ใช่ Real-time Replication System
㊱ State Bags ต่างจาก Shared Script อย่างไร
Shared Script คือ Code/File Loading
State Bags คือ State Replication Mechanism
ตัวอย่าง
Shared Script
→ กำหนดค่าเริ่มต้น
State Bag
→ Sync State ที่เปลี่ยนระหว่าง Runtime
เช่น
Config.MaxFuel = 100
อาจอยู่ Shared
ส่วน
รถคันนี้ fuel = 73
อาจเป็น Runtime State ที่ใช้ State Bag หรือระบบอื่นตาม Architecture
สองอย่างมีหน้าที่ต่างกัน
㊲ Shared Script ต่างจาก Export อย่างไร
Shared Script ทำให้ Code เดียวกันถูกโหลดทั้งสองฝั่ง
Export คือเปิด Function ของ Resource ให้ Resource อื่นเรียก
ตัวอย่าง
Shared Script
→ ภายใน Resource และ Runtime ที่โหลด
Export
→ Resource A เรียก Resource B
ดังนั้นถ้าต้องการให้ Resource อื่นใช้ Function ไม่ได้หมายความว่าต้องเปลี่ยน Function นั้นเป็น Shared Script
ควรใช้ Export ตาม Use Case
㊳ Shared Script ต่างจาก Event อย่างไร
Shared Script
โหลด Code
Event
ส่ง Signal หรือ Data
ตัวอย่าง
shared/config.lua
กำหนด Shop Location
ส่วนเมื่อ Player ซื้อ Item
Client
↓
TriggerServerEvent
↓
Server
เป็น Network Communication
สองระบบไม่แทนกัน
㊴ Shared Script ต่างจาก Callback อย่างไร
Callback ใช้ Request → Response
ตัวอย่าง
Client
↓
ขอข้อมูล Garage
↓
Server
↓
Database
↓
ส่งข้อมูลกลับ
Shared Scriptไม่สามารถดึง Database Result จาก Server มาให้ Client อัตโนมัติ
หาก Data เป็น Dynamic หรือ Sensitive ควรใช้ Callback/Event แทนการใส่ทุกอย่างใน Shared Config
㊵ ควรใส่ Item ทั้งหมดใน Shared Config ไหม
ขึ้นอยู่กับระบบ
ถ้า Client ต้องรู้ Item Definitions เพื่อแสดง
Label
Icon Name
Weight
UI Information
บางส่วนสามารถ Shared ได้
แต่ข้อมูลที่ Client ไม่จำเป็นต้องรู้หรือมีผล Security ควรเก็บ Server-side
หลักคือ
Client ต้องใช้จริงไหม?
ถ้าไม่ต้องใช้ อย่าส่งลง Client เพียงเพื่อความสะดวก
㊶ Shared Script ทำให้ Resource ใหญ่ขึ้นไหม
ไฟล์ที่กำหนดเป็น Shared Script จะถูกเพิ่มเข้า Resource Packfile และต้องถูกโหลดฝั่ง Clientด้วย
ดังนั้นไม่ควร Shared ไฟล์ขนาดใหญ่ที่ Client ไม่จำเป็นต้องใช้
ตัวอย่างข้อมูล Server-only หลายหมื่นรายการ หาก Client ไม่ใช้ ก็ไม่ควรใส่ Shared
การแบ่ง Data ให้เหมาะสมช่วยลดข้อมูลที่ Client ต้องรับและลด Complexity
㊷ Shared Script มีผลกับ Performance ไหม
ได้ หากใส่ Logic หนักไว้ใน Shared
จำไว้ว่า Code อาจทำงานทั้ง
Client
+
Server
ตัวอย่างที่ไม่เหมาะ
CreateThread(function()
while true do
Wait(0)
-- heavy logic
end
end)
ถ้าไฟล์นี้ Shared ก็อาจสร้างงานทั้งสอง Context โดยไม่จำเป็น
Shared Script ควรเน้น Code ที่ต้องใช้ทั้งสองฝั่งจริง ๆ
㊸ อย่าใส่ Thread ทุกอย่างใน Shared
ถ้า Thread ต้องทำเฉพาะ Client
ใส่ใน
client.lua
ถ้าต้องทำเฉพาะ Server
ใส่ใน
server.lua
ไม่ควรใส่ Shared เพียงเพราะอยากลดจำนวน File
การแยก Context ชัดเจนช่วย
Debug ง่าย
Security ดีขึ้น
Performance ชัดเจน
Developer คนอื่นอ่านง่าย
㊹ Shared Script Error ดูที่ไหน
เพราะ Shared Script ถูกโหลดสองฝั่ง Error อาจเกิดได้ทั้ง Client และ Server
ถ้า Code Error ฝั่ง Client
ดู
F8 Console
ถ้า Error ฝั่ง Server
ดู
Server Console
ดังนั้นถ้า config.lua เป็น Shared แล้ว Resource มีปัญหา ต้องตรวจทั้งสอง Console
㊺ Error ฝั่ง Client แต่ Server ปกติเป็นไปได้ไหม
ได้
ตัวอย่าง Shared Function เรียก Native ที่ใช้ Client ได้ แต่ Code Path บน Server ไม่ได้เรียก Function นั้น
หรือกลับกัน
จึงอาจเกิด Error เฉพาะ Context ใด Context หนึ่ง
นี่เป็นอีกเหตุผลว่าทำไม Shared Code ควรเล็กและ Runtime-neutral เท่าที่ทำได้
㊻ ตัวอย่าง Shared Script ที่ออกแบบไม่ดี
Config = {}
Config.DatabasePassword = 'secret'
function GetPlayerPosition()
return GetEntityCoords(
PlayerPedId()
)
end
function SaveToDatabase()
-- database logic
end
ไฟล์เดียวมีทั้ง
Secret
Client Native
Server Database Logic
แล้วโหลดด้วย
shared_script 'config.lua'
Structure แบบนี้สร้างทั้ง Security และ Maintainability Problem
㊼ ตัวอย่าง Structure ที่ดีกว่า
my_resource/
├── fxmanifest.lua
├── shared/
│ ├── config.lua
│ └── utils.lua
├── client/
│ ├── main.lua
│ └── ui.lua
└── server/
├── main.lua
├── database.lua
└── security.lua
Shared
Config
Constants
Pure Functions
Client
Ped
Vehicle
Marker
NUI
Server
Database
Security
Rewards
Permissions
แบบนี้อ่านและ Debug ง่ายกว่ามาก
㊽ ตัวอย่าง fxmanifest.lua สำหรับ Structure นี้
fx_version 'cerulean'
game 'gta5'
author 'Your Name'
description 'FiveM Shared Script Example'
version '1.0.0'
shared_scripts {
'shared/config.lua',
'shared/utils.lua'
}
client_scripts {
'client/main.lua',
'client/ui.lua'
}
server_scripts {
'server/main.lua',
'server/database.lua',
'server/security.lua'
}
นี่เป็น Resource Structure ที่เข้าใจ Context ได้จาก Folder ทันที
㊾ ลำดับ Script ใน fxmanifest.lua สำคัญไหม
สำคัญเมื่อ Script หนึ่งต้องใช้ Variable หรือ Function ที่ถูกประกาศจากไฟล์ก่อนหน้า
ตัวอย่าง
shared_scripts {
'shared/config.lua',
'shared/functions.lua'
}
ถ้า functions.lua ต้องใช้ Config จาก config.lua ก็ควรให้ Config ถูกโหลดก่อน
เช่นเดียวกับ Client และ Server Scripts
ดังนั้นอย่าพึ่ง Globbing โดยไม่เข้าใจ Dependency ระหว่างไฟล์ใน Resource ที่ซับซ้อน
㊿ Shared Script ใช้ require ได้ไหม
การแบ่ง Module สามารถทำได้ตามความสามารถของ Runtime และวิธีออกแบบ Resource แต่ไม่ควรสับสนกับ Resource Manifest
shared_script มีหน้าที่บอก FiveM ให้โหลด Script ทั้งสองฝั่ง
ส่วน Module System เป็นอีกเรื่องหนึ่ง
สำหรับมือใหม่ เริ่มจากโครงสร้าง
shared/config.lua
shared/functions.lua
client/main.lua
server/main.lua
ให้เข้าใจก่อน แล้วจึงต่อยอดเรื่อง Module Architecture
51 Shared Script ใช้ Global Variable ดีไหม
พบได้บ่อย เช่น
Config = {}
เพราะต้องการให้ Script อื่นใน Runtime เดียวกันอ่านได้
แต่ไม่ควรสร้าง Global จำนวนมากโดยไม่มี Structure
ตัวอย่างที่ดีกว่า
Config = {
Shops = {},
Garages = {},
Jobs = {}
}
แทน
Shop1 = ...
Shop2 = ...
Garage1 = ...
Job1 = ...
การรวมข้อมูลเป็น Table ช่วยให้ Maintain ง่ายกว่า
52 Shared Utility Function ควรเป็นแบบไหน
พยายามให้เป็น Pure Function
ตัวอย่าง
function Round(value)
return math.floor(
value + 0.5
)
end
Function นี้
ไม่ใช้ Client Native
ไม่ใช้ Server Native
ไม่ Query Database
ไม่พึ่ง Player
จึงสามารถใช้ได้ทั้งสอง Context อย่างปลอดภัยกว่า
53 Shared Script ควรเก็บ Locale ได้ไหม
ได้ หาก Client และ Server ต้องใช้ข้อความชุดเดียวกัน
ตัวอย่าง
Locales = {
shop_open = 'Open Shop',
no_money = 'Not enough money'
}
แต่ถ้า Locale ใหญ่มากและ Server ไม่จำเป็นต้องใช้ทั้งหมด อาจแยกตาม Context เพื่อไม่โหลดข้อมูลเกินความจำเป็น
ให้พิจารณาขนาดและ Use Case ของ Resource
54 Shared Script ควรเก็บ Vehicle Config ได้ไหม
ได้
ตัวอย่าง
Config.Vehicles = {
police = {
model = 'police',
price = 50000
}
}
Client อาจใช้
Model
Label
UI
Server ใช้
Price
Validation
Permission
แต่ Server ต้องอ่านราคาจาก Server Copy ของ Config เอง ไม่เชื่อราคาจาก Client
55 Shared Script ควรเก็บ Job Config ได้ไหม
ได้
เช่น
Config.Jobs = {
police = {
label = 'Police',
minimumGrade = 0
}
}
Client ใช้แสดง UI
Server ใช้ Validate
แต่ Player มี Job อะไรจริงต้องมาจาก Server Authority หรือ Framework State ที่เชื่อถือได้
อย่าให้ Client เปลี่ยน Config/Local State แล้วใช้เป็นหลักฐานว่าตัวเองเป็น Police
56 Shared Script กับ Security ต้องจำอะไรที่สุด
จำประโยคเดียวนี้:
Shared = Client เห็นได้
ดังนั้นอย่าใส่สิ่งที่ต้องเป็น Secret
และอย่าใช้ค่าที่ Client ส่งกลับมาเป็น Authority เพียงเพราะ Source Config เดิมเป็น Shared
Server ต้องตรวจข้อมูลที่มีผลต่อ
Money
Item
Reward
Permission
Ownership
Transactions
ด้วย Server-side Logic
57 Shared Script กับ Performance ต้องจำอะไรที่สุด
จำว่า Code ถูกโหลดสอง Context
ดังนั้นถามก่อนว่า
Client ต้องใช้ไหม?
Server ต้องใช้ไหม?
ถ้าคำตอบมีเพียงฝั่งเดียว อย่า Shared โดยไม่มีเหตุผล
Resource ที่แยก Context ดีจะ Optimize และ Debug ง่ายกว่า
58 Checklist ก่อนใส่ไฟล์เป็น Shared Script
ก่อนเพิ่มไฟล์ใน
shared_scripts {}
ถามว่า
① Client ต้องใช้ไฟล์นี้ไหม
② Server ต้องใช้ไฟล์นี้ไหม
③ มี Password หรือ Secret หรือไม่
④ มี Client-only Native หรือไม่
⑤ มี Server-only Native หรือไม่
⑥ มี Database Logic หรือไม่
⑦ มี Heavy Thread หรือไม่
⑧ Variable ต้อง Sync จริงหรือแค่มีค่าเริ่มต้นเหมือนกัน
⑨ ลำดับการโหลดถูกต้องหรือไม่
⑩ แยก Client/Server แล้วอ่านง่ายกว่าหรือไม่
ถ้าไฟล์มี Server Secret หรือ Database Logic ควรย้ายออกจาก Shared
59 Shared Script เหมาะกับมือใหม่ไหม
เหมาะ แต่ควรใช้กับเรื่องง่าย ๆ ก่อน เช่น
Config
Coordinates
Constants
Pure Utility Functions
อย่าเริ่มด้วย Shared Architecture ที่ซับซ้อนเกินไป
เมื่อเข้าใจ Client/Server Context แล้วจึงค่อยสร้าง Shared Module ที่ใหญ่ขึ้น
60 หลักการออกแบบ Shared Script ที่ดี
ใช้หลัก
Shared
→ สิ่งที่ทั้งสองฝั่งต้องรู้
Client
→ สิ่งที่เกี่ยวกับผู้เล่นและเกม Client
Server
→ สิ่งที่ต้องเชื่อถือและเป็นความลับ
ถ้าแยกสามส่วนนี้ชัด Resource จะมีโครงสร้างที่ดีตั้งแต่ต้น
คำถามที่พบบ่อยเกี่ยวกับ Shared Script FiveM
Shared Script FiveM คืออะไร
คือ Script ที่ FiveM โหลดทั้งฝั่ง Client และ Server
ใช้คำสั่งอะไรใน fxmanifest.lua
ใช้
shared_script 'config.lua'
หรือ
shared_scripts {
'shared/*.lua'
}
Shared Script เหมาะกับอะไร
เหมาะกับ Config, Constants, Shared Data และ Function ที่ใช้ได้ทั้ง Client และ Server
Shared Script เก็บ Password ได้ไหม
ไม่ควร เพราะไฟล์ Shared ถูกโหลดฝั่ง Client ด้วย
Shared Script เชื่อม Database ได้ไหม
Database Logic ควรอยู่ Server Script ไม่ควรทำเป็น Shared
Shared Variable Sync ระหว่าง Client กับ Server ไหม
ไม่ แต่ละ Runtime มี Memory และ State ของตัวเอง
ถ้า Client เปลี่ยน Config Server เปลี่ยนด้วยไหม
ไม่ การเปลี่ยน Variable ฝั่ง Client ไม่เปลี่ยน Server Copy โดยอัตโนมัติ
ถ้าต้องการ Sync State ใช้อะไร
ใช้ Network Events, Callbacks, State Bags หรือกลไกที่เหมาะกับระบบ
shared_script กับ client_script ต่างกันอย่างไร
shared_script โหลดทั้ง Client และ Server ส่วน client_script โหลดเฉพาะ Client
shared_script กับ server_script ต่างกันอย่างไร
shared_script โหลดทั้งสองฝั่ง ส่วน server_script โหลดเฉพาะ FXServer
Shared Script รองรับหลายไฟล์ไหม
รองรับ และ shared_script Directive รองรับ Globbing ตาม Resource Manifest
สรุป Shared Script FiveM คืออะไร
Shared Script FiveM คือ Script ที่ถูกโหลดให้ทำงานทั้ง Client และ Server ภายใน Resource เดียวกัน
เหมาะกับข้อมูลและ Logic เช่น
Config
Constants
Coordinates
Shared Tables
Shared Definitions
Pure Utility Functions
โดยสามารถกำหนดใน fxmanifest.lua
shared_script 'config.lua'
หรือ
shared_scripts {
'shared/*.lua'
}
สิ่งที่สำคัญที่สุดคือ Shared Script ไม่ใช่ Shared Memory การเปลี่ยน Variable บน Client ไม่ได้เปลี่ยน Server State และการเปลี่ยน Server Variableก็ไม่ได้ Sync ไป Client อัตโนมัติ
ถ้าต้องการสื่อสารหรือ Sync State ต้องใช้ Event, Callback, State Bag หรือระบบ Network ที่เหมาะสม
อีกเรื่องที่ FiveM Developer ต้องจำให้แม่นคือ
Shared Script
→ โหลดฝั่ง Client ด้วย
→ อย่าเก็บ Secret
Database Password, Private API Key, Security Token และ Server-only Logic ควรอยู่ใน Server Script เท่านั้น
สำหรับผู้ที่เรียน FiveM Developer กับ comsiam การแยก client, server และ shared ให้ถูกตั้งแต่ Resource แรกจะช่วยลดทั้ง Error, Security Problem และความยุ่งยากเมื่อ Script ใหญ่ขึ้น
เมื่อเข้าใจ Shared Script แล้ว ขั้นต่อไปคือการเข้าใจว่า Client กับ Server FiveM ต่างกันอย่างไรในภาพรวม ซึ่งเป็นพื้นฐานสำคัญก่อนเข้าสู่ Event, TriggerServerEvent, TriggerClientEvent และ Networking ในบทความต่อไปของ comsiam
Comments
Post a Comment