FiveM OneSync Infinity คืออะไร? ระบบรองรับผู้เล่นจำนวนมากของ FiveM ทำงานอย่างไร

FiveM OneSync Infinity คือสถาปัตยกรรมของ OneSync ที่ออกแบบให้ FiveM สามารถรองรับผู้เล่นจำนวนมากขึ้น โดยใช้ระบบ Player/Entity Culling, Focus Zone และการขยาย Object ID Space เพื่อไม่ให้ Client ต้องสร้างและ Synchronize ผู้เล่นกับ Entity ทุกตัวบน Server พร้อมกัน

เอกสาร Cfx.re ปัจจุบันระบุว่า OneSync: Infinity รองรับสถาปัตยกรรมได้สูงสุดประมาณ

2048 Players

โดยมีการเปลี่ยนแปลงสำคัญ เช่น

Object ID Space
8192
↓
65535

และใช้

Player / Ped / Vehicle Culling
+
Focus Zone

เพื่อจำกัดว่า Client แต่ละคนต้องรับรู้ Entity ใดบ้าง

แนวคิดสำคัญที่สุดคือ

Server มีผู้เล่นจำนวนมาก
↓
Client ไม่จำเป็นต้องเห็นทุกคน
↓
OneSync เลือกเฉพาะสิ่งที่อยู่ใน Scope
↓
Client สร้างเฉพาะ Entity ที่เกี่ยวข้อง

นี่ทำให้ FiveM Server สามารถ Scale จำนวน Player ได้มากขึ้นโดยไม่บังคับให้ Client แต่ละเครื่องจำลองผู้เล่นทั้งหมดบน Serverพร้อมกัน

① OneSync Infinity คืออะไร

OneSync Infinity เป็นส่วนหนึ่งของระบบ Synchronization ของ FiveM ที่เน้นการ Scale Server ให้รองรับ Player จำนวนมาก

ระบบใช้แนวคิดสำคัญ เช่น

Extended Object IDs
Player Culling
Entity Culling
Focus Zone
Server-side State Awareness

เมื่อรวมกันแล้ว Client แต่ละคนจะรับเฉพาะ Game State ที่จำเป็นต่อพื้นที่รอบตัวเอง

② Infinity ต่างจากการเพิ่ม sv_maxclients อย่างเดียวอย่างไร

การเพิ่ม

sv_maxclients 128

ไม่ได้แก้ปัญหาว่า Client จะต้องจัดการ Player และ Entity จำนวนมากอย่างไร

Infinity แก้ที่ Synchronization Architecture

เช่นแทนที่จะให้ Client มี

Player ทั้ง Server

ระบบให้ Clientมีเฉพาะ

Player ที่อยู่ใน Focus Zone

จึงสามารถ Scale ได้ดีกว่า

③ OneSync Infinity รองรับกี่คน

เอกสาร Cfx.re ปัจจุบันระบุ Architecture รองรับได้สูงสุดถึง

2048 Players

และระบุว่ามี Server ที่รองรับ Concurrent Players ระดับมากกว่า 1,000 คน

แต่ตัวเลขนี้ไม่ควรถูกตีความว่า Server ทุกเครื่องสามารถเปิด 2,048 Slots แล้วทำงานได้ดีทันที

ยังขึ้นกับ

  • Hardware

  • Network

  • Resources

  • Database

  • Script Performance

  • Entity Density

  • Subscription/Server Slot Entitlement

④ 2048 Players ไม่ได้แปลว่า Client เห็น 2048 คน

นี่คือจุดสำคัญที่สุด

Client หนึ่งไม่จำเป็นต้องสร้าง Player Peds ของ Player ทั้ง 2,048 คน

Infinity ใช้ Culling

2,048 Players บน Server
↓
Client A
↓
เห็นเฉพาะ Players รอบตัว

จึงต่างจากการพยายามจำลองผู้เล่นทั้งหมดบน Client เดียว

⑤ Object ID Space คืออะไร

Networked Entities ต้องมี Object IDs สำหรับระบบ Network

OneSync Infinity ขยาย Object ID Space จากเดิม

8192

หรือ

1 << 13

ไปเป็น

65535

หรือ

(1 << 16) - 1

ช่วยเพิ่มจำนวน Network Objects ที่ระบบสามารถระบุได้

⑥ ทำไมต้องขยายจาก 8192 เป็น 65535

Server ที่มีผู้เล่นจำนวนมากมี

  • Player Peds

  • Vehicles

  • NPCs

  • Objects

  • Mission Entities

จำนวนมากขึ้น

Object ID Space เดิมจึงเป็นข้อจำกัดสำคัญ

การขยายเป็น 16-bit Space ทำให้ระบบมีพื้นที่สำหรับ Network Object IDs มากขึ้น

