È questa l'architettura giusta per il nostro gioco mobile MMORPG?


14

In questi giorni sto cercando di progettare l'architettura di un nuovo gioco MMORPG per dispositivi mobili per la mia azienda. Questo gioco è simile a Mafia Wars, iMobsters o RISK. L'idea di base è preparare un esercito a combattere i tuoi avversari (utenti online).

Anche se in precedenza ho lavorato su più app mobili, ma questa è una novità per me. Dopo molte lotte, ho escogitato un'architettura che è illustrata con l'aiuto di un diagramma di flusso di alto livello:

Diagramma di flusso di alto livello

Abbiamo deciso di scegliere il modello client-server. Ci sarà un database centralizzato sul server. Ogni client avrà il proprio database locale che rimarrà sincronizzato con il server. Questo database funge da cache per l'archiviazione di oggetti che non cambiano frequentemente, ad esempio mappe, prodotti, inventario, ecc.

Con questo modello in atto, non sono sicuro di come affrontare i seguenti problemi:

  • Quale sarebbe il modo migliore per sincronizzare database di server e client?
  • Un evento deve essere salvato nel DB locale prima di aggiornarlo sul server? Cosa succede se l'app termina per qualche motivo prima di salvare le modifiche nel DB centralizzato?
  • Le richieste HTTP semplici serviranno allo scopo della sincronizzazione?
  • Come sapere quali utenti sono attualmente connessi? (Un modo potrebbe essere quello di fare in modo che il client continui a inviare una richiesta al server dopo ogni x minuti per notificare che è attivo. In caso contrario, considerare un client inattivo).
  • Sono sufficienti le convalide lato client? In caso contrario, come ripristinare un'azione se il server non convalida qualcosa?

Non sono sicuro che si tratti di una soluzione efficiente e di come si ridimensionerà. Gradirei davvero se le persone che hanno già lavorato su tali app possano condividere le loro esperienze che potrebbero aiutarmi a trovare qualcosa di meglio. Grazie in anticipo.

Informazioni addizionali:

Il lato client è implementato nel motore di gioco C ++ chiamato marmalade. Questo è un motore di gioco multipiattaforma, il che significa che puoi eseguire la tua app su tutti i principali sistemi operativi mobili. Possiamo certamente ottenere il threading e che è anche illustrato nel mio diagramma di flusso. Sto pensando di utilizzare MySQL per server e SQLite per client.

Questo non è un gioco a turni, quindi non c'è molta interazione con altri giocatori. Il server fornirà un elenco di giocatori online e potrai combatterli facendo clic sul pulsante di battaglia e dopo alcune animazioni, il risultato verrà annunciato.

Per la sincronizzazione del database ho in mente due soluzioni:

  1. Memorizza data / ora per ogni record. Tieni inoltre traccia dell'ultimo aggiornamento del DB locale. Durante la sincronizzazione, selezionare solo quelle righe che hanno un timestamp maggiore e inviare al DB locale. Mantieni un flag isDeleted per le righe eliminate in modo che ogni eliminazione si comporti semplicemente come un aggiornamento. Ma ho seri dubbi sulle prestazioni in quanto per ogni richiesta di sincronizzazione dovremmo scansionare il DB completo e cercare le righe aggiornate.
  2. Un'altra tecnica potrebbe essere quella di tenere un registro di ogni inserimento o aggiornamento che ha luogo contro un utente. Quando l'app client richiede la sincronizzazione, vai a questa tabella e scopri quali righe di quale tabella sono state aggiornate o inserite. Dopo che queste righe sono state trasferite correttamente sul client, rimuovere questo registro. Ma poi penso a cosa succede se un utente utilizza un altro dispositivo. Secondo la tabella dei registri, tutti gli aggiornamenti sono stati trasferiti per quell'utente, ma in realtà ciò è stato fatto su un altro dispositivo. Quindi potremmo dover tenere traccia anche del dispositivo. L'implementazione di questa tecnica richiede più tempo ma non è sicura che esegua la prima.

2
Vorrei prendere una valutazione di MsSQL e giocherellare con esso - non sono sicuro di ciò che MySQL fornisce in termini di replica, ma MsSQL 2008 R2 offre un CARICO di tecniche di replica (5-6) tra cui scegliere. Solo un pensiero, non odiare MySQL o altro, ma Microsoft è davvero brava a raggruppare.
Jonathan Dickinson,

