FiveM Export คืออะไร ใช้อย่างไร

FiveM Export คือระบบที่ทำให้ Resource หนึ่งเปิด Function ของตัวเองให้ Resource อื่นเรียกใช้งานได้โดยตรง เหมาะสำหรับสร้าง API ระหว่าง Resource เช่น ตรวจ Job, ดึงข้อมูล Vehicle, เรียก Notification, ตรวจ Permission หรือเรียก Function ของระบบกลาง

ตัวอย่าง Resource my_core เปิด Export

exports('GetServerName', function()
    return 'My FiveM Server'
end)

Resource อื่นสามารถเรียก

local serverName =
    exports['my_core']:GetServerName()

print(serverName)

จุดสำคัญคือ Export ไม่ใช่ Network Event

Client Export ทำงานใน Client Context ส่วน Server Export ทำงานใน Server Context ดังนั้นไม่สามารถใช้ Client Export เพื่อเรียก Function บน Server โดยตรงแทน TriggerServerEvent() ได้

แนวคิดคือ

Resource A
↓
Export Function
↓
Resource B เรียกใช้

Export จึงเป็นเครื่องมือสำคัญสำหรับสร้าง Resource แบบ Modular และลดการเขียน Code ซ้ำ

① FiveM Export คืออะไร

Export คือ Function ที่ Resource หนึ่งประกาศให้ Resource อื่นสามารถเรียกใช้ได้

สมมติมี

resources/
├── my_core/
└── my_garage/

my_core มี Function ตรวจ Job

exports('GetJob', function(playerId)
    return 'police'
end)

จากนั้น my_garage สามารถเรียก

local job =
    exports['my_core']:GetJob(
        playerId
    )

ทำให้ Garage ไม่ต้องเขียนระบบ Job ซ้ำเอง

② ทำไม FiveM ต้องมี Export

สมมติ Server มี Resource หลายตัว

Core
Garage
Inventory
Jobs
Banking
Police
Phone

หากทุก Resource เขียน Function ซ้ำกันเอง จะเกิด

  • Code ซ้ำ

  • Logic ไม่ตรงกัน

  • แก้ไขหลายจุด

  • Maintain ยาก

Export ช่วยสร้าง Resource กลาง

Core Resource
↓
API / Export
↓
Garage
Banking
Police
Phone

Resource อื่นจึงใช้ Logic ชุดเดียวกันได้

③ วิธีสร้าง Export Lua แบบปัจจุบัน

Cfx.re รองรับการสร้าง Export โดยใช้ Global exports

ตัวอย่าง

exports(
    'SayHello',
    function(name)
        print(
            'Hello ' .. tostring(name)
        )
    end
)

หรือแบบสั้น

exports('SayHello', function(name)
    print('Hello ' .. tostring(name))
end)

ชื่อ

SayHello

คือชื่อ Export ที่ Resource อื่นจะใช้เรียก

④ วิธีเรียก Export จาก Resource อื่น

สมมติ Resource ที่สร้าง Export ชื่อ

hello_resource

Resource อื่นเรียก

exports['hello_resource']:SayHello(
    'John'
)

หรือในรูปแบบที่ชื่อ Resource รองรับ Dot Syntax

exports.hello_resource:SayHello(
    'John'
)

รูปแบบ Bracket อ่านชัดและเหมาะเมื่อชื่อ Resource มีอักขระที่ Dot Syntax ใช้ไม่สะดวก

⑤ Export Return ค่าได้ไหม

ได้

ตัวอย่าง

exports('GetPrice', function()
    return 500
end)

Resource อื่นเรียก

local price =
    exports['shop_core']:GetPrice()

print(price)

จะได้

500

นี่เป็นความแตกต่างสำคัญระหว่าง Export กับ Event หลายประเภท

Export สามารถใช้เหมือน Function Call และรับ Return Value ได้โดยตรงใน Context ที่รองรับ

⑥ Export รับ Parameter ได้ไหม

ได้

ตัวอย่าง

exports(
    'CalculatePrice',
    function(price, amount)
        return price * amount
    end
)

Resource อื่น

local total =
    exports['shop_core']:CalculatePrice(
        100,
        5
    )

print(total)

ผลคือ

500

⑦ Export รับหลาย Parameter ได้ไหม

ได้

exports(
    'CanBuy',
    function(money, price, amount)
        local total =
            price * amount

        return money >= total
    end
)

เรียก

local canBuy =
    exports['shop_core']:CanBuy(
        5000,
        100,
        2
    )

