FiveM Server-side Entity คืออะไร? สร้างและจัดการ Entity ฝั่ง Server ด้วย OneSync

 FiveM Server-side Entity คือการที่ FXServer สามารถรับรู้ สร้าง ตรวจสอบ และจัดการ Entity เช่น Ped, Vehicle และ Object จาก Server Script ได้ผ่านระบบ OneSync/State Awareness โดยไม่จำเป็นต้องผลัก Logic ทุกอย่างไปให้ Client ทำเหมือนแนวทางเก่า

ตัวอย่าง Server สามารถหา Ped ของผู้เล่นได้

local ped =
    GetPlayerPed(source)

อ่านตำแหน่ง

local coords =
    GetEntityCoords(ped)

หรือสร้าง Vehicle จาก Server

local vehicle =
    CreateVehicleServerSetter(
        joaat('sultan'),
        'automobile',
        215.0,
        -810.0,
        30.0,
        157.0
    )

จากนั้น Server ยังสามารถกำหนด

Routing Bucket
State Bag
Orphan Mode
Mission State
Database Association

ให้ Entity ตาม Architecture ของ Resource

แต่สิ่งสำคัญคือ Server-side Entity ใน FiveM ไม่ได้หมายความว่า Server จำลอง Physics และเป็น Network Owner ของ Entity ทุกตัวตลอดเวลา

FiveM ยังใช้ระบบ Network Ownership ซึ่ง Client สามารถเป็นผู้ Simulation Entity และ Ownership สามารถเปลี่ยนได้

ดังนั้น Developer ต้องเข้าใจความแตกต่างระหว่าง

Server-created Entity
Server Entity Handle
Network ID
Network Owner
Server Authority

ให้ชัดเจน

① FiveM Server-side Entity คืออะไร

ใน OneSync State Awareness Server สามารถมี Entity Handle สำหรับ Networked Entity ได้

เอกสาร Cfx.re ระบุว่า Entity Handle ฝั่ง Serverใช้งานได้เมื่อ State Awareness ไม่ได้ถูก Disable

ดังนั้น Server สามารถทำงานกับ

Ped
Vehicle
Object
Entity

ผ่าน Server-side Natives ที่รองรับ

② เมื่อก่อนทำไม Entity Logic มักอยู่ Client

FiveM รุ่นเก่ามี Game Interaction จำนวนมากที่ต้องทำ Client-side

เช่น

Spawn Vehicle
หา Ped
อ่าน Coordinates
จัดการ Physics

แต่ OneSync เพิ่ม Server-side Synchronization State ทำให้ Server สามารถรับรู้ Entity State และใช้ Native ฝั่ง Serverได้มากขึ้น

นี่เปิดทางให้ Script ออกแบบแบบ Server-authoritative ได้ดีขึ้น

③ Server-side Entity ต้องใช้ OneSync ไหม

สำหรับ Server-side State Awareness ต้องใช้ OneSync

server.cfg

set onesync on

และ Resource ที่จำเป็นต้องใช้สามารถประกาศ

dependency '/onesync'

ใน fxmanifest.lua

เพื่อบอกว่า Resource ต้องการ State Awareness

④ Server-side Entity Handle คืออะไร

เป็น Handle ที่ Server Runtime ใช้อ้างถึง Entity

ตัวอย่าง

local ped =
    GetPlayerPed(source)

ped คือ Server-side Ped Handle

สามารถใช้กับ Server Native ที่รองรับ เช่น

local coords =
    GetEntityCoords(ped)

แต่ Handle ไม่ควรถูกมองเป็น Permanent ID

⑤ Server Entity Handle กับ Client Handle เหมือนกันไหม

ไม่ควรคาดหวังว่าเหมือนกัน

Entity เดียวกันอาจมี

Server Handle = X

Client A Handle = Y

Client B Handle = Z

หากต้องอ้าง Network Entity ข้าม Machines ควรใช้

Network ID

แทนการส่ง Local Handle ไปมา

⑥ Network ID ยังจำเป็นอยู่ไหม

จำเป็น

Entity Handle เหมาะกับ Runtime Context นั้น

แต่ Network ID ใช้บอกว่า

กำลังพูดถึง Network Entity ตัวไหน

ตัวอย่าง

local netId =
    NetworkGetNetworkIdFromEntity(
        vehicle
    )

แล้วสามารถส่ง Net ID ไปยัง Client ที่เกี่ยวข้อง

⑦ Server หา Player Ped ได้อย่างไร

ใช้

local ped =
    GetPlayerPed(
        source
    )

ตัวอย่าง Server Event

RegisterNetEvent(
    'job:complete',
    function()

        local src =
            source

        local ped =
            GetPlayerPed(src)

        if ped == 0 then
            return
        end
    end
)

source คือ Player Server ID

ส่วน ped คือ Entity

เป็นคนละ Identifier

⑧ Server อ่าน Coordinates ได้อย่างไร

หลังมี Ped

local coords =
    GetEntityCoords(ped)

สามารถใช้ตรวจ

  • Mission

  • Shop

  • Garage

  • Job

  • Interaction

  • Security

ได้

เช่นตรวจ Player อยู่ใกล้จุดส่งงานหรือไม่

⑨ Server Position Validation สำคัญอย่างไร

สมมติ Client ส่ง

TriggerServerEvent(
    'delivery:finish'
)

Server ไม่ควรเชื่อว่า Player อยู่จุด Delivery จริง

Server ตรวจเองได้

local ped =
    GetPlayerPed(src)

if ped == 0 then
    return
end

local coords =
    GetEntityCoords(ped)

if #(coords - finishCoords)
    > 10.0 then

    return
end

นี่เป็น Use Case สำคัญของ Server-side Entity State

⑩ Client Coordinates ยังต้องส่งไหม

ในหลาย Security-sensitive Use Cases ไม่จำเป็น

ไม่ควรออกแบบ

TriggerServerEvent(
    'job:finish',
    GetEntityCoords(
        PlayerPedId()
    )
)

แล้ว Serverเชื่อ Coordinates จาก Client

เมื่อ Server สามารถหาตำแหน่ง Player จาก Server-side Entity State ได้เอง ก็ควรใช้ข้อมูลนั้นเป็น Validation Source

⑪ Server สร้าง Vehicle ได้ไหม

ได้

OneSync รองรับ Server-created Vehicles

ตัวอย่างที่ Cfx.re แนะนำ

local vehicle =
    CreateVehicleServerSetter(
        joaat('blista'),
        'automobile',
        2204.795,
        -887.9213,
        1461.224,
        90.0
    )

Entity จะถูก Register กับ Server

⑫ CreateVehicleServerSetter คืออะไร

เป็น Server Setter สำหรับสร้าง Vehicle จาก Server

รูปแบบหลักคือ

CreateVehicleServerSetter(
    modelHash,
    type,
    x,
    y,
    z,
    heading
)

ข้อดีสำคัญคือสามารถสร้าง Server Entity แม้ยังไม่มี Player อยู่ใกล้ใน OneSync Focus Range

⑬ Vehicle Type ต้องระบุอะไร

Parameter type บอกประเภท Vehicle เช่น

automobile
bike
boat
heli
plane
submarine
trailer
train

ตาม Native/Vehicle Type ที่รองรับ

ตัวอย่างรถทั่วไป

CreateVehicleServerSetter(
    joaat('sultan'),
    'automobile',
    x,
    y,
    z,
    heading
)

ต้องเลือก Type ให้ตรงกับ Vehicle ที่สร้าง

⑭ CreateVehicle กับ CreateVehicleServerSetter ต่างกันอย่างไร

นี่สำคัญมาก

Server-side CreateVehicle() บางรูปแบบทำงานเป็น RPC Native

Concept คือ

Server
↓
เรียก CreateVehicle
↓
RPC
↓
Client ที่เหมาะสม
↓
สร้าง Entity

ส่วน CreateVehicleServerSetter() เป็น Server Setter ที่ Register Entity กับ Server โดยตรง

⑮ RPC Entity Creation มีข้อจำกัดอะไร

Cfx.re ระบุว่า Server RPC Entity Creation ต้องมี Player อยู่ใกล้ภายใน OneSync Focus Distance

และสามารถล้มเหลวได้ เช่น

Client ที่ถูกเลือก Disconnect
ไม่มี Player ใกล้
RPC ไม่สำเร็จ

ดังนั้นผลลัพธ์ต้องถูกตรวจเสมอ

⑯ ตัวอย่างตรวจ RPC Creation

Concept

local vehicle =
    CreateVehicle(
        joaat('adder'),
        x,
        y,
        z,
        heading,
        true,
        true
    )

if vehicle == 0 then
    print(
        'Vehicle creation failed'
    )

    return
end

หลังจากนั้นอาจต้องรอ Entity Existence พร้อม Timeout

⑰ อย่า Wait Entity แบบไม่มี Timeout

ไม่ควร

while not DoesEntityExist(
    vehicle
) do
    Wait(50)
end

เพราะถ้า Creation Fail Thread จะรอไม่จบ

ควรใช้

local timeout =
    GetGameTimer() + 10000

while not DoesEntityExist(
    vehicle
)
and GetGameTimer() <
    timeout do

    Wait(50)
end

แล้วตรวจอีกครั้ง

⑱ Server Setter รับประกันอะไร

เอกสาร Migration ปัจจุบันระบุ Server Setter เช่น

CREATE_VEHICLE_SERVER_SETTER
CREATE_PED
CREATE_OBJECT_NO_OFFSET

สามารถ Register Entity กับ Server ได้ทันที

แม้ยังไม่มี Player อยู่ใกล้

แต่ Entity ที่สร้างในสถานการณ์นี้อาจเริ่มเป็น

Orphaned Entity

⑲ Orphaned Entity คืออะไร

คือ Server-created Entity ที่ยังไม่มี Client เหมาะสมอยู่ใน Scope เพื่อ Simulation

Concept

Server
↓
Create Entity
↓
ไม่มี Client อยู่ใกล้
↓
Entity registered
↓
Owner = -1
↓
Orphaned

เมื่อ Player เข้า Scope จึงสามารถมี Client Owner ได้

⑳ วิธีตรวจ Entity Owner ฝั่ง Server

ใช้

local owner =
    NetworkGetEntityOwner(
        entity
    )

สำหรับ Orphaned Entity เอกสารปัจจุบันระบุว่าอาจได้

-1

เพราะยังไม่มี Client Owner

㉑ Owner -1 หมายถึง Entity ไม่มีจริงไหม

ไม่

นี่เป็นจุดสำคัญ

Server Setter สามารถ Register Entity กับ Serverได้แล้ว

แต่ยังไม่มี Client Simulation

ดังนั้น

owner == -1

ไม่จำเป็นต้องหมายถึง Entity Creation Failed

ต้องแยก

Server Entity Registration

ออกจาก

Client Simulation Ownership

㉒ Server Getter ใช้กับ Orphaned Entity ได้ไหม

เอกสาร Cfx.re ระบุว่า Server-side Getter บางตัวสามารถใช้กับ Orphaned Entity ได้

เช่นอ่าน Server State

แต่ RPC Native ที่ต้องส่งไป Client Owner อาจยังใช้ไม่ได้

เพราะไม่มี Owner

㉓ ตัวอย่าง RPC Native ที่อาจใช้ไม่ได้ตอน Orphaned

สมมติ Server สร้างรถในพื้นที่ไม่มี Player

จากนั้นเรียก RPC Setter ที่ต้อง Execute ฝั่ง Client

Server
↓
SetVehicleColours
↓
ต้องมี Client Owner
↓
ไม่มี
↓
อาจไม่ทำงาน

นี่เป็นสาเหตุของ Bug ที่ Developer พบได้บ่อย

㉔ วิธี Initialize Entity ที่ยังไม่มี Owner

Cfx.re แนะนำ Pattern ที่น่าสนใจคือ

Server Setter
↓
Create Entity
↓
Set State Bag
↓
Player เข้า Scope
↓
Client ได้ Ownership
↓
State Bag Handler
↓
Owner ทำ Local Initialization

ช่วยหลีกเลี่ยงการยิง RPC Native ก่อน Entity มี Owner

㉕ ตัวอย่าง Initialization State

Server

Entity(vehicle).state:set(
    'vehicle:needsInit',
    true,
    true
)

Client ใช้

AddStateBagChangeHandler(
    'vehicle:needsInit',
    nil,
    function(
        bagName,
        key,
        value
    )

        if not value then
            return
        end

        -- resolve entity
        -- check owner
        -- initialize

    end
)

นี่เป็น Pattern ที่เหมาะกับ Ownership Migration

㉖ Server สร้าง Ped ได้ไหม

ได้

OneSync Documentation แสดงการสร้าง Ped จาก Server

local ped =
    CreatePed(
        4,
        joaat('a_m_y_acult_01'),
        x,
        y,
        z,
        heading,
        true,
        true
    )

จากนั้นสามารถใช้ Server-side Entity Lifecycle APIs ต่อ

㉗ Server สร้าง Object ได้ไหม

ได้ตาม Server-side Native ที่รองรับ เช่น Server Setter สำหรับ Object

Use Cases เช่น

  • Mission Prop

  • Crate

  • Barrier

  • World Object

  • Interactive Object

จากนั้นควรจัด

Routing Bucket
State
Persistence
Cleanup

ให้ Entity ด้วย

㉘ Server-created Entity ดีกว่า Client-created เสมอไหม

ไม่เสมอ

Local Visual Entity เช่น

Preview Object
Temporary UI Prop
Client-only Effect

อาจเหมาะให้ Client สร้าง

แต่ Entity ที่มีผลกับ Multiplayer Gameplay เช่น

Mission Vehicle
Reward Vehicle
Mission Ped
Shared Object

มักได้ประโยชน์จาก Server-controlled Creation

㉙ Server-created Entity ช่วย Security อย่างไร

แทนให้ Clientทำ

ฉัน Spawn Vehicle แล้ว
เชื่อฉัน

เปลี่ยนเป็น

Client:
ขอ Vehicle

Server:
มีสิทธิ์ไหม?
Mission ถูกไหม?
Model ถูกไหม?
Cooldown ผ่านไหม?
↓
Server Create

ช่วยลด Client Authority

แต่ไม่ได้ทำให้ Entity Security สมบูรณ์โดยอัตโนมัติ

㉚ อย่าให้ Client ส่ง Model อะไรก็ได้

ตัวอย่าง Event ที่เสี่ยง

RegisterNetEvent(
    'vehicle:spawn',
    function(model)

        local vehicle =
            CreateVehicleServerSetter(
                joaat(model),
                'automobile',
                ...
            )
    end
)

ถ้าไม่มี Allowlist Client อาจพยายาม Spawn Model ที่ไม่ควรได้รับ

ควรใช้ Server-side Allowlist

㉛ ตัวอย่าง Model Allowlist

local AllowedVehicles = {
    sultan = 'automobile',
    blista = 'automobile'
}

Server

local vehicleType =
    AllowedVehicles[model]

if not vehicleType then
    return
end

แล้วจึง Spawn

Client ขอ Action

Server ตัดสินว่ารุ่นไหนอนุญาต

㉜ Entity Lockdown ช่วยได้ไหม

ช่วย

Routing Bucket สามารถตั้ง

SetRoutingBucketEntityLockdownMode(
    bucket,
    'strict'
)

เพื่อไม่ให้ Client สร้าง Entity ใน Bucket นั้น

จากนั้นใช้ Pattern

Client Request
↓
Server Validate
↓
Server Create

ช่วยลด Client-created Entity Abuse

㉝ strict เหมาะกับ Server ทุกแบบไหม

ไม่

Resource เก่าบางตัวพึ่ง Client Spawn

เช่น

Vehicle
Ped
Object

ถ้าเปิด strict อาจทำให้ Resource พัง

ต้อง Audit Resource ก่อนใช้ Entity Lockdown ที่เข้มงวด

㉞ Server Entity กับ Routing Bucket

Server-created Entity สามารถถูกย้ายไป Bucket

SetEntityRoutingBucket(
    vehicle,
    bucket
)

ตัวอย่าง Mission

Player
Bucket 100

Vehicle
Bucket 100

NPC
Bucket 100

Object
Bucket 100

ทุก Entity อยู่ Instance เดียวกัน

㉟ ตรวจ Entity Bucket

ใช้

local bucket =
    GetEntityRoutingBucket(
        entity
    )

มีประโยชน์กับ Debug

เช่น Player ไม่เห็น Vehicle เพราะ

Player Bucket = 100

Vehicle Bucket = 0

㊱ Server Entity กับ State Bag

Server สามารถผูก Custom State กับ Entity

Entity(vehicle).state:set(
    'fuel',
    100,
    true
)

หรือ

Entity(vehicle).state:set(
    'mission:id',
    missionId,
    true
)

Relevant Clients สามารถอ่าน State ตาม OneSync Replication Policy

㊲ ทำไม State Bag เหมาะกับ Server Entity

เพราะ Entity Ownership อาจเปลี่ยน

แต่ State ถูกผูกกับ Network Entity

ตัวอย่าง

Vehicle
↓
fuel = 80
mission:id = 15
↓
Owner A
↓
Ownership Migration
↓
Owner B

Custom State ไม่ต้องผูกกับ Local Variable ของ Owner A คนเดียว

㊳ Server Entity กับ Network ID

Serverสามารถหา Network ID

local netId =
    NetworkGetNetworkIdFromEntity(
        vehicle
    )

แล้วส่งให้ Client

TriggerClientEvent(
    'mission:setVehicle',
    src,
    netId
)

Client Resolve เมื่อ Entity อยู่ใน Scope

㊴ อย่าส่ง Server Entity Handle ไปใช้เป็น Client Handle

ถึงแม้บางเลขอาจบังเอิญดูใช้งานได้ในบาง Context ก็ไม่ควรออกแบบแบบนั้น

ควร

Server Entity Handle
↓
Network ID
↓
Client
↓
Local Entity Handle

เพราะ Handle เป็น Context-specific Reference

㊵ Client Resolve Net ID อย่างไร

if not NetworkDoesEntityExistWithNetworkId(
    netId
) then
    return
end

local vehicle =
    NetToVeh(netId)

if not DoesEntityExist(vehicle) then
    return
end

ถ้า Entity ยังไม่อยู่ Scope อาจต้องรอพร้อม Timeout

