1 poin oleh GN⁺ 2024-11-24 | Belum ada komentar. | Bagikan ke WhatsApp
  • Dalam lingkungan FPS cepat, informasi status yang datang terlambat bernilai rendah, sehingga Quake 3 memilih desain berpusat pada UDP/IP untuk mengurangi latensi
  • NetChannel mengabstraksikan komunikasi di atas UDP yang bisa mengalami kehilangan paket, dan server menghitung ulang hanya selisih status yang diperlukan melalui riwayat snapshot per klien
  • Server menggunakan Master Gamestate, 32 gamestate terbaru, dan dummy gamestate secara bersama-sama sehingga update penuh dan update delta dibuat dengan prosedur yang sama
  • Jika tidak ada ACK dari klien, server membandingkan snapshot terakhir yang telah dikonfirmasi dengan status saat ini, lalu memasukkan perubahan yang terlewat dan perubahan baru ke dalam satu pesan
  • Meski C tidak memiliki introspeksi bawaan, netField_t dan macro digunakan untuk menemukan selisih field, sementara NetChannel menghindari fragmentasi router dengan pemecahan awal 1400 byte

Model jaringan yang mengasumsikan UDP/IP

  • Model jaringan Quake 3 dinilai sebagai salah satu bagian paling elegan dari engine, dan pada level rendah komunikasi diabstraksikan oleh modul NetChannel yang pertama kali muncul di Quake World
  • Dalam game cepat, informasi yang terlewat pada pengiriman pertama segera menjadi usang, sehingga mengirim status terbaru lebih menguntungkan daripada mengirim ulang
  • Karena itu, engine ini tidak memiliki jejak TCP/IP, dan latensi yang ditimbulkan oleh transmisi andal dianggap sulit diterima
  • Pada stack jaringan ditambahkan dua lapisan yang saling eksklusif
    • Enkripsi menggunakan kunci yang telah dibagikan sebelumnya
    • Kompresi menggunakan kunci Huffman yang telah dihitung sebelumnya
  • Server mengurangi ukuran UDP datagram sekaligus mengompensasi ketidakandalan
    • Membuat paket delta melalui riwayat snapshot
    • Mengirim hanya field yang berubah dengan metode introspeksi memori

Peran server dan klien

  • Alur di sisi klien sederhana
    • Mengirim perintah ke server pada setiap frame
    • Menerima update gamestate dari server
  • Server harus menyebarkan Master Gamestate ke setiap klien sambil mempertimbangkan paket UDP yang hilang
  • Mekanisme inti terdiri dari tiga elemen
    • Master Gamestate: status game yang secara umum benar; perintah klien masuk melalui NetChannel, diubah menjadi event_t, lalu server memodifikasi status game
    • 32 gamestate terbaru per klien: status yang dikirim lewat jaringan disimpan dalam circular array dan disebut snapshot
    • dummy gamestate: status dengan semua field bernilai 0, digunakan sebagai acuan pembuatan delta ketika tidak ada status sebelumnya
  • Server menggunakan tiga elemen ini untuk membuat pesan update yang akan diteruskan ke NetChannel
  • Karena harus mempertahankan banyak gamestate per klien, penggunaan memori menjadi besar
    • Dalam pengukuran, digunakan 8MB untuk 4 pemain