แต่ไม่ได้หมายความว่าควร Spawn 65,535 Entities ในบริเวณเดียวกัน

⑦ 65535 คือจำนวน Entity ที่ Client รับได้พร้อมกันไหม

ไม่ควรตีความแบบนั้น

65535 ที่เอกสารพูดถึงคือ Object ID Space

ไม่ใช่คำรับประกันว่า

Client หนึ่ง
สามารถ Stream
65,535 Entities
ในระยะเดียวกัน

Game Engine และ Streaming System ยังมีข้อจำกัด

Entity Density สูงเกินไปยังทำให้ Client มีปัญหาได้

⑧ Culling คืออะไร

Culling คือการไม่ส่งหรือไม่สร้าง Entity บน Client เมื่อ Entity นั้นอยู่นอกพื้นที่ที่ Client จำเป็นต้องรู้

Concept

อยู่ใกล้
→ Sync

อยู่ไกล
→ Cull

ช่วยลด

  • Bandwidth

  • Client CPU

  • Memory Usage

  • Entity Processing

  • Network Traffic

⑨ Focus Zone คืออะไร

Focus Zone คือพื้นที่รอบ Player ที่ใช้กำหนดว่า Entity/Player ใดควรถูกสร้างและ Synchronize บน Client

เอกสาร OneSync ปัจจุบันระบุค่า Focus Zone ที่ Hardcoded อยู่ประมาณ

424 Units

รอบ Player

จึงเกิด Concept

Player
↓
Focus Zone ~424 units
↓
Relevant Entities

⑩ 424 Units เป็นเมตรไหม

ไม่ควรตีความตรง ๆ ว่าเป็นหน่วยเมตรจริงแบบ Physical Measurement

เอกสารใช้คำว่า Game Units

สำหรับ Script Logic ไม่ควร Hardcode Security หรือ Gameplay ทุกอย่างจากค่า 424 เพียงเพราะเป็น Default Culling Radius

ใช้ Distance ที่เหมาะกับ Use Case ของระบบตัวเอง

⑪ Player Culling คืออะไร

Infinity ไม่สร้าง Local Player Representation ของ Player ที่อยู่นอก Focus Zone โดยไม่จำเป็น

ดังนั้น

Server:
มี Player 500 คน

Client:
อาจเห็น Local Players เพียงจำนวนหนึ่ง

นี่มีผลต่อการเขียน Client Script อย่างมาก

⑫ Player Ped Culling คืออะไร

Player Ped ของผู้เล่นที่อยู่นอก Scope ไม่จำเป็นต้องถูกสร้างบน Client

ดังนั้น Script ฝั่ง Client ไม่ควรสมมติว่า

Player บน Server
=
ต้องมี Ped อยู่บน Client นี้

เสมอไป

นี่เป็นหนึ่งในความแตกต่างสำคัญของ Infinity Architecture

⑬ Vehicle Culling คืออะไร

Vehicle ที่อยู่นอกพื้นที่เกี่ยวข้องกับ Client ก็สามารถถูก Cull ตาม Synchronization Rules

ตัวอย่าง

Vehicle อยู่ไกลมาก
↓
Client ไม่จำเป็นต้องมี Vehicle Entity

ช่วยลดจำนวน Network Entities ที่ Client ต้องจัดการ

⑭ Scope คืออะไร

Scope หมายถึงชุด Players/Entities ที่ Client ปัจจุบันรับรู้ผ่าน Network

ตัวอย่าง

Player B เข้าใกล้ Player A
↓
B เข้า Scope ของ A

เมื่อออกไกล

B ออกจาก Scope

Local Representation จึงสามารถถูกสร้างหรือลบตามระบบ

⑮ Client เห็น Player ทุกคนไหม

ไม่

นี่คือสิ่งที่ FiveM Developer ต้องเปลี่ยนวิธีคิด

อย่าสมมติว่า

GetActivePlayers()

จะให้ Player ทุกคนที่ Connected อยู่บน Server

ใน OneSync Infinity Client รู้จัก Local Players ที่อยู่ใน Scope/Context เท่านั้น

⑯ GetActivePlayers ใช้ไม่ได้แล้วหรือไม่

ยังใช้ได้สำหรับ Local Players ที่ Client รู้จัก

แต่ไม่ควรใช้เพื่อถาม

ตอนนี้ Server มี Player ทุกคนใครบ้าง?

ถ้าต้องการ Global Player List ควรทำฝั่ง Server

⑰ Loop Player ทั้ง Server ควรทำที่ไหน

ถ้าต้อง Iterate Player Connected ทั้ง Server ให้ทำ Server-side

ตัวอย่าง

local players =
    GetPlayers()

