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

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด