Selama dua minggu terakhir saya telah mengerjakan mesin jaringan untuk game saya. Sebelum itu, saya tidak tahu apa-apa tentang teknologi jaringan dalam permainan, jadi saya membaca banyak artikel dan melakukan banyak percobaan untuk memahami semua konsep dan dapat menulis mesin jaringan saya sendiri.
Dalam panduan ini, saya ingin berbagi dengan Anda berbagai konsep yang perlu Anda pelajari sebelum menulis mesin gim Anda sendiri, serta sumber daya dan artikel terbaik untuk mempelajarinya.
Secara umum, ada dua jenis utama arsitektur jaringan: peer-to-peer dan client-server. Dalam arsitektur peer-to-peer (p2p), data ditransfer antara setiap pasangan pemain yang terhubung, dan dalam arsitektur client-server, data ditransmisikan hanya antara pemain dan server.
Meskipun arsitektur peer-to-peer masih digunakan di beberapa game, standarnya adalah client-server: lebih mudah diimplementasikan, membutuhkan lebar saluran yang lebih kecil dan memfasilitasi perlindungan terhadap kecurangan. Oleh karena itu, dalam panduan ini, kami akan fokus pada arsitektur client-server.
Secara khusus, kami paling tertarik pada server otoriter: dalam sistem seperti itu, server selalu benar. Sebagai contoh, jika seorang pemain berpikir bahwa ia berada di koordinat (10, 5), dan server mengatakan kepadanya bahwa ia berada di (5, 3), maka klien harus mengganti posisinya dengan posisinya yang ditransmisikan oleh server, dan bukan sebaliknya. Menggunakan server otoriter membuat pengakuan curang lebih mudah.
Ada tiga komponen utama untuk sistem jaringan game:
- Transport Protocol: bagaimana data ditransfer antara klien dan server.
- Protokol aplikasi: apa yang ditransfer dari klien ke server dan dari server ke klien dan dalam format apa.
- Logika aplikasi: bagaimana data yang dikirimkan digunakan untuk memperbarui status klien dan server.
Sangat penting untuk memahami peran setiap bagian dan kesulitan yang terkait dengannya.
Protokol transportasi
Langkah pertama adalah memilih protokol untuk mengangkut data antara server dan klien. Ada dua protokol Internet untuk ini:
TCP dan
UDP . Tapi Anda bisa membuat protokol transport Anda sendiri berdasarkan salah satunya atau menggunakan perpustakaan tempat mereka digunakan.
Membandingkan TCP dan UDP
Baik TCP dan UDP berbasis
IP . IP memungkinkan Anda untuk mentransfer paket dari sumber ke penerima, tetapi tidak menjamin bahwa paket yang dikirim cepat atau lambat akan mencapai penerima, bahwa paket tersebut akan mencapai setidaknya satu kali dan urutan paket akan tiba dalam urutan yang benar. Selain itu, paket mungkin hanya berisi ukuran data terbatas yang ditentukan oleh nilai
MTU .
UDP hanyalah lapisan tipis di atas IP. Karena itu, ia memiliki keterbatasan yang sama. Sebaliknya, TCP memiliki banyak fitur. Ini menyediakan koneksi yang dapat diandalkan dan dipesan antara dua node dengan pengecekan kesalahan. Oleh karena itu, TCP sangat nyaman dan digunakan di banyak protokol lain, misalnya, dalam
HTTP ,
FTP dan
SMTP . Tetapi semua fitur ini ada harganya:
penundaan .
Untuk memahami mengapa fungsi-fungsi ini dapat menyebabkan penundaan, Anda perlu memahami cara kerja TCP. Ketika node pengirim meneruskan paket ke node penerima, ia mengharapkan untuk menerima pengakuan (ACK). Jika setelah waktu tertentu ia tidak menerimanya (karena paket atau konfirmasi hilang, atau karena alasan lain), maka ia mengirim ulang paket tersebut. Selain itu, TCP memastikan bahwa paket diterima dalam urutan yang benar, oleh karena itu, sampai paket yang hilang diterima, semua paket lainnya tidak dapat diproses, bahkan jika mereka telah diterima oleh node penerima.
Tapi seperti yang mungkin Anda pahami, penundaan dalam permainan multi pemain sangat penting, terutama dalam genre aktif seperti FPS. Itulah sebabnya banyak game menggunakan UDP dengan protokol mereka sendiri.
Protokol asli berbasis UDP bisa lebih efisien daripada TCP karena berbagai alasan. Misalnya, ini mungkin menandai beberapa paket sebagai tepercaya dan yang lainnya tidak dipercaya. Oleh karena itu, ia tidak peduli jika paket yang tidak dipercaya mencapai penerima. Atau dapat memproses beberapa aliran data sehingga paket yang hilang dalam satu aliran tidak memperlambat aliran yang tersisa. Misalnya, mungkin ada aliran untuk input pemain dan aliran lain untuk pesan obrolan. Jika pesan obrolan yang bukan data penting hilang, itu tidak akan memperlambat input, yang mendesak. Atau, protokol eksklusif dapat mengimplementasikan keandalan secara berbeda dari pada TCP agar lebih efisien dalam permainan video.
Jadi, jika TCP benar-benar omong kosong, maka kita akan membuat protokol transport sendiri berdasarkan UDP?
Semuanya sedikit lebih rumit. Meskipun TCP hampir tidak optimal untuk sistem jaringan game, ia dapat bekerja dengan baik di game Anda dan menghemat waktu Anda yang berharga. Misalnya, keterlambatan mungkin bukan masalah untuk gim berbasis giliran atau gim yang hanya bisa dimainkan di LAN, di mana ada penundaan dan kehilangan paket jauh lebih sedikit daripada di Internet.
Banyak game yang sukses, termasuk World of Warcraft, Minecraft, dan Terraria, menggunakan TCP. Namun, sebagian besar FPS menggunakan protokol berbasis UDP eksklusif, jadi kami akan membicarakannya lebih lanjut di bawah ini.
Jika Anda memutuskan untuk menggunakan TCP, maka pastikan
algoritma Nagle dinonaktifkan, karena
mendukung paket sebelum mengirim, yang artinya meningkatkan penundaan.
Untuk mempelajari lebih lanjut tentang perbedaan antara UDP dan TCP dalam konteks game multi-pemain, Anda dapat membaca artikel oleh Glenn Fiedler
UDP vs. TCPProtokol sendiri
Jadi, Anda ingin membuat protokol transport Anda sendiri, tetapi tidak tahu harus mulai dari mana? Anda beruntung, karena Glenn Fiedler menulis dua artikel luar biasa tentang ini. Anda akan menemukan banyak pikiran pintar di dalamnya.
Artikel pertama,
Networking for Game Programmer 2008, lebih sederhana dari yang kedua,
Building A Game Network Protocol 2016. Saya sarankan Anda mulai dengan yang lebih tua.
Perlu diingat bahwa Glenn Fiedler adalah pendukung besar menggunakan protokol UDP sendiri. Dan setelah membaca artikelnya, Anda pasti akan mendapatkan lebih dari pendapatnya bahwa TCP memiliki kelemahan serius dalam video game, dan Anda ingin menerapkan protokol Anda sendiri.
Tetapi jika Anda baru mengenal jaringan, bantulah diri Anda sendiri dan gunakan TCP atau perpustakaan. Untuk berhasil mengimplementasikan protokol transport Anda sendiri, Anda harus belajar banyak.
Perpustakaan jaringan
Jika Anda membutuhkan sesuatu yang lebih efisien daripada TCP, tetapi Anda tidak ingin repot dengan menerapkan protokol Anda sendiri dan masuk ke banyak detail, maka Anda dapat menggunakan perpustakaan jaringan. Ada banyak dari mereka:
Saya belum mencoba semuanya, tetapi saya lebih suka ENet, karena mudah digunakan dan dapat diandalkan. Selain itu, ia memiliki dokumentasi yang jelas dan tutorial untuk pemula.
Protokol Transportasi: Kesimpulan
Untuk meringkas: ada dua protokol transport utama: TCP dan UDP. TCP memiliki banyak fitur berguna: keandalan, pemesanan paket, deteksi kesalahan. UDP tidak memiliki semua ini, tetapi TCP, pada dasarnya, telah meningkatkan penundaan yang tidak dapat diterima untuk beberapa game. Artinya, untuk memastikan latensi rendah, Anda dapat membuat protokol berbasis UDP Anda sendiri atau menggunakan perpustakaan yang mengimplementasikan protokol transport UDP dan disesuaikan untuk permainan video multi-pemain.
Pilihan antara TCP, UDP dan perpustakaan tergantung pada beberapa faktor. Pertama, dari kebutuhan gim: apakah perlu latensi rendah? Kedua, dari persyaratan protokol aplikasi: apakah perlu protokol yang dapat diandalkan? Seperti yang akan kita lihat di bagian selanjutnya, Anda dapat membuat protokol aplikasi yang protokolnya tidak dapat diandalkan cukup cocok. Akhirnya, Anda juga harus memperhitungkan pengalaman pengembang mesin jaringan.
Saya punya dua tips:
- Maksimalkan protokol transport dari sisa aplikasi sehingga dapat dengan mudah diganti tanpa menulis ulang seluruh kode.
- Jangan lakukan optimasi prematur. Jika Anda bukan spesialis jaringan dan tidak yakin apakah Anda memerlukan protokol transport berbasis UDP Anda sendiri, Anda bisa mulai dengan TCP atau pustaka yang menyediakan keandalan, lalu menguji dan mengukur kinerja. Jika Anda memiliki masalah dan Anda yakin bahwa alasannya ada pada protokol transport, maka mungkin sudah waktunya untuk membuat protokol transport Anda sendiri.
Di akhir bagian ini, saya sarankan Anda membaca
Pengantar Brian Hook
untuk Pemrograman Game Multi Pemain , yang mencakup banyak topik yang dibahas di sini.
Protokol aplikasi
Sekarang kita dapat bertukar data antara klien dan server, kita perlu memutuskan data mana yang akan ditransfer dan dalam format apa.
Skema klasik adalah bahwa klien mengirim input atau tindakan ke server, dan server mengirimkan status permainan saat ini kepada klien.
Server tidak mengirim status yang lengkap, tetapi difilter dengan entitas yang berada di sebelah pemain. Dia melakukan ini karena tiga alasan. Pertama, keadaan keseluruhan mungkin terlalu besar untuk transmisi frekuensi tinggi. Kedua, pelanggan terutama tertarik pada data visual dan audio, karena sebagian besar logika game disimulasikan pada server game. Ketiga, dalam beberapa permainan pemain tidak perlu mengetahui data tertentu, misalnya, posisi lawan di ujung peta, karena kalau tidak, ia dapat mengendus paket dan tahu persis ke mana harus bergerak untuk membunuhnya.
Serialisasi
Langkah pertama adalah mengubah data yang ingin kami kirim (input atau status permainan) ke dalam format yang sesuai untuk transmisi. Proses ini disebut
serialisasi .
Pikiran segera terlintas dalam pikiran untuk menggunakan format yang dapat dibaca manusia, seperti JSON atau XML. Tetapi itu akan benar-benar tidak efektif dan sia-sia akan menempati sebagian besar saluran.
Sebagai gantinya, Anda disarankan untuk menggunakan format biner yang jauh lebih ringkas. Artinya, paket hanya akan berisi beberapa byte. Di sini Anda perlu mempertimbangkan masalah
urutan byte , yang mungkin berbeda pada komputer yang berbeda.
Anda dapat menggunakan perpustakaan untuk membuat serialisasi data, misalnya:
Pastikan perpustakaan membuat arsip portabel dan mengurus urutan byte.
Solusi independen mungkin implementasi independen, tidak terlalu rumit, terutama jika Anda menggunakan pendekatan berorientasi data dalam kode. Selain itu, ini akan memungkinkan Anda untuk melakukan optimasi yang tidak selalu memungkinkan saat menggunakan perpustakaan.
Glenn Fiedler telah menulis dua artikel tentang serialisasi:
Paket Membaca dan Menulis dan
Strategi Serialisasi .
Kompresi
Jumlah data yang ditransfer antara klien dan server dibatasi oleh bandwidth saluran. Kompresi data memungkinkan Anda untuk mentransfer lebih banyak data di setiap foto, meningkatkan kecepatan refresh, atau hanya mengurangi persyaratan saluran.
Packing sedikit
Teknik pertama adalah pengepakan bit. Ini terdiri dalam menggunakan persis jumlah bit yang diperlukan untuk menggambarkan nilai yang diinginkan. Misalnya, jika Anda memiliki enumerasi yang dapat memiliki 16 nilai yang berbeda, maka alih-alih seluruh byte (8 bit), Anda hanya dapat menggunakan 4 bit.
Glenn Fiedler menjelaskan cara mengimplementasikan ini di bagian kedua artikel
Paket Membaca dan Menulis .
Pengepakan bit bekerja sangat baik dengan pengambilan sampel, yang akan menjadi topik bagian selanjutnya.
Diskretisasi
Diskretisasi adalah teknik kompresi lossy yang hanya menggunakan subset dari nilai yang mungkin untuk mengkodekan nilai. Cara termudah untuk menerapkan diskritisasi adalah dengan membulatkan angka floating point.
Glenn Fiedler (lagi!) Menunjukkan cara menerapkan pengambilan sampel dalam praktiknya di artikel
Snapshot Compression-nya .
Algoritma kompresi
Teknik selanjutnya adalah algoritma kompresi lossless.
Di sini, menurut pendapat saya, tiga algoritma paling menarik yang perlu Anda ketahui:
- Pengodean Huffman dengan kode pra-komputasi yang sangat cepat dan dapat memberikan hasil yang baik. Itu digunakan untuk mengompres paket di mesin jaringan Quake3.
- zlib adalah algoritma kompresi tujuan umum yang tidak pernah meningkatkan jumlah data. Seperti yang dapat dilihat di sini , telah digunakan dalam banyak aplikasi. Mungkin berlebihan untuk memperbarui status. Tetapi bisa berguna jika Anda perlu mengirim aset, teks panjang atau bantuan kepada klien dari server.
- Menyalin panjang seri mungkin adalah algoritma kompresi paling sederhana, tetapi sangat efektif untuk tipe data tertentu, dan dapat digunakan sebagai langkah preproses sebelum zlib. Ini sangat cocok untuk medan kompresi yang terdiri dari ubin atau voxel, di mana banyak elemen tetangga diulang.
Kompresi Delta
Teknik kompresi terbaru adalah kompresi delta. Itu terletak pada kenyataan bahwa hanya perbedaan antara kondisi permainan saat ini dan keadaan terakhir yang diterima oleh klien yang ditransmisikan.
Ini pertama kali digunakan dalam mesin jaringan Quake3. Berikut adalah dua artikel yang menjelaskan cara menggunakannya:
Glenn Fiedler juga menggunakannya di bagian kedua artikel
Snapshot Compression-nya .
Enkripsi
Selain itu, Anda mungkin perlu mengenkripsi transfer informasi antara klien dan server. Ada beberapa alasan untuk ini:
- privasi / kerahasiaan: pesan hanya dapat dibaca oleh penerima, dan tidak ada orang lain yang mengendus jaringan yang dapat membacanya.
- otentikasi: seseorang yang ingin memainkan peran pemain harus tahu kuncinya.
- pencegahan kecurangan: akan jauh lebih sulit bagi pemain jahat untuk membuat paket kecurangan mereka sendiri, mereka harus memainkan skema enkripsi dan menemukan kuncinya (yang berubah pada setiap koneksi).
Saya sangat merekomendasikan menggunakan perpustakaan untuk ini. Saya sarankan menggunakan
libsodium karena sangat sederhana dan memiliki tutorial yang bagus. Yang menarik adalah tutorial
pertukaran kunci , yang memungkinkan Anda untuk menghasilkan kunci baru dengan setiap koneksi baru.
Protokol Aplikasi: Kesimpulan
Kami akan mengakhiri dengan protokol aplikasi. Saya percaya bahwa kompresi sepenuhnya opsional dan keputusan untuk menggunakannya hanya tergantung pada permainan dan bandwidth yang diperlukan. Enkripsi, menurut pendapat saya, adalah wajib, tetapi dalam prototipe pertama yang dapat Anda lakukan tanpanya.
Logika aplikasi
Sekarang kami dapat memperbarui status di klien, tetapi kami mungkin mengalami masalah dengan penundaan. Setelah masuk, pemain harus menunggu pembaruan status permainan dari server untuk melihat apa dampaknya pada dunia.
Selain itu, di antara dua pembaruan negara, dunia ini sepenuhnya statis. Jika refresh rate dari state-nya rendah, maka pergerakannya akan sangat berkedut.
Ada beberapa teknik untuk mengurangi dampak dari masalah ini, dan di bagian selanjutnya saya akan membicarakannya.
Teknik Keterlambatan Penundaan
Semua teknik yang dijelaskan dalam bagian ini dibahas secara rinci dalam seri
Fast-Paced Multiplayer oleh Gabriel Gambetta. Saya sangat merekomendasikan membaca seri artikel yang luar biasa ini. Ini juga memiliki demo interaktif yang memungkinkan Anda melihat bagaimana teknik ini bekerja dalam praktiknya.
Teknik pertama adalah menerapkan input secara langsung, tanpa menunggu respons dari server. Ini disebut
prediksi sisi klien . Namun, ketika klien menerima pembaruan dari server, ia harus memastikan bahwa perkiraannya benar. Jika tidak demikian, maka ia hanya perlu mengubah statusnya sesuai yang diterima dari server, karena server tersebut otoriter. Teknik ini pertama kali digunakan di Quake. Anda dapat membaca lebih lanjut tentang hal ini di artikel
ulasan kode mesin Quake Engine oleh Fabien Sanglar [
terjemahan ke dalam Habré].
Set kedua teknik digunakan untuk memuluskan pergerakan entitas lain antara dua pembaruan status. Ada dua cara untuk mengatasi masalah ini: interpolasi dan ekstrapolasi. Dalam kasus interpolasi, dua status terakhir diambil dan transisi dari satu ke yang lain ditunjukkan. Kerugiannya adalah ia menyebabkan sebagian kecil dari keterlambatan, karena klien selalu melihat apa yang terjadi di masa lalu. Ekstrapolasi memperkirakan di mana entitas sekarang harus didasarkan pada keadaan terakhir yang diterima oleh klien. Kerugiannya adalah jika entitas benar-benar mengubah arah pergerakan, maka akan ada kesalahan besar antara perkiraan dan posisi nyata.
Teknik terakhir yang paling canggih, yang hanya berguna dalam FPS adalah
kompensasi lag . Saat menggunakan kompensasi lag, server memperhitungkan penundaan klien akun saat menembak target. Sebagai contoh, jika seorang pemain menyelesaikan headshot di layarnya, tetapi dalam kenyataannya tujuannya terletak di tempat lain karena keterlambatan, maka akan tidak jujur untuk menolak pemain untuk membunuh karena penundaan. Oleh karena itu, server memundurkan waktu kembali ke saat ketika pemain melepaskan tembakan untuk mensimulasikan apa yang dilihat pemain di layarnya dan memeriksa tabrakan antara tembakannya dan target.
Glenn Fiedler (seperti biasa!) Menulis artikel tahun 2004 di
Network Physics (2004) , yang meletakkan dasar untuk menyinkronkan simulasi fisika antara server dan klien. Pada 2014, ia menulis seri baru artikel
Fisika Jaringan yang menggambarkan teknik lain untuk menyinkronkan simulasi fisika.
Ada juga dua artikel di wiki Valve, Sumber Multiplayer Networking dan Metode Kompensasi Latensi di Klien / Server dalam Desain dan Optimasi Protokol dalam gim , yang membahas penundaan kompensasi.Pencegahan kecurangan
Ada dua teknik utama untuk mencegah kecurangan.Pertama: mempersulit pengiriman paket berbahaya oleh penipu. Seperti yang dinyatakan di atas, enkripsi adalah cara yang baik untuk mengimplementasikannya.Kedua: server otoriter hanya menerima perintah / input / tindakan. Klien seharusnya tidak dapat mengubah status di server, kecuali dengan mengirim input. Kemudian, setiap kali input diterima, server harus memeriksa validitas sebelum menggunakannya.Logika Aplikasi: Kesimpulan
Saya menyarankan Anda menerapkan metode simulasi keterlambatan besar dan kecepatan refresh rendah agar dapat menguji perilaku permainan Anda dalam kondisi yang buruk, bahkan ketika klien dan server berjalan di komputer yang sama. Ini akan sangat menyederhanakan implementasi teknik smooth smoothing.Sumber daya bermanfaat lainnya
Jika Anda ingin menjelajahi sumber daya lain pada model jaringan, Anda dapat menemukannya di sini: