Apakah ini arsitektur yang tepat untuk game mobile MMORPG kami?

14

Hari-hari ini saya mencoba merancang arsitektur game mobile MMORPG baru untuk perusahaan saya. Game ini mirip dengan Mafia Wars, iMobsters, atau RISIKO. Ide dasarnya adalah menyiapkan pasukan untuk melawan lawan Anda (pengguna online).

Meskipun saya sebelumnya telah bekerja pada beberapa aplikasi seluler tetapi ini adalah sesuatu yang baru bagi saya. Setelah banyak perjuangan, saya datang dengan arsitektur yang diilustrasikan dengan bantuan diagram alir tingkat tinggi:

Diagram aliran tingkat tinggi

Kami telah memutuskan untuk menggunakan model client-server. Akan ada database terpusat di server. Setiap klien akan memiliki database lokal sendiri yang akan tetap sinkron dengan server. Basis data ini bertindak sebagai cache untuk menyimpan hal-hal yang tidak sering berubah misalnya peta, produk, inventaris dll.

Dengan model ini, saya tidak yakin bagaimana mengatasi masalah berikut:

  • Apa cara terbaik untuk menyinkronkan server dan basis data klien?
  • Haruskah suatu peristiwa disimpan ke DB lokal sebelum memperbaruinya ke server? Bagaimana jika aplikasi berhenti karena suatu alasan sebelum menyimpan perubahan ke DB yang terpusat?
  • Apakah permintaan HTTP sederhana dapat digunakan untuk tujuan sinkronisasi?
  • Bagaimana cara mengetahui pengguna mana yang saat ini masuk? (Salah satu cara bisa membuat klien terus mengirim permintaan ke server setelah setiap x menit untuk memberitahukan bahwa itu aktif. Jika tidak, anggap klien tidak aktif).
  • Apakah validasi sisi klien sudah cukup? Jika tidak, bagaimana cara mengembalikan suatu tindakan jika server tidak memvalidasi sesuatu?

Saya tidak yakin apakah ini solusi yang efisien dan bagaimana skalanya. Saya akan sangat menghargai jika orang-orang yang sudah mengerjakan aplikasi semacam itu dapat berbagi pengalaman mereka yang mungkin membantu saya untuk menghasilkan sesuatu yang lebih baik. Terima kasih sebelumnya.

Informasi tambahan:

Sisi klien diimplementasikan dalam engine game C ++ yang disebut marmalade. Ini adalah mesin permainan lintas platform yang artinya Anda dapat menjalankan aplikasi di semua OS seluler utama. Kita tentu bisa mencapai threading dan yang juga diilustrasikan dalam diagram alir saya. Saya berencana menggunakan MySQL untuk server dan SQLite untuk klien.

Ini bukan permainan berbasis giliran sehingga tidak ada banyak interaksi dengan pemain lain. Server akan memberikan daftar pemain online dan Anda dapat melawan mereka dengan mengklik tombol pertempuran dan setelah beberapa animasi, hasilnya akan diumumkan.

Untuk sinkronisasi database saya memiliki dua solusi dalam pikiran:

  1. Simpan stempel waktu untuk setiap catatan. Catat juga kapan DB lokal terakhir diperbarui. Saat menyinkronkan, hanya pilih baris yang memiliki stempel waktu lebih besar dan kirim ke DB lokal. Simpan bendera isDeleted untuk baris yang dihapus sehingga setiap penghapusan hanya berperilaku sebagai pembaruan. Tetapi saya memiliki keraguan serius tentang kinerja karena untuk setiap permintaan sinkronisasi kami harus memindai DB lengkap dan mencari baris yang diperbarui.
  2. Teknik lain mungkin untuk menyimpan log dari setiap penyisipan atau pembaruan yang terjadi terhadap pengguna. Saat aplikasi klien meminta sinkronisasi, buka tabel ini dan cari tahu baris mana dari tabel yang telah diperbarui atau dimasukkan. Setelah baris-baris ini berhasil ditransfer ke klien, hapus log ini. Tapi kemudian saya memikirkan apa yang terjadi jika pengguna menggunakan perangkat lain. Menurut tabel log semua pembaruan telah ditransfer untuk pengguna itu tetapi sebenarnya itu dilakukan pada perangkat lain. Jadi kita mungkin harus melacak perangkat juga. Menerapkan teknik ini lebih memakan waktu tetapi tidak yakin apakah itu melakukan yang pertama.
