FiveM Entity Ownership คืออะไร

 FiveM Entity Ownership คือระบบที่กำหนดว่า Client ใดกำลังรับผิดชอบการควบคุมและ Synchronize Networked Entity ในช่วงเวลาหนึ่ง เช่น Vehicle, Ped หรือ Object ที่อยู่ในระบบ Network ของ FiveM

ตัวอย่างรถหนึ่งคันอาจมี

Network ID = 125

แต่ขณะหนึ่ง Player A อาจเป็น Network Owner

Vehicle
↓
Network ID 125
↓
Owner = Client A

เมื่อ Player A ออกจากพื้นที่ Ownership สามารถย้ายไป Client B ได้

Vehicle
↓
Owner A
↓
A ออกจาก Scope
↓
Ownership Migration
↓
Owner B

สิ่งสำคัญมากคือ Entity Ownership ไม่ได้หมายถึงความเป็นเจ้าของรถใน Database

Network Owner หมายถึงผู้ที่กำลังรับผิดชอบด้าน Network Simulation ของ Entity ไม่ได้หมายความว่า Player คนนั้นเป็นเจ้าของรถตามระบบ Garage หรือมี Permission ทำอะไรก็ได้กับ Entity

① FiveM Entity Ownership คืออะไร

Networked Entity ต้องมีระบบจัดการว่าใครเป็นผู้ Synchronize State บางส่วนของ Entity

เช่น

  • ตำแหน่ง

  • Movement

  • Physics

  • Vehicle Behavior

  • Ped Behavior

Client ที่รับหน้าที่นี้เรียกว่า Network Owner ในบริบททั่วไป

Concept คือ

Networked Entity
↓
Network Owner
↓
ส่ง Sync State
↓
Server
↓
Relevant Clients

ระบบ OneSync เป็นส่วนสำคัญในการจัดการสิ่งเหล่านี้

② Entity Ownership มีเฉพาะ Networked Entity หรือไม่

แนวคิด Network Ownership เกี่ยวข้องกับ Entity ที่ถูก Network

เช่น

Networked Vehicle
Networked Ped
Networked Object

Local Entity ที่สร้างเพื่อแสดงเฉพาะ Client เดียวไม่จำเป็นต้องมี Ownership แบบ Multiplayer ในลักษณะเดียวกัน

ดังนั้นก่อนพูดเรื่อง Owner ต้องถามก่อนว่า

Entity เป็น Networked หรือ Local?

③ Entity Handle กับ Ownership ต่างกันอย่างไร

Entity Handle คือ Local Reference

local vehicle =
    GetVehiclePedIsIn(
        PlayerPedId(),
        false
    )

ส่วน Ownership คือ

Client ไหนกำลังเป็น Network Owner

รถคันเดียวอาจมี

Client A Handle = 154
Client B Handle = 621
Network ID = 80
Owner = Client A

Handle, Network ID และ Owner จึงเป็นคนละข้อมูลกัน

④ Network ID กับ Ownership ต่างกันอย่างไร

จำง่าย ๆ ว่า

Network ID
=
Entity ตัวไหน

ส่วน

Entity Ownership
=
ใครกำลังควบคุม Network Entity นั้น

ตัวอย่าง

Net ID 80
Owner = Player A

ต่อมา

Net ID 80
Owner = Player B

Network ID ยังเป็น Entity เดิม แต่ Owner เปลี่ยนได้

⑤ Ownership เปลี่ยนได้ไหม

ได้

นี่เรียกว่า Ownership Migration

FiveM/OneSync สามารถเปลี่ยนผู้ควบคุม Network Entity ตามสถานการณ์ เช่น

  • Player เคลื่อนที่ออกไป

  • Entity ออกจาก Scope

  • Owner Disconnect

  • Client ใหม่เหมาะสมกว่าในการควบคุม Entity

ดังนั้น Script ไม่ควรคิดว่า

Client ที่สร้าง Entity
=
Owner ตลอดชีวิต Entity

⑥ Ownership Migration คืออะไร

สมมติ Player A อยู่ใกล้ Vehicle

Vehicle
↓
Owner A

Player A ขับออกจากพื้นที่หรือออกนอก Scope ของ Entity

ระบบอาจ

Disown / Migrate
↓
เลือก Client ที่เหมาะสม
↓
Owner B

Cfx.re ระบุว่า Entity ที่ออกนอก Range สามารถถูก Culled และ Migrated/Disowned ได้

จึงเป็น Behavior ปกติของ OneSync

⑦ Scope เกี่ยวกับ Ownership อย่างไร

OneSync ใช้ Scope/Culling เพื่อลด Entity ที่ Client ไม่จำเป็นต้องรับรู้

เมื่อ Entity ออกจาก Scope ของ Owner เดิม

Entity
↓
Out of Scope
↓
Owner เดิมอาจไม่ควบคุมต่อ

Ownership จึงสามารถเปลี่ยน

