FiveM Network ID คืออะไร? ต่างจาก Entity Handle อย่างไร และใช้ Sync Entity ข้าม Client/Server แบบไหน

 FiveM Network ID หรือ NetID คือหมายเลขที่ใช้ระบุ Networked Entity ข้ามระหว่าง Client และ Server เช่น Vehicle, Ped หรือ Object โดย Cfx.re ระบุว่า Network ID เป็นค่า 16-bit integer ที่แทน Entity หนึ่งในช่วงเวลาหนึ่ง และค่าจะไม่เปลี่ยนตลอด Lifetime ของ Entity นั้น แม้หมายเลขอาจถูกนำกลับมาใช้ใหม่หลัง Entity เดิมหมดอายุแล้ว.

จุดสำคัญที่สุดที่ Developer ต้องเข้าใจคือ Entity Handle กับ Network ID ไม่ใช่สิ่งเดียวกัน เพราะ Entity Handle เป็น Local Reference ของแต่ละเครื่อง ขณะที่ Network ID ถูกออกแบบให้ใช้ระบุ Entity ข้าม Clients และ Server.

Meta SEO

Meta Title: FiveM Network ID คืออะไร? NetID ต่างจาก Entity Handle อย่างไร

Meta Description: อธิบาย FiveM Network ID หรือ NetID แบบละเอียด วิธีใช้ NetworkGetNetworkIdFromEntity, NetworkGetEntityFromNetworkId, Scope, OneSync และวิธีแก้ NetID หา Entity ไม่เจอ

Focus Keyword: FiveM Network ID

Related Keywords: FiveM NetID, FiveM Entity Handle, NetworkGetNetworkIdFromEntity, NetworkGetEntityFromNetworkId, FiveM OneSync, FiveM Entity Sync

สารบัญ

① FiveM Network ID คืออะไร

Network ID คือ ID สำหรับ Networked Entity

ตัวอย่างเชิงแนวคิด:

Vehicle
Entity Handle บน Client A = 328514

Entity Handle บน Client B = 425918

Network ID = 143

Client A และ Client B อาจมี Local Entity Handle คนละเลข แต่ยังสามารถอ้างถึง Entity เดียวกันด้วย Network ID เดียวกันได้.

② Network ID มีไว้ทำอะไร

ใช้เมื่อ Resource ต้องการระบุ Entity ข้าม Context เช่น:

Client A
↓
ส่ง NetID
↓
Server
↓
ส่ง NetID
↓
Client B

แทนการส่ง Entity Handle จาก Client A ไป Client B ซึ่งไม่รับประกันว่าจะหมายถึง Entity เดียวกัน.

③ Network ID เป็นค่าถาวรหรือไม่

ไม่ใช่ Permanent ID

Cfx.re ระบุว่า Network ID สามารถถูกนำกลับมาใช้ใหม่ได้ภายหลัง แต่จะไม่เปลี่ยนระหว่าง Lifetime ของ Entity เดิม.

ดังนั้นไม่ควรเก็บ NetID เป็น Primary Key ถาวรใน Database

④ Network ID มีขนาดเท่าไร

Cfx.re ระบุว่า Entity Network ID เป็นค่า 16-bit integer.

นี่เป็นอีกเหตุผลที่หมายเลขสามารถถูก Reuse ได้หลัง Entity เดิมหมดอายุ

⑤ NetID ใช้เป็น Database ID รถได้ไหม

ไม่ควร

ถ้ารถต้องกลับมาหลัง Server Restart ควรมี Persistent ID ของระบบเอง เช่น:

vehicleDatabaseId = 1824

ส่วน NetID ใช้อ้าง Network Entity ที่กำลังมีอยู่ใน Runtime

แนวคิดจึงเป็น:

Database Vehicle ID
= ตัวตนถาวร

Network ID
= ตัวตนใน Network Runtime

Entity Handle
= Local Reference

⑥ Entity Handle คืออะไร

Cfx.re อธิบายว่า Entity Handle เป็นค่าที่อ้างถึง Internal Game Representation ของ Entity และเป็น Local ต่อ Client หนึ่งเครื่อง.

เช่น:

local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)

