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?
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 .
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 .
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:
Prendendo l'esempio precedente, il server invia il msg "attendiamo altri giocatori", attiva il proprio timer ed imposta il tipo fase:
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.
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 ).
Grazie.
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?
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 .
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 .
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 ).
Grazie.
Last edited: