Bagaimana cara menjaga jam server-klien tetap sinkron untuk game jaringan yang presisi seperti Quake 3?

15

Saya sedang mengerjakan penembak top-down 2D dan melakukan yang terbaik untuk menyalin konsep yang digunakan dalam game berjejaring seperti Quake 3.

  • Saya memiliki server otoritatif.
  • Server mengirimkan snapshot ke klien.
  • Snapshots berisi cap waktu dan posisi entitas.
  • Entitas diinterpolasi di antara posisi snapshot sehingga gerakan terlihat mulus.
  • Karena kebutuhan, interpolasi entitas terjadi sedikit "di masa lalu" sehingga kami memiliki beberapa snapshot untuk interpolasi.

Masalah yang saya hadapi adalah "sinkronisasi jam".

  • Demi kesederhanaan, mari kita berpura-pura sejenak bahwa tidak ada latensi saat mentransfer paket ke dan dari server.
  • Jika jam server lebih cepat 60 detik dari jam klien, maka cap waktu snapshot akan lebih dari 60000 ms dari cap waktu lokal klien.
  • Oleh karena itu, snapshot entitas akan mengumpulkan dan duduk selama sekitar 60 detik sebelum klien melihat entitas tertentu melakukan gerakannya, karena dibutuhkan waktu yang lama untuk clock klien untuk mengejar ketinggalan.

Saya telah berhasil mengatasinya dengan menghitung perbedaan antara server dan jam klien setiap kali snapshot diterima.

// For simplicity, don't worry about latency for now...
client_server_clock_delta = snapshot.server_timestamp - client_timestamp;

Ketika menentukan seberapa jauh ke dalam interpolasi entitas, saya hanya menambahkan perbedaan pada waktu klien saat ini. Masalah dengan ini, bagaimanapun, adalah bahwa hal itu akan menyebabkan jerkiness karena perbedaan antara dua jam akan tiba-tiba berfluktuasi karena snapshot tiba lebih cepat / lebih lambat daripada yang lain.

Bagaimana saya bisa menyinkronkan jam cukup dekat sehingga satu-satunya keterlambatan yang dapat dipahami adalah bahwa yang hard-coded untuk interpolasi, dan apa yang disebabkan oleh latensi jaringan biasa?

Dengan kata lain, bagaimana saya bisa mencegah interpolasi dari mulai terlambat atau terlalu cepat ketika jam secara signifikan tidak tersinkronisasi, tanpa memperkenalkan jerkiness?

Sunting: Menurut Wikipedia , NTP dapat digunakan untuk menyinkronkan jam melalui internet dalam hitungan beberapa milidetik. Namun, protokolnya tampak rumit, dan mungkin berlebihan untuk digunakan dalam game?

Joncom
sumber
bagaimana rumitnya ? Ini adalah permintaan dan respons masing-masing dengan stempel waktu pengiriman dan kedatangan, kemudian sedikit matematika untuk mendapatkan delta
ratchet freak
@ scratchetfreak: Menurut ( mine-control.com/zack/timesync/timesync.html ), "Sayangnya, NTP sangat rumit dan, yang lebih penting, lambat untuk konvergen pada delta waktu yang akurat. Ini membuat NTP kurang ideal untuk jaringan permainan di mana pemain mengharapkan permainan untuk segera dimulai ... "
Joncom

Jawaban:

9

Setelah mencari-cari, sepertinya menyinkronkan jam 2 atau lebih komputer bukanlah tugas yang sepele. Protokol seperti NTP melakukan pekerjaan dengan baik tetapi seharusnya lambat dan terlalu rumit untuk praktis dalam gim. Selain itu, ia menggunakan UDP yang tidak akan berfungsi untuk saya karena saya bekerja dengan soket web, yang tidak mendukung UDP.

Saya menemukan metode di sini , yang tampaknya relatif sederhana:

Ia mengklaim untuk menyinkronkan jam ke dalam 150 ms (atau lebih baik) satu sama lain.

Saya tidak tahu apakah itu akan cukup baik untuk tujuan saya, tetapi saya belum dapat menemukan alternatif yang lebih tepat.

Berikut algoritma yang disediakannya:

Diperlukan teknik sinkronisasi jam sederhana untuk gim. Idealnya, ia harus memiliki properti berikut: cukup akurat (150 ms atau lebih baik), cepat untuk menyatu, mudah diimplementasikan, dapat berjalan pada protokol berbasis aliran seperti TCP.

Algoritma sederhana dengan properti ini adalah sebagai berikut:

  1. Klien mencap waktu lokal saat ini pada paket "permintaan waktu" dan mengirim ke server
  2. Setelah diterima oleh server, server akan mencap waktu server dan kembali
  3. Setelah diterima oleh klien, klien mengurangi waktu saat ini dari waktu yang dikirim dan membaginya dengan dua untuk menghitung latensi. Ini mengurangi waktu saat ini dari waktu server untuk menentukan delta waktu client-server dan menambahkan setengah-latensi untuk mendapatkan delta jam yang benar. (Sejauh ini algothim ini sangat mirip dengan SNTP)
  4. Hasil pertama harus segera digunakan untuk memperbarui jam karena akan membuat jam lokal setidaknya menjadi rata rata (setidaknya zona waktu yang tepat!)
  5. Klien mengulangi langkah 1 hingga 3 lima kali atau lebih, berhenti beberapa detik setiap kali. Lalu lintas lain mungkin diizinkan untuk sementara, tetapi harus diminimalkan untuk hasil terbaik
  6. Hasil tanda terima paket diakumulasikan dan diurutkan dalam latensi terendah hingga latensi tertinggi. Latensi median ditentukan dengan memilih sampel titik tengah dari daftar yang dipesan ini.
  7. Semua sampel di atas sekitar 1 standar-deviasi dari median dibuang dan sisa sampel rata-rata menggunakan rata-rata aritmatika.