Export จึงสามารถสร้าง API ที่ซับซ้อนได้ตามต้องการ

⑧ Export Return Table ได้ไหม

ได้

ตัวอย่าง

exports('GetVehicleData', function()
    return {
        model = 'sultan',
        plate = 'ABC123',
        stored = true
    }
end)

Resource อื่น

local vehicle =
    exports['garage_core']:GetVehicleData()

print(vehicle.model)
print(vehicle.plate)

แต่ควร Return เฉพาะข้อมูลที่ Consumer ต้องใช้

⑨ Client Export คืออะไร

Client Export คือ Export ที่ถูกสร้างจาก Client Script

ตัวอย่าง client.lua

exports('GetPlayerCoords', function()
    local ped =
        PlayerPedId()

    return GetEntityCoords(ped)
end)

Client Resource อื่นสามารถเรียก

local coords =
    exports['player_core']:GetPlayerCoords()

Export นี้ทำงานใน Client Context

⑩ Server Export คืออะไร

Server Export คือ Export ที่ประกาศจาก Server Script

ตัวอย่าง

exports(
    'GetPlayerJob',
    function(playerId)
        -- server logic
        return 'police'
    end
)

Server Resource อื่นเรียก

local job =
    exports['player_core']:GetPlayerJob(
        playerId
    )

Export นี้ทำงานใน Server Context

⑪ Client Export เรียก Server Export โดยตรงได้ไหม

ไม่ควรมอง Export เป็นระบบข้าม Network

หลักคือ

Client Export
→ Client Context

Server Export
→ Server Context

ถ้า Client ต้องขอข้อมูล Server ต้องใช้กลไก เช่น

TriggerServerEvent
Callback
Network Event

ตาม Use Case

ไม่ใช่

Client
↓
Server Export โดยตรง

Export ไม่ได้มาแทน Client/Server Networking

⑫ Server Export เรียก Client Function ได้ไหม

ไม่โดยตรงในลักษณะ Network Call

ถ้า Server ต้องการให้ Client ทำสิ่งใด ใช้

TriggerClientEvent()

หรือระบบ Network/Callback ที่เหมาะสม

จำง่าย ๆ ว่า

Export
=
Resource ↔ Resource
ใน Runtime Context เดียวกัน

Network Event
=
Client ↔ Server

⑬ Export ต่างจาก Event อย่างไร

Export คล้าย Function Call

Resource A
↓
เรียก Export
↓
Resource B Function
↓
ได้ Return Value

Event มีลักษณะ

Resource A
↓
Trigger Event
↓
Listener ที่สนใจ

ตัวอย่าง Export

GetPlayerJob()

เหมาะกับการ “ขอข้อมูล”

ตัวอย่าง Event

player:jobChanged

เหมาะกับการ “แจ้งว่าเหตุการณ์เกิดขึ้น”

⑭ ควรใช้ Export หรือ Event

ใช้ Export เมื่อ Resource ต้องการ

เรียก Function
หรือ
ขอข้อมูล

เช่น

GetPlayerData
GetVehiclePrice
HasPermission
AddItem
ShowNotification

ใช้ Event เมื่อ

ต้องแจ้งเหตุการณ์

เช่น

vehicleStored
playerLoaded
jobChanged
transactionCompleted

ไม่มีคำตอบว่าตัวไหนดีกว่าเสมอไป

ต้องเลือกตาม Intent

⑮ Export ต่างจาก Callback อย่างไร

Export เหมาะมากกับ Resource ที่อยู่ Context เดียวกัน

เช่น

Server Resource A
↓
Server Export
↓
Server Resource B

Callback มักใช้กับ Async Request/Response โดยเฉพาะ

Client
↓
Server
↓
Response
↓
Client

ถ้า Server Resource A ต้องการข้อมูลจาก Server Resource B โดยตรง การใช้ Export มักง่ายกว่าส่ง Client/Server Callback วนไปมา

⑯ Export ต่างจาก TriggerEvent อย่างไร

TriggerEvent() ไม่ได้ Return Function Result โดยตรงในรูปแบบเดียวกับ Export

ตัวอย่าง

TriggerEvent(
    'core:getJob',
    playerId
)

เป็น Event Notification/Execution

แต่

local job =
    exports['core']:GetJob(
        playerId
    )

เป็น Direct Function API และได้ Result ทันทีตาม Function

จึงเหมาะกับคนละ Use Case

