Italian Sto ancora là...!

LucaMs

Expert
Licensed User
Longtime User
Da una vita sono "bloccato" nel cercare una soluzione ai problemi di sincronizzazione tra server e client (giochetto).

I motivi sono vari:

1) la difficoltà intrinseca;
2) probabilmente il timore del fiasco, una volta pubblicata l'app (in effetti, però, penso di aver visto in questi giorni un'app che ha qualcosa in comune con la mia - ad esempio è inedita - e che, penso in poco tempo, ha superato i 50.000 download);
3) se i nostri governanti non riescono a buttar giù una schifezza di legge elettorale da 3 anni, probabilmente mi sento invogliato a prendermela comoda (anche se sono in ritardo di almeno 13 anni!)
4) appunto me la prendo troppo comoda: affronto il problema 5 minuti, mi deprimo e mollo per settimane o mesi.
5) sono distratto da circa 13509835092039 altri problemi.

Mi sa che la premessa è stata leggermente prolissa; magari è questo, non sono sintetico? :p

Faccio subito un esempio, che poi è proprio il primo momento in cui si manifesta il problema (o forse ce ne sono altri che avvengono in precedenza e che ho risolto in maniera pedestre).

Ho una stanza di gioco nella quale è necessario che sia presente un numero minimo di giocatori (nm) prima che la partita possa iniziare; esiste anche un numero massimo.

Nel momento in cui nm giocatori "entrano nella stanza" (qui devo specificare: l'utente sceglie una stanza, invia l'identificativo della stanza al server, questo verifica la correttezza della scelta, invia l'ok al client e questo apre l'activity "stanza di gioco". Più avanti spiegherò perché ho specificato questo), in quel momento, dicevo, il server invia un "messaggio" ai partecipanti, per indicare che da quel momento parte un countdown (15 secondi) per attendere altri giocatori, dopodiché la partita inizierà.

Il server informa quindi i giocatori al tavolo e fa partire un proprio timer (15 sec); ogni client, ricevuto il messaggio, fa partire il proprio timer (15 sec) e vede una "progress bar" che mostra appunto il countdown.

Oltre al fatto che i client potrebbero ricevere il messaggio in maniera non sincronizzata (ed avviene di sicuro) c'è anche il fatto che mentre i primi nm - 1 giocatori riceveranno questo msg dal server stando già dentro l'activity-stanza, il giocatore nm lo riceverà prima ancora che l'activity sia stata avviata (questo ha una motivazione e potrei anche modificarlo, ma non è questo il punto principale).

Beh, mi sono perso io stesso, spiegando, figuramoci voi, scarsamente interessati, a dire poco :D.

Insomma, questo è solo un caso specifico ma la sincronizzazione è un problema che si presenta in ogni fase del gioco.

Uno dei tentativi effettuati per risolvere il problema era stato il fare in modo che il server inviasse anche un timestamp, ovvero, assieme al comando, inviava il proprio orario; il client effettuava la differenza tra l'orario ricevuto e quello proprio (oltretutto sperando che quello del dispositivo sia aggiornato) e decurtava questa differenza dalla durata del countdown. Piccolo problema, capitava addirittura che questa differenza fosse negativa, come se il client avesse ricevuto il msg prima ancora che il server lo avesse inviato :eek::mad:.

Ora sto pensando ad una "soluzione" piuttosto intricata e non semplicissima da gestire; ne ho appena una vaga idea e ve la sottopongo, magari vi piace e sarete in grado di perfezionarla; oppure avrete un'idea più semplice e migliore.