umair
sumber
2
Saya akan mengambil evaluasi MsSQL dan mengutak-atiknya - Saya tidak yakin apa yang disediakan MySQL dalam hal replikasi tetapi MsSQL 2008 R2 memberi Anda LOAD teknik replikasi (5-6) untuk dipilih. Hanya pemikiran, tidak membenci MySQL atau apa pun, tetapi Microsoft benar-benar bagus dalam pengelompokan.
Jonathan Dickinson
1
@ Jonathan Dickinson: Ada MySQL Cluster: P
Coyote
1
Lihat juga PostgreSQL - versi 9 memiliki teknologi replikasi, PostgreSQL memiliki reputasi lama yang sangat baik untuk menangani beban berat dengan sangat baik, banyak pengembang tampaknya menjadi kacang optimisasi, dan yang terbaik adalah open source dan sepenuhnya gratis untuk semua penggunaan (termasuk komersial).
Randolf Richardson
masalahnya adalah kita tidak bisa menggunakan database yang sama di sisi klien dan server. Saya pikir DB yang paling cocok untuk klien adalah SQLite sehingga tidak menghabiskan banyak sumber daya karena aplikasi akan berjalan pada perangkat seluler. Sementara orang yang bekerja di bagian server hanya tahu MySQL, jadi itu sebabnya kami memilih untuk itu. Saya juga pernah mendengar bahwa sebagian besar game MMO menggunakan database MySQL.
umair
1
@air: Sisi klien bisa berupa SQLite (di iPhone misalnya sudah tersedia). Tetapi Anda dapat memilih untuk hanya menyimpan data yang relevan yang Anda butuhkan dalam file (Apa SQLite lakukan) dan tidak repot dengan DB sisi klien sama sekali seperti yang Anda lakukan dengan aplikasi Javascript.
Coyote

Jawaban:

15

Jika ini bukan permainan "waktu nyata" dalam arti bahwa para pemain tidak perlu melihat hasil langsung dari tindakan pemain lain di kancah permainan, maka Anda harus menyetujui permintaan HTTP. Tapi Perlu diingat overhead HTTP.

Yang mengatakan menggunakan HTTP tidak akan menyelamatkan Anda dari merancang protokol komunikasi Anda dengan hati-hati. Tetapi jika Anda bertanggung jawab atas server dan sisi klien Anda beruntung karena Anda dapat menyesuaikan protokol saat Anda membutuhkannya.

Untuk menyinkronkan antara Master DB dan DB Klien Anda dapat menggunakan protokol transport apa pun yang Anda miliki aksesnya, HTTP atau lainnya. Bagian yang penting adalah logika di balik sinkronisasi. Untuk pendekatan langsung, cukup menyatukan dari server semua perubahan terbaru yang diperlukan oleh klien sejak timestamp terakhir dalam DB klien. Terapkan ke DB klien dan ikuti itu. Jika Anda memiliki lebih banyak perubahan pada sisi klien, unggahlah jika masih relevan, jika tidak, buang.

Beberapa game bahkan tidak menggunakan DB lokal, mereka hanya melacak status dengan mengumpulkan info yang relevan dari server saat dibutuhkan.

Jika kehilangan acara lokal tidak dapat diterima maka ya, Anda harus memiliki penyimpanan lokal dan menyimpan ke penyimpanan itu sesering mungkin. Anda dapat mencoba melakukan itu sebelum setiap jaringan mengirim.

Untuk memeriksa pengguna aktif, kami biasa melakukan ping dengan HTTP setiap 20 detik pada game yang sukses ... Nilai ini langsung naik karena server telah kelebihan beban :( Tim server tidak memikirkan kesuksesan. Jadi saya akan meminta Anda untuk menambahkan pesan atau semacam header khusus dalam protokol komunikasi Anda yang akan memungkinkan Anda mengkonfigurasi ulang klien Anda (untuk penyetelan beban frekuensi ping dan nilai-nilai terkait komunikasi lainnya).

Validasi sisi klien sudah cukup jika Anda tidak keberatan curang, peretas, dan skrip otomatis lainnya yang menyerang permainan Anda. Server hanya dapat menolak tindakan Anda dan mengirim pesan kesalahan dengan beberapa detail. Anda menangani pesan kesalahan dengan memutar kembali perubahan lokal atau dengan tidak menerapkannya ke penyimpanan lokal Anda jika Anda bisa menunggu sampai server merespons untuk benar-benar menyimpan perubahan.

Kami menggunakan pola perintah di game kami untuk memungkinkan rollback sederhana dan efisien dari tindakan pengguna yang gagal atau tidak valid. Perintah dimainkan secara lokal dikirim ke rekan-rekan atau diterjemahkan ke pesan server dan kemudian diterapkan pada rekan-rekan atau diperiksa di server, jika ada masalah perintah tidak dimainkan dan adegan permainan kembali ke keadaan awal dengan pemberitahuan.

Menggunakan HTTP bukanlah ide yang buruk karena, nanti, ini akan memungkinkan Anda untuk berintegrasi dengan sangat mudah dengan klien Flash atau HTML5, fleksibel, Anda dapat menggunakan semua jenis bahasa scripting server dan dengan teknik penyeimbangan beban dasar tambahkan lebih banyak server nanti tanpa banyak usaha untuk skala backend Anda.

Anda memiliki banyak hal yang harus dilakukan tetapi ini adalah pekerjaan yang menyenangkan ... Jadi nikmatilah!

Anjing hutan
sumber
3
+1 untuk 'HTML mungkin cukup baik' karena mungkin :).
Jonathan Dickinson
1
@Coyote: terima kasih telah meluangkan waktu dan memberikan jawaban terperinci. Sangat menyukai ide HTTP Anda untuk memberikan dukungan kepada klien lain.
umair 5'11
1
Semoga pasukan bersamamu :)
Coyote
5