ตัวแปร vehicle คือ Entity Handle ใน Context ของ Client เครื่องนั้น

⑦ Entity Handle เหมือนกันทุก Client ไหม

ไม่

Cfx.re ระบุชัดว่าไม่ควรคาดหวังว่า Entity เดียวกันจะมี Handle เดียวกันบน Clients ต่างเครื่อง.

ตัวอย่าง:

รถคันเดียวกัน

Client A → Handle 20482
Client B → Handle 72193

แต่สามารถมี NetID เดียวกัน

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

ได้

Cfx.re ระบุว่า Entity เดิม หากถูก Recreated ใน Local หรือ Remote Context สามารถได้รับ Handle ใหม่ได้.

จึงไม่ควร Cache Handle ระยะยาวโดยไม่ตรวจว่า Entity ยังมีอยู่

⑨ Entity Handle กับ Network ID ต่างกันอย่างไร

ค่าขอบเขตใช้ข้าม Client/Server
Entity HandleLocalไม่ควร
Network IDNetworkได้
Database IDPersistent Storageแล้วแต่ระบบ

Cfx.re แนะนำให้ใช้ Network ID หากต้องอ้าง Entity ข้าม Machines.

⑩ จำง่ายที่สุด

Handle = Local

NetID = Network

Database ID = Persistent

ถ้าเข้าใจ 3 ตัวนี้ Developer จะลด Bug Networking ได้มาก

⑪ แปลง Entity Handle เป็น Network ID อย่างไร

ใช้:

NetworkGetNetworkIdFromEntity(entity)

Native Reference ของ Cfx.re มี Function นี้สำหรับเปลี่ยน Entity Handle เป็น Network ID.

ตัวอย่าง:

local vehicle = GetVehiclePedIsIn(
    PlayerPedId(),
    false
)

if vehicle ~= 0 then
    local netId =
        NetworkGetNetworkIdFromEntity(vehicle)

    print(('NetID: %s'):format(netId))
end

⑫ ต้องตรวจ Entity ก่อนแปลงไหม

ควร

ตัวอย่าง:

if DoesEntityExist(entity) then
    local netId =
        NetworkGetNetworkIdFromEntity(entity)
end

เพราะการใช้ Handle ที่ Entity ถูกลบไปแล้วสามารถทำให้ Logic ของ Resource ผิดจากที่คาด

⑬ PedToNet, VehToNet ใช้ได้ไหม

Cfx.re ระบุว่ามี Alias สำหรับการแปลงประเภท Entity เช่นแนวคิด PED_TO_NET หรือ NET_TO_VEH นอกเหนือจาก Functions แบบ Generic.

แต่สำหรับ Resource ที่ต้องรองรับ Entity หลายประเภท การใช้ Generic Network ID APIs มักอ่านง่ายกว่า

⑭ ส่ง Entity Handle เข้า TriggerServerEvent ได้ไหม

ตัวเลขสามารถถูกส่งได้ในเชิงข้อมูล แต่ ไม่ควรถือว่า Server หรือ Client อีกเครื่องจะตีความ Handle ตัวนั้นเป็น Entity เดียวกัน

เพราะ Entity Handle เป็น Local Reference.

ควรส่ง:

local netId =
    NetworkGetNetworkIdFromEntity(entity)

TriggerServerEvent(
    'myresource:entityAction',
    netId
)

แทน

⑮ แปลง Network ID กลับเป็น Entity อย่างไร

ใช้:

NetworkGetEntityFromNetworkId(netId)

Cfx.re ระบุว่า Native นี้คืน Local Entity Handle ของ Network ID ที่กำหนด.

ตัวอย่าง:

local entity =
    NetworkGetEntityFromNetworkId(netId)

⑯ NetID มีแล้วแปลงเป็น Entity ได้ทันทีเสมอไหม

ไม่

Cfx.re ระบุว่า Network ID ไม่จำเป็นต้องอยู่ใน Scope ของ Client และต้องตรวจการมีอยู่ก่อนใช้งาน.

นี่เป็นหนึ่งในเรื่องสำคัญที่สุดของ OneSync

⑰ ตรวจ NetID ว่ามี Entity อยู่หรือไม่อย่างไร