for _, playerId in ipairs(
    players
) do

    print(playerId)
end

จากนั้นค่อยส่งเฉพาะข้อมูลที่ Client จำเป็นต้องรู้

⑱ ทำไม Global Player Logic ต้องอยู่ Server

เพราะ Server รู้ Connections ทั้งหมด

ส่วน Client ถูกออกแบบให้รับรู้เฉพาะ Relevant Players

จึงควรเป็น

Global Player Logic
→ Server

ส่วน

Nearby Player Rendering
→ Client

นี่เป็น Architecture ที่เหมาะกับ Infinity มากกว่า

⑲ ตัวอย่าง Script ที่มีปัญหากับ Infinity

สมมติ Client ทำ

for _, player in ipairs(
    GetActivePlayers()
) do

    -- คิดว่านี่คือทุกคนใน Server
end

จากนั้นใช้ List นี้สร้าง Scoreboard

Scoreboard อาจไม่ครบ

เพราะ Players นอก Scope ไม่ได้มี Local Player Representation

⑳ Scoreboard ควรทำอย่างไร

ให้ Server เป็นผู้สร้าง Player List

Client
↓
ขอ Scoreboard
↓
Server
↓
Get Players
↓
เลือกข้อมูลที่อนุญาต
↓
ส่ง Client
↓
NUI Display

อย่าสร้าง Global Scoreboard จาก GetActivePlayers() อย่างเดียว

㉑ Admin Player List ก็เช่นเดียวกัน

Admin Menu ที่ต้องเห็นทุกคนควรใช้ข้อมูล Server-side

เช่น

Server ID
Name
Job
Ping
Permission

ตามข้อมูลที่ Server มีสิทธิ์เปิดเผย

Client Scope ไม่ควรเป็น Source of Truth สำหรับ Global Admin List

㉒ Player ID กับ Server ID ต้องแยกกัน

Client Player Index เป็น Local

Server ID ใช้อ้าง Player Connection ฝั่ง Server

ตัวอย่าง Client

local serverId =
    GetPlayerServerId(
        player
    )

แต่ Player ที่อยู่นอก Scope อาจไม่มี Local Player Index ให้ Client นั้น

ดังนั้น Server ID เหมาะกับ Global Server Logic มากกว่า

㉓ Entity Handle กับ Infinity

Entity Handle ยังเป็น Local Reference

ตัวอย่างรถคันเดียว

Client A
Handle 100

Client B
Handle 450

ถ้ารถอยู่ใน Scope ของทั้งสอง

แต่เมื่อออก Scope Client A อาจไม่มี Local Handle ของรถนั้นอีก

จึงต้องออกแบบ Entity Lifecycle อย่างระมัดระวัง

㉔ Network ID ยังสำคัญไหม

สำคัญมาก

Network ID ใช้อ้าง Networked Entity ข้าม Machines ในช่วง Lifetime ของ Entity

Concept

Entity
↓
Network ID
↓
OneSync Infinity
↓
Relevant Clients

แต่ Net ID ไม่รับประกันว่า Client ทุกคนมี Local Entity นั้นอยู่

㉕ มี Net ID แล้ว NetToVeh ได้เสมอไหม

ไม่

ถ้า Vehicle อยู่นอก Scope

Client อาจไม่มี Local Vehicle Entity

ควรตรวจ

NetworkDoesEntityExistWithNetworkId(
    netId
)

ก่อน Resolve

ตาม Use Case

㉖ ตัวอย่างรอ Entity เข้า Scope

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

อย่ารอแบบ Infinite Loop โดยไม่มี Timeout

㉗ Entity ออกจาก Scope แล้ว Handle เดิมใช้ต่อได้ไหม

ไม่ควรถือว่า Valid

หากเก็บ

local missionVehicle =
    vehicle

ไว้นาน

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

if not DoesEntityExist(
    missionVehicle
) then

    return
end

เพราะ Local Entity สามารถถูก Cull/Recreated ได้ตาม Network Lifecycle

㉘ Ownership เกี่ยวกับ Infinity อย่างไร

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

Ownership สามารถ

Migrate
หรือ
Disown

ได้

Cfx.re ระบุว่า Faraway Entities จะไม่ถูกควบคุมโดย Original Owner ต่อไปเสมอ

ช่วยลดปัญหา Entity ถูกผูกกับ Client ที่อยู่ไกล

㉙ Client ที่ Spawn รถเป็น Owner ตลอดไหม

ไม่

Infinity ยิ่งทำให้สมมติฐานนี้อันตราย

Client A Spawn Vehicle
↓
A Own
↓
A ออกไกล
↓
Entity Cull/Migrate
↓
Owner อาจเปลี่ยน