นี่เป็นเหตุผลหนึ่งที่ Entity Control ใน FiveM ไม่ควรถูกผูกกับ Player คนเดียวแบบถาวร

⑧ Culling คืออะไร

Culling คือการลด Network State ที่ Client ไม่จำเป็นต้องได้รับ

ตัวอย่าง

Player อยู่ไกล Entity มาก
↓
Entity ไม่อยู่ใน Focus Area
↓
ไม่จำเป็นต้องมี Local Entity บน Client

OneSync Documentation ปัจจุบันระบุ Focus Zone เริ่มต้นประมาณ 424 Units ในบริบทของ Player/Entity Culling

ระบบนี้ช่วยลด Network และ Client Processing

⑨ NetworkGetEntityOwner คืออะไร

FiveM มี Native

NetworkGetEntityOwner(
    entity
)

สำหรับตรวจ Network Owner ของ Entity ตาม Context ที่รองรับ

Client สามารถใช้ Pattern เช่น

local owner =
    NetworkGetEntityOwner(entity)

if owner == PlayerId() then
    print('I own this entity')
end

มีประโยชน์กับ Logic ที่ควรทำเฉพาะ Client ที่เป็น Owner

⑩ ตัวอย่างตรวจว่าเราเป็น Owner หรือไม่

local vehicle =
    GetVehiclePedIsIn(
        PlayerPedId(),
        false
    )

if not DoesEntityExist(vehicle) then
    return
end

local owner =
    NetworkGetEntityOwner(vehicle)

if owner == PlayerId() then
    print(
        'This client owns the vehicle'
    )
end

แต่ Owner อาจเปลี่ยนหลังจาก Check ได้

ดังนั้นอย่าใช้ Check ครั้งเดียวแล้วถือว่าจริงตลอดไป

⑪ Ownership สามารถเปลี่ยนเร็วได้ไหม

ได้

Cfx.re ระบุในตัวอย่าง Server-created Entity ว่า Owner สามารถเปลี่ยนอย่างรวดเร็ว หากมี Clients หลายคนอยู่ใกล้ Entity ขณะสร้าง

ดังนั้น Code ประเภท

ตรวจ Owner
↓
Wait นาน
↓
ทำ Action

อาจไม่สามารถรับประกันว่า Client ยังเป็น Owner อยู่ตอน Action ทำงาน

⑫ Owner เปลี่ยนหลัง Check ได้อย่างไร

ตัวอย่าง

if NetworkGetEntityOwner(entity)
    == PlayerId() then

    Wait(2000)

    -- Owner อาจเปลี่ยนแล้ว
end

ระหว่าง Wait(2000)

Player อาจเคลื่อนที่หรือ Networking State เปลี่ยน

ถ้า Ownership สำคัญกับ Action ควรตรวจใหม่ก่อนทำงานตาม Use Case

⑬ Entity Owner กับ Database Owner เหมือนกันไหม

ไม่เหมือนกันโดยเด็ดขาด

สมมติ Database บอก

Vehicle ABC123
Owner = character_154

แต่ Network Owner ขณะนี้อาจเป็น

Player 37

เพราะ Player 37 กำลังขับหรืออยู่ในตำแหน่งเหมาะสมต่อ Network Simulation

ดังนั้น

Network Owner
≠
Vehicle Legal Owner

⑭ อย่าใช้ Network Owner เป็น Vehicle Ownership Permission

ไม่ควรเขียน Logic เช่น

ถ้าเป็น Network Owner
→ ขายรถได้

เพราะ Player ที่กำลังขับรถของคนอื่นอาจกลายเป็น Network Owner ได้

การขาย/เก็บ/โอนรถต้องตรวจ

Plate
Database Vehicle ID
Character Identifier
Database Ownership

ตามระบบ Garage จริง

⑮ Entity Owner ไม่ใช่ Admin Permission

เช่นเดียวกัน

เป็น Network Owner

ไม่ได้หมายความว่า Player มี Permission

  • Delete

  • Sell

  • Transfer

  • Modify

  • Give Reward

Security Permission ต้องตรวจ Server-side แยกต่างหาก

⑯ Entity Ownership กับ Server Authority ต่างกันอย่างไร

Server Authority หมายถึง Server เป็นผู้ตัดสิน Business Logic สำคัญ

เช่น

Money
Inventory
Vehicle Ownership
Reward
Permission

Entity Network Ownership เป็นเรื่องการ Synchronize Game Entity

ดังนั้น Server สามารถเป็น Authority ด้าน Economy แม้ Physics ของ Vehicle จะถูก Synchronize ผ่าน Client Owner

สอง Concept นี้ไม่ขัดกัน

⑰ FiveM มี Server เป็น Network Owner ของทุก Entity ไหม

ไม่ควรมอง FiveM แบบ Multiplayer Engine ที่ Server Simulation Entity ทุกตัวตลอดเวลา