Se ogni comando inviato dal server (e qui c'è il problema che un conto è se i destinatari sono tutti i giocatori nella stanza, altro è quando sia indirizzato ad un solo giocatore... PROBABILMENTE) lo considerassi come una sorta di HTTPJOB, per il quale attendo un evento "Completed"?

Potrei creare una struttura-fase (un type, ad esempio - ah, mentre scrivo sto pensando che forse una classe, con i suoi eventi, sarebbe anche molto meglio), composta, ad esempio, da:

B4X:
Type tFase(Nome As String, Inizio As Long, Durata As Long)

Prendendo l'esempio precedente, il server invia il msg "attendiamo altri giocatori", attiva il proprio timer ed imposta il tipo fase:

B4X:
Dim FaseAttesa As tFase
FaseAttesa.Initialize
FaseAttesa.Nome = "AttesaAltriGiocatori"
FaseAttesa.Durata = 15000
tmrFase.initialize("tmrFase", FaseAttesa.Durata)
FaseAttesa.Inizio = DateTime.Now

Quindi si attende un "fase completata" da ciascun client. Sul server ogni giocatore è legato ad un websocket; più precisamente ogni classe websocket handler (o meglio ogni istanza di questa classe), che ho chiamato wshUser, rappresenta un giocatore e possiede un proprio oggetto websocket. Da "questa classe" si chiama una routine di gestione eventi che metterei nella classe che rappresenta la stanza da gioco, quindi in comune tra gli n giocatori.

B4X:
Sub GestioneFasi(Utente as wshUser)
    ' qui potrei utilizzare una map globale, con oggetto wshUser come chiave e un boolean Completata come valore.
    mapFase.Put(Utente, True)
    ' qui verificherei se le dimensioni della map e quella del numero di giocatori sia uguale e quindi l'operazione sia stata completata da tutti i client.
    If mapFase.Size = NumeroGiocatoriNellaStanza Then
          tmrFase.Enabled = False
          tmrFase_Tick ' brutto, si dovrebbe chiamare una routine specifica, non quella del timer.
    End If
End Sub

' la routine sopra ha grosse lacune; dovrebbe, ad esempio, considerare il nome della fase ed altro.
' Anche il timer probabilmente dovrebbe essere legato a quella specifica fase, boh, non ho pensato moltissimo

tmrFase_Tick
    ' tempo SERVER scaduto per l'operazione - potrei magari impostare questo tempo maggiore di x secondi rispetto a quello concesso ai client, 3-5 secondi in più
     tmrFase.Enabled = False
     ' passaggio a fase successiva
     mapFase.Clear

E' tutto solo abbozzato; ad esempio non si considera l'eventuale disconnessione (volontaria o no) di un cient, non ho scritto come passare alla fase successiva, non ho sfruttato il nome-fase, etc.


Come vedete è contorto, incompleto e pure molto impreciso, abbozzato.


Se avete un'idea più semplice, affidabile, robusta, magari spiegatemela... e meglio di quanto abbia fatto io (che ci vuole davvero poco :p).


Grazie.
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
penso di aver visto in questi giorni un'app che ha qualcosa in comune con la mia - ad esempio è inedita - e che, penso in poco tempo, ha superato i 50.000 download

1) l'autore (o autori) è di Roma ! (non è che per caso è pure un membro di b4x?)
2) utilizza i Google Play Game Services (GPGS).

Utilizzare i GPGS ha, penso, grossi vantaggi ma forse qualche svantaggio:

PRO:

1) non si deve implementare tutta la fase di registrazione-login degli utenti (né su client, né su server), cosa non di poco conto. Il contro è che si può accedere al gioco solo tramite un account Google (per quanto ne so);

2) si possono scegliere avversari che si trovino nella propria zona geografica (non lo sapevo; a fatica ho aggiunto la possibilità di scegliere stanze della propria Nazione);

3) tutto il "corollario", come classifica, obiettivi e non so cos'altro è già predisposto e disponibile.


CONTRO:

1) è disponibile una libreria di Informatix per accedere a questi GPGS; ma se Google cambia qualcosa? Dovrei sperare ed attendere che l'autore aggiorni la propria libreria;

2) non so quanta libertà diano questi servizi di Google. Ad esempio, vorrei aggiungere una chat "parallela", ovvero non interna al gioco. I servizi non credo che lo consentano e quindi come potrei poi ottenere un riferimento al giocatore, da sfruttare tramite websocket?

3) durante la partita vorrei che i giocatori potessero inviarsi "oggetti"; esempio: nel poker di Zynga ci si inviano bevande, oggetti gradevoli ed altri insultati.

4) non mi è chiarissimo come funzionino. Pare che lo "stato del gioco" venga trasferito ai vari client ed il client di turno lo modifichi. Se fosse solo così, mi sembrerebbe molto limitato ed ingestibile; eppure il romano di cui sopra lo ha gestito e realizzato.