⑰ Export เหมาะกับ Core Resource อย่างไร

สมมติสร้าง Resource

my_core

มีระบบกลาง เช่น

Player
Permissions
Jobs
Notifications
Utilities

สามารถเปิด Export

GetPlayer
GetJob
HasPermission
Notify
GetIdentifier

Resource อื่นไม่ต้องรู้ Implementation ภายใน my_core

เพียงรู้ API ที่ Export เปิดไว้

นี่คือแนวคิด Encapsulation

⑱ Encapsulation คืออะไร

หมายถึงซ่อนรายละเอียดภายใน Resource

ตัวอย่าง Resource Garage ไม่จำเป็นต้องรู้ว่า Core เก็บ Job แบบไหน

Garage เรียกเพียง

local job =
    exports['my_core']:GetJob(
        playerId
    )

ถ้าวันหนึ่ง Core เปลี่ยน Database หรือ Internal Table

Garage อาจไม่ต้องเปลี่ยนเลย หาก Export Contract ยังเหมือนเดิม

นี่เป็นข้อดีของ API Design

⑲ Export ช่วยลด Dependency กับ Framework ได้ไหม

ได้ถ้าออกแบบ Adapter Resource

ตัวอย่าง Resource อื่นเรียก

exports['my_bridge']:GetPlayerJob(
    playerId
)

ภายใน my_bridge อาจเชื่อมกับ

ESX
QBCore
Qbox

เมื่อเปลี่ยน Framework อาจแก้เฉพาะ Bridge มากกว่าต้องแก้ Script ทุกตัว

แนวคิดนี้มีประโยชน์มากกับ Server ขนาดใหญ่

⑳ Export ใช้กับ ESX ได้ไหม

ได้

Resource สามารถเรียก Export ของ Resource/Framework ที่เปิดไว้

และ Resource ของคุณเองก็สามารถเปิด Export ให้ ESX Script อื่นเรียกได้

แต่ต้องตรวจ Documentation ของ ESX Version ที่ใช้งานจริง

อย่าเดาชื่อ Export จาก Script เก่า

㉑ Export ใช้กับ QBCore ได้ไหม

ได้

QBCore Resource จำนวนมากมี API, Exports หรือ Core Object Pattern ของตัวเอง

แนวคิด Export ของ FiveM ยังเหมือนเดิม

Resource
↓
เปิด Function
↓
Resource อื่นเรียก

แต่ชื่อ Function และ Contract ขึ้นอยู่กับ Framework/Resource

㉒ Export ใช้กับ Qbox ได้ไหม

ได้

Qbox และ Resource สมัยใหม่จำนวนมากพึ่ง

  • Exports

  • ox_lib

  • State Bags

  • Events

ร่วมกัน

Developer จึงควรเข้าใจว่า Export เหมาะกับ Direct API ส่วน Event เหมาะกับ Notification และ Callback เหมาะกับ Request/Response ข้าม Context

㉓ Export ใช้กับ ox_lib ได้ไหม

ได้

Resource สามารถใช้ Export และ ox_lib อยู่ใน Project เดียวกัน

แต่ ox_lib API หลายส่วนใช้ Global lib หรือ Module ของ Library มากกว่าการเรียกทุกอย่างผ่าน FiveM Export

ควรใช้ API ตาม Documentation ของ Library นั้นโดยตรง

อย่าบังคับทุก Dependency ให้ผ่าน Export หาก Library ออกแบบอีกแบบหนึ่ง

㉔ FiveM มี Export ตัวอย่างจริงไหม

มี

Resource มาตรฐานอย่าง chat มี Exports เช่นการเพิ่มข้อความหรือ Suggestion

ตัวอย่าง Lua สามารถพบ Pattern

exports.chat:addMessage({
    args = {
        'SYSTEM',
        'Hello'
    }
})

นี่เป็นตัวอย่าง Resource หนึ่งเรียก API ของอีก Resource โดยตรง

㉕ วิธีสร้าง Export ที่ Return Boolean

ตัวอย่าง

exports(
    'IsPlayerAllowed',
    function(playerId)
        return playerId ~= nil
    end
)

Resource อื่น

local allowed =
    exports['permission_core']:IsPlayerAllowed(
        playerId
    )

if not allowed then
    return
end

เหมาะกับ Permission Helper หรือ Validation Helper ที่อยู่ Server-side

㉖ วิธีสร้าง Export สำหรับ Notification

Client Resource