จึงไม่ควรออกแบบ Mission Logic โดยผูกกับ Client A ตลอด Lifetime

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

สำหรับ Lifetime ของ Network Entity เดิม Network ID จะยังใช้อ้าง Entity เดิม

Ownership เปลี่ยนได้โดยไม่จำเป็นต้องเปลี่ยน Network ID

นี่ทำให้ Net ID เหมาะกับ Runtime Entity References มากกว่า Local Handle

㉛ OneSync Infinity กับ Server-created Entity

Server-created Entities มีประโยชน์มากใน Infinity Architecture

เช่น Server สร้าง Mission Vehicle

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

จากนั้น OneSync จัด Network Lifecycle ให้ Relevant Clients

㉜ ทำไม Server-created Entity เหมาะกับ Server ใหญ่

เพราะช่วยลดการให้ Client เป็นผู้ Author Entity Creation

Flow

Client ขอ Mission
↓
Server Validate
↓
Server Create Entity
↓
OneSync Sync เฉพาะ Relevant Clients

เหมาะกับ Server-authoritative Architecture มากกว่า Client Spawn ทุกอย่างเอง

㉝ Server-created Entity ต้องมี Player อยู่ใกล้ไหม

ต้องแยก API

Server-side RPC Entity Creation บางแบบอาจต้องมี Client ใกล้เพื่อ Execute และสามารถ Fail ได้

Cfx.re Migration Docs จึงแนะนำให้เข้าใจความต่างระหว่าง

Server RPC creation

กับ

Server Setter

และตรวจ Failure เสมอ

㉞ CreateVehicleServerSetter น่าสนใจอย่างไร

CreateVehicleServerSetter() เป็น Server Setter ที่ Cfx.re ใช้ใน OneSync Examples

ช่วย Register/Create Vehicle จาก Server-side State โดยตรงมากกว่า RPC-style Creation ในหลาย Use Cases

ตัวอย่าง

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

จากนั้นตรวจ

if vehicle == 0 then
    return
end

㉟ Orphaned Entity คืออะไร

Server-created Entity อาจยังไม่มี Client Owner เมื่อไม่มี Player อยู่ใน Scope

Concept

Server Entity
↓
ไม่มี Client ใกล้
↓
Orphaned

เมื่อ Player เข้ามาในพื้นที่จึงสามารถเกิด Client Simulation Ownership ได้

㊱ SetEntityOrphanMode เกี่ยวข้องอย่างไร

Server สามารถกำหนด

SetEntityOrphanMode(
    entity,
    2
)

เพื่อใช้ KeepEntity Mode สำหรับ Runtime Entity ที่ต้องการให้ Server ไม่ Cleanup เพียงเพราะไม่มี Client Owner

เหมาะกับ Server-created Mission/Persistent-runtime Entity บางประเภท

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

ไม่

Infinity Runtime Persistence ไม่ใช่ Database Persistence

ถ้าต้องการรถคงอยู่หลัง Server Restart ต้องเก็บ

Database ID
Model
Properties
Coords
Owner

แล้ว Spawn Entity ใหม่เมื่อ Serverกลับมา

㊳ Infinity กับ State Bags

State Bags ทำงานได้ดีมากกับ Scoped Entity Architecture

ตัวอย่าง Server Set

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

เมื่อ Entityถูก Replicate ไป Relevant Client State ที่เกี่ยวข้องสามารถเดินทางตาม Replication Policy

ช่วยลดการ Broadcast Custom Events ไปทุกคน

㊴ ทำไม State Bags เหมาะกับ Infinity

เพราะ State สามารถผูกกับ

  • Player

  • Entity

  • Global State

และ Relevant Clients รับตาม State Awareness

ไม่จำเป็นต้องทำ

TriggerClientEvent(
    'vehicle:update',
    -1,
    netId,
    state
)

ทุกครั้งสำหรับ Entity ที่มีเพียง Player ใกล้ ๆ เท่านั้นที่ต้องใช้

㊵ playerEnteredScope ควรใช้ไหม

ใช้ได้ แต่ Cfx.re มี Performance Warning ชัดเจน

เพราะเมื่อ Players จำนวนมากอยู่ใน Scope Events จะถูกเรียกเพิ่มตาม Pair ของ Players ที่เข้า/ออก Scope

ตัวอย่างมี 32 Players รอบ Player หนึ่ง

ระบบอาจมี Scope Event Calls จำนวนมาก

จึงไม่เหมาะสำหรับ State Synchronization ทั่วไป

㊶ Cfx.re แนะนำอะไรแทน Scope Events

ถ้า Use Case เป็น State

Cfx.re แนะนำพิจารณา

State Bags
+
AddStateBagChangeHandler

แทน playerEnteredScope / playerLeftScope