Insomma, "sono nel più completo marasma", come direbbe qualcuno!
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Hai provato con firebase, senza andare a toccare i GPGS? Propagare ai partecipanti un'informazione è molto semplice. Io l'utilizzo e funziona egregiamente.
Il problema non è inviare dati ai client, ma la sincronizzazione dei dati (e delle "azioni"). Firebase al massimo mi consentirebbe di inviare notifiche (appunto, dati).



Le funzioni che faresti fare ai server l'assegni al creatore della stanza...
Se lascio fare qualcosa ai client, basta un hacker e salta tutto.


Grazie comunque.
 

Star-Dust

Expert
Licensed User
Longtime User
Il problema dell'Hacker o lamer di turno lo avrai sempre in ogni caso... certo senza server gli faciliteresti il lavoro.

Il problema del server è una questione chiave non solo per i giochi ma per molte App anche Business. Vedi la discussione sulle APP RISTORANTI.
Se avessi un server tuo o meglio un azienda che investe su di te e ti da un badget da usare per Housing e/o Hosting, il problema non ci sarebbe.

Ma tornando a noi (mettiamo i piedi a terra) dobbiamo scegliere una soluzione fra quelle a portata di mano. Scegliendone una, godi dei vantaggi che ti offre quella soluzione ma ne accetti anche i limiti. Non c'é una soluzione semplice, che ti garantisce velocità, sincronia, sicurezza, di facile configurazione a costo zero.
Quindi devi capire a cosa puoi rinunciare: alla sicurezza ? alla sincronizzazione? La semplicità?
 

moster67

Expert
Licensed User
Longtime User
Perché non prepari tutti (server, app etc) e poi vai a creare un gruppo di beta-tester e poi testate, testate e testate?!
Dopo un periodo di prova puoi fare le tue valutazioni e decidi come procedere (abbandonare, proseguire, modificare etc).
Se non fai così, rimani per sempre al "punto zero".
 

LucaMs

Expert
Licensed User
Longtime User
Innanzitutto, grazie per le risposte.


Quindi devi capire a cosa puoi rinunciare: alla sicurezza ? alla sincronizzazione? La semplicità?
Malgrado la lentezza (dovuta a molti e lunghi periodi di sospensione), sia client che server sono già stati sviluppati in gran parte.
Non scelsi di usare i servizi di Google per vari motivi (le possibili limitazioni rispetto alle mie "esigenze" ed il fatto che dovrei affidarmi ad una libreria b4a per la quale non prevedo un futuro roseo - ovvero potrebbe capitare che non venga aggiornata dopo eventuali modifiche apportate da Google a questi servizi).

L'unica cosa alla quale, a malincuore, si potrebbe rinunciare sarebbe la semplicità (di sviluppo), a sicurezza e soprattutto alla sincronizzazione non si può (immaginate una partita a poker nella quale un giocatore rilanci mentre la mano, per il server e per gli altri client sia già terminata!).


Perché non prepari tutti (server, app etc) e poi vai a creare un gruppo di beta-tester e poi testate, testate e testate?!
Sono già pronti ma non servono test, fin dall'inizio si nota il problema sincronizzazione.
Penso che non ci sia altra strada che richiedere una sorta di "ricevuta" dal client per ogni comando che il server invia; però potrebbe succedere che il msg venga ricevuto dal client in tempi accettabili e invece sia la risposta a tardare, per qualche motivo (momentanea lentezza della connessione del client, ad esempio). Quindi dovrei associare ad ogni comando un timer (magari mettendoli in una map) e dare un tempo limite di risposta a ciascuno ma, in caso di "mancata ricevuta", si potrebbero e magari dovrebbero compiere azioni specifiche a seconda del comando o dell'utente al quale si è inviato.

Trovare un sistema unico, ben strutturato, organizzato, non è semplice, ANCHE perché c'è da considerare le eventuali sospensioni dell'app da parte dell'utente (brevi pause dell'Activity) e perché non tutti i comandi riguardano tutti gli utenti.

Andare avanti e per ogni situazione trovare un "escamotage" anziché rendere la cosa generale è la strada più comoda ma anche quella che più probabilmente porterà a creare un sw pasticciato, poco gestibile e difficilmente aggiornabile.

Va beh, mi capiterà almeno un giorno su 365 in cui sarò sufficientemente lucido per trovare la strada migliore :)