exports(
    'Notify',
    function(message)
        print(
            '[NOTIFY]',
            message
        )
    end
)

Resource Client อื่น

exports['my_notify']:Notify(
    'Vehicle stored'
)

Resource ที่เรียกไม่จำเป็นต้องรู้ว่า Notification ภายในใช้

  • NUI

  • Native

  • Scaleform

แบบไหน

㉗ Export สำหรับ Inventory

ตัวอย่างแนวคิด Server Export

exports(
    'HasItem',
    function(playerId, itemName, amount)
        -- inventory logic
        return true
    end
)

Resource Crafting เรียก

local hasItem =
    exports['inventory_core']:HasItem(
        playerId,
        'water',
        2
    )

ช่วยให้ Inventory เป็น Source of Truth เพียง Resource เดียว

㉘ Export สำหรับ Garage

Garage Core อาจเปิด

GetVehicle
IsVehicleOwned
StoreVehicle
GetGarage

Resource Police หรือ Mechanic สามารถใช้ API เหล่านี้แทนการ Query Database Garage โดยตรง

ช่วยลดการผูก Resource กับ Database Schema ของกันและกัน

㉙ ทำไมไม่ควรให้ทุก Resource Query Database ของกันและกัน

สมมติ Garage เปลี่ยน Table จาก

owned_vehicles

เป็น

player_vehicles

ถ้า Resource 20 ตัว Query Table นี้โดยตรง ทุกตัวต้องแก้

แต่ถ้าทั้งหมดเรียก

exports['garage']:GetVehicle(...)

Garage สามารถเปลี่ยน Database ภายในได้โดยคง API เดิม

นี่คือประโยชน์ของ Abstraction

㉚ Export สามารถเปลี่ยน Database Implementation ได้

ตัวอย่างเดิม

Resource A
↓
Garage Export
↓
MySQL

วันหนึ่งเปลี่ยนเป็น

Resource A
↓
Garage Export
↓
Cache + MySQL

Resource A ไม่จำเป็นต้องรู้

ตราบใดที่

GetVehicle()

ยัง Return Data ตาม Contract เดิม

㉛ Export API ควรตั้งชื่ออย่างไร

ควรเป็นชื่อ Function ที่เข้าใจง่าย

ตัวอย่าง

GetPlayer
GetPlayerJob
HasItem
AddItem
RemoveItem
GetVehicle
StoreVehicle
Notify

หลีกเลี่ยง

DoThing
Func1
Data2
X
Test

เพราะ Export คือ Public API ของ Resource

ชื่อควรบอกว่าทำอะไร

㉜ Export Name ตัวพิมพ์ใหญ่เล็กสำคัญไหม

ควรเรียกให้ตรงกับชื่อที่ประกาศ

ถ้าประกาศ

exports(
    'GetPlayerJob',
    function()
    end
)

ก็ควรเรียก

exports['core']:GetPlayerJob()

ไม่ควรเปลี่ยนเป็น

getplayerjob
GetplayerJob

โดยคิดว่า Runtime จะจับคู่ให้อัตโนมัติ

ใช้ Naming Convention เดียวทั้ง Project

㉝ Resource Name สำคัญไหม

สำคัญมาก

ถ้า Resource ชื่อ

my_core

ต้องเรียก

exports['my_core']:GetPlayer()

ถ้าเรียกผิดเป็น

exports['core']:GetPlayer()

จะไม่พบ Export ของ Resource ที่ต้องการ

จึงต้องรู้ชื่อ Folder/Resource จริง

㉞ Resource Name มี - ใช้อย่างไร

หากชื่อ Resource เช่น

my-core

รูปแบบ Bracket ชัดเจนกว่า

exports['my-core']:GetPlayer()

แทน Dot Syntax

จึงนิยมใช้

exports['resource-name']:ExportName()

ใน Code ที่ต้องการรองรับชื่อ Resource หลากหลาย

㉟ Export ไม่พบ แก้อย่างไร

ถ้า Error เกี่ยวกับ

No such export

หรือ Export ไม่พบ ให้ตรวจ

① Resource Name ถูกไหม
② Export Name ถูกไหม
③ Resource Start แล้วหรือยัง
④ Script ที่ประกาศ Export ถูกโหลดหรือยัง
⑤ Client/Server Context ตรงกันไหม
⑥ Dependency Start ก่อนหรือยัง
⑦ มี Error ก่อนประกาศ Export หรือไม่

อย่าแก้ Function ก่อนตรวจ Resource State