㊶ Server Delete Entity ได้ไหม

Server สามารถใช้ Server-side Entity APIs ที่รองรับ เช่น

if DoesEntityExist(entity) then
    DeleteEntity(entity)
end

เหมาะกับ Server-owned Mission Lifecycle

แต่ต้องระวังว่า Entity Handle ยัง Valid หรือไม่ก่อน Delete

㊷ Entity Cleanup สำคัญแค่ไหน

สำคัญมาก

Server-created Entities ที่ไม่ Cleanup สามารถสะสมเป็น

  • Vehicles

  • Peds

  • Objects

จนเพิ่ม

  • Network Load

  • Memory

  • Entity Density

  • Client Streaming Cost

ทุกระบบ Spawn ควรมี Delete Strategy

㊸ Mission Entity Lifecycle ควรเป็นอย่างไร

Mission Start
↓
Server Create Entities
↓
Store Entity References
↓
Mission Active
↓
Mission Complete / Cancel
↓
Delete Entities
↓
Clear State

อย่าสร้าง Entity แล้วลืม

㊹ เก็บ Server Entity Handle ใน Table ได้ไหม

ได้ใน Runtime

local Missions = {}

Missions[id] = {
    vehicle = vehicle
}

ตอนใช้ควรตรวจ

local entity =
    Missions[id].vehicle

if not DoesEntityExist(entity) then
    return
end

และ Clear เมื่อ Entity Lifecycle จบ

㊺ เก็บ Entity Handle ใน Database ได้ไหม

ไม่ควร

Entity Handle เป็น Runtime Reference

Database ควรเก็บ Persistent Data เช่น

vehicle_id
plate
model
coords
owner
properties

แล้วสร้าง Runtime Entity ใหม่เมื่อต้องการ

㊻ เก็บ Network ID ใน Database ได้ไหม

ก็ไม่ควรใช้เป็น Permanent Identity

Network ID สามารถถูก Reuse หลัง Entity เดิมหมด Lifetime

จึงเหมาะกับ

Runtime cross-machine reference

ไม่ใช่

Permanent database key

㊼ SetEntityOrphanMode คืออะไร

ใช้กำหนด Orphan Mode ของ Server-created Entity

ตัวอย่าง

SetEntityOrphanMode(
    vehicle,
    2
)

Mode 2 คือ KeepEntity ตาม OneSync Documentation

ช่วยให้ Server ไม่ Delete Entity เพียงเพราะไม่มี Client Owner

㊽ KeepEntity คือ Persistence ไหม

เป็น Runtime Persistence ในความหมายหนึ่ง

แต่ไม่ใช่ Database Persistence

KeepEntity
≠
อยู่หลัง FXServer Restart

ถ้าต้องการ Persistence ข้าม Restart ต้องใช้ Persistent Storage แล้ว Respawn Entity

㊾ Client ยัง Delete KeepEntity ได้ไหม

เอกสาร OneSync ระบุว่า KeepEntity รับประกันว่า Server จะไม่ลบ Entity จากเหตุ Orphan Lifecycle

แต่ Client ยังสามารถ Request การ Delete Entity ได้ใน Networking Model ที่อนุญาต

ดังนั้นหากต้องการ Security เพิ่มต้องพิจารณา

  • Entity Lockdown

  • Control Filtering

  • Server Validation

  • Resource Design

ร่วมด้วย

㊿ Network Ownership ยังมีผลกับ Server Entity ไหม

มี

Server-created ไม่ได้หมายความว่า Serverเป็น Network Owner ตลอดเวลา

เมื่อ Client เข้ามาใน Scope Client สามารถรับ Network Simulation Ownership

และ Ownership สามารถ Migration ได้

นี่เป็น Behavior หลักของ OneSync

51 Server-side Entity Authority คืออะไร

ควรแยกสองคำ

Network Ownership

กับ

Business Authority

แม้ Client A เป็น Network Owner ของ Vehicle

Server ยังสามารถเป็น Authority ว่า

ใครเป็นเจ้าของรถ
รถขายได้ไหม
รถเป็น Mission Vehicle ไหม
Reward เท่าไร

ได้

นี่คือ Architecture ที่เหมาะกับ FiveM

52 Server Entity กับ ESX

ESX สามารถใช้ Server Entity APIs ร่วมกับ Framework Data ได้

ตัวอย่าง

ESX Player
↓
Database Vehicle Ownership
↓
Server Entity
↓
Network ID
↓
State Bag

Framework จัด Business State

OneSync จัด Multiplayer Entity State

53 Server Entity กับ QBCore

หลักเดียวกัน

QBCore Player/Vehicle Data สามารถใช้ร่วมกับ