ใช้ Native:

NetworkDoesEntityExistWithNetworkId(netId)

Cfx.re มี Native นี้โดยตรงสำหรับตรวจว่า Network Entity ของ ID ที่กำหนดมีอยู่ใน Context นั้นหรือไม่.

ตัวอย่าง:

if NetworkDoesEntityExistWithNetworkId(netId) then
    local entity =
        NetworkGetEntityFromNetworkId(netId)
end

⑱ ทำไม Network ID ถูกต้องแต่ Entity เป็น 0

หนึ่งในสาเหตุสำคัญคือ Entity ยังไม่อยู่ใน Scope ของ Client

Cfx.re เตือนว่า Network ID อาจมีอยู่ใน Server แต่ไม่ได้ Exist ใน Client Context นั้น.

ดังนั้น:

Valid NetID

ไม่ได้แปลว่า:

Entity พร้อมใช้งานบน Client นี้แล้ว

⑲ Scope คืออะไร

ใน OneSync Client ไม่จำเป็นต้อง Synchronize Entities ทั้ง Server

ระบบจะสนใจ Players และ Entities ที่เกี่ยวข้องกับ Client ตาม Network Scope ซึ่งช่วยลดปริมาณข้อมูลที่ต้องสร้างและ Synchronize.

⑳ NetID อยู่นอก Scope หมายความว่าอะไร

หมายความว่า Server หรือ Client อื่นอาจรู้จัก Entity นั้น แต่ Client ปัจจุบันยังไม่ได้มี Local Representation ของ Entity

ดังนั้น:

NetworkDoesEntityExistWithNetworkId(netId)

อาจคืน False

แม้ Entity จะยังมีอยู่บน Server.

㉑ ควร Wait จน Entity เข้า Scope ไหม

เฉพาะเมื่อ Gameplay Logic ต้องการ Entity นั้นจริง และต้องมี Timeout

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

local timeout = GetGameTimer() + 5000

while not NetworkDoesEntityExistWithNetworkId(netId) do
    if GetGameTimer() > timeout then
        return
    end

    Wait(100)
end

local entity =
    NetworkGetEntityFromNetworkId(netId)

อย่า Loop แบบไม่มี Timeout เพราะหาก Entity ไม่มีวันเข้า Scope Resource จะรอตลอด

㉒ อย่า Wait(0) แบบไม่จำกัด

ตัวอย่างที่ไม่ควรทำ:

while not NetworkDoesEntityExistWithNetworkId(netId) do
    Wait(0)
end

หาก Entity ถูกลบหรือไม่มีวันเข้า Scope Loop นี้อาจทำงานต่อโดยไม่มีจุดจบ

ควรมี:

Timeout
Cancellation
Entity lifecycle check

เสมอ

㉓ Player อยู่ไกล Entity จึงหาไม่เจอได้ไหม

ได้

OneSync ใช้ Culling/Scope เพื่อลด Entity Synchronization ที่ไม่จำเป็น และ Network ID ที่อยู่นอก Client Scope อาจไม่มี Local Entity Handle.

นี่อาจเป็น Behavior ปกติ ไม่ใช่ Bug

㉔ Routing Bucket มีผลกับ Scope ไหม

มีผลกับ Visibility/Isolation ของ Players และ Entities

หาก Player กับ Entity อยู่คนละ Routing Bucket พวกเขาอาจไม่เห็นกันตาม OneSync Routing Isolation.

ดังนั้น NetID หา Entity ไม่เจอควรตรวจ Bucket ด้วย

㉕ Entity Ownership คืออะไร

Networked Entity อาจมี Client Owner ที่รับผิดชอบ Synchronization บางส่วน และ Ownership สามารถเปลี่ยนตาม Network Conditions/Scope

OneSync Documentation อธิบายว่า Entities ที่อยู่ไกลจาก Owner สามารถถูก Culled และ Migrated/Disowned ได้.

ดังนั้นอย่าสมมติ Client คนเดิมจะเป็น Owner ตลอด Lifetime

㉖ NetID เปลี่ยนเมื่อ Owner เปลี่ยนไหม