㊱ Export ต้องรอ Resource Start ไหม

ต้องให้ Resource ที่ให้ Export พร้อมทำงานก่อน Consumer เรียก

ตัวอย่าง

my_core
↓
สร้าง Export
↓
my_garage
↓
เรียก Export

ถ้า my_garage เริ่มก่อนและเรียก Export ตอน my_core ยังไม่พร้อม อาจเกิด Error ได้

จึงควรกำหนด Dependency/Start Order ให้ถูกต้อง

㊲ dependency ใน fxmanifest.lua ช่วยอย่างไร

Resource ที่ต้องพึ่ง Resource อื่นสามารถกำหนด Dependency

ตัวอย่าง

dependency 'my_core'

หรือหลายตัวตาม Manifest Syntax

ทำให้ Dependency ของ Resource ชัดเจนกว่าอาศัยลำดับ ensure อย่างเดียว

ตัวอย่าง Concept

my_garage
depends on
my_core

เมื่ออ่าน Resource ก็รู้ทันทีว่าต้องมี my_core

㊳ server.cfg ควรจัดลำดับอย่างไร

ตัวอย่าง

ensure my_core
ensure my_inventory
ensure my_garage

Resource ที่เป็น Core/Dependency ควรพร้อมก่อน Resource ที่เรียก API ของมัน

แต่สำหรับ Dependency ที่เป็นเงื่อนไขจริง ควรประกาศใน fxmanifest.lua ให้ชัดด้วย

ไม่ควรพึ่ง Delay แบบ

Wait(5000)

เพื่อหวังว่าอีก Resource จะพร้อม

㊴ อย่าใช้ Wait แก้ Export not found

ตัวอย่างที่ไม่ควรใช้เป็น Solution หลัก

Wait(10000)

exports['core']:GetPlayer()

เพราะไม่ได้แก้ Dependency จริง

Server ที่ Load ช้าอาจยัง Error

Server ที่ Load เร็วก็เสียเวลาโดยไม่จำเป็น

ควรแก้ Resource Dependency และ Lifecycle ให้ถูกต้อง

㊵ Runtime Export พร้อมเมื่อไร

Cfx.re ระบุว่าการใช้ Export แบบที่ประกาศผ่าน Manifest จะพร้อมใช้งานหลัง Scheduler Tick แรก

นี่เป็นอีกเหตุผลที่ Initialization และ Resource Start Order มีความสำคัญ

สำหรับ Resource ใหม่ Cfx.re แนะนำให้พิจารณา Runtime Registration เช่น

exports(
    'GetData',
    function()
    end
)

แทน Manifest-style Export ในหลายกรณี

㊶ export ใน fxmanifest.lua คืออะไร

FiveM Resource Manifest รองรับ

export 'FunctionName'

สำหรับ Client Export

ตัวอย่าง

client_script 'client.lua'

export 'GetData'

และ Function ต้องถูกประกาศในรูปแบบที่ Explicit Export สามารถเข้าถึงได้

อย่างไรก็ตาม Cfx.re แนะนำให้ใช้ Runtime exports('name', function...) หรือ Runtime Export API ของภาษาแทนใน Resource ใหม่เมื่อเหมาะสม

㊷ server_export คืออะไร

สำหรับ Server มี Manifest Directive

server_export 'FunctionName'

ตัวอย่าง

server_script 'server.lua'

server_export 'GetPlayer'

Resource อื่นฝั่ง Server สามารถเรียก Export นี้ได้เมื่อพร้อม

แต่เช่นเดียวกับ Client Export เอกสารปัจจุบันชี้ไปทาง Runtime Export API มากกว่า

㊸ Runtime Export ดีกว่า Manifest Export อย่างไร

ตัวอย่าง Runtime

exports(
    'GetPlayer',
    function(playerId)
        return players[playerId]
    end
)

ข้อดีคือ

  • Function อยู่ใกล้การประกาศ API

  • ไม่ต้องเพิ่มชื่อ Exportใน Manifest

  • เห็น Implementation ชัด

  • Local Function สามารถถูก Wrap ใน Export ได้ง่าย

ทำให้ Resource อ่านง่ายขึ้น

㊹ Manifest Export ยังใช้ได้ไหม

ยังมีอยู่ใน Resource Manifest ปัจจุบัน

ทั้ง

export
server_export

ยังถูก Document

แต่เอกสาร Cfx.re ระบุในส่วน export ว่าให้ลองใช้ Runtime exports('name', ..) หรือ Exports.Add แทน