1
@Jonathan Dickinson: c'è MySQL Cluster: P
Coyote il

1
Guarda anche PostgreSQL: la versione 9 ha una tecnologia di replica, PostgreSQL ha un'eccellente reputazione di lunga data per la gestione di carichi pesanti davvero bene, molti degli sviluppatori sembrano essere ottimisti e, soprattutto, è open source e completamente gratuito per tutti gli usi (incluso commerciale).
Randolf Richardson,

il fatto è che non possiamo usare lo stesso database sul lato client e server. Penso che il DB più adatto per il client sia SQLite in modo che non consumi molte risorse poiché l'app sarebbe in esecuzione su dispositivi mobili. Mentre la persona che lavora sulla parte server conosce solo MySQL, ecco perché abbiamo optato per questo. Inoltre ho sentito che la maggior parte dei giochi MMO utilizzano il database MySQL.
Umair,

1
@umair: il lato client può essere SQLite (ad esempio sull'iPhone è già disponibile). Ma puoi scegliere di archiviare semplicemente i dati rilevanti di cui hai bisogno in un file (cosa fa comunque SQLite) e di non disturbarti con il DB lato client come faresti con un'app Javascript.
Coyote,

Risposte:


15

Se non è un gioco "in tempo reale", nel senso che i giocatori non hanno bisogno di vedere il risultato immediato delle azioni di un altro giocatore su una scena di gioco, allora dovresti stare bene con le richieste HTTP. Ma tieni presente il sovraccarico di HTTP.

Detto questo, l'utilizzo di HTTP non ti salverà dalla progettazione con cura del protocollo di comunicazione. Ma se sei responsabile sia del server che del lato client, sei fortunato perché puoi adattare il protocollo quando ne hai bisogno.

Per sincronizzare tra il DB master e il DB client è possibile utilizzare qualsiasi protocollo di trasporto a cui si ha accesso, HTTP o altri. La parte importante è la logica dietro la sincronizzazione. Per un approccio semplice, è sufficiente raggruppare dal server tutte le ultime modifiche necessarie al client dall'ultimo timestamp nel DB client. Applicalo al DB client e procedi con quello. Se hai altre modifiche sul lato client caricalo se è ancora rilevante, altrimenti scarta.

Alcuni giochi non usano nemmeno un DB locale, ma tengono semplicemente traccia dello stato raggruppando le informazioni rilevanti dal server quando necessario.

Se la perdita di eventi locali non è accettabile, sì, è necessario disporre di memoria locale e salvare su tale memoria il più spesso possibile. Puoi provare a farlo prima di inviare ogni rete.

Per verificare la presenza di utenti attivi, eseguivamo il ping con HTTP ogni 20 secondi su una partita andata a buon fine ... Questo valore è aumentato immediatamente poiché i server sono stati sovraccaricati :( Il team del server non ha pensato al successo. Quindi ti consiglio di aggiungere un messaggio o una sorta di intestazione speciale nel protocollo di comunicazione che consente di riconfigurare i client (per il bilanciamento del carico della frequenza di ping e altri valori relativi alla comunicazione).

Le convalide lato client sono sufficienti se non ti dispiace cheat, hacker e altri script automatici che attaccano il tuo gioco. Il server può semplicemente rifiutare la tua azione e inviare un messaggio di errore con alcuni dettagli. È possibile gestire quel messaggio di errore eseguendo il rollback delle modifiche locali o non applicandole alla memoria locale se è possibile attendere fino a quando il server non risponde per salvare effettivamente le modifiche.

Abbiamo utilizzato il modello di comando nel nostro gioco per consentire il rollback semplice ed efficiente di azioni dell'utente non riuscite o non valide. I comandi venivano riprodotti localmente inviati ai peer o tradotti sul messaggio del server e quindi applicati ai peer o controllati sul server, in caso di problemi i comandi non venivano riprodotti e la scena del gioco tornava allo stato iniziale con una notifica.

L'uso dell'HTTP non è una cattiva idea in quanto, in seguito, ti permetterà di integrarti molto facilmente con i client Flash o HTML5, è flessibile, puoi usare qualsiasi tipo di linguaggio di scripting del server e con le tecniche di bilanciamento del carico di base aggiungere più server in seguito senza molto sforzo per ridimensionare il tuo backend.

Hai molto da fare ma è un lavoro divertente ... Quindi divertiti!


3
+1 per "HTML probabilmente è abbastanza buono" perché probabilmente lo è :).
Jonathan Dickinson,

1
@Coyote: grazie per aver dedicato del tempo e fornito una risposta così dettagliata. Mi piace molto la tua idea HTTP per fornire supporto ad altri client.
Umair,

1
Possa la forza essere con te :)
Coyote,

