Saya mengembangkan game strategi waktu nyata untuk kursus ilmu komputer yang saya ikuti. Salah satu aspek yang lebih sulit dari itu adalah jaringan dan sinkronisasi klien-server. Saya sudah membaca tentang topik ini (termasuk 1500 pemanah ), tetapi saya telah memutuskan untuk mengambil pendekatan client-server yang bertentangan dengan model lain (misalnya melalui LAN).
Game strategi waktu nyata ini dilengkapi dengan beberapa masalah. Untungnya, setiap tindakan yang dilakukan pemain adalah deterministik. Namun, ada beberapa peristiwa yang terjadi pada interval yang dijadwalkan. Misalnya, permainan terdiri dari ubin, dan ketika seorang pemain mengambil ubin, 'tingkat energi', nilai pada ubin itu, harus tumbuh satu setiap detik setelah diambil. Ini adalah penjelasan yang sangat cepat yang seharusnya membenarkan kasus penggunaan saya.
Saat ini saya sedang melakukan thin client, yang hanya mengirim paket ke server dan menunggu jawaban. Namun, ada beberapa masalah.
Ketika permainan antara pemain berkembang menjadi permainan akhir, sering kali ada lebih dari 50 peristiwa per detik (karena acara yang dijadwalkan, dijelaskan sebelumnya, menumpuk), dan kesalahan sinkronisasi mulai muncul saat itu. Masalah terbesar saya adalah bahwa bahkan penyimpangan kecil dalam keadaan di antara klien bisa berarti keputusan yang berbeda yang diambil klien, yang berubah menjadi permainan yang sepenuhnya terpisah. Masalah lain (yang tidak sepenting sekarang) adalah bahwa ada latensi dan seseorang harus menunggu beberapa milidetik, bahkan beberapa detik setelah mereka bergerak untuk melihat hasilnya.
Saya bertanya-tanya strategi dan algoritma apa yang dapat saya gunakan untuk menjadikan ini lebih mudah, lebih cepat, dan lebih menyenangkan bagi pengguna akhir. Ini sangat menarik mengingat tingginya jumlah acara per detik, bersama dengan beberapa pemain per game.
TL; DR membuat RTS dengan> 50 acara per detik, bagaimana cara menyinkronkan klien?
Jawaban:
Tujuan Anda untuk menyinkronkan 50 peristiwa per detik secara real-time bagi saya sepertinya tidak realistis. Inilah mengapa pendekatan kunci-langkah yang dibicarakan dalam artikel 1500 pemanah , well, bicarakan!
Dalam satu kalimat: Satu-satunya cara untuk menyinkronkan terlalu banyak item dalam waktu yang terlalu singkat melalui jaringan yang terlalu lambat adalah TIDAK mensinkronkan terlalu banyak item dalam waktu yang terlalu singkat melalui jaringan yang terlalu lambat, tetapi sebaliknya menyatakan keadaan secara deterministik pada semua klien dan hanya menyinkronkan kebutuhan telanjang (input pengguna).
sumber
Saya pikir ada masalah Anda; gim Anda hanya boleh memiliki satu garis waktu (untuk hal-hal yang mempengaruhi gameplay). Anda mengatakan bahwa hal-hal tertentu tumbuh pada kecepatan X per detik ; mengetahui berapa banyak permainan langkah berada dalam kedua dan mengkonversi yang untuk tingkat X per langkah permainan Y . Kemudian meskipun permainan mungkin melambat, semuanya tetap deterministik.
Memiliki permainan yang berjalan secara independen ke waktu nyata memiliki keuntungan lain:
Anda juga menyebutkan bahwa Anda mengalami masalah ketika ada> 50 peristiwa, atau ada keterlambatan hingga detik. Skala ini jauh lebih kecil daripada skenario yang dijelaskan dalam 1500 pemanah , jadi lihat apakah Anda dapat membuat profil game Anda dan mencari tahu di mana perlambatannya.
sumber
Pertama, untuk menyelesaikan masalah dengan acara terjadwal, jangan menyiarkan acara ketika itu terjadi , tetapi ketika mereka awalnya dijadwalkan. Artinya, alih-alih mengirim pesan "kenaikan energi ubin ( x , y )" setiap detik, cukup kirim pesan tunggal yang mengatakan "tambah energi ubin ( x , y ) sekali per detik hingga penuh, atau sampai terputus ". Setiap klien kemudian bertanggung jawab untuk menjadwalkan pembaruan secara lokal.
Bahkan, Anda dapat mengambil prinsip ini lebih jauh, dan hanya mengirimkan tindakan pemain : semua yang lain dapat dihitung secara lokal oleh setiap klien (dan server, sebagaimana diperlukan).
(Tentu saja, Anda mungkin juga sesekali harus mengirimkan checksum dari status permainan, untuk mendeteksi desinkronisasi yang tidak disengaja, dan memiliki beberapa mekanisme untuk menyinkronkan kembali klien jika itu terjadi, misalnya dengan mengirim ulang semua data game dari salinan otorisasi server ke klien. Tapi ini semoga menjadi peristiwa yang langka, hanya ditemui dalam pengujian atau selama kegagalan yang langka.)
Kedua, untuk menjaga agar klien tetap sinkron, pastikan game Anda deterministik. Jawaban lain telah memberikan saran yang bagus untuk ini, tetapi izinkan saya memasukkan ringkasan singkat tentang apa yang harus dilakukan:
Jadikan game Anda berbasis giliran secara internal, dengan setiap belokan atau "centang", katakanlah, 1/50 detik. (Faktanya, Anda mungkin bisa lolos dengan putaran 1/10 detik atau lebih lama.) Setiap aksi pemain yang terjadi selama satu putaran harus diperlakukan secara simultan. Semua pesan, setidaknya dari server ke klien, harus ditandai dengan nomor belokan, sehingga setiap klien tahu di mana setiap peristiwa terjadi.
Karena game Anda menggunakan arsitektur client-server, Anda dapat membuat server bertindak sebagai wasit terakhir dari apa yang terjadi selama setiap belokan, yang menyederhanakan beberapa hal. Namun, harap dicatat bahwa itu berarti bahwa klien juga harus mengkonfirmasi ulang tindakan mereka sendiri dari server: jika klien mengirim pesan yang mengatakan "Saya memindahkan unit X satu ubin kiri", dan balasan server tidak mengatakan apa pun tentang unit X bergerak, klien harus menganggap itu tidak terjadi, dan mungkin membatalkan animasi gerakan prediktif apa pun yang mungkin sudah mereka mulai mainkan.
Tetapkan urutan yang konsisten untuk acara "simultan" yang terjadi pada giliran yang sama, sehingga setiap klien akan menjalankannya dalam urutan yang sama. Pesanan ini dapat berupa apa saja, asalkan bersifat deterministik dan sama untuk semua klien (dan server).
Misalnya, Anda dapat terlebih dahulu menambah semua sumber daya (yang dapat dilakukan sekaligus, jika pertumbuhan sumber daya di satu ubin tidak dapat mengganggu dengan yang lain), lalu pindahkan unit masing-masing pemain dalam urutan yang telah ditentukan, kemudian pindahkan unit NPC. Agar adil bagi pemain, Anda mungkin ingin memvariasikan urutan pergerakan unit di antara belokan, sehingga setiap pemain dapat melakukan yang sama lebih sering; ini baik-baik saja, asalkan dilakukan secara deterministik (misalnya berdasarkan pada nomor belokan).
Jika Anda menggunakan matematika floating-point, pastikan Anda menggunakannya dalam mode IEEE yang ketat. Ini dapat memperlambat segalanya, tetapi itu adalah harga yang kecil untuk membayar konsistensi antara klien. Pastikan juga tidak ada pembulatan yang tidak disengaja terjadi selama komunikasi (mis. Klien mengirimkan nilai yang dibulatkan ke server, tetapi masih menggunakan nilai yang tidak dikelilingi secara internal). Seperti disebutkan di atas, memiliki protokol untuk mendeteksi dan memulihkan dari desinkronisasi juga merupakan ide bagus, untuk berjaga-jaga.
sumber
Anda harus membuat logika permainan Anda sepenuhnya independen dari waktu nyata dan pada dasarnya menjadikannya Berbasis Turn. Dengan begitu Anda tahu persis giliran di mana "perubahan energi ubin terjadi". Dalam kasus Anda, setiap belokan hanya 1/50 detik.
Dengan begitu Anda hanya perlu khawatir tentang input pemain, semuanya dikelola oleh logika permainan dan benar-benar identik pada semua klien. Bahkan jika game terhenti sesaat, karena Net lag, atau perhitungan yang sangat rumit, peristiwa itu masih terjadi secara sinkron untuk semua orang.
sumber
Pertama-tama Anda harus memahami bahwa float PC / matematika ganda TIDAK deterministik, kecuali jika Anda menentukan untuk menggunakan IEEE-754 untuk perhitungan Anda (akan lambat)
Maka ini adalah bagaimana saya akan mengimplementasikannya: klien terhubung ke server, dan mempersingkat waktu (mengurus ping latency!) (Untuk gameplay yang panjang mungkin perlu melakukan sinkronisasi ulang stempel waktu / putaran)
sekarang, setiap kali klien melakukan tindakan, itu termasuk timestamp / turn, dan terserah server untuk menolak timestamp / turn yang buruk. Kemudian server mengirim kembali aksi ke klien, dan setiap kali giliran "ditutup" (alias server tidak akan menerima giliran / timestamp yang sudah sangat lama), server mengirim dan mengakhiri aksi belok ke klien.
Klien akan memiliki 2 "dunia": satu disinkronkan dengan tikungan-akhir, yang lain dihitung mulai dari tikungan-akhir, menjumlahkan tindakan yang tiba pada antrian, sampai giliran klien saat ini / cap waktu.
karena server akan menerima tindakan yang agak lama, klien dapat menambahkan tindakannya sendiri secara langsung dalam antrian, sehingga waktu bolak-balik melalui jaringan akan disembunyikan, setidaknya untuk tindakan Anda sendiri.
Hal terakhir adalah mengantri lebih banyak tindakan sehingga Anda dapat mengisi paket MTU, sehingga biaya overhead protocoll lebih sedikit; ide yang bagus adalah melakukannya di server, jadi setiap acara akhir-putaran berisi tindakan antrian.
saya menggunakan algoritm ini pada gim penembakan real-time, dan berfungsi dengan baik (dengan klien tanpa dan chaching tindakannya sendiri, tetapi dengan server ping serendah 20/50 ms), juga setiap server end-turn X mengirim khusus "semua paket "peta klien, untuk memperbaiki nilai drift.
sumber