ดังนั้น Resource ใหม่ควรเข้าใจ Runtime Export API เป็นหลัก

㊺ Export ใช้ Local Function ได้ไหม

Runtime Export ใช้ได้ดีมากกับ Local Function

ตัวอย่าง

local function getPrice(item)
    return 500
end

จากนั้น

exports(
    'GetPrice',
    getPrice
)

หรือ

exports(
    'GetPrice',
    function(item)
        return getPrice(item)
    end
)

Internal Function ยังสามารถเป็น Local ไม่ต้องเปิด Global ทั้งไฟล์

㊻ ทำไม Local Function ดี

ช่วยลด Global Namespace Pollution

แทนที่จะสร้าง

function GetPrice()
end

เป็น Global โดยไม่จำเป็น

สามารถใช้

local function getPrice()
end

แล้วเปิดเฉพาะ API ที่ต้องการผ่าน Export

ทำให้ Resource มี Encapsulation ดีขึ้น

㊼ Export ควรเปิดทุก Function ไหม

ไม่ควร

Resource อาจมี Internal Functions เช่น

validateInput
loadCache
saveInternalState
cleanupVehicle
parseDatabaseRow

ถ้า Resource อื่นไม่จำเป็นต้องใช้ ก็ไม่ควร Export

หลักคือ

Public API
→ Export

Internal Logic
→ Local Function

ช่วยลด Coupling

㊽ Export มากเกินไปมีข้อเสียอะไร

ถ้า Resource เปิด Export 100 ตัวโดยไม่มี Structure

Consumer อาจเริ่มพึ่ง Internal Behavior จำนวนมาก

วันหนึ่งแก้ Resource จะกระทบ Resource อื่นจำนวนมาก

จึงควรออกแบบ Public API ให้

  • เล็ก

  • ชัด

  • Stable

  • มี Contract

ไม่ใช่ Export ทุก Function เพราะทำได้

㊾ Export Contract คืออะไร

ควรกำหนดว่า Export

ชื่ออะไร
รับ Parameter อะไร
Return อะไร
ใช้ฝั่งไหน
Error แบบไหน

ตัวอย่าง

Export:
GetVehicle

Context:
Server

Arguments:
plate: string

Returns:
table | nil

Purpose:
คืนข้อมูลรถตามทะเบียน

Developer คนอื่นจะใช้ API ได้ถูกต้อง

㊿ Export Error ควร Return อย่างไร

ขึ้นอยู่กับ API

ตัวอย่างไม่พบ Vehicle

return nil

หรือ API ที่ต้องการ Response ชัดเจน

return {
    success = false,
    error = 'vehicle_not_found'
}

Project ใหญ่ควรกำหนด Convention เดียวกัน

เพื่อไม่ให้ Consumer ต้องเดาว่า

nil
false
{}

แต่ละอย่างหมายถึงอะไร

51 Export กับ Async Function

ต้องระวัง

Export Function ที่ทำ Async Operation เช่น Database Query อาจไม่ได้ Return Result แบบ Sync ตามที่ Caller คาด

ตัวอย่าง

Export
↓
Database Query
↓
Response มาทีหลัง

ในกรณีนี้อาจต้องใช้

  • Callback

  • Promise

  • Await

  • Async API

ตาม Runtime และ Library

อย่าคิดว่า Export ทุกตัว Return Database Result ได้ทันที

52 Export + Citizen.Await ใช้ได้ไหม

สามารถออกแบบ Export ที่ภายในใช้ Promise/Await ใน Context ที่รองรับได้

แนวคิด

Caller
↓
Export
↓
Promise
↓
Database Async
↓
Resolve
↓
Return

แต่ต้องเข้าใจ Coroutine และ Blocking/Yield Behavior ของ Runtime

อย่าใช้ Await ใน Export ที่ถูกเรียกถี่มากโดยไม่ดู Performance

53 Export Query Database ทุก Tick ดีไหม

ไม่

ตัวอย่าง

ทุก Tick
↓
GetPlayerData Export
↓
Database SELECT

เป็น Architecture ที่ไม่ดี

Export เป็นเพียง API Layer ไม่ได้ทำให้ Database Query ฟรี

ควรใช้

  • Cache

  • Memory State

  • Event-driven Updates

ตาม Use Case

54 Export มีผลกับ Performance ไหม

Function Call ผ่าน Export มี Overhead บ้างเมื่อเทียบกับ Local Function Call แต่ปัญหาใหญ่ส่วนมากอยู่ใน Logic ภายใน Export เช่น

  • Database

  • Loop

  • Native Calls

  • Serialization

  • Heavy Calculation

ดังนั้นอย่า Optimize โดยกลัว Export ก่อนดู Profiler และ Logic จริง

55 Export ใช้ทุก Frame ได้ไหม

บาง Export ที่เบามากอาจถูกใช้ได้ แต่ไม่ควรเรียก Cross-resource API จำนวนมากทุก Frameโดยไม่จำเป็น

ตัวอย่าง

ทุก Frame
↓
Export A
↓
Export B
↓
Export C
↓
Heavy Logic

อาจเพิ่ม Client CPU

ถ้าค่าคงเดิมนาน ควร Cache เมื่อเหมาะสม

56 Export กับ Security ต้องคิดอะไร

Server Export ไม่ได้เปิดให้ Client เรียกผ่าน Network โดยตรงเหมือน Network Event

จึงเหมาะกับ Server-to-Server API มาก

แต่ Security ยังต้องพิจารณา

  • Resource ไหนเรียกได้

  • Parameter ถูกต้องไหม

  • Export ทำ Action สำคัญอะไร

  • Caller สามารถส่งข้อมูลจาก Client เข้ามาต่อได้ไหม

ถ้า Export ถูกเรียกจาก Network Event Handler ต้อง Validate Client Input ก่อนหรือภายใน Business Logic ตาม Architecture

57 อย่าใช้ Export ข้าม Security Validation

ตัวอย่าง Server Event

RegisterNetEvent(
    'money:add',
    function(amount)
        exports['economy']:AddMoney(
            source,
            amount
        )
    end
)

แม้ AddMoney จะเป็น Server Export แต่ Code ยังอันตรายถ้า amount มาจาก Clientโดยไม่ Validate

Export ไม่ได้ทำให้ Client Input ปลอดภัย

Flow ที่ดีคือ

Client Request
↓
Server Validate
↓
Export
↓
Economy Action

58 Export ไม่ทำให้ Client เป็น Authority

ถ้า Client Resource เรียก Client Export

exports['hud']:SetMoney(
    9999999
)

อาจเปลี่ยนแค่ Display บน Client

แต่เงินจริงต้องอยู่ Server

ดังนั้น Client Export ไม่ควรถูกใช้แทน Server-side Economy Authority

59 Checklist ก่อนสร้าง FiveM Export

ก่อนเปิด Export ใหม่ ให้ถามว่า

① Resource อื่นต้องเรียก Function นี้จริงไหม?
② เป็น Client Export หรือ Server Export?
③ ต้องข้าม Network หรือไม่?
④ ถ้าต้องข้าม Network Export อาจไม่ใช่คำตอบหรือไม่?
⑤ Parameter มีอะไร?
⑥ Return Type คืออะไร?
⑦ Function เป็น Sync หรือ Async?
⑧ มี Database Query หรือไม่?
⑨ Export ถูกเรียกบ่อยแค่ไหน?
⑩ ควร Cache Result หรือไม่?
⑪ API Name ชัดหรือไม่?
⑫ Internal Function จำเป็นต้องเปิด Public จริงไหม?
⑬ Dependency ถูกประกาศหรือยัง?
⑭ Error Contract ชัดหรือไม่?

ถ้าตอบได้ครบ Export API จะดูแลง่ายขึ้นมาก

⑥⓪ ตัวอย่าง Export สำหรับมือใหม่

สร้าง Resource

simple_math

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

server_script 'server.lua'

server.lua

exports(
    'Add',
    function(a, b)
        return a + b
    end
)

สร้าง Resource อีกตัว

math_test

server.lua

RegisterCommand(
    'testadd',
    function()
        local result =
            exports['simple_math']:Add(
                10,
                20
            )

        print(
            'Result:',
            result
        )
    end,
    true
)

ผลคือ

Result: 30

Flow คือ

math_test
↓
exports['simple_math']
↓
Add(10, 20)
↓
simple_math
↓
return 30
↓
math_test

นี่เป็นตัวอย่างที่ดีสำหรับทำความเข้าใจ FiveM Export ก่อนนำไปใช้กับ Framework หรือ Database

คำถามที่พบบ่อยเกี่ยวกับ FiveM Export

FiveM Export คืออะไร

คือระบบสำหรับเปิด Function ของ Resource หนึ่งให้ Resource อื่นเรียกใช้งานได้