เอกสาร Cfx.re ปัจจุบันระบุว่า FiveM ไม่มี Server-based Authoritative Ownership Model แบบบาง Multiplayer Platforms ในลักษณะเดียวกัน

Network Ownership อาศัย Client Forwarding และ Ownership Events/Behavior ของ OneSync

ดังนั้นต้องออกแบบ Resource ให้เหมาะกับระบบ FiveM จริง

⑱ Server-created Entity แปลว่า Server เป็น Owner ตลอดไหม

ไม่จำเป็น

Server สามารถสร้าง Entity ผ่าน Server-side APIs

แต่เมื่อ Entity เข้าสู่โลกและมี Client ที่เหมาะสม Client สามารถเข้ามารับ Network Simulation Ownership ได้

ดังนั้น

Created by Server
≠
Network Owner เป็น Server ตลอดเวลา

นี่เป็นรายละเอียดสำคัญมาก

⑲ Orphaned Entity คืออะไร

เมื่อ Server สร้าง Entity ด้วย Server Setter แต่ยังไม่มี Client ที่เหมาะสมอยู่ใน Scope Entity อาจอยู่ในสถานะ Orphaned

Concept คือ

Server Create Entity
↓
ยังไม่มี Client ใกล้
↓
Orphaned Entity
↓
Owner = none

Cfx.re ระบุว่า NETWORK_GET_ENTITY_OWNER สามารถคืน -1 สำหรับ Orphaned Entity

⑳ Owner -1 หมายถึงอะไร

ในบริบท Server-created Orphaned Entity ตามเอกสารปัจจุบัน

NetworkGetEntityOwner(entity)
=
-1

หมายถึงยังไม่มี Client Owner ที่กำลัง Simulation Entity นั้น

เมื่อ Client ที่เหมาะสมเข้าสู่ Scope Entity จึงสามารถถูกสร้างใน Game World/Simulation และมี Ownership ตามระบบ

㉑ Orphaned Entity ยังมีอยู่บน Server ไหม

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

แต่ Entity อาจยังไม่มี Client Simulation

Concept คือ

Server knows entity
↓
No suitable client
↓
Orphaned
↓
Client arrives
↓
Entity becomes simulated

นี่ต่างจากการคิดว่าถ้าไม่มี Owner แล้ว Entity ต้องถูกลบทันที

㉒ RPC Native กับ Ownership เกี่ยวกันอย่างไร

Cfx.re อธิบายว่า Server-side RPC Natives บางตัวจะถูกเรียกจริงบน Client โดยทั่วไปคือ Client ที่ Own Entity

เช่น Action บางอย่างกับ Vehicle

Flow อาจเป็น

Server calls RPC native
↓
Network
↓
Entity Owner Client
↓
Native executes

ดังนั้น RPC Call มีโอกาสล้มเหลวและไม่ควรถูกมองว่ารับประกัน 100%

㉓ ทำไม Server RPC Native บางครั้งไม่ทำงาน

สาเหตุหนึ่งคือยังไม่มี Client Owner

เช่น Server Setter สร้าง Entity ในพื้นที่ไม่มี Player

Entity อยู่ Orphaned

Owner = -1

RPC Native ที่ต้องทำงานบน Client Owner จึงยังไม่มี Client ที่จะ Execute

นี่คือเหตุผลที่ Server Getter กับ Server RPC Setter อาจมี Behavior ต่างกัน

㉔ Server Setter คืออะไร

Server Setter Natives บางตัวสามารถสร้าง Entity ลง Server State ได้โดยตรง เช่นแนวทาง

CreateVehicleServerSetter(...)

Cfx.re แนะนำแนวทาง Server-created Entities สำหรับระบบหลายประเภท

โดยเฉพาะ Entity ที่ Server ต้องควบคุม Lifecycle และ Security มากขึ้น

㉕ ทำไม Server-created Entity ดีกว่า Client-created ในบางระบบ

สมมติ Mission ให้รถรางวัล

ถ้า Client เป็นผู้สร้าง

Client
↓
Spawn Vehicle
↓
บอก Server

ผู้โกงอาจพยายาม Abuse Client Spawn Flow

Architecture ที่แข็งแรงกว่าคือ

Client
↓
Request
↓
Server Validate
↓
Server Create Vehicle
↓
Network Sync

ช่วยลด Authority ที่ Client มีต่อ Entity Creation

㉖ Server-created ไม่ได้แก้ทุกปัญหา

แม้ Server จะสร้าง Vehicle

ยังต้องคิด

  • Ownership

  • Initialization

  • State Bags

  • Routing Bucket

  • Persistence

  • Cleanup

  • Database Ownership

  • Entity Lockdown

ดังนั้น Server Creation เป็นส่วนหนึ่งของ Architecture ไม่ใช่ Solution ทั้งหมด

㉗ State Bag ช่วยเรื่อง Ownership อย่างไร