Apa cara terbaik untuk menyinkronkan server dan basis data klien?

Cara termudah adalah dengan mengimplementasikan database sebagai satu file yang dapat Anda transfer. Jika Anda mencoba untuk membandingkan perbedaan antara database maka itu adalah dunia yang menyakitkan, dan saya berbicara dari pengalaman.

Perhatikan bahwa server Anda tidak boleh mempercayai keputusan yang dibuat klien berdasarkan database lokal itu, karena klien dapat mengubahnya. Itu harus ada murni untuk detail presentasi.

Haruskah suatu peristiwa disimpan ke DB lokal sebelum memperbaruinya ke server?

Tidak. Bagaimana jika server memutuskan acara tidak pernah terjadi? Klien tidak seharusnya membuat keputusan tentang peristiwa, karena klien tidak dapat dipercaya.

Saya perhatikan Anda juga berbicara tentang DB lokal dalam dua cara: satu, untuk "hal-hal yang tidak sering berubah" dan dua, untuk acara. Ini tidak boleh benar-benar berada di database yang sama, karena alasan di atas - Anda tidak ingin mulai mencoba untuk menggabungkan atau mengubah baris data individual dalam database. Misalnya, integritas referensial menjadi masalah ketika klien memiliki referensi ke item yang Anda putuskan untuk hapus dari server. Atau jika klien mengubah satu baris dan server mengubah satu baris, perubahan mana yang diutamakan, dan mengapa?

Apakah permintaan HTTP sederhana dapat digunakan untuk tujuan sinkronisasi?

Ya, asalkan mereka cukup jarang atau kecil. HTTP tidak efisien bandwidth jadi ingatlah itu.

Bagaimana cara mengetahui pengguna mana yang saat ini masuk? (Salah satu cara bisa membuat klien terus mengirim permintaan ke server setelah setiap x menit untuk memberitahukan bahwa itu aktif. Jika tidak, anggap klien tidak aktif).

Jika Anda menggunakan protokol sementara seperti HTTP, maka itu ide yang masuk akal. Ketika server menerima pesan dari klien, Anda dapat memperbarui waktu 'terakhir terlihat' untuk klien itu.

Apakah validasi sisi klien sudah cukup? Jika tidak, bagaimana cara mengembalikan suatu tindakan jika server tidak memvalidasi sesuatu?

Tidak, tidak sama sekali. Klien ada di tangan musuh. Cara mengembalikan suatu tindakan sepenuhnya tergantung pada apa yang Anda anggap sebagai tindakan dan apa efeknya. Rute termudah adalah tidak mengimplementasikan tindakan sama sekali sampai server merespons untuk mengizinkannya. Rute yang sedikit rumit adalah untuk memastikan setiap tindakan memiliki kemampuan rollback untuk membatalkan tindakan, dan menyimpan semua tindakan yang tidak diakui pada klien. Ketika server mengakui mereka, hapus mereka dari cache. Jika suatu tindakan ditolak, kembalikan setiap tindakan dalam urutan terbalik hingga dan termasuk yang ditolak.

Kylotan
sumber
1
Kamu berhasil. Anda benar tentang menyinkronkan DB +1 +1 Klien tidak boleh mengubah DB sendiri ... Cukup panggil fungsi yang berjalan di server dan perbarui DB menggunakan logika game, lalu ambil hasilnya.
Coyote
@ Silyl: terima kasih telah menunjukkan aliran dalam model saya
umair
@Kylotan: "Cara termudah adalah mengimplementasikan database sebagai satu file yang dapat Anda transfer. Jika Anda mencoba untuk membandingkan perbedaan antara database maka itu adalah dunia yang menyakitkan, dan saya berbicara dari pengalaman." ini berarti bahwa semua data diunduh setiap kali aplikasi diluncurkan. Ada hal-hal yang tidak akan terlalu sering berubah sehingga tidak dapat diterima untuk mengunduhnya setiap waktu. Ini juga akan meningkatkan waktu peluncuran aplikasi secara signifikan. Adakah pikiran?
umair tanggal
Jika datanya kecil, maka masalahnya hilang. Saya akan terkejut jika database terpusat Anda dari data statis sangat besar. Tapi bagaimanapun, jika Anda membagi data statis menjadi bagian-bagian yang lebih kecil, maka Anda hanya perlu mengirim yang sudah berubah sejak terakhir kali. Tentu, Anda dapat melakukan ini pada basis per baris, jika Anda ingin menjaga waktu modifikasi di setiap baris. Secara umum saya lebih suka memiliki data statis di luar DB relasional, untuk memungkinkan saya menimpanya dengan mudah.
Kylotan