Server-created Vehicle
Routing Bucket
State Bags
Network ID

ได้

อย่าคิดว่า Framework API ต้องแทน FiveM Core Entity APIs ทุกอย่าง

54 Server Entity กับ Qbox

Qbox ก็ใช้แนวคิดเดียวกัน

Developer ที่เข้าใจ Core Entity Architecture จะสามารถสร้าง Bridge Layer

Framework
↓
Business State

OneSync
↓
Entity State

ได้ง่ายกว่า

55 Server Entity Performance ต้องระวังอะไร

อย่าทำ Server Thread แบบ

while true do
    Wait(0)

    for _, entity in pairs(
        thousandsOfEntities
    ) do

        GetEntityCoords(entity)
    end
end

Server-side Entity API ก็มี Cost

ควรออกแบบ

  • Event-driven

  • Reasonable intervals

  • Spatial logic

  • Cached State

  • Cleanup

ตาม Requirement

56 Server-created Entity จำนวนมากได้ไม่จำกัดไหม

ไม่

แม้ OneSync มี Server-side Streaming และ Object ID Space ขนาดใหญ่ แต่ Game Clients ยังมี Streaming/Entity Limits

Cfx.re เตือนว่า Entities จำนวนมากเกินไปใน Range เดียวสามารถทำให้ Clients มีปัญหาหรือ Timeout ได้

ดังนั้น

Server-created
≠
Unlimited entities

57 RPC Native ต้องระวังอะไร

จำสามข้อ

① RPC Native อาจต้องมี Client Owner
② RPC Calls สามารถล้มเหลว
③ Ownership เปลี่ยนได้

ดังนั้น Critical Initialization ไม่ควรพึ่ง RPC Call หนึ่งครั้งแล้วไม่ตรวจผล

Server Setter + State Bag + Owner Handler อาจ Robust กว่าในหลาย Use Cases

58 วิธี Debug Server-side Entity

เริ่มจาก Print

print(
    'entity:',
    entity
)

print(
    'exists:',
    DoesEntityExist(entity)
)

print(
    'owner:',
    NetworkGetEntityOwner(entity)
)

print(
    'bucket:',
    GetEntityRoutingBucket(entity)
)

หา Network ID

local netId =
    NetworkGetNetworkIdFromEntity(
        entity
    )

print(
    'netId:',
    netId
)

แล้วตรวจ State Bags และ Client Scope ต่อ

59 Checklist ก่อนใช้ Server-side Entity

ตรวจอย่างน้อย

① OneSync เปิดหรือยัง?
② Resource ต้องประกาศ /onesync หรือไม่?
③ Entity ควรสร้าง Server หรือ Client?
④ ถ้า Server สร้าง ใช้ Server Setter หรือ RPC?
⑤ RPC Creation สามารถ Fail หรือไม่?
⑥ ตรวจ entity == 0 หรือยัง?
⑦ มี Timeout ตอน Wait Entity หรือไม่?
⑧ Entity เป็น Orphaned ได้ไหม?
⑨ Owner == -1 ถูก Handle หรือไม่?
⑩ Initialization ต้องใช้ Client Owner หรือไม่?
⑪ ใช้ State Bag ช่วยได้ไหม?
⑫ Entity อยู่ Routing Bucket ไหน?
⑬ Entity ต้อง KeepEntity หรือไม่?
⑭ Entity มี Network ID หรือไม่?
⑮ Client ได้รับ Net ID หรือ Handle?
⑯ Security ตรวจ Model/Permission หรือไม่?
⑰ Client สามารถ Spawn Entity เองได้หรือไม่?
⑱ ต้องใช้ Entity Lockdown หรือไม่?
⑲ Entity Cleanup เมื่อไร?
⑳ Entity Handle ถูกเก็บใน Database หรือไม่?
㉑ Network ID ถูกใช้เป็น Persistent ID หรือไม่?
㉒ Entity Density สูงเกินไปหรือไม่?
㉓ Server Loop Entity หนักเกินไปหรือไม่?

⑥⓪ ตัวอย่าง Server-side Mission Vehicle แบบสมบูรณ์

server.lua

local MissionVehicles = {}

local AllowedVehicles = {
    sultan = 'automobile',
    blista = 'automobile'
}

