- 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.