Protokol Game RTS

18

Saya sudah memikirkan game multi-pemain RTS. Bagian yang sepertinya tidak bisa kudapatkan adalah menjaga pergerakan unit tersinkronisasi. Jika saya memindahkan unit A ke tempat XY, saya harus mengkomunikasikannya kembali ke server yang menyampaikan ke klien lain.

Saya ingin tahu seperti apa bentuk komunikasi itu. Apakah Anda hanya berkomunikasi dengan server bahwa saya memindahkan unit A ke XY dari JZ? Mungkin Anda perlu mengkomunikasikan gerakan coord dengan coord saja? Metodologi apa yang paling efisien untuk mengkomunikasikan perpindahan unit dari satu klien ke klien lainnya?

EDIT

Ini adalah pertanyaan yang diposting ulang dari stackoverflow . Saya menemukan bahwa situs ini mungkin tempat yang lebih baik untuk pertanyaan itu.

Salah satu jawaban yang lebih baik dari pos itu:

Saya berasumsi Anda bermaksud menggunakan paradigma jaringan Client-Server? Dalam hal ini Anda tidak bisa mempercayai klien untuk menangani penentuan posisi unit yang sebenarnya, Anda harus mendelegasikan tugas itu ke server. Anda kemudian mengambil daftar perintah dari setiap klien per-centang, dan menghitung pergerakan setiap unit, setelah ini selesai, centang berikutnya Anda menyampaikan posisi masing-masing unit yang relevan untuk setiap klien (baik secara keseluruhan peta, atau per-view basis), dan mulai proses lagi.

Darthg8r
sumber
2
Jawabannya sangat tergantung metode mana yang ingin Anda gunakan. Klien - klien atau klien - server. Klien ke server lebih mudah tetapi membutuhkan server yang dapat dipercaya
Cem Kalyoncu

Jawaban:

25

Anda tidak ingin menyinkronkan posisi semua unit dari server ke setiap klien; yang akan memakan bandwidth jauh lebih banyak dari yang Anda butuhkan. Anda juga harus berurusan dengan posisi unit interpolasi / ekstrapolasi, dll. Hampir tidak ada klien / server RTS profesional!

Sebaliknya, Anda hanya ingin mengirim perintah para pemain. Alih-alih memindahkan unit segera ketika pemain mengklik, Anda akan mengantri-perintah untuk dilakukan di beberapa titik di masa depan - biasanya hanya beberapa frame. Semua orang mengirim perintah kepada semua orang. Beberapa frame kemudian, semua orang mengeksekusi semua perintah, dan karena gim ini deterministik, mereka semua melihat hasil yang sama.

Kekurangannya adalah bahwa setiap pemain selambat pemain paling lambat - jika ada yang ketinggalan mengirim perintah, semua orang harus memperlambat dan menunggunya untuk mengejar ketinggalan (di Starcraft 2, ini adalah "XXX memperlambat permainan " dialog).


Bahkan, ada satu hal lagi yang biasanya dilakukan: menghilangkan server sekaligus . Mintalah setiap klien mengirim perintah mereka ke setiap klien lainnya. Ini mengurangi lag (alih-alih perintah dari Anda -> server -> lawan, itu hanya pergi dari Anda -> lawan) dan membuat pengkodean lebih mudah, karena Anda tidak perlu lagi kode server yang terpisah. Arsitektur semacam ini disebut peer-to-peer (P2P).

Kelemahannya adalah bahwa sekarang Anda membutuhkan cara untuk menyelesaikan konflik, tetapi karena perintah pemain tidak tergantung satu sama lain di sebagian besar RTS, ini biasanya tidak terlalu menjadi masalah. Selain itu, skalanya tidak baik - setiap kali Anda menambahkan pemain baru, setiap pemain harus mengirim perintah kepadanya. Anda tidak akan membuat RTS MMO menggunakan P2P.


Pengaturan ini (hanya mengirim perintah menggunakan P2P) adalah cara sebagian besar RTS, termasuk Starcraft, C&C, dan AoE bekerja, dan merupakan satu-satunya cara AoE mungkin dapat mendukung 1500 unit pada koneksi 28,8 kbps .

(gambar jejaring di AoE)