RegisterNetEvent(
    'mission:createVehicle',
    function(model)

        local src =
            source

        if type(model) ~= 'string' then
            return
        end

        local vehicleType =
            AllowedVehicles[model]

        if not vehicleType then
            return
        end

        if MissionVehicles[src] then
            local oldVehicle =
                MissionVehicles[src]

            if DoesEntityExist(
                oldVehicle
            ) then
                return
            end

            MissionVehicles[src] =
                nil
        end

        local ped =
            GetPlayerPed(src)

        if ped == 0 then
            return
        end

        local coords =
            GetEntityCoords(ped)

        local heading =
            GetEntityHeading(ped)

        local bucket =
            GetPlayerRoutingBucket(src)

        local vehicle =
            CreateVehicleServerSetter(
                joaat(model),
                vehicleType,
                coords.x + 5.0,
                coords.y,
                coords.z,
                heading
            )

        if vehicle == 0 then
            return
        end

        SetEntityRoutingBucket(
            vehicle,
            bucket
        )

        SetEntityOrphanMode(
            vehicle,
            2
        )

        Entity(vehicle).state:set(
            'mission:vehicle',
            true,
            true
        )

        Entity(vehicle).state:set(
            'mission:owner',
            src,
            true
        )

        MissionVehicles[src] =
            vehicle

        local netId =
            NetworkGetNetworkIdFromEntity(
                vehicle
            )

        TriggerClientEvent(
            'mission:vehicleCreated',
            src,
            netId
        )
    end
)

client.lua

RegisterNetEvent(
    'mission:vehicleCreated',
    function(netId)

        if type(netId) ~= 'number' then
            return
        end

        local timeout =
            GetGameTimer() + 5000

        while not
            NetworkDoesEntityExistWithNetworkId(
                netId
            ) do

            if GetGameTimer() >
                timeout then

                return
            end

            Wait(0)
        end

        local vehicle =
            NetToVeh(netId)

        if not DoesEntityExist(
            vehicle
        ) then
            return
        end

        print(
            'Mission vehicle ready:',
            vehicle
        )
    end
)

Cleanup

local function removeMissionVehicle(
    src
)

    local vehicle =
        MissionVehicles[src]

    if not vehicle then
        return
    end

    MissionVehicles[src] =
        nil

    if DoesEntityExist(
        vehicle
    ) then

        DeleteEntity(
            vehicle
        )
    end
end

AddEventHandler(
    'playerDropped',
    function()

        removeMissionVehicle(
            source
        )

    end
)

Flow ของระบบคือ

Client
↓
ขอ Mission Vehicle
↓
Server Validate Model
↓
Server หา Player Position
↓
Server หา Routing Bucket
↓
CreateVehicleServerSetter
↓
Set Bucket
↓
KeepEntity
↓
Set State Bags
↓
Network ID
↓
Client
↓
Resolve Local Vehicle

นี่เป็นพื้นฐาน Server-side Entity Architecture ที่สามารถต่อยอดได้อีกมาก

คำถามที่พบบ่อยเกี่ยวกับ FiveM Server-side Entity

FiveM Server-side Entity คืออะไร

คือการที่ Server สามารถรับรู้และจัดการ Entity Handles/State เช่น Ped, Vehicle และ Object ผ่าน OneSync State Awareness

ต้องเปิด OneSync ไหม

ต้องใช้ State Awareness สำหรับ Server-side Entity Features ที่เกี่ยวข้อง

Server หา Player Ped ได้ไหม

ได้ เช่น

GetPlayerPed(source)

Server หา Coordinates ได้ไหม

ได้

GetEntityCoords(
    ped
)

ตาม Server-side API ที่รองรับ

Server สร้าง Vehicle ได้ไหม

ได้

วิธีที่แนะนำคืออะไร

สำหรับ Server Setter ใช้ API เช่น

CreateVehicleServerSetter(...)

ใน Use Case ที่เหมาะสม

Server CreateVehicle() กับ Server Setter เหมือนกันไหม

ไม่ Server CreateVehicle() สามารถเป็น RPC Creation ซึ่งต้องพึ่ง Client ใกล้และสามารถล้มเหลวได้ ส่วน Server Setter Register Entity กับ Serverโดยตรง

RPC Native คืออะไร

คือ Native ที่ Server เรียกแต่ Operation ถูกส่งไปทำบน Client ซึ่งโดยทั่วไปเป็น Client Owner

RPC Native สำเร็จเสมอไหม

ไม่ Cfx.re ระบุว่าสามารถ Fail ได้

Orphaned Entity คืออะไร

Server-created Entity ที่ยังไม่มี Client อยู่ใน Scope รับ Simulation Ownership

NetworkGetEntityOwner คืน -1 หมายถึงอะไร

สำหรับ Orphaned Entity หมายถึงยังไม่มี Client Owner

Entity ยังมีอยู่ถ้า Owner -1 ไหม

Server Setter สามารถ Register Entity กับ Serverได้แม้ยังไม่มี Client Simulation

SetEntityOrphanMode 2 คืออะไร

