FiveM Resource Folder จัดโครงสร้างอย่างไร? แบ่ง Client Server Shared NUI และ Stream ให้ถูกต้อง
การจัด FiveM Resource Folder ที่ดีควรแบ่งไฟล์ตามหน้าที่อย่างชัดเจน เช่น client/, server/, shared/, html/ และ stream/ โดยมี fxmanifest.lua อยู่ที่ Root ของ Resource เพื่อให้ Resource อ่านง่าย แก้ไขง่าย และขยายระบบได้โดยไม่กลายเป็น Script ขนาดใหญ่ที่ทุกอย่างพันกัน
ตัวอย่างโครงสร้างที่เหมาะกับ Resource ทั่วไปคือ
my_resource/
├── fxmanifest.lua
│
├── shared/
│ ├── config.lua
│ └── constants.lua
│
├── client/
│ ├── main.lua
│ ├── events.lua
│ └── ui.lua
│
├── server/
│ ├── main.lua
│ ├── events.lua
│ └── database.lua
│
└── html/
├── index.html
├── app.js
└── style.css
ถ้ามี Game Assets เช่น Vehicle, Object หรือ Map สามารถเพิ่ม
stream/
ได้
ตัวอย่าง
my_resource/
├── fxmanifest.lua
├── client/
├── server/
├── shared/
├── html/
└── stream/
แต่ต้องเข้าใจว่า ชื่อ Folder อย่าง client, server, shared หรือ modules เป็น Convention ที่ Developer ตั้งเพื่อจัดระเบียบ ไม่ใช่ชื่อ Folder ที่ FiveM บังคับ
FiveM จะรู้ว่า Script ใดทำงานฝั่งไหนจาก fxmanifest.lua
เช่น
client_scripts {
'client/main.lua',
'client/ui.lua'
}
server_scripts {
'server/main.lua',
'server/database.lua'
}
shared_scripts {
'shared/config.lua'
}
① FiveM Resource Folder คืออะไร
Resource Folder คือ Folder หลักของ Resource หนึ่งตัว
ตัวอย่าง
resources/
└── [local]/
└── my_garage/
my_garage คือ Resource
ภายในต้องมี Manifest
my_garage/
└── fxmanifest.lua
จากนั้นจึงเพิ่ม Scripts และ Assets ตามต้องการ
② fxmanifest.lua ต้องอยู่ตรงไหน
ควรอยู่ Root ของ Resource
ถูกต้อง:
my_garage/
├── fxmanifest.lua
├── client/
└── server/
ไม่ใช่
my_garage/
└── config/
└── fxmanifest.lua
เพราะ FiveM ใช้ Manifest ที่ Root เพื่อ Parse Resource นั้น
③ Resource ต้องมี client Folder ไหม
ไม่บังคับ
Resource เล็กอาจเป็น
my_resource/
├── fxmanifest.lua
└── client.lua
ได้เลย
Folder client/ เป็นเพียงวิธีจัดไฟล์ให้เป็นระเบียบเมื่อ Resource เริ่มมีหลาย Script
④ Resource ต้องมี server Folder ไหม
ไม่บังคับเช่นกัน
Resource เล็กสามารถมี
my_resource/
├── fxmanifest.lua
└── server.lua
แต่ Resource ใหญ่ควรแยก
server/
เพื่อให้อ่านง่าย
⑤ shared Folder เป็น Folder พิเศษไหม
ไม่
FiveM ไม่ได้เห็น Folder ชื่อ shared แล้วโหลดอัตโนมัติ
คุณต้องประกาศ
shared_scripts {
'shared/config.lua'
}
ใน fxmanifest.lua
สิ่งที่ทำให้ Script เป็น Shared คือ Manifest ไม่ใช่ชื่อ Folder
⑥ client Folder ทำหน้าที่อะไร
ใช้เก็บ Code ที่ทำงานบน Client
เช่น
client/
├── main.lua
├── events.lua
├── controls.lua
├── markers.lua
├── vehicles.lua
└── ui.lua
เหมาะกับ
Controls
Camera
Marker
Blip
Local Entity
NUI
Rendering
PlayerPedId
⑦ server Folder ทำหน้าที่อะไร
ใช้เก็บ Code ฝั่ง FXServer
เช่น
server/
├── main.lua
├── events.lua
├── commands.lua
├── database.lua
└── permissions.lua
เหมาะกับ
Database
Money
Inventory
Permissions
Validation
Global State
Secure Network Events
⑧ shared Folder ควรเก็บอะไร
เหมาะกับสิ่งที่ทั้ง Client และ Server ต้องอ่าน เช่น
shared/
├── config.lua
├── constants.lua
└── utils.lua
ตัวอย่าง
Config = {}
Config.InteractDistance = 3.0
Config.JobName = 'police'
แต่ไม่ควรใส่ Secrets
⑨ Shared ไม่ได้หมายถึง Shared Memory
ถ้า Client เปลี่ยน
Config.Test = 100
Server ไม่ได้เห็น 100 โดยอัตโนมัติ
เพราะ
Client Runtime
≠
Server Runtime
Shared Script เพียงถูกโหลดทั้งสองฝั่ง
หากต้อง Sync Runtime State ต้องใช้ Events, State Bags, Callbacks หรือ Database ตาม Use Case
⑩ Folder html ใช้ทำอะไร
นิยมใช้เก็บ NUI
เช่น
html/
├── index.html
├── style.css
├── app.js
├── images/
└── fonts/
จากนั้น Manifest
ui_page 'html/index.html'
files {
'html/index.html',
'html/style.css',
'html/app.js'
}
⑪ ต้องชื่อ html เท่านั้นไหม
ไม่
สามารถตั้ง
web/
ui/
nui/
frontend/
ก็ได้
เช่น
ui_page 'web/index.html'
FiveM สนใจ Path ใน Manifest ไม่ได้บังคับว่าต้องชื่อ html
⑫ stream Folder คืออะไร
stream/ ใช้กับ Streaming Game Assets เช่น
Models
Textures
YMAP
YTYP
Vehicles
Props
Clothing Assets
ตัวอย่าง
vehicle_resource/
├── fxmanifest.lua
└── stream/
├── mycar.yft
└── mycar.ytd
Resource Asset จะถูกโหลดตามระบบ Streaming ของ FiveM
⑬ Vehicle Resource ควรจัดอย่างไร
ตัวอย่าง
my_vehicle/
├── fxmanifest.lua
├── data/
│ ├── vehicles.meta
│ ├── handling.meta
│ ├── carcols.meta
│ └── carvariations.meta
└── stream/
├── mycar.yft
└── mycar.ytd
Manifest จึงอ้าง Data Files จาก data/
ช่วยแยก .meta ออกจาก Models ชัดเจน
⑭ ทำไมไม่วาง .meta ทุกไฟล์ไว้ Root
ทำได้
แต่ถ้ามีหลาย Files Root จะรกอย่างรวดเร็ว
เช่น
my_vehicle/
├── fxmanifest.lua
├── vehicles.meta
├── handling.meta
├── carcols.meta
├── carvariations.meta
├── vehiclelayouts.meta
├── client.lua
└── ...
การสร้าง
data/
ช่วยให้ Structure อ่านง่ายกว่า
⑮ Map Resource จัดอย่างไร
ตัวอย่างง่าย
my_map/
├── fxmanifest.lua
└── stream/
├── map.ymap
├── props.ytyp
└── textures.ytd
Manifest อาจมี
fx_version 'cerulean'
game 'gta5'
this_is_a_map 'yes'
แล้วเพิ่ม Data File เพิ่มหาก Asset ต้องใช้
⑯ Config ควรมีไฟล์เดียวไหม
Resource เล็กใช้
config.lua
ได้
แต่ระบบใหญ่สามารถแยก
shared/
├── config.lua
├── locations.lua
├── vehicles.lua
└── locale.lua
เพื่อไม่ให้ config.lua ยาวหลายพันบรรทัด
⑰ config.lua ควรอยู่ shared เสมอไหม
ไม่
ต้องดูข้อมูล
ถ้า Client และ Server ต้องใช้
shared/config.lua
เหมาะ
แต่ถ้ามีข้อมูลอย่าง
Reward Formula
Security Limits
Server-only Prices
Private Settings
และ Clientไม่จำเป็นต้องรู้ ควรเก็บ Server-side
⑱ อย่าเปิดข้อมูล Server สำคัญผ่าน Shared
ตัวอย่างที่ไม่เหมาะ
Config.AdminToken =
'secret'
Config.DatabasePassword =
'password'
ใน Shared Script
เพราะ Shared Script ถูกโหลดบน Client ด้วย
หลักคือ
Client ต้องรู้?
→ Shared/Client
Client ไม่ต้องรู้?
→ Server
⑲ main.lua ควรมีอะไร
main.lua ควรเป็น Entry/Coordinator หลัก ไม่จำเป็นต้องใส่ทุก Feature ลงไฟล์เดียว
เช่น
client/main.lua
อาจดูแล
Initialization
Main Events
Module Coordination
แล้ว Feature แยกออกไป
garage.lua
menu.lua
vehicle.lua
⑳ อย่าสร้าง main.lua 20,000 บรรทัด
Resource อาจยังทำงาน
แต่ Maintenance จะยากมาก
แทน
client/main.lua
20,000 lines
ลองแบ่ง
client/
├── main.lua
├── garage.lua
├── impound.lua
├── vehicle.lua
├── blips.lua
└── ui.lua
ตาม Domain
㉑ แยกตาม Technical Layer หรือ Feature ดี
มีสองแนวทางหลัก
แยกตาม Technical Layer
client/
server/
shared/
แยกตาม Feature
modules/
├── garage/
├── impound/
└── vehicle/
Resource ใหญ่สามารถใช้สองแบบร่วมกันได้
㉒ ตัวอย่างแบบ Layer-first
garage/
├── fxmanifest.lua
├── shared/
│ ├── config.lua
│ └── constants.lua
├── client/
│ ├── main.lua
│ ├── garage.lua
│ └── impound.lua
└── server/
├── main.lua
├── garage.lua
├── impound.lua
└── database.lua
เหมาะกับ Resource ขนาดเล็กถึงกลาง
㉓ ตัวอย่างแบบ Feature-first
garage/
├── fxmanifest.lua
├── modules/
│ ├── garage/
│ │ ├── client.lua
│ │ ├── server.lua
│ │ └── shared.lua
│ │
│ └── impound/
│ ├── client.lua
│ ├── server.lua
│ └── shared.lua
│
└── shared/
└── config.lua
เหมาะเมื่อแต่ละ Feature มี Logic จำนวนมาก
㉔ modules เป็น Folder พิเศษไหม
ไม่
modules/ เป็น Convention ของ Developer
ถ้าต้องการให้ FiveM โหลด
modules/garage/client.lua
ต้องประกาศใน Manifest
client_script
'modules/garage/client.lua'
หรือใช้ Globbing ที่เหมาะสม
㉕ ใช้ Globbing จัด Folder ได้ไหม
ได้สำหรับ Script Directives ที่รองรับ
เช่น
client_scripts {
'client/**/*.lua'
}
และ
server_scripts {
'server/**/*.lua'
}
ช่วยลด Manifest ที่ต้องระบุ File ทีละตัว
㉖ *.lua ต่างจาก **/*.lua อย่างไร
Concept คือ
client/*.lua
Match Files ใน Folder ระดับนั้น
เช่น
client/main.lua
client/menu.lua
ส่วน
client/**/*.lua
ใช้กับ Subfolders ด้วย
เช่น
client/modules/garage.lua
client/ui/menu/main.lua
㉗ Globbing ดีไหม
ดีสำหรับ Resource ที่ File เยอะ
แต่มีข้อเสียคือคุณอาจมอง Load Order ไม่ชัด
Manifest
client_scripts {
'client/**/*.lua'
}
สะดวกมาก
แต่ถ้า File หนึ่งต้องใช้ Function ที่ File อื่นสร้างก่อน อาจกลายเป็น Initialization Bug
㉘ Resource ที่ Load Order สำคัญควรทำอย่างไร
เขียน Files แบบ Explicit
shared_scripts {
'shared/config.lua',
'shared/constants.lua',
'shared/utils.lua'
}
client_scripts {
'client/core.lua',
'client/events.lua',
'client/ui.lua'
}
เปิด Manifest แล้วเห็น Order ชัดเจน
㉙ อย่าแก้ Load Order ด้วย Wait
ไม่ควรทำ
CreateThread(function()
Wait(5000)
SomeFunction()
end)
เพียงเพื่อหวังว่า File อื่นพร้อมแล้ว
ถ้าเป็น File ใน Resource เดียวกันควรจัด Initialization/Load Order ให้ถูก
ถ้าเป็น Resource อื่นให้ใช้ Dependency
㉚ Database Folder ควรอยู่ไหน
นิยม
server/database.lua
หรือ
server/db/
├── players.lua
├── vehicles.lua
└── garage.lua
เพราะ Database Queries ส่วนใหญ่ควรอยู่ Server-side
ไม่ควรให้ Client Execute SQL โดยตรง
㉛ SQL File เก็บไว้ไหน
ถ้า Resource มี Installer SQL สามารถจัด
sql/
└── install.sql
เช่น
my_garage/
├── fxmanifest.lua
├── sql/
│ └── install.sql
└── server/
SQL Installer ไม่จำเป็นต้องเป็น Script ที่ FiveM Run อัตโนมัติ
เป็น File สำหรับ Setup Database ตามระบบ Resource
㉜ locales Folder ใช้ทำอะไร
Resource หลายภาษามักจัด
locales/
├── en.lua
└── th.lua
หรือ
locales/
├── en.json
└── th.json
ขึ้นกับ Library/Architecture
ช่วยไม่ให้ Text กระจายอยู่ทั่ว Scripts
㉝ utils Folder ควรใช้เมื่อไร
ถ้ามี Utility หลายกลุ่มสามารถแบ่ง
shared/utils/
client/utils/
server/utils/
ตาม Context
เช่น
shared/utils/math.lua
server/utils/validation.lua
client/utils/draw.lua
หลีกเลี่ยง utils.lua หนึ่งไฟล์ที่กลายเป็นที่ทิ้งทุก Function
㉞ events.lua ดีไหม
ดีถ้า Resource มี Events เยอะ
เช่น
client/events.lua
server/events.lua
แต่ไม่ควรสร้าง File ชื่อเดียวกันสิบชุดจนหา Handler ไม่เจอ
ถ้าใหญ่ให้แยกตาม Feature
server/events/garage.lua
server/events/impound.lua
㉟ commands.lua ควรแยกไหม
ถ้า Commands หลายตัว แยก
server/commands.lua
หรือ
client/commands.lua
จะช่วยให้ค้นง่าย
เช่น
/garage
/impound
/admincar
ไม่ต้องค้น RegisterCommand ทั่ว Resource
㊱ callbacks.lua ควรแยกไหม
ถ้า Framework/Library ใช้ Callback จำนวนมากสามารถทำ
server/callbacks.lua
client/callbacks.lua
หรือแยกตาม Feature
ช่วยให้ Request/Response APIs หาได้ง่าย
㊲ bridge Folder คืออะไร
Resource ที่รองรับหลาย Framework มักสร้าง
bridge/
├── esx/
├── qb/
└── qbox/
ตัวอย่าง
bridge/
├── client.lua
└── server.lua
หรือ
bridge/
├── esx.lua
├── qb.lua
└── qbox.lua
เพื่อแยก Framework-specific Code ออกจาก Core Logic
㊳ ทำไม Bridge Architecture ดี
เพราะ Core Resource ไม่ควรเต็มไปด้วย
if Config.Framework == 'esx' then
...
elseif Config.Framework == 'qb' then
...
elseif Config.Framework == 'qbox' then
...
end
ทุก Function
แยก Adapter Layer ช่วยลด Coupling
㊴ Core Folder ใช้เมื่อไร
Resource ใหญ่สามารถมี
core/
สำหรับ Domain Logic ที่ไม่ควรผูก UI หรือ Framework
เช่น
core/
├── vehicles.lua
├── validation.lua
└── missions.lua
แต่ต้องจำว่า Folder เองไม่ได้บอก FiveM ว่าทำงาน Client หรือ Server
Manifest ยังเป็นผู้กำหนด
㊵ NUI Frontend ใหญ่ควรจัดอย่างไร
ถ้าเป็น HTML/JS ธรรมดา
web/
├── index.html
├── css/
├── js/
├── images/
└── fonts/
ถ้าใช้ React/Vue/Svelte อาจมี
web/
├── src/
├── public/
└── dist/
โดย FiveM Manifest ควรอ้าง Build Output ที่ต้องใช้จริง เช่น dist/
㊶ อย่าเอา node_modules ส่ง Client ถ้าไม่จำเป็น
Frontend Project อาจมี
node_modules/
ขนาดใหญ่มาก
FiveM Resource Production ควรใช้ Build/Bundled Output
เช่น
web/dist/
แล้ว Manifest อ้าง Files ที่ Build แล้ว
ไม่ควรส่ง Source Dependencies ทั้ง Project ไป Client โดยไม่มีเหตุผล
㊷ TypeScript Resource ควรแยก src กับ dist ไหม
เป็นแนวทางที่ดี
เช่น
my_resource/
├── fxmanifest.lua
├── src/
│ ├── client/
│ └── server/
└── dist/
├── client.js
└── server.js
Manifest
client_script 'dist/client.js'
server_script 'dist/server.js'
TypeScript Source มีไว้ Development
FiveM Execute JavaScript Build Output
㊸ build Folder ควร Commit ไหม
ขึ้นกับ Deployment Workflow
ถ้า Production Server ดึง Resource แล้วไม่มี Build Pipeline ก็อาจต้องเก็บ Build Output
ถ้ามี CI/CD อาจ Build ตอน Deploy
นี่เป็น Project Management Choice ไม่ใช่ FiveM Requirement
㊹ server_only Resource จัดอย่างไร
ตัวอย่าง
backend_service/
├── fxmanifest.lua
└── server/
├── main.lua
└── database.lua
Manifest
fx_version 'cerulean'
game 'gta5'
server_only 'yes'
server_scripts {
'server/database.lua',
'server/main.lua'
}
Cfx.re ระบุว่า server_only ทำให้ Clients ไม่ดาวน์โหลด Content ของ Resource นั้น
㊺ server_only ใช้กับ Resource ที่มี client.lua ได้ไหม
ไม่ควรออกแบบแบบนั้น
ถ้า Resource จำเป็นต้องส่ง Client Scripts/Assets ก็ไม่ใช่ Server-only Resource
แยก Backend Resource ออกมาต่างหากหาก Architecture ต้องการซ่อนและแยก Server Component อย่างชัดเจน
㊻ Resource หนึ่งควรใหญ่แค่ไหน
ไม่มีจำนวน Lines ตายตัว
ควรแบ่ง Resource ตาม
Business Domain
Lifecycle
Dependencies
Ownership
Deployment
เช่น
inventory
garage
housing
phone
มักเหมาะเป็นคนละ Resource เพราะมี Domain และ Dependencies ต่างกัน
㊼ แตก Resource เยอะเกินไปก็ไม่ดี
อย่าเปลี่ยน Function ทุกตัวเป็น Resource
เช่น
garage-open
garage-store
garage-spawn
garage-delete
เป็น 4 Resources อาจทำให้ Dependency และ Event Communication ซับซ้อนเกินไป
Feature ที่อยู่ Domain เดียวกันควรอยู่ Resource เดียวกันหากไม่มีเหตุผลให้แยก
㊽ เมื่อไรควรแยก Resource ใหม่
พิจารณาเมื่อระบบ
มี Lifecycle ของตัวเอง
เปิด/ปิดแยกได้
มี Dependency ต่างกัน
ใช้ซ้ำหลาย Project
มี Public API ชัดเจน
มี Owner/Team ต่างกัน
นี่เป็น Boundary ที่มีเหตุผลมากกว่าจำนวน Lines อย่างเดียว
㊾ Dependency ระหว่าง Resource ต้องจัดอย่างไร
ถ้า garage ต้องใช้ oxmysql
Manifest สามารถประกาศ
dependency 'oxmysql'
ถ้าต้องหลายตัว
dependencies {
'ox_lib',
'oxmysql'
}
Resource Dependency จะลงรายละเอียดเต็มในหัวข้อ 448 ถัดไป
㊿ server.cfg ควรจัด Category ไหม
สามารถจัด Resources ระดับ Server เช่น
resources/
├── [core]/
├── [standalone]/
├── [jobs]/
├── [vehicles]/
└── [maps]/
Cfx.re รองรับ Folder [brackets] เป็น Category และสามารถมี Resources หลายตัวอยู่ภายในได้
Category ไม่ใช่ Resource ตัวเดียว
51 Category ซ้อนกันได้ไหม
เอกสาร Cfx.re แสดงว่า Category Folder สามารถซ้อนกันได้
เช่น
resources/
└── [custom]/
└── [jobs]/
└── police/
└── fxmanifest.lua
Resource จริงคือ
police
ส่วน [custom] และ [jobs] ใช้จัดกลุ่ม
52 ชื่อ Resource ควรตั้งอย่างไร
ควรสั้นและคงที่
เช่น
com_garage
com_inventory
city_jobs
police_tools
ไม่แนะนำ
garage-final-v2-fixed-final2
เพราะ Resource Name ถูกใช้กับ
ensure
restart
exports
dependencies
NUI
logs
53 เปลี่ยนชื่อ Resource ทีหลังมีผลไหม
มีได้
เช่น Resource อื่นใช้
exports['old_garage']:GetVehicle()
หรือ
dependency 'old_garage'
ถ้า Rename Folder เป็น new_garage APIs เหล่านี้ต้องปรับด้วย
ดังนั้นเลือก Naming Convention ตั้งแต่ต้นจะลดงานภายหลัง
54 Resource Folder ควรใช้ตัวพิมพ์เล็กหรือไม่
แนะนำใช้ตัวพิมพ์เล็กสม่ำเสมอ
เช่น
my_garage
และ Paths
client/main.lua
โดยเฉพาะ Server Linux ที่ Case Sensitivity ทำให้
Client/Main.lua
กับ
client/main.lua
อาจถือเป็นคนละ Path
55 ไฟล์ที่ไม่ต้องให้ Client ใช้ควรอยู่ที่ไหน
ให้อยู่ Server-side และอย่าประกาศเป็น
shared_script
หรือ
files
โดยไม่จำเป็น
เพราะ client_script และ shared_script ถูกเพิ่มเข้า Resource Packfile สำหรับ Client ตาม Manifest Behavior
Server-only Code ควรเป็น server_script
56 files ใน Manifest มีผลอย่างไร
file/files ใช้เพิ่ม Files เข้า Resource Packfile เพื่อให้ Client ดาวน์โหลดเมื่อโหลด Resource
เช่น
files {
'html/index.html',
'html/app.js',
'html/style.css'
}
ดังนั้นอย่าใส่ Server-only Files ลง files โดยไม่จำเป็น
57 Resource Structure ที่ควรหลีกเลี่ยง
ตัวอย่าง
my_resource/
├── fxmanifest.lua
├── client.lua
├── client2.lua
├── client_new.lua
├── client_fixed.lua
├── client_final.lua
├── server.lua
├── server_new.lua
├── server_old.lua
├── config2.lua
└── test.lua
แม้ทำงานได้ แต่ Maintain ยาก
ควรเปลี่ยนเป็น Folder/Feature ที่มีความหมาย
58 Structure ที่ดีสำหรับ Resource ขนาดกลาง
com_garage/
├── fxmanifest.lua
│
├── shared/
│ ├── config.lua
│ ├── constants.lua
│ └── locales.lua
│
├── client/
│ ├── main.lua
│ ├── garage.lua
│ ├── impound.lua
│ ├── vehicles.lua
│ └── ui.lua
│
├── server/
│ ├── main.lua
│ ├── garage.lua
│ ├── impound.lua
│ ├── database.lua
│ └── validation.lua
│
└── web/
├── index.html
├── css/
│ └── app.css
├── js/
│ └── app.js
└── images/
อ่านชื่อ File ก็พอเดา Architecture ได้
59 Checklist จัด Resource Folder
ตรวจอย่างน้อย
① fxmanifest.lua อยู่ Root หรือไม่?
② Resource Name ชัดเจนหรือไม่?
③ Client Logic อยู่ Client หรือไม่?
④ Server Authority อยู่ Server หรือไม่?
⑤ Shared มีเฉพาะข้อมูลที่ Client รู้ได้หรือไม่?
⑥ Secrets หลุดไป Shared หรือไม่?
⑦ Database อยู่ Server หรือไม่?
⑧ NUI แยก Folder ชัดหรือไม่?
⑨ NUI Assets ประกาศใน files ครบหรือไม่?
⑩ Streaming Assets อยู่ stream หรือไม่?
⑪ Data Files แยกจาก Scripts หรือไม่?
⑫ Config ใหญ่เกินไปหรือไม่?
⑬ มีไฟล์ final/fixed/old กระจายหรือไม่?
⑭ main.lua ใหญ่เกินไปหรือไม่?
⑮ แบ่ง Features เหมาะสมหรือไม่?
⑯ ใช้ modules เมื่อมีเหตุผลหรือไม่?
⑰ Globbing ทำให้ Load Order คลุมเครือหรือไม่?
⑱ Dependencies ถูกประกาศหรือไม่?
⑲ Resource Name ถูก Hardcode ที่ไหนบ้าง?
⑳ Path Case ตรงกันหรือไม่?
㉑ Resource Restart แล้ว Initialize ใหม่ได้หรือไม่?
㉒ Resource Stop แล้ว Cleanup ได้หรือไม่?
㉓ Temporary Entities ถูกจัดการหรือไม่?
㉔ Resource ใหญ่ควรถูกแยกเป็นหลาย Resource หรือยัง?
⑥⓪ ตัวอย่าง Resource Folder สำหรับ Production
ตัวอย่าง Resource Garage ที่รองรับ Framework, Database และ NUI
com_garage/
│
├── fxmanifest.lua
│
├── shared/
│ ├── config.lua
│ ├── constants.lua
│ └── locales.lua
│
├── bridge/
│ ├── esx.lua
│ ├── qb.lua
│ └── qbox.lua
│
├── client/
│ ├── main.lua
│ ├── events.lua
│ ├── garage.lua
│ ├── impound.lua
│ ├── vehicles.lua
│ └── ui.lua
│
├── server/
│ ├── main.lua
│ ├── events.lua
│ ├── garage.lua
│ ├── impound.lua
│ ├── validation.lua
│ └── database.lua
│
├── web/
│ ├── index.html
│ ├── css/
│ │ └── app.css
│ ├── js/
│ │ └── app.js
│ └── images/
│ └── logo.webp
│
└── sql/
└── install.sql
ตัวอย่าง fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'comsiam'
description 'FiveM garage system'
version '1.0.0'
dependencies {
'/onesync',
'ox_lib',
'oxmysql'
}
shared_scripts {
'shared/config.lua',
'shared/constants.lua',
'shared/locales.lua'
}
client_scripts {
'client/main.lua',
'client/events.lua',
'client/garage.lua',
'client/impound.lua',
'client/vehicles.lua',
'client/ui.lua'
}
server_scripts {
'server/database.lua',
'server/validation.lua',
'server/events.lua',
'server/garage.lua',
'server/impound.lua',
'server/main.lua'
}
ui_page 'web/index.html'
files {
'web/index.html',
'web/css/*.css',
'web/js/*.js',
'web/images/*'
}
แนวคิดคือ
fxmanifest.lua
→ Resource Contract
shared/
→ Shared definitions
client/
→ Local gameplay/UI
server/
→ Authority/Database
bridge/
→ Framework adapters
web/
→ NUI frontend
sql/
→ Database installation
นี่เป็น Structure ที่ขยายต่อได้โดยไม่ต้องรวมทุกอย่างไว้ในไฟล์เดียว
Resource ที่มี Streaming Assets ควรจัดอย่างไร
ถ้าเป็น Script + Assets สามารถเพิ่ม
stream/
เช่น
com_job/
├── fxmanifest.lua
├── shared/
├── client/
├── server/
├── stream/
│ ├── prop_box.ydr
│ └── prop_box.ytd
└── data/
└── props.meta
จากนั้นประกาศ Data Files ที่จำเป็นผ่าน Manifest
อย่าเอา Streaming Asset ไปปนกับ Lua Scripts โดยไม่มีเหตุผล
Resource ขนาดใหญ่ควรแยกเป็นหลาย Resource เมื่อไร
สมมติ Server มี
Garage
Inventory
Banking
Phone
Housing
Jobs
ไม่ควรนำทั้งหมดมาไว้ใน
city_core/
ตัวเดียวเพียงเพราะสามารถทำได้
ควรแยกเมื่อแต่ละระบบมี
Lifecycle ของตัวเอง
Dependencies ของตัวเอง
Public API ของตัวเอง
Database Tables ของตัวเอง
ทีมดูแลต่างกัน
จะทำให้สามารถ
restart garage
โดยไม่ต้อง Restart Inventory หรือ Phone ไปด้วย
Resource ควร Restart ได้อย่างปลอดภัย
Structure ที่ดีต้องไปพร้อม Lifecycle Design
Resource ที่สร้าง
Blips
Entities
Cameras
NUI Focus
Temporary States
ควร Cleanup เมื่อ Resource Stop
เพราะ Resource ใน FiveM สามารถ Start, Stop และ Restart แยกจากกันได้
ดังนั้น Architecture ที่พึ่ง “Server จะไม่ Restart Resource นี้” เป็น Architecture ที่เปราะบาง
คำถามที่พบบ่อยเกี่ยวกับ FiveM Resource Folder
FiveM Resource ควรมีโครงสร้างอย่างไร
พื้นฐานแนะนำ
resource/
├── fxmanifest.lua
├── shared/
├── client/
└── server/
จากนั้นเพิ่ม html, web, stream, data หรือ Folder อื่นตาม Feature
client, server, shared เป็นชื่อบังคับไหม
ไม่ เป็น Convention เพื่อจัดระเบียบ Files
FiveM รู้ได้อย่างไรว่า File เป็น Client หรือ Server
จาก fxmanifest.lua
เช่น
client_script 'client/main.lua'
server_script 'server/main.lua'
fxmanifest.lua ต้องอยู่ไหน
Root ของ Resource
HTML Folder ต้องชื่อ html ไหม
ไม่ จะชื่อ web, ui หรือ nui ก็ได้ หาก Manifest Paths ถูกต้อง
stream Folder ใช้ทำอะไร
ใช้สำหรับ Streaming Game Assets เช่น Models, Textures, Maps และ Vehicle Assets
modules เป็น Folder พิเศษไหม
ไม่ เป็น Folder Convention ของ Developer
ใช้ Folder ซ้อนหลายระดับได้ไหม
ได้ หาก Manifest Paths/Globbing รองรับและถูกต้อง
ใช้ client/**/*.lua ได้ไหม
client_script รองรับ Globbing รวมถึง Recursive Pattern ตาม Resource Manifest Documentation
ควรใช้ Wildcard ทุกไฟล์ไหม
ไม่ ถ้า Load Order สำคัญ การระบุ Files ตรง ๆ มักอ่านและ Debug ง่ายกว่า
Config ควรเป็น Shared ไหม
เฉพาะข้อมูลที่ Client และ Server ต้องใช้และปลอดภัยให้ Client รู้
Database Password อยู่ Shared ได้ไหม
ไม่ควร
Database Code ควรอยู่ไหน
Server-side
NUI Files ต้องประกาศที่ไหน
ใช้ ui_page และ files ใน Manifest
files ทำอะไร
เพิ่ม Files เข้า Resource Packfile เพื่อให้ Client ดาวน์โหลด
Server-only Resource ทำได้ไหม
ได้ด้วย server_only 'yes' สำหรับ Resource ที่ไม่มี Client Content
SQL File ต้องประกาศใน Manifest ไหม
ไม่จำเป็นหากเป็นเพียง Installation Script ที่ FiveM ไม่ต้องโหลด Runtime
Vehicle .meta ควรอยู่ไหน
นิยมจัด data/ แล้วใช้ files/data_file ตามประเภท
Resource ใหญ่ควรแยกหลาย Folder หรือหลาย Resource
ทั้งสองอย่างได้ ให้แยก Folder ก่อนตาม Layer/Feature และแยก Resource เมื่อ Domain/Lifecycle/Dependency สมควรแยก
Category Folder คืออะไร
Folder ชื่อแบบ
[jobs]
ใช้จัดกลุ่ม Resources และไม่ใช่ Resource เอง
Category ซ้อนกันได้ไหม
ได้ตาม Resource Directory Structure ที่ Cfx.re รองรับ
เปลี่ยนชื่อ Resource Folder ได้ไหม
ได้ แต่ Exports, Dependencies, Configuration และ NUI ที่อ้างชื่อ Resource อาจต้องเปลี่ยนตาม
Folder Structure มีผลกับ FPS ไหม
การแบ่ง Folder เองไม่ได้ทำให้ FPS สูงขึ้น แต่ Architecture ที่เป็นระเบียบช่วยให้หาและแก้ Code ที่กิน CPU/Network ได้ง่ายขึ้น
สรุป FiveM Resource Folder จัดโครงสร้างอย่างไร
โครงสร้าง FiveM Resource ที่ดีไม่มี Folder Layout เดียวที่บังคับ แต่ควรแบ่ง Code ตาม Context และหน้าที่ให้ชัดเจน
โครงสร้างพื้นฐานที่แนะนำคือ
my_resource/
├── fxmanifest.lua
├── shared/
├── client/
└── server/
หากมี NUI
├── web/
หากมี Game Assets
├── stream/
หากมี Data Files
├── data/
และหากเป็น Resource ใหญ่ อาจเพิ่ม
├── bridge/
├── modules/
├── locales/
└── sql/
ได้ตาม Architecture
สิ่งสำคัญคืออย่าเข้าใจว่า FiveM จะโหลด Folder เหล่านี้อัตโนมัติเพียงเพราะตั้งชื่อ client, server หรือ shared
ผู้กำหนดจริงคือ
fxmanifest.lua
เช่น
shared_scripts {
'shared/config.lua'
}
client_scripts {
'client/main.lua',
'client/ui.lua'
}
server_scripts {
'server/database.lua',
'server/main.lua'
}
และหากใช้ Globbing
client_scripts {
'client/**/*.lua'
}
ต้องระวัง Load Order ของ Code ที่มี Dependency กัน
หลักง่าย ๆ คือ
Client-only
→ client/
Server authority
→ server/
ใช้ทั้งสองฝั่งและเปิดเผยได้
→ shared/
NUI
→ web/ หรือ html/
Game Assets
→ stream/
Database setup
→ sql/
Framework compatibility
→ bridge/
สำหรับ Developer ที่เรียนกับ comsiam เป้าหมายของการจัด Folder ไม่ใช่ทำให้ Resource ดูซับซ้อน แต่คือทำให้เปิด Project มาแล้วรู้ทันทีว่า Code ส่วนไหนทำหน้าที่อะไร และเมื่อเกิด Error สามารถไล่หาได้โดยไม่ต้องค้นไฟล์หลายหมื่นบรรทัด
หลักสำคัญจาก comsiam คือ อย่าสร้าง Folder เพิ่มเพียงเพื่อให้ Project ดูเป็นมืออาชีพ ให้แบ่ง Folder เมื่อมันสร้าง Boundary ที่ชัดเจนจริง ๆ ระหว่าง Client, Server, Shared, UI, Data และ Feature ต่าง ๆ
หัวข้อถัดไปคือ FiveM Resource Dependency คืออะไร ซึ่งจะต่อจากโครงสร้าง Resource โดยตรง และอธิบายว่า Resource A ต้องรอ Resource B อย่างไร, dependency/dependencies ทำงานอย่างไร, Start Order, /onesync, Minimum Server Build และวิธีแก้ปัญหา No such export หรือ Dependency Start ไม่ทัน
Comments
Post a Comment