Satu-satunya kehalusan dari algoritma ini adalah bahwa paket di atas satu standar deviasi di atas median dibuang. Tujuan dari ini adalah untuk menghilangkan paket-paket yang dikirim kembali oleh TCP. Untuk memvisualisasikan ini, bayangkan bahwa sampel dari lima paket dikirim melalui TCP dan kebetulan tidak ada pengiriman ulang. Dalam hal ini, histogram latensi akan memiliki mode tunggal (klaster) yang berpusat di sekitar median latensi. Sekarang bayangkan bahwa dalam percobaan lain, satu paket kelimanya dikirimkan kembali. Pengiriman ulang akan menyebabkan sampel yang satu ini jatuh jauh ke kanan pada histogram latensi, rata-rata dua kali lebih jauh dari median mode primer. Dengan hanya memotong semua sampel yang jatuh lebih dari satu standar deviasi dari median, mode nyasar ini dengan mudah dihilangkan dengan asumsi bahwa mereka tidak termasuk sebagian besar statistik.

Solusi ini muncul untuk menjawab pertanyaan saya dengan memuaskan, karena menyinkronkan jam dan kemudian berhenti, memberikan waktu untuk mengalir secara linear. Sedangkan metode awal saya memperbarui jam terus-menerus, menyebabkan waktu untuk melompat-lompat sedikit saat foto diterima.

Joncom
sumber
Bagaimana ini terbukti bekerja untuk Anda? Saya berada dalam situasi yang sama sekarang. Saya menggunakan kerangka kerja server yang hanya mendukung TCP, karenanya saya tidak bisa menggunakan NTP, yang mengirimkan datagram UDP. Saya kesulitan menemukan Algoritma Sinkronisasi Waktu yang mengklaim dapat melakukan sinkronisasi waktu yang andal melalui TCP. Sinkronisasi dalam satu detik akan cukup untuk kebutuhan saya.
dynamokaj
@dynamokaj Bekerja cukup baik.
Joncom
Keren. Apakah mungkin Anda dapat membagikan implementasinya?
dynamokaj
@dynamokaj Sepertinya saya tidak dapat menemukan implementasi seperti itu dalam proyek apa pun yang dapat saya pikirkan saat ini. Cara lain yang cukup berhasil bagi saya adalah: 1) segera gunakan latensi yang Anda hitung dari satu permintaan / respons ping dan kemudian, 2) untuk semua tanggapan di masa depan seperti tween menuju nilai baru secara bertahap, tidak secara instan. Ini memiliki efek "rata-rata" yang telah banyak akurat untuk tujuan saya.
Joncom
Tidak masalah. Saya menjalankan layanan backend saya di Google App Engine, karenanya pada infrastruktur Google di mana server disinkronkan menggunakan Google NTP Server: time.google.com ( developers.google.com/time ) Karena itu saya menggunakan klien NTP berikut untuk klien Xamarin Mobile saya untuk mendapatkan offset antara klien dan server. components.xamarin.com/view/rebex-time - Terima kasih telah meluangkan waktu untuk menjawab.
dynamokaj
1

Pada dasarnya, Anda tidak dapat memperbaiki dunia [seluruh] dan, pada akhirnya, Anda harus menarik garis.

Jika server dan semua klien berbagi frame-rate yang sama, mereka hanya perlu melakukan sinkronisasi saat menghubungkan, dan kadang-kadang, setelahnya, terutama setelah acara latensi. Latensi tidak memengaruhi aliran waktu atau kemampuan PC untuk mengukurnya sehingga, dalam banyak kasus, alih-alih interpolasi, Anda harus mengekstrapolasi. Ini menciptakan efek yang sama sekali tidak diinginkan tetapi, sekali lagi, ini adalah apa itu dan Anda harus memilih yang paling buruk dari semua kejahatan yang tersedia.

Pertimbangkan bahwa dalam banyak MMO populer, pemain yang tertinggal jelas secara visual. Jika Anda melihat mereka berjalan di tempat, langsung ke dinding, klien Anda memperkirakan. Ketika klien Anda menerima data baru, pemain (pada klien mereka) mungkin telah bergerak cukup jauh dan akan "karet gelang" atau teleport ke lokasi baru ("jerkiness" yang Anda sebutkan?). Ini terjadi bahkan di game-game besar bermerek.

Secara teknis, ini adalah masalah dengan infrastruktur jaringan pemain, bukan gim Anda. Titik di mana ia bergerak dari satu ke yang lain adalah garis yang harus Anda gambar. Kode Anda, pada 3 komputer yang terpisah, harus lebih atau kurang merekam jumlah waktu yang sama berlalu. Jika Anda tidak menerima pembaruan, itu tidak akan mempengaruhi tingkat bingkai Pembaruan () Anda; jika ada, itu harus lebih cepat karena mungkin ada kurang untuk memperbarui.

"Jika Anda memiliki internet payah, Anda tidak dapat memainkan game ini secara kompetitif."
Itu bukan masalah atau bug.

Jon
sumber