Grazie.
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Detto fra di noi nel passato ho giocato su siti internet (forse qualcosa di simile a quello che vuoi creare tu) in cui tu aspetti la mossa della contro parte e il server (o la connessione) era stralenta, e attendevi un infinità.
(Un misto fra server lenti, troppe connessioni che non riusciva a gestire bene e infine internet a 56k analogiche :):))

A questo punto io personalmente andrei su B4J.
Circa 10 anni fà avevo creato un Server IRC in Java (o meglio ho acquistato il codice sorgente e l'ho modificato per i mie bisogni:p:p:p) e ho realizzato un server in casa mia (IP pubblico con DDNS) e per circa un anno ha retto una meraviglia, reggeva un centinaio di connessione (con ADSL)

Quindi mi sentirei di suggerire B4J... poi vedi tu.
 

LucaMs

Expert
Licensed User
Longtime User
Ho già il server b4j (pessimo, dato che è sviluppato da me :p) e client b4a ed intendo usare quello, ormai; certo che i servizi di Google, che semplificano buona parte del lavoro, mi attirano, ma, come mi pare di aver detto, forse mi limiterebbero per certe funzionalità (come fare una chat parallela?) e, soprattutto, dipenderei da una libreria della quale non si conosce il futuro.

Cmq, la notte porta consiglio (a me sempre pessimi consigli, di solito :D) e quella passata mi ha dato un suggerimento forse da seguire, simile a quanto ho scritto in precedenza nel primo post.

Creare una classe che gestisca delle "ricevute" dai client.

Es. fase descritta sempre in #1, server e client devono attendere 15 secondi l'entrata di altri giocatori.
Il server passa questa informazione-comando ai client ed anche alla classe; la classe avvia un proprio timer, con 5 secondi in più rispetto ai 15 passati ai client (prevedendo problemi di disallineamento); inoltre sarà questa classe a ricevere dai client la notifica "ok, il mio tempo è scaduto". Quando la classe avrà ricevuto il msg da tutti i giocatori, fermerà il proprio timer e scatenerà un evento ricevuto dalla classe di gioco (del server, ovviamente), nella quale partirà la fase successiva; se invece scadesse il tempo stabilito nella classe "ricevute", si dovrà verificare se qualche client non abbia risposto e lo si toglierà dalla stanza di gioco (magari informandolo, per gentilezza :)).
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Ho posto una questione nel forum "internazionale" relativa al mio ultimo post qui.

Sperando di ottenere anche un paio di migliaia di utenti connessi contemporaneamente (magari!!!), avrei 500 / 1000 timer "attivi" contemporaneamente; potrebbe questo essere un problema (sovraccarico)?

Anche perché tutti i timer starebbero nel thread principale.
 

Star-Dust

Expert
Licensed User
Longtime User
Che dispositivo possiedi.
Ma forse supponi che altri italiani abbiano acquistato il tuo stesso modello? :p:p:p
 

LucaMs

Expert
Licensed User
Longtime User
Qui so' solo spine, altro che rose e fiori.

Alla mia domanda sui "1000" timer nel main thread Erel ha risposto che è anche possibile metterli, se l'intervallo non è troppo breve, ma è preferibile usare i "background workers", che non conosco affatto e manco mi è chiaro come funzionino, leggendo adesso al volo, dato che nell'esempio c'è uno strano StartMessageLoop che pensavo dovesse essere l'unico in un server b4j.

Rileggerò con calma e chiederò ad Erel qualche chiarimento (a meno che qualcuno di voi li abbia già usati).
 

Star-Dust

Expert
Licensed User
Longtime User
Ho letto il post di Erel, molto interessante. Purtroppo non ho ancora usato mano su B4J, non saprei come aiutarti.
 

tigrot

Well-Known Member
Licensed User
Longtime User
Tecnica multitask. Immagina che il tuo programma possa eseguire contemporaneamente in diversi statements. Il problema è la rientranza dei dati. Una task ti distrugge i dati dell'altra, se non architettata ad hoc. Ne ho scritto una di applicazioni anni fa, ma in C++.
 

Star-Dust

Expert
Licensed User
Longtime User
Tecnica multitask. Immagina che il tuo programma possa eseguire contemporaneamente in diversi statements. Il problema è la rientranza dei dati. Una task ti distrugge i dati dell'altra, se non architettata ad hoc. Ne ho scritto una di applicazioni anni fa, ma in C++.

si deve creare una pila FIFO che mette in lista i dati ricevuti?
 
Top