เพราะ Scale ได้เหมาะกับ Scoped State มากกว่าในหลายกรณี

㊷ OneSync Infinity กับ Routing Bucket

Routing Bucket ยังทำงานร่วมกับ Infinity

ตัวอย่าง

Bucket 0
→ Main World

Bucket 100
→ Party A

Bucket 200
→ Party B

ภายในแต่ละ Bucket ยังมี Scope/Culling ตามระบบ OneSync

ดังนั้น Instance และ Culling ทำงานคนละ Layer แต่ใช้ร่วมกันได้

㊸ Routing Bucket ทำให้ Client เห็นทุกคนใน Bucket ไหม

ไม่ควรสมมติแบบนั้น

แม้อยู่ Bucket เดียวกัน หาก Player อยู่ไกลกันมาก Scope/Culling ยังสามารถมีผล

Routing Bucket กำหนด Routing Context

ส่วน Focus Zone กำหนด Relevant Local Scope

จึงเป็น

Bucket
+
Scope

ไม่ใช่ Bucket อย่างเดียว

㊹ Infinity กับ Entity Lockdown

Server สามารถใช้ Routing Bucket Entity Lockdown

SetRoutingBucketEntityLockdownMode(
    bucket,
    'strict'
)

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

เหมาะกับ Server ใหญ่ที่ต้องการลด Client-authored Entities

㊺ Infinity เป็น Anti-Cheat ไหม

ไม่

Infinity เป็น Synchronization Architecture

แม้

  • Culling

  • Server-created Entity

  • Entity Lockdown

  • State Awareness

จะช่วยให้ Server ออกแบบ Security ได้ดีขึ้น

แต่ Network Events ยังต้อง Validate

㊻ Secure Event ยังต้องทำเหมือนเดิมไหม

ต้องทำ

ตัวอย่าง Client

TriggerServerEvent(
    'job:complete'
)

Server ยังต้องตรวจ

Player
Position
Mission State
Permission
Inventory
Reward

OneSync ช่วยให้ Server ตรวจ Entity/Position ได้ดีขึ้น

แต่ไม่ได้ Validate Business Logic ให้อัตโนมัติ

㊼ Infinity ช่วย Server Position Validation อย่างไร

Serverสามารถ

local ped =
    GetPlayerPed(source)

local coords =
    GetEntityCoords(ped)

แล้วตรวจ Distance จาก Mission Point

แทนให้ Clientส่ง Coordinates แล้วเชื่อ

นี่เป็นประโยชน์สำคัญของ State Awareness

㊽ Infinity กับ ESX ใช้ได้ไหม

ได้

ESX จัดการ Framework Layer เช่น

Job
Money
Inventory
Player Data

Infinity จัดการ

Player/Entity Networking
Scope
Ownership
State Awareness

ใช้ร่วมกันได้

㊾ Infinity กับ QBCore/Qbox

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

ไม่ว่า Framework จะเป็น

ESX
QBCore
Qbox
Standalone

Core Multiplayer Networking ยังใช้ OneSync Concepts

ดังนั้น Script ที่เข้าใจ Infinity จะย้าย Framework ได้ง่ายกว่า

㊿ OneSync Infinity ทำให้ FPS ดีขึ้นไหม

เอกสาร Cfx.re ระบุว่า Infinity มี Performance ที่ดีกว่า Plain OneSync ในบางส่วนเนื่องจาก Player Ped Culling

แต่ไม่ได้หมายความว่า

เปิด Infinity
→ FPS สูงทันทีทุก Server

ถ้า Resource ยังทำ

  • Draw ทุกอย่างทุก Frame

  • Spawn Entity จำนวนมหาศาล

  • Send NUI ทุก Tick

  • Broadcast Events ตลอด

  • Heavy Client Loops

FPS ก็ยังต่ำได้

51 Infinity ทำให้ Server CPU เบาลงเสมอไหม

ไม่ควรรับประกันแบบนั้น

Culling ช่วยลด Unnecessary Synchronization

แต่ Server ที่มี Player มากขึ้นก็มี

  • Events มากขึ้น

  • Database Queries มากขึ้น

  • Framework Work มากขึ้น

  • Voice Traffic มากขึ้น

  • Entity State มากขึ้น

Resource Architecture ยังเป็นตัวกำหนด Performance อย่างมาก

52 Entity เยอะในพื้นที่เดียวกันยังเป็นปัญหาไหม

เป็น

เอกสาร Migration เตือนว่า หากมี Entities ใน Range มากกว่าที่ Game รองรับ Clients ในพื้นที่สามารถเกิดปัญหาหรือ Timeout

ดังนั้น

Infinity
≠
Unlimited local entities

