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 แบบง่าย

ประเภทClientServerเหมาะกับ
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

Popular posts from this blog

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

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

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