State Bags มีประโยชน์มากเมื่อ Server สร้าง Entity แต่ Action บางอย่างต้องทำโดย Client Owner

ตัวอย่าง Server

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

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

Client ที่รับ Ownership สามารถเห็น State แล้วทำ Initialization

㉘ ตัวอย่าง Owner-based Initialization

Client Concept:

AddStateBagChangeHandler(
    'needsSetup',
    nil,
    function(bagName, key, value)

        if not value then
            return
        end

        local entity =
            GetEntityFromStateBagName(
                bagName
            )

        if entity == 0 then
            return
        end

        if NetworkGetEntityOwner(
            entity
        ) ~= PlayerId() then
            return
        end

        -- owner-only initialization

    end
)

ช่วยให้ Action ที่จำเป็นต้องทำโดย Local Owner ถูกส่งให้ Client ที่เหมาะสม

㉙ ทำไมต้องระวัง Owner-based Initialization

เพราะ Owner สามารถเปลี่ยนระหว่าง Execution

Cfx.re แสดง Pattern ที่ต้องตรวจ State ซ้ำและให้ Owner ที่สำเร็จเป็นผู้ Clear State

เช่น

needsSetup = true
↓
หลาย Clients เห็น
↓
เฉพาะ Current Owner ทำ
↓
needsSetup = nil

ช่วยป้องกัน Initialization ซ้ำ

㉚ State Bag เหมาะกว่า TriggerClientEvent ไป Owner หรือไม่

บางกรณีใช่

ถ้า Owner สามารถเปลี่ยนก่อน Event ถึงหรือก่อน Action จบ การผูก Intent ไว้กับ Entity State สามารถ Robust กว่า

Client Owner ใหม่ยังสามารถเห็น State และทำงานต่อได้

แต่ต้องเลือกตาม Use Case

State Bag ไม่ได้แทน Event ทุกประเภท

㉛ NetworkHasControlOfEntity คืออะไร

Client-side Network APIs มี Concept การตรวจว่า Client ปัจจุบันมี Control ของ Entity หรือไม่

Developer มักใช้

NetworkHasControlOfEntity(entity)

ก่อนทำ Action ที่ต้องใช้ Network Control

Concept คือ

Do I currently control this entity?

แต่ Control สามารถเปลี่ยนได้เช่นเดียวกับ Ownership

㉜ NetworkRequestControlOfEntity คืออะไร

Client สามารถมี API สำหรับ Request Control ของ Network Entity

Concept เช่น

NetworkRequestControlOfEntity(
    entity
)

แต่การ Request ไม่ได้หมายความว่าจะได้รับ Control เสมอไป

FiveM Server สามารถกรอง Control Requests และ Ownership ของ Entity อาจเปลี่ยนตาม Networking State

ดังนั้นอย่าเขียน Script โดยสมมติว่า

Request Control
=
ได้ Control ทันที 100%

㉝ อย่า Request Control ใน Infinite Loop แบบไม่จำกัด

ตัวอย่างที่ควรหลีกเลี่ยง

while not NetworkHasControlOfEntity(
    entity
) do

    NetworkRequestControlOfEntity(
        entity
    )

    Wait(0)
end

ถ้า Control ไม่เคยถูกให้ Loop อาจทำงานต่อไปไม่จบ

ควรมี Timeout

㉞ ตัวอย่าง Request Control พร้อม Timeout

local timeout =
    GetGameTimer() + 2000

while not NetworkHasControlOfEntity(
    entity
) do

    NetworkRequestControlOfEntity(
        entity
    )

    if GetGameTimer() >
        timeout then

        print(
            'Failed to get control'
        )

        return
    end

    Wait(0)
end

ช่วยให้ Resource ไม่ติดอยู่กับ Control Request ตลอดไป

㉟ ทำไม Request Control อาจถูก Block

FiveM มี Server Setting

sv_filterRequestControl

สำหรับควบคุม Client Control Requests

เอกสารปัจจุบันระบุ Mode ตั้งแต่ 0 ถึง 4

โดย Mode ที่เข้มขึ้นสามารถ Block Request ในสถานการณ์ต่าง ๆ จนถึงไม่อนุญาต Control Request ทั้งหมด

ดังนั้น Script ไม่ควรพึ่ง Request Control เป็น Guarantee

㊱ sv_filterRequestControl = 0

ตามเอกสารปัจจุบัน

0
=
No filtering

เป็นค่า Default ที่ระบุไว้ใน Documentation ปัจจุบัน

แต่ Server Admin สามารถปรับให้เข้มขึ้นได้ตาม Security Requirement

㊲ sv_filterRequestControl = 1 และ 2

Mode 1 และ 2 เพิ่มข้อจำกัดกับ Request Control ของ Vehicles ที่ถูก Own/Settled ตามเงื่อนไขของระบบ

รายละเอียดเกี่ยวข้องกับ Settle Timer และ Ownership State