วิธีสร้าง Export Lua FiveM

ใช้

exports(
    'FunctionName',
    function(...)
        -- logic
    end
)

วิธีเรียก Export

ใช้

exports['resource_name']:FunctionName()

Export Return ค่าได้ไหม

ได้

Export รับ Parameter ได้ไหม

ได้

Export Return Table ได้ไหม

ได้

Client Export กับ Server Export ต่างกันอย่างไร

Client Export ทำงานฝั่ง Client ส่วน Server Export ทำงานฝั่ง Server

Client เรียก Server Export โดยตรงได้ไหม

ไม่ควรใช้ Export เป็น Network RPC หาก Client ต้องติดต่อ Server ต้องใช้ Event/Callback หรือระบบ Networking ที่เหมาะสม

Export ต่างจาก Event อย่างไร

Export เหมือน Direct Function API และสามารถ Return ค่า ส่วน Event เหมาะกับการแจ้งเหตุการณ์

Export ต่างจาก Callback อย่างไร

Export เหมาะกับ Resource-to-Resource API ใน Context เดียวกัน ส่วน Callback เหมาะกับ Request/Response โดยเฉพาะ Async และ Client↔Server

Export ใช้กับ ESX ได้ไหม

ได้ หาก Resource หรือ Framework เปิด Export ที่ต้องการ

Export ใช้กับ QBCore ได้ไหม

ได้

Export ใช้กับ Qbox ได้ไหม

ได้

export ใน fxmanifest.lua ยังใช้ได้ไหม

ยังมีใน Resource Manifest แต่เอกสาร Cfx.re แนะนำให้ลองใช้ Runtime exports('name', function...) หรือ Runtime Export API ของภาษาแทนใน Resource ใหม่

server_export ยังมีไหม

ยังมีใน Resource Manifest สำหรับ Server Export

Export not found แก้อย่างไร

ตรวจ Resource Name, Export Name, Client/Server Context, Resource State, Dependency และ Error ของ Resource ที่ประกาศ Export

Export Query Database ได้ไหม

ได้ฝั่ง Server แต่ต้องจัดการ Async และ Performance ให้ถูกต้อง

Export ปลอดภัยกว่า Network Event ไหม

Server Export ไม่ได้เปิด Network Interface ให้ Client ในรูปแบบเดียวกับ Net Event แต่ถ้าข้อมูลต้นทางมาจาก Client ยังต้อง Validate ก่อนทำ Action สำคัญ

สรุป FiveM Export คืออะไร ใช้อย่างไร

FiveM Export คือระบบสำหรับสร้าง Public Function API ให้ Resource อื่นเรียกใช้งานได้โดยตรง

รูปแบบที่แนะนำสำหรับ Lua คือ

exports(
    'GetData',
    function(...)
        return data
    end
)

Resource อื่นเรียก

local data =
    exports['resource_name']:GetData()

สิ่งที่ต้องจำคือ

Export
=
Resource → Resource API

ไม่ใช่

Client → Server Network RPC

Client Export และ Server Export อยู่คนละ Execution Context ถ้าต้องการสื่อสารข้าม Client/Server ต้องใช้ Network Event หรือ Callback ที่เหมาะสม

สำหรับ Resource ใหม่ Cfx.re รองรับและแนะนำ Runtime Export Registration เช่น

exports('GetData', function()
end)

ขณะที่ export และ server_export ใน fxmanifest.lua ยังคงมีอยู่ใน Resource Manifest ปัจจุบัน แต่ Runtime API มักทำให้ Code และ Public API อยู่ใกล้กันมากกว่า

สำหรับผู้ที่เรียน FiveM Developer กับ comsiam สิ่งที่ควรฝึกต่อจาก Syntax คือการออกแบบ Export ให้เป็น API ที่เล็ก ชัด และ Stable ไม่เปิด Internal Function ทุกอย่างออกมาโดยไม่จำเป็น

เมื่อออกแบบ Export ได้ดี Resource จะ Modular ขึ้น เปลี่ยน Database หรือ Framework ภายในได้ง่าย และ Resource อื่นไม่ต้องรู้ Implementation ทั้งหมด ซึ่งเป็นหลักสำคัญของ FiveM Server ขนาดใหญ่ที่ comsiam ต้องการให้เข้าใจก่อนเข้าสู่หัวข้อถัดไปคือ FiveM Native คืออะไร

Comments

Popular posts from this blog

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

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

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