Membuat update penuh dan update parsial dengan snapshot

  • Contoh menggunakan situasi saat mengirim update ke Client1, ketika status Client2 terdiri dari empat field: pos[X], pos[Y], pos[Z], health
  • Komunikasi dilakukan melalui UDP/IP, dan di internet pesan bisa sering hilang
  • Frame server pertama

    • Server menerapkan semua update yang diterima dari semua klien ke Master Gamestate, lalu menyebarkan status ke Client1
    • Modul jaringan selalu mengikuti prosedur yang sama
    • Master Gamestate disalin ke slot berikutnya dalam riwayat klien
    • Snapshot yang disalin dibandingkan dengan snapshot lain
    • Pada update pertama, karena riwayat Client1 tidak memiliki snapshot yang valid, perbandingan dilakukan dengan dummy snapshot
    • Karena semua field pada dummy snapshot bernilai 0, hasilnya adalah update penuh
    • Di depan setiap field ditempelkan bit marker yang menunjukkan apakah ada perubahan
    • Contoh update penuh menggunakan 132 bit
    • Formatnya adalah [1 A_on32bits 1 B_on32bits 1 B_on32bits 1 C_on32bits]
  • Frame server kedua

    • Pada frame berikutnya, Client2 bergerak pada sumbu Y sehingga nilai pos[1] menjadi E
    • Client1 telah meng-ACK penerimaan update sebelumnya, sehingga Snapshot1 berada dalam status ACK
    • Server menyalin Master Gamestate ke slot riwayat berikutnya untuk membuat Snapshot2, lalu membandingkannya dengan Snapshot1 yang valid
    • Hasilnya, hanya pos[1] = E yang berubah yang dikirim lewat jaringan
    • Karena setiap field memiliki bit marker, update parsial ini menggunakan 36 bit
    • Formatnya adalah [0 1 32bitsNewValue 0 0]
  • Frame server ketiga

    • Pada frame berikutnya, Client2 kehilangan health sehingga health = H
    • Client1 tidak meng-ACK update terakhir
    • Paket UDP dari server mungkin hilang, atau ACK dari klien mungkin hilang
    • Dalam kedua kasus, snapshot tersebut tidak dapat digunakan
    • Server menyalin Master Gamestate ke slot berikutnya untuk membuat Snapshot3, lalu membandingkannya dengan Snapshot1 yang terakhir di-ACK
    • Pesan yang dikirim adalah update parsial, yang berisi perubahan sebelumnya pos[1] = E dan perubahan baru health = H sekaligus
    • Jika Snapshot1 sudah terlalu lama sehingga tidak dapat digunakan, engine kembali mengirim update penuh dengan dummy snapshot sebagai acuan

Cara mengompensasi kehilangan dengan prosedur yang sama

  • Kesederhanaan sistem snapshot terletak pada kenyataan bahwa algoritme yang sama secara otomatis menangani dua pekerjaan
    • Membuat update penuh atau update parsial
    • Mengirim ulang informasi lama yang belum diterima bersama informasi baru dalam satu pesan
  • Kehilangan paket UDP tidak ditangani dengan alur terpisah yang rumit; sistem menghitung selisih antara snapshot terakhir yang di-ACK dan Master Gamestate saat ini untuk mengompensasinya
  • Jika tidak ada status sebelumnya atau status tersebut tidak dapat digunakan, sistem memulihkan dengan mengirim status penuh berdasarkan dummy snapshot

Cara menemukan selisih field di C

  • Quake 3 tidak memiliki introspeksi dalam bahasa C, tetapi posisi setiap field disusun sebelumnya melalui array netField_t dan direktif preprosesor
  • netField_t berisi nama field, offset, dan jumlah bit
  • Macro NETF(x) menggunakan operator stringizing dan perhitungan offset terhadap entityState_t agar informasi field dapat ditulis secara singkat
  • Contoh strukturnya sebagai berikut
typedef struct { char *name; int offset; int bits; } netField_t;

// using the stringizing operator to save typing...
#define NETF(x) #x,(int)&((entityState_t*)0)->x

netField_t entityStateFields[] = {
    { NETF(pos.trTime), 32 },
    { NETF(pos.trBase[0]), 0 },
    { NETF(pos.trBase[1]), 0 },
    ...
}
  • Implementasi lengkapnya ada dalam bagian dari MSG_WriteDeltaEntity
  • Quake 3 tidak menafsirkan makna dari objek yang dibandingkan; ia mengikuti index, offset, dan size di entityStateFields untuk mengirim selisih lewat jaringan

Alasan membagi lebih dulu menjadi 1400 byte

  • Modul NetChannel membagi pesan menjadi potongan 1400 byte meskipun ukuran maksimum UDP datagram adalah 65507 byte
  • Kode terkait ada di Netchan_Transmit
  • Karena MTU sebagian besar jaringan adalah 1500 byte, pemecahan 1400 byte adalah pilihan untuk mencegah router melakukan fragmentasi paket di jalur internet
  • Ada dua alasan fragmentasi router perlu dihindari
    • Saat masuk ke jaringan, router harus menahan paket selama memecahnya menjadi fragmen
    • Saat keluar dari jaringan, router harus menunggu semua fragmen datagram lalu melakukan perakitan ulang yang mahal

Pesan yang wajib terkirim

  • Sistem snapshot mengompensasi UDP datagram yang hilang di jaringan, tetapi sebagian pesan dan perintah harus tetap terkirim
  • Contohnya adalah ketika pemain keluar atau ketika server meminta klien memuat level baru
  • Jaminan ini diabstraksikan oleh NetChannel

Bacaan terkait

Belum ada komentar.

Belum ada komentar.