โดยหลัก Network ID ของ Entity ไม่เปลี่ยนระหว่าง Lifetime ของ Entity แม้ Ownership/Local Handle สามารถเปลี่ยนได้.

นี่คือประโยชน์สำคัญของ NetID

㉗ Entity Handle เปลี่ยนแต่ NetID เดิมได้ไหม

ได้

Cfx.re ระบุว่า Entity เดียวกันสามารถมี Handle เปลี่ยนเมื่อถูก Recreated ใน Local Context แต่ Network ID ยังคงเป็นตัวอ้าง Network Entity ใน Lifetime นั้น.

㉘ ต้อง Request Control ทุกครั้งไหม

ไม่ควรทำเป็นสูตรตายตัว

ก่อนใช้ Network Control Native ต้องเข้าใจก่อนว่า:

  • Entity ใครเป็น Owner

  • Action ที่ทำต้องมี Control จริงหรือไม่

  • Logic ควรอยู่ Server หรือ Client

  • OneSync มี Server-side Native ที่เหมาะกว่าหรือไม่

OneSync รองรับ Server-created Entities และ Server-side Entity Operations หลายประเภท จึงไม่จำเป็นต้องโยนทุก Action ไปหา Client Owner.

㉙ Network ID เกี่ยวข้องกับ OneSync อย่างไร

OneSync เป็น Network Synchronization Engine ที่จัดการ Entity State และ Scope

Network ID เป็นตัวระบุ Network Entity ภายในระบบนี้สำหรับการอ้าง Entity ข้าม Context.

จึงมักพบคู่กันใน FiveM Resources

㉚ Server มี Entity Handle ได้ไหม

Cfx.re ระบุว่า Server-side Entity Handles ใช้งานได้เมื่อ State Awareness ไม่ได้ถูกปิด.

และ OneSync รองรับ Server-created Peds, Vehicles และ Objects.

ดังนั้น Server-side Entity Logic ปัจจุบันทำได้มากกว่าแนวทาง FiveM รุ่นเก่า

㉛ Server สร้าง Entity แล้วรับ NetID ได้ไหม

ได้ตาม Context ของ OneSync

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

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

local netId =
    NetworkGetNetworkIdFromEntity(vehicle)

OneSync Documentation ระบุ Server-created Entities เป็นหนึ่งใน Best Practices สำหรับระบบที่ต้องการให้ Server เป็นผู้ควบคุม Entity.

㉜ Server-created Entity ดีกว่าทุกกรณีไหม

ไม่จำเป็น

แต่สำหรับ Entity สำคัญที่ Server ต้องควบคุม การให้ Server Validate และสร้าง Entity เองมักทำให้ Authority ชัดเจนกว่า Client-created Entity

Cfx.re แนะนำ Server-created Entities เป็นแนวทางสำคัญภายใต้ OneSync.

㉝ NetID ของ Entity ที่ถูกลบเกิดอะไรขึ้น

เมื่อ Entity หมด Lifetime Network ID เดิมสามารถถูก Reuse สำหรับ Entity อื่นในอนาคตได้.

ดังนั้น Cache NetID ระยะยาวต้องมี Lifecycle Management

㉞ ต้องตรวจ Entity ทุกครั้งก่อนใช้ Cached NetID ไหม

ควรอย่างน้อยในจุดที่ Entity อาจถูกลบหรือออก Scope

เช่น:

if not NetworkDoesEntityExistWithNetworkId(netId) then
    return
end

local entity =
    NetworkGetEntityFromNetworkId(netId)

ช่วยป้องกันการใช้ Reference เก่า.

㉟ ใช้ Network ID กับ TriggerServerEvent อย่างไร

Client:

local vehicle =
    GetVehiclePedIsIn(
        PlayerPedId(),
        false
    )

if vehicle ~= 0 then
    local netId =
        NetworkGetNetworkIdFromEntity(vehicle)

    TriggerServerEvent(
        'garage:requestAction',
        netId
    )
end

Server สามารถรับ NetID แล้วตรวจ Entity ตาม OneSync Context แทนการรับ Local Handle ของ Client

㊱ Server ควรเชื่อ NetID จาก Client ไหม

ไม่ควรเชื่อโดยตรง