5

Quale sarebbe il modo migliore per sincronizzare database di server e client?

Il modo più semplice è implementare il database come un singolo file che è possibile trasferire. Se inizi a provare a confrontare le differenze tra i database, questo è un mondo di dolore e parlo per esperienza.

Si noti che il server non deve fidarsi delle decisioni prese dal client in base a quel database locale, poiché il client può modificarlo. Deve esistere puramente per i dettagli di presentazione.

Un evento deve essere salvato nel DB locale prima di aggiornarlo sul server?

No. Cosa succede se il server decide che l'evento non si è mai verificato? Il cliente non dovrebbe comunque prendere decisioni sugli eventi, perché non si può fidare di esso.

Ho notato che stai anche parlando del DB locale in due modi: uno, per "cose ​​che non cambiano frequentemente" e due, per eventi. Questi non dovrebbero essere nello stesso database, per i motivi sopra riportati: non si desidera iniziare a provare a unire o diffre le singole righe di dati nei database. Ad esempio, l'integrità referenziale diventa un problema quando un client ha un riferimento a un elemento che si decide di rimuovere dal server. Oppure se il client cambia una riga e il server cambia una riga, quale modifica ha la precedenza e perché?

Le richieste HTTP semplici serviranno allo scopo della sincronizzazione?

Sì, a condizione che siano abbastanza rari o piccoli. HTTP non è efficiente in termini di larghezza di banda, quindi tienilo a mente.

Come sapere quali utenti sono attualmente connessi? (Un modo potrebbe essere quello di fare in modo che il client continui a inviare una richiesta al server dopo ogni x minuti per notificare che è attivo. In caso contrario, considerare un client inattivo).

Se si utilizza un protocollo temporaneo come HTTP, questa è un'idea ragionevole. Quando il server riceve un messaggio da un client, è possibile aggiornare l'ora dell'ultimo accesso per quel client.

Sono sufficienti le convalide lato client? In caso contrario, come ripristinare un'azione se il server non convalida qualcosa?

No, per niente. Il cliente è nelle mani del nemico. Come ripristinare un'azione dipende interamente da ciò che consideri un'azione e dagli effetti che potrebbe avere. Il percorso più semplice è non implementare affatto l'azione fino a quando il server non risponde per consentirlo. Un percorso leggermente più complicato è garantire che ogni azione abbia una capacità di rollback per annullare l'azione e memorizzare nella cache tutte le azioni non riconosciute sul client. Quando il server li riconosce, eliminali dalla cache. Se un'azione viene rifiutata, ripristina ogni azione in ordine inverso fino a quella rifiutata inclusa.


1
L'hai inchiodato. Hai ragione sulla sincronizzazione dei DB +1 Il client non dovrebbe mai cambiare il DB da solo ... Basta chiamare le funzioni che girano sul server e aggiornare il DB usando le logiche del gioco, quindi recuperare i risultati.
Coyote,

@Kylotan: grazie per aver segnalato i flussi nel mio modello
umair,

@Kylotan: "Il modo più semplice è implementare il database come un singolo file che puoi trasferire. Se inizi a provare a confrontare le differenze tra i database, allora è un mondo doloroso, e parlo per esperienza." ciò significa che tutti i dati vengono scaricati ogni volta che viene avviata l'applicazione. Ci sono cose che non cambieranno molto frequentemente e quindi potrebbe non essere accettabile scaricarle ogni volta. Ciò consentirà inoltre di aumentare notevolmente i tempi di avvio dell'app. qualche idea?
Umair,

Se i dati sono piccoli, il problema scompare. Sarei sorpreso se il tuo database centralizzato di dati statici fosse molto grande. Tuttavia, se dividi i dati statici in parti più piccole, devi solo inviare quelli che sono stati modificati dall'ultima volta. Certo, puoi farlo su una riga per riga, se sei disposto a mantenere un tempo di modifica in ogni riga. In genere preferisco avere dati statici al di fuori del DB relazionale, per consentirmi di sovrascriverli facilmente.
Kylotan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.