Developer Resource จึงไม่ควรออกแบบโดยคาดว่า Control Request ทุกครั้งจะสำเร็จในทุก Server Configuration

㊳ sv_filterRequestControl = 3

Mode 3 จำกัด Control Requests มากขึ้น โดย Block Requests สำหรับ Controlled หรือ Settled Entities ตาม Documentation ปัจจุบัน

Server ที่ใช้ Setting นี้อาจทำให้ Script เก่าบางตัวที่ Request Control แบบ Aggressive ทำงานต่างจากเดิม

㊴ sv_filterRequestControl = 4

Mode 4 คือ

Disallow all control requests

ตามเอกสาร Cfx.re ปัจจุบัน

ดังนั้น Resource ที่ต้องพึ่ง Client ขอ Ownership จาก Entity อื่นตลอดเวลาอาจไม่ Compatible กับ Server ที่ตั้ง Security Mode แบบนี้

㊵ Resource ควรออกแบบอย่างไรแทน Request Control มากเกินไป

พิจารณาใช้

  • Server-created Entity

  • State Bags

  • Server-side Validation

  • Owner-aware Logic

  • Routing Bucket

  • Proper Entity Lifecycle

แทนการให้ Client ทุกคนพยายามแย่ง Control Entity

Architecture ที่ดีควรลด Ownership Fighting

㊶ Ownership Fighting คืออะไร

สมมติ Clients หลายคนทำ

Client A
→ Request Control

Client B
→ Request Control

Client C
→ Request Control

ซ้ำ ๆ กับ Entity เดียวกัน

อาจทำให้ระบบซับซ้อนและเกิด Unstable Behavior

โดยเฉพาะถ้า Script หลาย Resources ต่างคิดว่าตัวเองต้อง Own Entity

ควรออกแบบให้มี Responsibility ชัดเจน

㊷ Client ที่ใกล้ที่สุดต้องเป็น Owner เสมอไหม

ไม่ควรเขียน Business Logic โดยยึดกฎง่าย ๆ ว่า

Nearest Player
=
Owner แน่นอน

Networking Scheduler เป็นผู้จัดการ Ownership ตาม State และเงื่อนไขของระบบ

Developer ควรตรวจ Owner จริงเมื่อจำเป็น

ไม่ควรเดา Owner จาก Distance อย่างเดียว

㊸ Player ขับรถแล้วต้องเป็น Owner เสมอไหม

มักมีความสัมพันธ์ด้าน Simulation แต่ไม่ควรใช้สมมติฐานนี้เป็น Security Logic

หากต้องการรู้ Player ขับรถหรือไม่ ให้ตรวจ Gameplay State

ถ้าต้องการตรวจ Network Owner ให้ใช้ Network Ownership API

อย่าผสมสองเรื่องเข้าด้วยกัน

㊹ Network Owner Disconnect เกิดอะไรขึ้น

ถ้า Client Owner Disconnect ระบบต้องจัดการ Network Entity ต่อ

Entity สามารถ

  • Migrate

  • Become Unowned/Orphaned

  • ถูก Cleanup

ขึ้นกับ Entity Lifecycle, Script Ownership และ OneSync State

ดังนั้น Resource ต้องรับมือกรณี Owner หายไปได้

㊺ อย่าผูก Mission กับ Owner Player เพียงคนเดียว

สมมติ Mission Vehicle มี Owner เป็น Player 15

ไม่ควรเก็บ Logic ว่า

ถ้า Player 15 หาย
→ Mission Vehicle ใช้งานไม่ได้

Mission Identity ควรอยู่ใน Server State เช่น

missionId
vehicle database/runtime reference
network id
party
state

Network Owner เปลี่ยนได้โดยไม่ควรทำให้ Business State หาย

㊻ Network Owner กับ Script Ownership ต่างกันอย่างไร

นี่เป็น Concept ที่สับสนได้ง่าย

Network Ownership เกี่ยวกับ Client ที่ควบคุม Network Simulation

ส่วน Script Ownership/Mission Entity Lifecycle เกี่ยวกับ Script/Resource ที่ถือ Entity ตามระบบ Game/OneSync

ไม่ควรถือว่าเป็นคำเดียวกัน

Resource ระดับสูงควรแยกทั้งสอง Concept

㊼ SetEntityAsMissionEntity คือ Network Ownership ไหม

ไม่

การ Mark Entity เป็น Mission Entity เกี่ยวกับ Script/Entity Lifecycle และ Cleanup Behavior

ไม่ได้หมายความว่า Client นั้นจะเป็น Network Owner ตลอดไป

Network Ownership ยังสามารถ Migration ได้ตาม OneSync

㊽ SetEntityOrphanMode คืออะไร

Server-side OneSync มี

SetEntityOrphanMode(
    entity,
    mode
)

สำหรับควบคุม Server Entity Persistence Behavior เมื่อไม่มี Owner