Cfx.re แนะนำว่า Client-triggered Events ต้อง Validate ข้อมูลฝั่ง Server เพราะ Client สามารถ Trigger Network Events และส่ง Arguments ได้.

Server ควรตรวจอย่างน้อย:

NetID มี Entity จริงหรือไม่
Entity Type ถูกหรือไม่
Player มีสิทธิ์ใช้งานหรือไม่
Entity อยู่ใน State ที่ถูกต้องหรือไม่
Action นี้อนุญาตหรือไม่

㊲ อย่าส่งแค่ NetID แล้วให้ Server ทำทุกอย่างทันที

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

RegisterNetEvent(
    'garage:deleteVehicle',
    function(netId)
        local entity =
            NetworkGetEntityFromNetworkId(netId)

        DeleteEntity(entity)
    end
)

โดยไม่มี Validation

Server ต้องตรวจว่า Player มีสิทธิ์จัดการ Entity นั้นก่อน ตามหลัก Server-side Validation ของ Cfx.re.

㊳ Event Direction ควรเป็นอย่างไร

Architecture ที่ดีกว่า:

Client
↓
ส่งคำขอ + NetID
↓
Server
↓
ตรวจ source + Entity + Permission + State
↓
Server ทำ Action
↓
ส่ง Result กลับ Client

NetID เป็นเพียง Reference ไม่ใช่ Permission

㊴ Network ID ใช้ร่วมกับ State Bags ได้ไหม

ได้

หลัง Client/Server Resolve NetID เป็น Entity Handle แล้ว สามารถอ่านหรือกำหนด State Bag ของ Entity นั้นได้ตาม Context

State Bags เป็น Key/Value State ที่ผูกกับ Entities ใน State Awareness Mode.

ตัวอย่าง:

local entity =
    NetworkGetEntityFromNetworkId(netId)

if entity ~= 0 then
    local locked =
        Entity(entity).state['garage:locked']
end

㊵ ควรส่ง State ผ่าน Event หรือ State Bag

ขึ้นกับประเภทข้อมูล

Event

เหมาะกับ Action:

เปิด UI
ขอทำ Action
แจ้ง Result

State Bag

เหมาะกับ Current State:

รถล็อกหรือไม่
Entity Owner ID
Entity Mode

State Bag ช่วยผูก Runtime State กับ Entity โดยตรง.

㊶ NetID กับ State Bag Key ต่างกันอย่างไร

NetID บอกว่า:

“Entity ไหน”

State Bag บอกว่า:

“Entity นั้นมี State อะไร”

ตัวอย่าง:

NetID 143
↓
Vehicle Entity
↓
garage:locked = true
garage:owner = 1824

สองระบบจึงทำงานร่วมกันได้ดี

㊷ State Bag ช่วยลดการส่ง NetID ซ้ำได้ไหม

ในบาง Architecture ได้

หาก State ผูกกับ Entity อยู่แล้ว Client ที่มี Entity ใน Scope สามารถอ่าน State ที่ Replicate ได้ โดยไม่ต้องส่ง Request ไป Serverทุกครั้ง.

แต่ Action สำคัญยังต้องใช้ Server-side Validation

㊸ FiveM Network ID หา Entity ไม่เจอ แก้อย่างไร

ตรวจตามลำดับ:

  1. NetID ถูกต้องหรือไม่

  2. Entity ยังมีอยู่หรือไม่

  3. NetID อยู่ใน Scope หรือไม่

  4. Player กับ Entity อยู่ Routing Bucket เดียวกันหรือไม่

  5. Entity ถูก Delete ไปแล้วหรือไม่

  6. ใช้ NetworkDoesEntityExistWithNetworkId() หรือไม่

  7. แปลงด้วย NetworkGetEntityFromNetworkId() หลังตรวจหรือไม่

  8. Client/Server Context ถูกหรือไม่

Cfx.re ระบุชัดว่า NetID ไม่จำเป็นต้องอยู่ใน Scope บน Client ทุกเครื่อง.

㊹ NetworkGetEntityFromNetworkId ได้ 0

ตรวจ:

if not NetworkDoesEntityExistWithNetworkId(netId) then
    print('Net entity not in scope')
    return
end

ก่อน