คือ KeepEntity Mode ซึ่งช่วยไม่ให้ Server Cleanup Entity เพราะไม่มี Client Owner

KeepEntity อยู่หลัง Restart ไหม

ไม่ ต้องใช้ Database/Persistent Storage หากต้องการข้าม Restart

Server-created Entity มี Server เป็น Owner ตลอดไหม

ไม่ Network Simulation Ownership ยังสามารถไปอยู่กับ Client และ Migration ได้

Server-created Entity ปลอดภัยจาก Cheat ไหม

ช่วยลด Client Creation Authority แต่ยังต้องมี Event Validation, Entity Lockdown และ Security Logic ตามระบบ

Client ส่ง Model ให้ Server ได้ไหม

ส่งเป็น Request ได้ แต่ Server ควรตรวจ Allowlist ก่อนสร้าง

Server ส่ง Entity ไป Client อย่างไร

ควรส่ง Network ID มากกว่า Server Entity Handle

Network ID เก็บ Database ได้ไหม

ไม่ควรเป็น Permanent Identity

Entity Handle เก็บ Database ได้ไหม

ไม่ควร

Server Entity อยู่ Routing Bucket ได้ไหม

ได้ด้วย SetEntityRoutingBucket()

Server Entity มี State Bag ได้ไหม

ได้ผ่าน Entity(entity).state

Server Delete Entity ได้ไหม

ได้ผ่าน Server-side Native ที่รองรับ เช่น DeleteEntity() ตาม Context

Entity จำนวนมากไม่จำกัดใช่ไหม

ไม่ Client/Game Streaming Limits ยังมีอยู่ และ Entity Density สูงเกินไปสามารถสร้างปัญหาได้

สรุป FiveM Server-side Entity คืออะไร

FiveM Server-side Entity คือความสามารถของ FXServer ภายใต้ OneSync ที่ทำให้ Server รับรู้ สร้าง ตรวจสอบ และจัดการ Networked Ped, Vehicle และ Object ได้มากขึ้น

ตัวอย่าง Player State

local ped =
    GetPlayerPed(source)

local coords =
    GetEntityCoords(ped)

ช่วยให้ Server ตรวจ Gameplay State เองได้ เช่น

Player อยู่จุด Mission จริงไหม?
Player อยู่ใกล้ Shop ไหม?
Player อยู่ใกล้ Vehicle หรือไม่?

ส่วน Entity Creation สามารถใช้ Server Setter เช่น

local vehicle =
    CreateVehicleServerSetter(
        joaat('sultan'),
        'automobile',
        x,
        y,
        z,
        heading
    )

แล้วจัดการต่อด้วย

Routing Bucket
Orphan Mode
State Bag
Network ID
Lifecycle
Cleanup

แต่ต้องแยกให้ชัดว่า

Server-created Entity
≠
Server เป็น Network Owner ตลอดเวลา

Entity สามารถเริ่มเป็น Orphaned เมื่อยังไม่มี Client ใน Scope และ NetworkGetEntityOwner() สามารถคืน -1

เมื่อ Client เข้ามา Ownership สามารถถูกมอบให้ Client และภายหลัง Migration ได้อีก

อีกเรื่องสำคัญคือ Server-side RPC Natives ไม่ได้มี Guarantee ว่าจะทำงานทุกครั้ง เพราะ Operation อาจต้องถูก Execute บน Client Owner

ดังนั้น Resource ขั้นสูงควรแยกระหว่าง

Server Setter
RPC Native
Server Getter
Client-owner Initialization

ให้ถูกต้อง

สำหรับผู้ที่เรียน FiveM Developer กับ comsiam จุดแข็งที่สุดของ Server-side Entity ไม่ใช่เพียง “Spawn รถจาก Server ได้” แต่คือการย้าย Authority ของ Gameplay สำคัญกลับมาอยู่บน Server ให้มากขึ้น เช่น Serverเป็นคนตรวจ Player, เลือก Model, สร้าง Entity, กำหนด Instance และควบคุม Lifecycle

หลักสำคัญจาก comsiam คือ Client ควรขอให้ Server ทำสิ่งหนึ่ง ส่วน Server ตรวจว่าทำได้หรือไม่ แล้วจึงสร้างหรือเปลี่ยน Entity ตาม State ที่ Serverเชื่อถือได้

หัวข้อถัดไปคือ FiveM Entity Lockdown คืออะไร ป้องกัน Client Spawn Entity อย่างไร ซึ่งจะต่อจาก Server-created Entity โดยตรง และเจาะการใช้ strict, relaxed, inactive รวมถึง Routing Bucket เพื่อสร้างระบบที่ Client ไม่สามารถ Spawn Entity ได้ตามใจ

Comments

Popular posts from this blog

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

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

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