Cfx.re แนะนำ KeepEntity Mode ใน Use Case ที่ต้องการให้ Entity ไม่ถูก Server ลบเพราะไม่มี Client Owner

ตัวอย่าง

SetEntityOrphanMode(
    vehicle,
    2
)

㊾ KeepEntity ทำให้ Entity อยู่ข้าม Restart ไหม

ไม่

มันเกี่ยวกับ Runtime Entity Lifecycle

ไม่ได้ทำให้ Entity กลายเป็น Database Persistent Object

ถ้า Server Restart

ข้อมูล Entity Runtime หายได้ตามระบบ

ถ้าต้องการ Spawn กลับต้องเก็บ Persistent Data เช่น

Database ID
Model
Coords
Heading
Properties

แล้วสร้างใหม่

㊿ Entity Ownership กับ Routing Bucket

Routing Bucket มีผลกับ Entity Scope

ถ้า Player และ Entity อยู่คนละ Bucket

Player
Bucket 1

Vehicle
Bucket 2

พวกเขาไม่ได้อยู่ Routing Context เดียวกัน

จึงอาจไม่มี Client Owner/Visibility ตามที่ Developerคาด

ระบบ Instance ต้องจัด Bucket ให้ถูก

51 Entity Lockdown กับ Ownership

Entity Lockdown ไม่ใช่ Ownership System โดยตรง

มันควบคุมว่า Clients สามารถสร้าง Entity ใน Routing Bucket ได้แค่ไหน

ตัวอย่าง

SetRoutingBucketEntityLockdownMode(
    bucketId,
    'strict'
)

strict ไม่อนุญาต Client-created Entities ใน Bucket นั้น

ช่วยสร้าง Architecture

Client
↓
Request
↓
Server
↓
Create Entity
↓
OneSync Ownership

ที่ควบคุมได้มากขึ้น

52 strict ไม่ได้หมายความว่า Server Simulation ทุก Entity

แม้ Entity Lockdown เป็น strict

มันหมายถึง Client ไม่สามารถ Author Entity Creation ใน Bucket นั้น

แต่เมื่อ Networked Entity มีอยู่ Network Simulation Ownership ยังทำงานตาม OneSync Model

จึงไม่ควรสับสน

Entity Creation Authority

กับ

Entity Network Ownership

53 Entity Ownership กับ Security

Network Owner ไม่ควรมีสิทธิ์กำหนด Business State

เช่น Client ที่ Own Vehicle ไม่ควรสามารถบอก Server ว่า

vehiclePrice = 0
vehicleOwner = me
stored = true
reward = 100000

แล้ว Server เชื่อ

Server-side Validation ยังจำเป็นเหมือนเดิม

54 Owner ส่ง Entity State มา Server เชื่อได้ไหม

Network Sync State ของ Game เป็นส่วนหนึ่งของ Networking System

แต่ Custom Business Data จาก Client ยังต้องคิดเรื่อง Trust

ตัวอย่าง

Client Owner says:
fuel = 999999

ถ้า Fuel มีผลกับ Economy หรือ Reward Server อาจต้อง Validate

การเป็น Network Owner ไม่ได้ทำให้ Client กลายเป็น Trusted Business Authority

55 Entity Ownership กับ State Bag Security

State Bag มี Replication Policy

โดยทั่วไป Entity State สามารถถูกจัดการตาม Ownership/Server Rules ที่เกี่ยวข้อง

แต่ Developer ต้องกำหนดว่า State ไหน

Presentation only

กับ State ไหน

Security critical

ข้อมูล Security-critical ควรให้ Server เป็น Authority

หัวข้อ State Bag จะลงรายละเอียดต่อไป

56 Entity Owner Debug อย่างไร

Client สามารถเริ่มจาก

local entity =
    GetVehiclePedIsIn(
        PlayerPedId(),
        false
    )

if not DoesEntityExist(entity) then
    return
end

local netId =
    NetworkGetNetworkIdFromEntity(
        entity
    )

local owner =
    NetworkGetEntityOwner(
        entity
    )

print(
    'entity:',
    entity
)

print(
    'net id:',
    netId
)

print(
    'owner:',
    owner
)

print(
    'my player id:',
    PlayerId()
)

ช่วยดูว่า Client ปัจจุบันเป็น Owner หรือไม่

57 Entity Control ไม่ทำงานตรวจอะไร

ตรวจ

① Entity มีจริงไหม?
② เป็น Networked Entity หรือไม่?
③ อยู่ใน Scope หรือไม่?
④ Network ID ถูกต้องไหม?
⑤ Current Owner คือใคร?
⑥ Client มี Control หรือไม่?
⑦ Control Request ถูก Filter หรือไม่?
⑧ Routing Bucket ตรงไหม?
⑨ Entity เป็น Orphaned หรือไม่?
⑩ Owner เปลี่ยนระหว่าง Operation หรือไม่?
⑪ RPC Native ที่ใช้รับประกันหรือไม่?
⑫ Entity ถูก Delete แล้วหรือไม่?