แล้วค่อย:

local entity =
    NetworkGetEntityFromNetworkId(netId)

Native ตัวหลังคืน Local Entity Handle ของ NetID ที่กำหนด.

㊺ Entity มีบน Server แต่ไม่มีบน Client

นี่สามารถเป็น Behavior ปกติของ OneSync Scope

Client ไม่จำเป็นต้องสร้าง Entity ทุกตัวที่ Server รู้จัก และ Cfx.re ระบุว่า Network ID อาจไม่อยู่ใน Scope ของ Client.

อย่าแก้ด้วยการ Force ทุก Entity ให้ทุก Client เห็นโดยไม่จำเป็น

㊻ NetID ใช้ได้เมื่ออยู่ใกล้ แต่ใช้ไม่ได้เมื่อไกล

มีน้ำหนักสูงว่าเกี่ยวข้องกับ Scope/Culling

OneSync ลดการ Synchronize Entities ที่ไม่เกี่ยวข้องกับ Client เพื่อลด Network และ Processing Load.

ควรออกแบบ Resource ให้รองรับ Entity ที่เข้าและออก Scope

㊼ Resource ใช้ Entity Handle ข้าม Client แล้วพัง

ให้เปลี่ยน Architecture เป็น:

Client A Entity Handle
↓
Convert to Network ID
↓
Network Event
↓
Client B
↓
ตรวจ NetID in scope
↓
Convert to Local Entity Handle

เพราะ Entity Handles เป็น Local ต่อแต่ละ Client.

㊽ NetID เก่าชี้ Entity ผิด

จำว่า Network IDs สามารถถูก Reuse หลัง Entity เดิมหมด Lifetime.

จึงไม่ควรเก็บ:

netId = 143

ไว้ใน Database แล้วสมมติว่าเลข 143 จะเป็นรถเดิมหลัง Restart หรืออีกหลายชั่วโมงโดยไม่ตรวจ Lifecycle

㊾ วิธี Debug NetID แบบเร็ว

เพิ่ม Log:

print(('NetID=%s'):format(netId))

local exists =
    NetworkDoesEntityExistWithNetworkId(netId)

print(('Exists=%s'):format(tostring(exists)))

if exists then
    local entity =
        NetworkGetEntityFromNetworkId(netId)

    print(('Entity=%s'):format(entity))
end

จากนั้นเปรียบเทียบ:

  • Client A

  • Server

  • Client B

เพื่อดูว่า NetID อยู่ใน Scope ที่ใด

㊿ Checklist FiveM Network ID

ก่อนใช้ Production ตรวจว่า:

  1. เข้าใจ Handle กับ NetID ต่างกัน

  2. ไม่ส่ง Local Handle ข้าม Clients

  3. ใช้ NetworkGetNetworkIdFromEntity

  4. ตรวจ NetID ก่อน Resolve

  5. ใช้ NetworkDoesEntityExistWithNetworkId

  6. ใช้ NetworkGetEntityFromNetworkId

  7. เข้าใจ Scope

  8. เข้าใจ Routing Bucket

  9. เข้าใจ Entity Ownership

  10. ไม่ใช้ NetID เป็น Database Primary Key

  11. ไม่ Cache NetID หลัง Entity ถูกลบ

  12. มี Timeout เมื่อรอ Entity

  13. ไม่ Loop รอ Entity แบบไม่สิ้นสุด

  14. Validate NetID จาก Client ฝั่ง Server

  15. ตรวจ Permission ก่อน Entity Action

  16. ใช้ State Bags กับ Runtime State เมื่อเหมาะสม

  17. ใช้ Server-created Entity เมื่อเหมาะสม

  18. Cleanup References เมื่อ Entity ถูกลบ

  19. ทดสอบผู้เล่นหลายคน

  20. ทดสอบ Entity เข้า/ออก Scope

ตาราง FiveM Entity IDs ที่ควรรู้

ID/Referenceใช้ทำอะไรถาวรหรือไม่
Entity HandleLocal Entity Referenceไม่
Network IDNetwork Entity Referenceไม่
Server IDPlayer ใน Sessionไม่
Player IdentifierAccount Identityยาวกว่าระดับ Session
Database IDPersistent Recordขึ้นกับระบบ