Berikut adalah beberapa tips untuk menulis RTS P2P:

  • Untuk alasan yang jelas, pengaturan ini hanya dapat berfungsi jika game Anda menggunakan waktu langkah tetap - Anda tidak ingin hasil perhitungan bergantung pada framerate! Memperbaiki langkah lebih mudah untuk bekerja dengan sebagian besar hal, jadi ini seharusnya tidak menjadi masalah.
  • Agar ini berfungsi, hasil dari setiap perintah harus sepenuhnya deterministik .
    • Ini biasanya cukup mudah jika Anda membatasi diri Anda pada satu sistem (seperti Windows 32-bit) dan memaksa semua klien untuk menggunakan executable yang sama: pastikan setiap generator nomor acak memiliki seed yang sama dan selalu dipanggil dalam urutan yang sama; sangat berhati-hati saat beralih ke koleksi yang tidak terurut ; dll.
    • Ini sangat sulit jika Anda berencana membuat game dapat dimainkan di berbagai platform yang berbeda, atau (seperti yang sering terjadi pada Linux) memungkinkan klien untuk menyusun kode sendiri. Tidak hanya perpustakaan sistem yang berbeda hampir dijamin untuk menggunakan implementasi yang berbeda rand(), cos()dll, tetapi hampir semua matematika floating-point keluar dari pertanyaan (lihat di sini , di sini , dan di sini ) ! Dalam hal ini, Anda mungkin lebih baik menggunakan client-server.
  • Anda akan ingin mengirim semua posisi unit sesekali, setidaknya saat debugging, untuk mendeteksi bug desync (yang, percayalah, Anda akan miliki). Apakah Anda menyimpannya di pertandingan final terserah Anda - saya akan menyinkronkan setidaknya beberapa unit (atau menggunakan semacam checksum), untuk mendeteksi upaya peretasan.
BlueRaja - Danny Pflughoeft
sumber
Pos yang bagus. Hal kecil untuk ditambahkan, bahkan kompiler yang sama mengoptimalkan, debug / rilis dan flag lainnya dapat mengubah hasilnya. Hati-hati!
Peter Ølsted
14

Saya telah membuat RTS jaringan-TCP, di mana saya melewati perintah sendiri, bukan hasil dari perintah . Misalnya, seorang pemain memberikan perintah bergerak. Jika perintah pemindahan berlaku sesuai dengan klien itu, itu akan dikirim ke server. Server kemudian mengirimkannya kembali ke semua klien, yang memvalidasi dan menjalankannya.

Jadi semua mesin klien menjalankan game sendiri, kode server menerima pesan dan mengirimkannya kembali ke semua klien. Jika klien memberikan perintah pindah, itu tidak akan mulai menjalankannya sampai diterima kembali dari server.

Server mengirimkan nomor 'centang' di mana untuk mengeksekusi perintah juga, yang merupakan beberapa centang di depan centang 'saat ini'. Dengan cara ini semua perintah dapat dieksekusi pada 'centang' yang sama pada semua mesin.

Satu manfaat metode ini adalah tidak bergantung pada mesin klien individual untuk memvalidasi perintah. Jika saya melewati hasil pemindahan, saya mungkin bisa meretasnya untuk memindahkan unit saya lebih cepat. Semua klien harus menjalankan perintah yang sama dan jika satu mesin mengeksekusinya secara berbeda, itu akan menjadi jelas.

Memvalidasi sisi klien perintah sebelum mengirimnya ke server tidak perlu, tetapi secara teori menghemat lalu lintas jaringan. Saya menggunakan kode validasi yang sama untuk memberi tahu UI bahwa langkah itu mungkin, jadi tidak perlu menulis kode tambahan.

Adapun seperti apa pesan-pesan itu. Saya tidak peduli dengan efisiensi ultra karena ini adalah game jaringan pertama saya. Saya melewati perintah sebagai string. Perintah akan diformat seperti ini:"<player_id>:<command>:<parameters>"

Untuk contoh buat, perintah langkah mungkin terlihat seperti ini: "3:move:522:100:200". Ini berarti Pemain 3ingin moveunit 522ke ( 100, 200).

Server melewati perintah ke semua klien, termasuk orang yang mengirimkannya, dengan sejumlah centang terpasang seperti ini: "153238:3:move:522:100:200".

Kemudian semua klien akan menjalankan perintah ini ketika tick 153238 dieksekusi.

Philip
sumber
Saya menambahkan sedikit info lebih lanjut ke pertanyaan. Jawaban dari SO tampaknya bertentangan dengan apa yang Anda katakan dan saya ingin membahas detail yang lebih baik.
Darthg8r
Ya, itu cara lain untuk melakukannya, tetapi bagi saya sepertinya akan lebih sulit untuk melewati begitu banyak kondisi permainan di sekitar, daripada hanya perintah. Game saya cukup sederhana sehingga semuanya dapat berjalan di setiap mesin klien. Untuk MMO, atau untuk sesuatu seperti Minecraft, Anda tidak memiliki seluruh simulasi berjalan di sisi klien, sehingga Anda hanya menyampaikan informasi yang relevan untuk setiap klien secara individual.
Philip