อย่าแก้ทุกปัญหาด้วย Loop NetworkRequestControlOfEntity() ไม่จำกัด

58 Architecture สำหรับ Mission Vehicle ที่ดีกว่า

Flow ที่แข็งแรงกว่าอาจเป็น

Client ขอ Mission
↓
Server Validate
↓
Server Create Vehicle
↓
Server เก็บ Network ID / Mission State
↓
State Bag กำหนด Initial State
↓
Client ที่เป็น Owner ทำ Local Initialization ที่จำเป็น
↓
Server ตรวจ Result/State
↓
Ownership เปลี่ยนได้
↓
Mission ยังดำเนินต่อ

Business State ไม่ผูกกับ Owner คนใดคนหนึ่ง

59 Checklist FiveM Entity Ownership

ก่อนเขียนระบบ Network Entity ให้ตรวจ

① Entity เป็น Networked หรือ Local?
② Network ID คืออะไร?
③ Current Owner คือใคร?
④ Owner จำเป็นกับ Logic นี้จริงไหม?
⑤ Ownership เปลี่ยนได้หรือไม่?
⑥ หลัง Wait ต้องตรวจ Owner ใหม่ไหม?
⑦ Entity อยู่ใน Scope หรือไม่?
⑧ Entity เป็น Orphaned หรือไม่?
⑨ ต้องใช้ Server-created Entity หรือไม่?
⑩ Action นี้เป็น RPC Native หรือไม่?
⑪ RPC Failure ถูก Handle หรือไม่?
⑫ ต้องใช้ State Bag สำหรับ Initialization หรือไม่?
⑬ ต้อง Request Control จริงไหม?
⑭ Request Control มี Timeout หรือไม่?
⑮ sv_filterRequestControl อาจ Block หรือไม่?
⑯ Network Owner ถูกสับสนกับ Database Owner หรือไม่?
⑰ Security-critical Logic อยู่ Server หรือยัง?
⑱ Routing Bucket ถูกต้องไหม?
⑲ ต้องใช้ Entity Lockdown หรือไม่?
⑳ Cleanup/Persistence ถูกออกแบบหรือยัง?

⑥⓪ หลักจำ Entity Ownership แบบง่ายที่สุด

จำ 4 บรรทัดนี้

Network ID
=
Entity ตัวไหน
Entity Owner
=
ใครกำลังควบคุม Network Simulation
Database Owner
=
ใครเป็นเจ้าของทรัพย์สินตามระบบเซิร์ฟเวอร์

และ

Network Owner
สามารถเปลี่ยนได้

ถ้าแยก 4 Concept นี้ได้ การทำ Multiplayer Entity ใน FiveM จะง่ายขึ้นมาก

ตัวอย่างตรวจ Owner พร้อม State Bag

server.lua

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

if vehicle == 0 then
    return
end

SetEntityOrphanMode(
    vehicle,
    2
)

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

client.lua

AddStateBagChangeHandler(
    'vehicleNeedsInit',
    nil,
    function(
        bagName,
        key,
        value
    )

        if not value then
            return
        end

        local entity =
            GetEntityFromStateBagName(
                bagName
            )

        if entity == 0 then
            return
        end

        if NetworkGetEntityOwner(
            entity
        ) ~= PlayerId() then
            return
        end

        SetVehicleOnGroundProperly(
            entity
        )

        Entity(entity).state:set(
            'vehicleNeedsInit',
            nil,
            true
        )
    end
)

แนวคิดคือ

Server
↓
Create Vehicle
↓
Set State
↓
Entity เข้า Scope
↓
Client ได้ Ownership
↓
Owner ทำ Initialization
↓
Clear State

เหมาะกว่า Hardcode ว่า Player คนที่ขอ Spawn ต้องเป็น Owner ตลอดไป

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

FiveM Entity Ownership คืออะไร

คือระบบที่กำหนด Client ซึ่งกำลังรับผิดชอบ Network Simulation/Control ของ Networked Entity ในช่วงเวลาหนึ่ง

Entity Owner เปลี่ยนได้ไหม

ได้ Ownership สามารถ Migration เมื่อ Scope หรือ Networking Situation เปลี่ยน

Client ที่สร้างรถเป็น Owner ตลอดไหม

ไม่ควรสมมติแบบนั้น Ownership สามารถเปลี่ยนได้

Network ID เปลี่ยนเมื่อ Owner เปลี่ยนไหม

ไม่ใน Lifetime ของ Network Entity เดิม

Network Owner คือเจ้าของรถใน Database ไหม

ไม่ เป็นคนละ Concept

Network Owner มีสิทธิ์ขายรถไหม

ไม่โดยอัตโนมัติ ต้องตรวจ Database/Server Permission