Entity Handle และ Network ID มีพฤติกรรมตาม Network IDs Documentation ของ Cfx.re.

ตัวอย่าง Client ส่ง Vehicle NetID ไป Server

client.lua

RegisterCommand('checkvehicle', function()
    local ped = PlayerPedId()
    local vehicle =
        GetVehiclePedIsIn(ped, false)

    if vehicle == 0 then
        return
    end

    local netId =
        NetworkGetNetworkIdFromEntity(vehicle)

    TriggerServerEvent(
        'example:checkVehicle',
        netId
    )
end, false)

server.lua

RegisterNetEvent(
    'example:checkVehicle',
    function(netId)
        local src = source

        local entity =
            NetworkGetEntityFromNetworkId(netId)

        if entity == 0 then
            return
        end

        -- ตรวจ permission/state เพิ่มก่อนทำ Action
        print(
            ('Player %s referenced entity %s')
            :format(src, entity)
        )
    end
)

Server ควร Validate Client-triggered Event ก่อนทำ Action สำคัญเสมอ.

ตัวอย่าง Client รับ NetID จาก Server

RegisterNetEvent(
    'example:receiveEntity',
    function(netId)

        if not NetworkDoesEntityExistWithNetworkId(netId) then
            print('Entity not in scope')
            return
        end

        local entity =
            NetworkGetEntityFromNetworkId(netId)

        print(
            ('Local entity handle: %s')
            :format(entity)
        )
    end
)

Cfx.re แนะนำให้ตรวจ Network Entity Exists ก่อนใช้งานเมื่อ NetID อาจอยู่นอก Scope.

ตัวอย่าง Wait NetID แบบมี Timeout

local function waitForNetEntity(netId, timeoutMs)
    local expires =
        GetGameTimer() + timeoutMs

    while not NetworkDoesEntityExistWithNetworkId(netId) do
        if GetGameTimer() >= expires then
            return nil
        end

        Wait(100)
    end

    return NetworkGetEntityFromNetworkId(netId)
end

วิธีนี้ปลอดภัยกว่า Loop ที่ไม่มี Timeout เพราะ Entity อาจอยู่นอก Scopeหรือถูกลบไปแล้ว

FiveM Entity Handle ส่งไป Server ได้ไหม

อย่าส่งเพื่อใช้เป็น Network Entity Reference

ควร:

local netId =
    NetworkGetNetworkIdFromEntity(entity)

TriggerServerEvent(
    'resource:event',
    netId
)

เพราะ Handle เป็น Local ส่วน Network ID ถูกออกแบบสำหรับการอ้าง Entity ข้าม Machines.

FiveM NetID ส่งไป Client อื่นได้ไหม

ได้ในฐานะ Network Entity Reference

แต่ Client ปลายทางต้องตรวจว่า NetID นั้นอยู่ใน Scope และมี Local Entity ก่อนใช้งาน.

FiveM Network ID เปลี่ยนไหม

ตลอด Lifetime ของ Entity เดิม Network ID จะไม่เปลี่ยน แต่หลัง Entity หมด Lifetime หมายเลขดังกล่าวสามารถถูก Reuse ได้.

ดังนั้น NetID เหมาะกับ Runtime Networking ไม่ใช่ Persistent Identity

FiveM Entity Handle เปลี่ยนไหม

เปลี่ยนได้ และ Entity เดียวกันสามารถมี Handle คนละค่าบน Client แต่ละเครื่อง.

นี่คือเหตุผลว่าทำไมการส่ง Handle ระหว่าง Clients จึงไม่ใช่แนวทางที่ถูกต้อง

FiveM Network ID กับ OneSync ต่างกันอย่างไร

OneSync คือระบบ Synchronization

Network ID คือ Reference ของ Entity ที่ทำงานอยู่ใน Network Synchronization นั้น

OneSync ยังเกี่ยวข้องกับ Scope, Server-created Entities และ State Awareness เพิ่มเติม.

FiveM Network ID กับ State Bags ต่างกันอย่างไร

Network ID ระบุว่า:

Entity ตัวไหน

State Bag ระบุว่า:

Entity ตัวนั้นมี State อะไร

State Bags เป็น Key/Value Runtime State ที่ผูกกับ Entities ใน State Awareness Mode.

FiveM NetID หาไม่เจอเฉพาะบาง Player

มีโอกาสสูงว่า Client คนนั้นไม่มี Entity อยู่ใน Scope

ตรวจ:

Distance
Routing Bucket
Entity Lifecycle
Network Scope

ก่อนคิดว่า NetID เสีย

Cfx.re ระบุว่า Network ID ไม่จำเป็นต้องอยู่ใน Scope ของ Client ทุกเครื่อง.

FAQ FiveM Network ID

FiveM Network ID คืออะไร

เป็น 16-bit integer ที่ใช้แทน Networked Entity ในช่วง Lifetime หนึ่ง และใช้สำหรับอ้าง Entity ข้าม Clients/Server.

Network ID กับ Entity Handle ต่างกันอย่างไร

Entity Handle เป็น Local Reference ของแต่ละ Client ส่วน Network ID ใช้เป็น Network Reference ข้าม Machines.

แปลง Entity เป็น NetID อย่างไร

ใช้:

NetworkGetNetworkIdFromEntity(entity)

แปลง NetID เป็น Entity อย่างไร

ใช้:

NetworkGetEntityFromNetworkId(netId)

เพื่อรับ Local Entity Handle.

ทำไม NetID มีแต่หา Entity ไม่เจอ

เพราะ Network ID อาจอยู่นอก Client Scope ต้องตรวจด้วย:

NetworkDoesEntityExistWithNetworkId(netId)

ก่อนใช้งาน.

Network ID ใช้เป็น Database ID ได้ไหม

ไม่ควร เพราะ Network ID สามารถถูก Reuse หลัง Entity หมด Lifetime.

ส่ง Entity Handle ไป Client อื่นได้ไหม

ไม่ควรใช้เป็น Network Reference เพราะ Entity Handle เป็น Local ต่อแต่ละ Client ให้แปลงเป็น Network ID ก่อน.

NetID เปลี่ยนเมื่อ Entity Owner เปลี่ยนไหม

Network ID ไม่เปลี่ยนระหว่าง Lifetime ของ Entity แม้ Local Handle หรือ Ownership สามารถเปลี่ยนได้.

ประเด็นสำคัญ

FiveM Network ID หรือ NetID คือ Reference ที่ออกแบบมาเพื่อระบุ Networked Entity ข้าม Clients และ Server โดย Cfx.re ระบุว่า NetID เป็นค่า 16-bit ที่คงเดิมระหว่าง Lifetime ของ Entity แต่สามารถถูกนำกลับมาใช้ใหม่หลัง Entity นั้นหมดอายุแล้ว.

สูตรที่ Developer ควรจำให้แม่นคือ:

Entity Handle
= Local

Network ID
= Network

Database ID
= Persistent

เมื่อต้องส่ง Entity ข้าม Client/Server ให้ใช้ NetworkGetNetworkIdFromEntity() ก่อน และปลายทางต้องตรวจ NetworkDoesEntityExistWithNetworkId() ก่อนใช้ NetworkGetEntityFromNetworkId() เพราะ NetID ไม่จำเป็นต้องอยู่ใน Scope ของ Client ทุกเครื่อง.

เมื่อ NetID ถูกส่งมาจาก Client ฝั่ง Server ยังต้องตรวจ Entity, State และ Permission ก่อนทำ Action สำคัญ เพราะ Cfx.re แนะนำให้ Validate ข้อมูลจาก Client ที่ส่งผ่าน Network Events ไม่ใช่เชื่อ Arguments โดยอัตโนมัติ.

สำหรับผู้อ่าน comsiam ให้จำสูตร “Handle → NetID → Network → Handle” และ comsiam แนะนำให้ Resource ทุกตัวที่อ้าง Entity ข้ามเครื่องใช้ NetID พร้อมตรวจ Scope และ Entity Lifecycle เสมอ จะช่วยลดปัญหา Entity เป็น 0, รถหาย, Object คนละตัว และ Script ทำงานเฉพาะบาง Client

Comments

Popular posts from this blog

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

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

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