อย่าสร้างรถหรือ NPC หลายพันตัวในจุดเดียวแล้วคาดว่า Culling จะแก้ทั้งหมด

53 Server 1000 คนควรอยู่จุดเดียวกันไหม

ในทางเทคนิค Player Density สูงมากยังสร้างภาระมหาศาลกับ

  • Client Rendering

  • Networking

  • Voice

  • Physics

  • Player Peds

  • UI

Infinity เหมาะกับ Server ที่ Player กระจายตัวและใช้ Culling ได้อย่างมีประสิทธิภาพ

ไม่ได้ทำให้ Crowd Size ไร้ข้อจำกัด

54 Voice Chat เกี่ยวกับ Infinity ไหม

ใน Server ผู้เล่นจำนวนมาก Voice Architecture ก็สำคัญ

แม้ OneSync ช่วย Networking ของ Game State แต่ Voice Resource ยังต้องจัด Proximity, Channels และ Routing อย่างเหมาะสม

อย่า Broadcast Voice/Data ไป Player ทุกคนโดยไม่มีเหตุผล

55 Client Script สำหรับ Infinity ควรออกแบบอย่างไร

หลักสำคัญคือ

อย่าสมมติว่า Client เห็นทุก Player
อย่าสมมติว่า Entity อยู่ตลอด
อย่าเก็บ Handle แบบถาวร
อย่า Broadcast ทุก State
อย่า Iterate Global Players ฝั่ง Client

และ

Global Logic → Server
Nearby Logic → Client
State → State Bags เมื่อเหมาะสม
Entity Reference → Network ID

56 Server Script สำหรับ Infinity ควรออกแบบอย่างไร

Server ควรเป็น Authority ของ

  • Player Lists

  • Mission State

  • Economy

  • Inventory

  • Permissions

  • Global Match State

และใช้ OneSync State Awareness สำหรับ

  • Player Position

  • Entity Validation

  • Routing Buckets

  • State Bags

  • Server-created Entities

ตาม Use Case

57 ต้องตั้งค่า Infinity แยกจาก OneSync ไหม

ในเอกสารตั้งค่า FXServer ปัจจุบัน การเปิด OneSync ใช้

set onesync on

และเอกสาร OneSync อธิบาย Infinity เป็น Architecture/Mode ของระบบ OneSync ปัจจุบัน

สำหรับ Server ใหม่ไม่ควร Copy Configuration เก่าจากบทความหลายปีก่อนแล้วเปิด ConVars แบบสุ่ม

ใช้ Configuration และ Server Artifact ปัจจุบันเป็นหลัก

58 Resource ระบุว่าต้องใช้ OneSync ได้ไหม

ได้ใน fxmanifest.lua

dependencies {
    '/onesync'
}

/onesync เป็น Runtime Constraint ที่หมายถึง Resource ต้องการ State Awareness ไม่ถูก Disable

เหมาะกับ Resource ที่ใช้

  • Server Entity Natives

  • State Bags

  • Routing Buckets

อย่างจริงจัง

59 Checklist ทำ Script ให้รองรับ OneSync Infinity

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

① Client สมมติว่าเห็น Player ทุกคนหรือไม่?
② Global Player List อยู่ Server หรือยัง?
③ GetActivePlayers ถูกใช้ผิดวัตถุประสงค์หรือไม่?
④ Entity Handle ถูกเก็บนานเกินไปหรือไม่?
⑤ ก่อนใช้ Handle ตรวจ DoesEntityExist หรือยัง?
⑥ ข้าม Client ใช้ Network ID หรือยัง?
⑦ ตรวจ Entity อยู่ใน Scope หรือยัง?
⑧ มี Infinite Wait รอ Entity หรือไม่?
⑨ Timeout ถูกใส่หรือยัง?
⑩ Ownership สามารถ Migration ได้หรือไม่?
⑪ Logic ผูกกับ Original Owner หรือไม่?
⑫ Server-created Entity เหมาะกว่าหรือไม่?
⑬ RPC Native Failure ถูก Handle หรือยัง?
⑭ State ใช้ State Bag ได้หรือไม่?
⑮ ใช้ playerEnteredScope มากเกินไปหรือไม่?
⑯ Routing Bucket ถูกออกแบบถูกหรือไม่?
⑰ Entity Lockdown เหมาะกับระบบหรือไม่?
⑱ Security Event Validate Server-side หรือไม่?
⑲ Player Position ตรวจ Server-side หรือไม่?
⑳ Spawn Entities หนาแน่นเกินไปหรือไม่?
㉑ Broadcast Event ไปทุกคนโดยไม่จำเป็นหรือไม่?
㉒ Resource Threads Scale ตาม Player Count หรือไม่?
㉓ Database Queries Scale ได้หรือไม่?
㉔ Voice/Proximity ถูกออกแบบสำหรับ Player จำนวนมากหรือไม่?