วิธีตรวจ Owner ฝั่ง Client

ใช้ Native เช่น

NetworkGetEntityOwner(
    entity
)

และสามารถเทียบกับ

PlayerId()

ตาม Use Case

NetworkHasControlOfEntity คืออะไร

ใช้ตรวจว่า Client ปัจจุบันมี Network Control ของ Entity หรือไม่

Request Control ได้ไหม

มี NetworkRequestControlOfEntity() แต่ Request ไม่ได้รับประกันว่าจะสำเร็จ

ทำไม Request Control ไม่สำเร็จ

อาจเกี่ยวกับ Current Ownership, Scope หรือ Server Setting อย่าง sv_filterRequestControl

ควร Loop Request Control ไปเรื่อย ๆ ไหม

ไม่ ควรมี Timeout และ Error Handling

Orphaned Entity คืออะไร

Server-created Entity ที่ยังไม่มี Client ที่เหมาะสมรับ Simulation Ownership

Owner ของ Orphaned Entity คืออะไร

Cfx.re ระบุว่า NETWORK_GET_ENTITY_OWNER สามารถคืน -1 ในสถานะนี้

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

ไม่ เมื่อมี Client ที่เหมาะสม Network Simulation Ownership สามารถอยู่กับ Client ได้

SetEntityOrphanMode ทำอะไร

ใช้ควบคุม Runtime Persistence Behavior ของ Server Entity เมื่อไม่มี Owner

KeepEntity ทำให้รถอยู่หลัง Restart ไหม

ไม่ Database Persistence เป็นอีกระบบหนึ่ง

State Bag ช่วย Ownership อย่างไร

ช่วยเก็บ Intent/State ไว้กับ Entity ทำให้ Current Owner หรือ Owner ใหม่สามารถตอบสนองต่อ State ได้ตาม Architecture

Entity Lockdown คือ Ownership หรือไม่

ไม่ เป็นระบบควบคุมการสร้าง Entity จาก Client

Routing Bucket มีผลกับ Ownership ไหม

มีผลทางอ้อมผ่าน Scope และ Routing Context ของ Player/Entity

Network Owner เชื่อถือเรื่อง Money/Inventory ได้ไหม

ไม่ Network Ownership ไม่ได้ทำให้ Client เป็น Trusted Authority ด้าน Business Logic

สรุป FiveM Entity Ownership คืออะไร

FiveM Entity Ownership คือกลไกของ Networking ที่กำหนด Client ซึ่งกำลังรับผิดชอบการควบคุมและ Synchronize Networked Entity ในช่วงหนึ่ง

ตัวอย่าง

Vehicle
↓
Net ID 125
↓
Owner A
↓
A ออกจาก Scope
↓
Ownership Migration
↓
Owner B

สิ่งสำคัญที่สุดคือ Ownership สามารถเปลี่ยนได้

ดังนั้นอย่าออกแบบ Script ว่า

คน Spawn
=
Owner ตลอดไป

และต้องแยกให้ออกระหว่าง

Network Owner
→ ผู้รับผิดชอบ Network Simulation

กับ

Database Owner
→ เจ้าของรถ/ทรัพย์สินในระบบเซิร์ฟเวอร์

รวมถึง

Server Authority
→ ผู้ตัดสิน Money, Inventory, Permission และ Business Logic

อีกประเด็นที่สำคัญคือ Server-created Entity สามารถเริ่มต้นในสถานะ Orphaned หากยังไม่มี Client เหมาะสมอยู่ใน Scope และ Cfx.re ระบุว่า NetworkGetEntityOwner() สามารถคืน -1 ในกรณีนี้

เมื่อ Client เข้ามาใกล้ Entity จึงสามารถได้รับ Ownership และทำ Simulation ต่อ

สำหรับคนที่เรียน FiveM Developer กับ comsiam การเข้าใจ Ownership จะช่วยอธิบายปัญหาหลายอย่าง เช่น ทำไม Client หนึ่งสั่ง Vehicle ได้แต่อีก Client สั่งไม่ได้ ทำไม Entity Owner เปลี่ยนหลัง Player เดินออกไป และทำไม Script ที่พยายาม NetworkRequestControlOfEntity() ตลอดเวลาจึงไม่ใช่ Architecture ที่ดี

หลักที่ควรจำจาก comsiam คือ Network Owner เป็นผู้ควบคุมการ Sync ในช่วงเวลานั้น ไม่ใช่เจ้าของข้อมูลหรือผู้มีสิทธิ์เหนือ Entity ทุกอย่าง

หัวข้อถัดไปคือ FiveM State Bag คืออะไร ซึ่งเป็นระบบสำคัญสำหรับเก็บและ Replicate Custom State ระหว่าง Server, Entity และ Clients โดยไม่ต้อง Broadcast Network Event ซ้ำ ๆ ทุกครั้งที่ State เปลี่ยน

Comments

Popular posts from this blog

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

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

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