⑥⓪ ภาพรวม OneSync Infinity แบบเข้าใจง่าย

มอง Server ใหญ่แบบนี้

                   FXServer
                       │
               1000+ Players
                       │
                OneSync Infinity
                       │
        ┌──────────────┼──────────────┐
        │              │              │
   Object IDs        Scope          State
   up to 65535    Focus Zone      Awareness
        │           ~424 units        │
        │              │              │
        └──── Player/Entity Culling ──┘
                       │
                 Relevant State
                       │
              ┌────────┴────────┐
              │                 │
          Client A           Client B
              │                 │
        Nearby entities    Nearby entities

Client แต่ละคนไม่ได้รับโลก Multiplayer ทั้งหมด

แต่ได้รับเฉพาะส่วนที่เกี่ยวข้องกับตัวเอง

นี่คือหัวใจของ Infinity

ตัวอย่าง Global Player List ที่เหมาะกับ Infinity

server.lua

RegisterNetEvent(
    'players:requestList',
    function()

        local src =
            source

        local result = {}

        for _, playerId in ipairs(
            GetPlayers()
        ) do

            result[#result + 1] = {
                id =
                    tonumber(
                        playerId
                    ),

                name =
                    GetPlayerName(
                        playerId
                    )
            }
        end

        TriggerClientEvent(
            'players:receiveList',
            src,
            result
        )
    end
)

Client ไม่ต้องพยายามสร้าง Global List จาก Local Scope

client.lua

RegisterNetEvent(
    'players:receiveList',
    function(players)

        print(
            'Players:',
            #players
        )

        -- Update NUI
    end
)

Flow คือ

Client
↓
Request
↓
Server
↓
Global Connected Players
↓
Filtered Result
↓
Client

เหมาะกับ Infinity Architecture มากกว่า

ตัวอย่าง Nearby Player Logic ฝั่ง Client

ถ้าต้องการทำ Nametag เฉพาะคนใกล้ตัว

CreateThread(function()

    while true do

        local sleep =
            1000

        local myPed =
            PlayerPedId()

        local myCoords =
            GetEntityCoords(
                myPed
            )

        for _, player in ipairs(
            GetActivePlayers()
        ) do

            if player ~=
                PlayerId() then

                local ped =
                    GetPlayerPed(
                        player
                    )

                if DoesEntityExist(
                    ped
                ) then

                    local coords =
                        GetEntityCoords(
                            ped
                        )

                    local distance =
                        #(myCoords - coords)

                    if distance <
                        30.0 then

                        sleep = 0

                        -- Draw nearby UI
                    end
                end
            end
        end

        Wait(sleep)
    end
end)

ตรงนี้ GetActivePlayers() เหมาะเพราะ Requirement คือ

Nearby Players ที่ Client รู้จัก

ไม่ใช่ Global Player List

นี่คือการเลือก API ให้ตรงกับ Infinity Model

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

FiveM OneSync Infinity คืออะไร

คือ OneSync Architecture สำหรับ Scale Player จำนวนมาก โดยใช้ Object ID Space ที่ใหญ่ขึ้นและ Player/Entity Culling ตาม Focus Zone

OneSync Infinity รองรับกี่คน

เอกสาร Cfx.re ปัจจุบันระบุสูงสุดถึงประมาณ 2,048 Players

เปิด 2,048 Slots แล้วรองรับได้ทันทีไหม

ไม่ ประสิทธิภาพจริงขึ้นกับ Hardware, Resources, Database, Network, Subscription และ Server Design

Object ID เพิ่มเป็นเท่าไร

เอกสารระบุจาก 8,192 เป็น 65,535

Focus Zone เท่าไร

เอกสารปัจจุบันระบุ Hardcoded ประมาณ 424 Game Units รอบ Player

Client เห็น Player ทุกคนหรือไม่

ไม่ Players ที่อยู่นอก Focus Zone ไม่จำเป็นต้องถูกสร้าง Local บน Client

GetActivePlayers คือ Player ทุกคนบน Server ไหม

ไม่ควรใช้ในความหมายดังกล่าวภายใต้ Infinity มันสะท้อน Players ที่ Client มี Local Representation ตาม Scope

ถ้าต้องการ Player ทุกคนทำอย่างไร

ทำ Server-side เช่นผ่าน GetPlayers() แล้วส่งข้อมูลที่ Client จำเป็นต้องใช้

Network ID ยังใช้ไหม

ใช้ และสำคัญสำหรับอ้าง Networked Entity ข้าม Client/Server

มี Network ID แล้ว Entity ต้องอยู่บน Client ไหม

ไม่ Entity อาจอยู่นอก Scope

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

ได้เมื่อ Scope และ Networking State เปลี่ยน

คน Spawn Entity เป็น Owner ตลอดไหม

ไม่

State Bags เหมาะกับ Infinity ไหม

เหมาะมากสำหรับ Scoped State Replication ในหลาย Use Cases

playerEnteredScope ควรใช้ไหม

ใช้ได้แต่ Cfx.re เตือนเรื่อง Scaling Cost และแนะนำ State Bags เมื่อ Use Case เหมาะสม

Routing Bucket ยังใช้ได้ไหม

ได้ และทำงานร่วมกับ Scope/Culling

Bucket เดียวกันหมายความว่าเห็นกันทั่ว Map ไหม

ไม่ควรสมมติแบบนั้น Scope/Culling ยังมีผล

Infinity เป็น Anti-Cheat ไหม

ไม่

Infinity ช่วย Security อย่างไร

ช่วยให้ Server มี State Awareness มากขึ้น เช่นตรวจ Player Position/Entity State ฝั่ง Server แต่ Business Logic ยังต้อง Validate เอง

Infinity ทำให้ FPS สูงขึ้นไหม

Culling ช่วยลด Entity Processing ในหลายสถานการณ์ แต่ Script ที่เขียนไม่ดีหรือ Entity Density สูงยังทำ FPS ต่ำได้

Infinity ทำให้ Entity Unlimited ไหม

ไม่ Local Game/Streaming Limits ยังมีอยู่

Server 1,000 คนอยู่จุดเดียวกันได้สบายไหม

ไม่ควรคาดหวังแบบนั้น Player Density สูงยังสร้างภาระ Client, Network, Physics และ Voice อย่างมาก

ต้องใช้ set onesync on ไหม

เอกสารตั้งค่า FXServer ปัจจุบันใช้

set onesync on

เพื่อเปิด OneSync/Server-side State Awareness

สรุป FiveM OneSync Infinity คืออะไร

FiveM OneSync Infinity คือสถาปัตยกรรมที่ทำให้ FiveM สามารถ Scale ไปสู่ Server ผู้เล่นจำนวนมาก โดยไม่บังคับให้ Client แต่ละคนสร้างและ Synchronize Player กับ Entity ทั้ง Serverพร้อมกัน

เทคโนโลยีหลักประกอบด้วย

Object ID Space
8192
↓
65535

ร่วมกับ

Focus Zone
≈ 424 Game Units

และ

Player Culling
Ped Culling
Vehicle/Entity Culling

ดังนั้น

Server
→ อาจมี 1000+ Players

แต่

Client
→ รู้จักเฉพาะ Relevant Players/Entities

ผลกระทบต่อ Developer สำคัญมาก

อย่าเขียน Client Script โดยคิดว่า

GetActivePlayers()
=
Player ทุกคนใน Server

หากต้องการ Global Player Data ให้ทำฝั่ง Server

ในทางกลับกัน หากต้องการ Nearby Gameplay

Nametag
Nearby Interaction
3D Text
Local Player Effects

Client-side Scope กลับเป็นสิ่งที่เหมาะสม

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

Network ID
≠
Entity ต้องอยู่ใน Client เสมอ

จึงควรตรวจ Network Existence, Entity Existence และมี Timeout เมื่อต้องรอ Streaming

นอกจากนี้ Ownership สามารถ Migration เมื่อ Entity เข้าออก Scope ดังนั้น Resource ไม่ควรผูก Multiplayer Logic กับ Client Owner คนเดิมถาวร

สำหรับผู้ที่กำลังเรียน FiveM Developer กับ comsiam สิ่งที่ควรเปลี่ยนวิธีคิดหลังเข้าใจ Infinity คือ Client ไม่ได้เป็นสำเนาของ Server World ทั้งหมด แต่เป็นหน้าต่างที่เห็นเฉพาะส่วนของโลก Multiplayer ที่เกี่ยวข้องกับตัวเอง

หลักสำคัญจาก comsiam คือ Global Logic ให้อยู่ Server, Nearby Logic ให้อยู่ Client และใช้ OneSync Scope, Network ID, State Bags และ Server-side State Awareness ให้ตรงกับหน้าที่ของแต่ละระบบ

หัวข้อถัดไปคือ วิธีเปิด OneSync FiveM ซึ่งจะเข้าสู่การตั้งค่าจริงใน server.cfg, วิธีตรวจว่า OneSync ทำงานหรือไม่, sv_maxclients, Resource Constraint /onesync และปัญหาที่มักเกิดจากการใช้ Config เก่าหรือเข้าใจ OneSync Legacy/Infinity ผิด

Comments

Popular posts from this blog

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

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

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