View Full Version : [CICLO 10] Storia 2
Storia: Definizione del gameturn, un intero incrementato di 1 alla fine di ogni loop in Game (ovvero ogni 20 millisecondi). Ogni valore in millisecondi presente nel codice del gioco dovrà essere convertito in gameturn.
Introduzione di un sistema di logging degli eventi, che salvi su file ogni pressione e rilascio dei vari tasti, insieme al relativo gameturn.
Punti cardine da tenere a mente durante i lavori:
* Mai fare a gara a chi finisce il task per primo, meglio procedere con calma, altrimenti perderemo molto più tempo in seguito
* Evitiamo di complicarci la vita, esiste di certo una soluzione più semplice di quella che abbiamo pensato di implementare
* MAI aggiungere elementi non richiesti esplicitamente dai task: se mai serviranno, se ne parlerà nelle prossime storie
* Comunichiamo il più possibile, se qualcosa non è chiaro discutiamone tutti i dettagli fino ad eliminare ogni dubbio, anche il più insignificante
* Postare sempre la test list PRIMA di mettere mano al codice
Ecco i task, come sempre di Vic:
10.2.1: VICIUS + ??: completato
Separare il game loop e la logica del gioco dalla sua inizializazione in una classe esterna di nome GameLoop. Creare dei test per la nuova classe.
10.2.2: cionci + nihil84: completato
Aggiungere alla nuova classe un contatore detto gameTurn che deve essere incrementato alla fine di ogni ciclo del loop (quindi ogni 20 ms).
10.2.3: cionci + nihil84: 2 giorni
Creare un sistema di logging all'interno di GameLoop che scrive se specificato scrive su un file il valore del contatore gameturn e tutti gli eventi di pressione e rilascio dei tasti. Il formato del file deve essere il piu semplice possibile. COme esempio questo
seed xxxxx
gameturn
azione
azione
...
gameturn
azione
...
Giusto per avere un esempio pratico:
seed 234518458724
01
ruby-topaz pair created
02
move left
05
move right
rotate clockwise
11
move right
mirroring
17
pair dropped
diamond-emerald pair created
...
Le azioni per ogni gameTurn sono semplici stringhe.
Ciao ;)
I task 1 e 3 sono da svolgere solo in pair programming.
Il formato del file non mi piace tanto.
A me piacerebbe di più in XML...ma so benissimo che, giustamente, lo boccierete...YANGI
Forse conviene una riga con
| gameturn | tasto1 (0 o 1) | tasto 2 ( 0 o 1)| ... | tastoN (0 o 1)|
Esempio:
0 | 0 | 0 | .... | 0 |
1 | 0 | 1 | .... | 0 |
2 | 1 | 0 | .... | 0 |
3 | 0 | 1 | .... | 1 |
....
In questo modo, leggendo riga per riga e separando con un carattere particolare, con uno stringTokenizer in 5 min si legge il Log...e nello stesso tempo si scrive. :sofico:
Che ne dite??
Il formato del file non mi piace tanto.
A me piacerebbe di più in XML...ma so benissimo che, giustamente, lo boccierete...YANGI
Forse conviene una riga con
| gameturn | tasto1 (0 o 1) | tasto 2 ( 0 o 1)| ... | tastoN (0 o 1)|
Esempio:
0 | 0 | 0 | .... | 0 |
1 | 0 | 1 | .... | 0 |
2 | 1 | 0 | .... | 0 |
3 | 0 | 1 | .... | 1 |
....
In questo modo, leggendo riga per riga e separando con un carattere particolare, con uno stringTokenizer in 5 min si legge il Log...e nello stesso tempo si scrive. :sofico:
Che ne dite??
Uhm ma in questo modo non puoi rappresentare la pressione e il rilascio di un tasto nello stesso gameturn oppure la ripetizione piu volte dello stesso tasto.
ciao ;)
Esatto, i gameturn sono molto brevi ma non istantanei, quindi dobbiamo prevedere la possibilità di più eventi in ognuno.
A questo punto, direi che è molto più pratica la sintassi di Vic.
Ovviamente i gameturn in cui non succede nulla (ovvero nessun tasto viene premuto e nessuno dei tasti già premuti da prima viene rilasciato) non devono essere loggati.
Io lo farei in maniera simile a quanto suggerito da Bonfo...ogni riga è un Gameturn...quindi ci rispiarmiamo il conteggio...
CODICE_TASTO TIPO_AZIONE CODICE_TASTO TIPO_AZIONE e così via per tutta la riga relativa a questo Gameturn...
Semplice da gestire...basta leggere/scrivere uno \n alla fine del gameturn...e, secondo me, non c'è nemmeno bisogno di contare i gameturn... Se volete aggiungere anche il valore del gameturn (forse ho capito perchè), può essere comunque aggiunto molto semplicemente...tanto i campi sono tutti prefissati: intero|byte ed eventualmente un intero per il gameturn...
Uhm ma in questo modo non puoi rappresentare la pressione e il rilascio di un tasto nello stesso gameturn oppure la ripetizione piu volte dello stesso tasto.
ciao ;)
non penso sia possibile, visto che la Game.processInput() viene eseguita una volta per Game.gameLoop()
in teoria uno potrebbe premere e rilasciare un tasto senza che gridController se ne accorga
il gameTurn del movimento puo essere utile per ricostruire la "storia" della partita.
il gameTurn del movimento puo essere utile per ricostruire la "storia" della partita.
Ma se su ogni riga ho un gameturn è ovvio che il gameturn è l'indice della riga ;)
Per i tasti...mi sembra che gli eventi vengano gestiti tutti, anche pressione e rilascio dellos tesso tasto all'interno dello stesso intervallo di tempo...
Bhè...il problema che volevo risolvere era la presenza della coppi CODICE_TASTO CODICE_AZIONE.
In questo modo bisogna ogni volta "parsare" il CODICE_TASTO.
Con la mia proposta per gestire più avvenimenti in uno stesso gameturn si potrebbe anche fare:
3 | 1 | 0 | .... | 0 |
3 | 0 | 0 | .... | 0 |
3 | 1 | 1 | .... | 0 |
Con più righe che fanno capo allo stesso gameturn. Forse, però, si complicano un po' le cose così... :(
In ogni caso ho una domanda.
Se 2 avvenimenti avvengono durante lo stesso gameturn...come facciamo poi a riprodurlo??
Ovvero se la nostra "frequenza di campionamento" è troppo bassa perdiamo di risoluzione...ovvero i 2 avvenimenti verranno riprodotti istantaneamente.
Questo non può porre problemi in casi estremi??
Esempio
D
X
XX
La gemma X sta cadendo...viene premuto ( -> ) dopo che ormai D ha superato la soglia della gemma X e quindi non si muove...nel log viene registrato ( -> ).
Cosa accade se tale avvenimento, quando riprodotto "istantaneamente" all'inizio del gameturn, fa si che D si riesca a spostare e si posi sopra la prima X??
(Mi sono spiegato?? :mbe: )
Bisogna anche dire che sono solo 20ms...
...però non si sa mai.
Ma se su ogni riga ho un gameturn è ovvio che il gameturn è l'indice della riga ;)
si, ma in questa maniera scrivi anche gameturn in cui nn è successo niente, che si potrebbero nn scrivere(sono cmq 2 soluzioni complementari)
Per i tasti...mi sembra che gli eventi vengano gestiti tutti, anche pressione e rilascio dellos tesso tasto all'interno dello stesso intervallo di tempo...
[/quote]
nn zo, sarebbe bello fare un test alzando il rate del loop in Game
Ora che ci penso, non credo sia sufficiente salvare il solo evento... Questo perchè manca un riferimento al timestamp...e potrebbe creare casini nelle ripetizioni... Quindi bisogna anche salvare il timestamp...
Salvando il timestamp è sempre necessario il gameturn ?!!?
si, ma in questa maniera scrivi anche gameturn in cui nn è successo niente, che si potrebbero nn scrivere(sono cmq 2 soluzioni complementari)
Esatto. Loggare due timestamp identici tra loro non avrebbe senso, quindi quelli uguali verranno saltati (o avremmo 1000 righe in 20 secondi, un pò troppe per quando si va a caccia di bug).
Ora che ci penso, non credo sia sufficiente salvare il solo evento... Questo perchè manca un riferimento al timestamp...e potrebbe creare casini nelle ripetizioni... Quindi bisogna anche salvare il timestamp...
Salvando il timestamp è sempre necessario il gameturn ?!!?
Il gameturn non è altro che un livello di astrazione superiore del timer che usiamo ora. Insomma, possiamo considerarlo come una versione custom del timestamp, in un formato più intellegibile e per noi più utile (indica infatti il numero di loop trascorsi).
Quindi, avendo il gameturn, non credo serva a molto loggare il timestamp.
Jocchan: mi ero ftto l'idea che ci potessero essere timestamp diversi all'interno dello stesso gameturn..ora verifico come vengono inizializzati...
Sì, in effetti possono esistere...anche se io impedirei questa cosa... Ora ci provo...
A questo punto il timestamp negli eventi mi sembra un ripetizione...
No...confermo che bisgona scrivere/trasmettere anche i timestamp che tra l'altro bisognerebbe uniformare con l'intera applicazione...
Lo stato dei tasti viene letto ogni 2/3 ms...mentre vengono gestiti ogni 60 ms... Quindi bisogna ad ogni costo scrivere anche il timestamp...
Fatto... Viene usato il timer di Game anche per Input...
Resta comunque il fatto che vadano spediti i timestamp...di fatto è necessario un aggiustamento del timer della finestra secondaria pari al ping fra i due utenti...
ma se dal gioco vengono considerati solo i comandi letti nel gameLoop, non basta loggare solo quelli?(od ho capito male io come funziona?)
ps. aggiustare il timer della seconda finestra a seconda del ping, puo essere fatto come ottimizzazione, ma il ping puo essere variabile, quindi non si puo usare come regola aurea
ma se dal gioco vengono considerati solo i comandi letti nel gameLoop, non basta loggare solo quelli?(od ho capito male io come funziona?)
Sicuramente può bastare, ma gli eventi vengono registrati con un proprio timer..."appiattire" tutti gli eventi al gameloop (che non avviene ogni 20 ms, ma gira di continuo, l'unica pausa che c'è è di 1 ms), e di conseguenza ad un timer costante per tutti, potrebbe significare la perdita o l'inserimento di una ripetizione che altrimenti non ci dovrebbe essere...
Nella storia è riportato erroneamente che ogni loop di Game finisce/inizia ogni 20 ms... Il loop gira continuamente...c'è solo una pausa di 1ms alla fine di ogni loop...
ps. aggiustare il timer della seconda finestra a seconda del ping, puo essere fatto come ottimizzazione, ma il ping puo essere variabile, quindi non si puo usare come regola aurea
Certamente, ma può essere una stima anche fatta molto spesso...
Ho in mente di fare una modifica anche all'aggiornamento dei timestamp....
Vi spiego, ora, praticamente da ogni parte, funzina così:
if(nuovoTimeStamp > vecchioTimeStamp + X)
{
vecchioTimeStamp = nuovoTimeStamp;
faiAzione();
}
Secondo me va SEMPRE trasformato in questo modo:
if(nuovoTimeStamp >= vecchioTimeStamp + X)
{
vecchioTimeStamp += X;
faiAzione();
}
Il primo metodo va benissimo in locale, quando i ritardi sono minimi, ma il secondo, oltre ad andare bene in locale va bene anche per la griglia secondaria...che in questo modo non si perderebbe mai una "azione"...
inoltre sarebbe compatibile con l'aumento/diminuzione dello sfasamento del timer...
Cionci, penso che la storia del loop ogni 20ms dipenda da un refactoring da fare a Game.
Mi sembra strano che sia ogni 20 ms... Anzi, sarebbe dannoso, visto che l'UpdateRate viene fatto ogni 10 ms e lo stesso FrameRate ogni 20 ms... Di fatto si antrebbe ad aumentare queste due grandezze...
Cionci, penso che la storia del loop ogni 20ms dipenda da un refactoring da fare a Game.
No il refactoring di Game serve solo a togliere la logica del gioco da Game e spostarla in GameLoop.
Quanto dice cionci mi ha fatto riflettere.
GameTurn ci serve? Per ora non è altro che un'astrazione del timestamp da scrivere su un file. Ma il timestamp lo dobbiamo scrivere quindi non vedo la necessita di averlo.
ciao ;)
^TiGeRShArK^
08-02-2006, 12:59
ehm... non ho capito bene...
dovremmo inviare TUTTI gli eventi generati da un utente al diamonds dell'altro utente??? :eek:
ma supponendo anke 3 ms come intervallo per leggere un evento avremo la bellezza di circa 300 eventi al secondo...
mi sembrano un pò tantini onestamente... già trasmettendo 10 byte ad evento arriveremmo a 3 KB/s ke secondo me è il limite massimo...
inoltre per le considerazioni effettuate nell'altro thread secondo me non possiamo basarci sulla ricostruzione della storia del gioco basandoci sugli eventi perchè ci troveremmo a trattare con problematiche di ritrasmissione in caso di errore che non ci possiamo permettere di utilizzare per problemi di latenza..
imho la cosa migliore è usare (tanto x fare un esempio "alla femminina") la stessa tecnica utilizzata nei satelliti artificiali.... aumentare la ridondanza per prevenire gli errori piuttosto che effettuare ritrasmissioni... in questo modo avremo un pò di ridondanza in + ma in caso di errori potremo sempre e comunque gestire l'aggiornamento del gioco senza rimanere blokkati anke per 300 ms e ritrovarci comunque un evento fuori sequenza ke non potremo piu' gestire avendo già processato gli eventi precedenti e perdendo quindi la consistenza tra le due piattaforme del gioco....
spero di essermi spiegato anke se forse sono stato un pò criptico... :fagiano:
Qui si parla solamente di scrivere gli eventi su un file...mi sembra che abbiate già parlato di questa cosa su nsn... Credo comnuque che la trasmissione sulla rete e questa storia siano relazionati strettamente...
Quello che ora c'è da capire è: questo storia è relativa alla comunicazione sulla rete e semplicente riguarda solo quel famoso "log dei tasti"...
Emulando il nostro coach, fek, mi viene da dire YANGI.
La storia riguarda un log degli eventi locale.
La rete....ci si pensa quandò sarà il momento. Non è compresa nella storia. Stiamo rischiando una confusione infinita!!! :ot:
L'obbiettivo è ottenere un file con il log degli eventi dell'utente locale per poi avere la possibilità di debuggare...per adesso.
Il punto è....come lo facciamo sto file. Non ci dovrebbe interessare la rete.
Il gameturn è una astrazione utile o no?? Il timestamp è obbligatorio da spedire??
Come fa il log a tenere conto delgi avvenimenti ripetuti com e doppia pressione o mantenimento della pressione per più tempo??
Queste sopno le cose alle quali dobbiamo rispondere...il resto YANGI
Ora dico la mia.
Il gameTurn semplifica il file rispetto un timestamp: quindi ci vuole :D
IMHO il GameTurn toglie informazioni...e potrebbe portare ad una intepretazione errata del log...
Poi ancora...il GameTurn non è definito come tale... Cosa si intende per GameTurn ?
1) il tempo fra un update dello stato delle gemme in grid e il successivo
2) il tempo fra un rendering e il successivo
3) il tempo fra un'elaborazione degli ingressi e la successiva
4) il tempo fra l'inizio e la fine di un gameloop (ricordo che in questo caso non ci sono temporizzazioni a regolare tale intervallo)
In ogni caso potremmo fare direttamente un log degli eventi nel momento in cui vengono gestiti (quindi da dentro l'event handler)... In tal caso sì, potremmo riprodurre esattamente le mosse (e le ripetizioni) ed in questo caso proprio in base al gameturn che però definirei come il punto 3 sopra...
Ne ho discusso con Fek, ed abbiamo definito meglio a cosa vogliamo arrivare con questa storia: un sistema di logging degli eventi facilmente intellegibile.
I gameturn servono solo per semplificarci la vita. Avranno applicazioni anche più avanti, ma (YAGNI) per ora li implementiamo solo per il logging.
Cambieremo però alcuni dettagli, ed ora insieme a Vicius vedremo di riorganizzare anche i task, in modo da rendere il tutto ad un livello di astrazione ancora superiore: invece dei codici dei tasti e del loro stato, loggheremo direttamente delle stringhe che descrivano l'azione che è effettivamente avvenuta su schermo, saltando ovviamente i gameturn in cui non è successo nulla, ed ordinando gli eventi di ogni gameturn in base ai timestamp (non loggati).
La forma finale sarà qualcosa del tipo:
01
ruby-topaz pair created
02
move left
05
move right
rotate clockwise
11
move right
mirroring
17
pair dropped
diamond-emerald pair created
...
I gameturn li usiamo insomma per separare questi "blocchi" di eventi, e per avere un'idea del tempo trascorso tra l'uno e l'altro (20ms significa 1/50 di secondo).
^TiGeRShArK^
08-02-2006, 15:29
Ne ho discusso con Fek, ed abbiamo definito meglio a cosa vogliamo arrivare con questa storia: un sistema di logging degli eventi facilmente intellegibile.
ah ecco... perfetto..
essendomi perso la discussione su nsn pensavo fosse collegato alla trasmissione in rete.... non avevo capito ke era per debug :p
I gameturn li usiamo insomma per separare questi "blocchi" di eventi, e per avere un'idea del tempo trascorso tra l'uno e l'altro (20ms significa 1/50 di secondo).
Ok...allora ci si può sincronizzare direttamente con il lancio degli eventi (un turno ogni InputRate ms).... In questo modo, come ho scritto sopra, ogni volta che viene chiamata GridController.reactToInput vengono loggati tutti gli eventi che vengono svolti i quel turno...di fatto è molto più semplice e lineare...
Perchè non si salva il random seed invece di scrivere i nomi delle gemme generate ?
Perchè non si salva il random seed invece di scrivere i nomi delle gemme generate ?
eh ma poi come ricrei la sequenza?
Se salvi il random seed e reinizializzi il random generator con lo stesso seed, la sequenza è automaticamente identica alla precedente...
Dopo il log sarà così:
423422543 <- random seed
01
pair created
02
move left
05
move right
rotate clockwise
11
move right
mirroring
17
pair dropped
pair created
Se salvi il random seed e reinizializzi il random generator con lo stesso seed, la sequenza è automaticamente identica alla precedente...
Dopo il log sarà così:
423422543 <- random seed
01
pair created
02
move left
05
move right
rotate clockwise
11
move right
mirroring
17
pair dropped
pair created
dipende se si vuol dare leggibilità al log oppure no(secondo me sarebbe opportuno fosse leggibile)
Ma a cosa servirebbe farlo leggibile ?!?!? Secondo me dovrebbe solamente essere riprodubile...la leggibilità credo che sia secondaria...
Mi sembra che l'ultima proposta fattaci sia mooolto più orientata alla leggibilità che alla riproducibilità...
...anche se io sono d'accordo con te, cionci. :stordita:
Meglio andare sulla leggibilità ;)
Vabbè...in ogni caso niente vieta di salvare anche il random seed ed usare quello per riprodurre la sequenza... Scrivere le gemme generate ci può anche stare, ma sicuramente leggerle da lì per poi riprodurre la sequenza è diversi ordini più complicato che usare il random seed ;)
Vabbè...in ogni caso niente vieta di salvare anche il random seed ed usare quello per riprodurre la sequenza... Scrivere le gemme generate ci può anche stare, ma sicuramente leggerle da lì per poi riprodurre la sequenza è diversi ordini più complicato che usare il random seed ;)
in genere il compito di un log, è illustrare cosa sta facendo l'applicazione(in caso di errore/eventi particolari), e il dare in pasto il log a un programma per vedere cosa fa è "complicato".
Certo nulla vieta di mettere anche il seed all'inizio(cosa che si puo aggiungere in caso si voglia fare riprodurre il log a Diamonds)
Allora, se questo ci dà dei vantaggi in termini di riproducibilità, nella prima riga salveremo il seed.
Potrebbe servirci per controllare se la lista delle gemme è corretta. Se vogliamo un po' di leggibilità in più basta fare cosi:
seed 234518458724
ciao ;)
in genere il compito di un log, è illustrare cosa sta facendo l'applicazione(in caso di errore/eventi particolari), e il dare in pasto il log a un programma per vedere cosa fa è "complicato".
Secondo me il log non serve a niente se non può essere riprodotto dall'interno di diamond... A leggere il log non capiremmo mai niente ;)
Basterebbe poter inserire manualmente il seed in modalita` debug... Con la lista dei comandi si riproduce...
Secondo me il log non serve a niente se non può essere riprodotto dall'interno di diamond... A leggere il log non capiremmo mai niente ;)
Bingo. Il log e il record interno ci devono servire per poter riprodurre come un replay cio' che e' accaduto dall'interno del gioco. Ci servira' anche per fare regression testing: in pratica avremo tutta una batteria di test che leggono dei log e testano che qualche particolare proprieta' sia valida. Quando qualcuno ci manda un log con un bug, ad esempio, questo verra' inserito nella batteria di test, e poi ci sara' il fix.
Bingo. Il log e il record interno ci devono servire per poter riprodurre come un replay cio' che e' accaduto dall'interno del gioco. Ci servira' anche per fare regression testing: in pratica avremo tutta una batteria di test che leggono dei log e testano che qualche particolare proprieta' sia valida. Quando qualcuno ci manda un log con un bug, ad esempio, questo verra' inserito nella batteria di test, e poi ci sara' il fix.
Allora il gameturn e la legibilità ci servono veramente a poco. Ci serve sapere il momento esatto in cui avviene un evento quindi dobbiamo per forza di cose stampare il timestamp del timer.
ciao ;)
Allora il gameturn e la legibilità ci servono veramente a poco. Ci serve sapere il momento esatto in cui avviene un evento quindi dobbiamo per forza di cose stampare il timestamp del timer.
ciao ;)
Dove ci serve sapere il momento esatto di preciso? Si puo' modificare il codice di modo che non serva piu'? Oppure spostare il capture degli eventi a valle rispetto a quel codice?
Dove ci serve sapere il momento esatto di preciso? Si puo' modificare il codice di modo che non serva piu'? Oppure spostare il capture degli eventi a valle rispetto a quel codice?
Se vogliamo riprodurre esattamente l'input del utente mi pare sia l'unico modo.
IL gameturn non lo trovo una semplificazione ma una informazione superflua visto che abbiamo il timer. Con soli 20 millisecondi tra un gameturn e l'altro l'unico modo per far avvenire due eventi nello stesso è riuscire a premere più di 50 tasti nello stesso istante che è semplicemente inumano. In sostanza il file sarebbe composto da:
gameturn
evento
gameturn
evento
Inoltre venendo incrementato ogni 20 millisecondi il numero del gameturn aumenta molto velocemente e perde ogni significato per un osservatore umano.
Anche le stringhe leggibili sono superflue. Lo sviluppatore non andrà mai a leggere direttamente il file di log per confermare la presenza del bug o fare del debugging. Come hai detto tu stesso sarà dato in pasto ai test o ad una versione per il debug del gioco quindi preferisco avere una versione più scarna ma facile da leggere.
In pratica qualcosa di simile a questo:
4532184645513
00021232 left
00021245 up
00021325 mirror
00032099 clockwise
Se fosse per me scriverei direttamente tutto in binario e userei i codici dei tasti al posto delle stringhe ma forse è un po troppo. :D
ciao ;)
No...VICIUS...a questo punto sono contrario al timestamp... Seguite il mio discorso:
Teniamo un contatore che conta le volte che viene chiamata InputReactor.reactToInput (quindi viene incrementato una volta ogni 60ms circa)...
Ora sappiamo che verrà fatta la gestione degli eventi (e qui è di fatto come se gli eventi avvenissiro tutti al momento della chiamata di reactToInput)...quindi memorizzando il contatore e gli eventi (anche le ripetizioni) riusciremo a riprodurre la sequenza esatta degli eventi...
Manca solo una cosa...la GridController.update e la GridController.reactToInput non sono sincronizzate fra loro o meglio lo sono ma non strettamente... Questo potrebbe provocare sfasamenti nell'applicazione della gravità fra la sequenza riprodotta e quella originale...
Basta sincronizzare strettamente GridController.update e GridController.reactToInput (ad esempio eseguendo la seconda ogni 6 volte che viene eseguita la prima)... Ecco ora abbiamo la garanzia che sia la caduta verticale che i movimenti saranno sincronizzati esattamente e la sequenza potrà essere riprodotta senza problemi...
e spostare il gameturn nell processInput()?
è qua che viene controller.reactToInput(timer)(viene fatto un update sulla keyboard è ne viene preso lo stato e in base a questo stato vengono sparati gli handler dei vari tasti); è qua che avviene effettivamente l'azione utente, percui il timestamp (imho) non serve. Serve sapere l'ennesimo ciclo di lettura degli input in cui abbiamo trovato il tasto z premuto.
come ottimizzazione del traffico, si potrebbe stabilire la regola che logga il gameturn di quando il tasto viene premuto e il gameturn in cui viene rilasciato.(anche se sarebbe meglio l'evento...)
per cui la struttura sarebbe
[code<gameturn>
<evento1 stato='i'>
<evento2 stato='e'>
</gameturn>
[/code]
ho usatlo l'xml per chiarezza :)
chiamate contatore che conta le volte che viene chiamata InputReactor.reactToInput come gameturn, e io e cionci diciamo piu o meno la stessa cosa :)
thebol: sì, ho fatto una modifica...boh...
viene fatto un update sulla keyboard è ne viene preso lo stato e in base a questo stato vengono sparati gli handler dei vari tasti
L'update dei tasti viene fatto in Input.update()...
Direi comunque che quello che dobbiamo memorizzare siano gli eventi eseguiti (AbstractKeyEventHandler.execute)...
thebol: sì, ho fatto una modifica...boh...
L'update dei tasti viene fatto in Input.update()...
Direi comunque che quello che dobbiamo memorizzare siano gli eventi eseguiti (AbstractKeyEventHandler.execute)...
public void update()
{
keyboard.update();
}
l'update di Input :)
ho solo saltato un passaggio
in teoria si potrebbe modificare la reactToInput di gridController per ritornare un lista degli eventi processati a quel giro
Come dicevo, basterebbe andare a scrivere gli eventi al momento dell'esecuzione, sarebbe la cosa più semplice...
Ho spostato qualche post nella discussione sul netcode...
Manca solo una cosa...la GridController.update e la GridController.reactToInput non sono sincronizzate fra loro o meglio lo sono ma non strettamente... Questo potrebbe provocare sfasamenti nell'applicazione della gravità fra la sequenza riprodotta e quella originale...
Era proprio di questi sfasamenti e perdite di sincronia. Scrivere direttamente i timestamp mi sembrava l'unica maniera per ora di realizzare qualcosa di affidabile, pero se mi dici che con qualche modifica possiamo ottenere lo stesso risultato allora sono perfettamente favorevole alla soluzione del gameturn.
ciao ;)
Sicuramente... Come dicevo basta eseguire una reactToInput ogni TOT (pari al rapporto fra InputRate e UpdateRate) update... Di fatto la reactToInput avverrà sempre prima (o dopo) dello stesso (se si contasse) update che ha generato l'input...
Faccio un esempio:
int numeroDiUpdateFraDueReactToInput = 6;
if(timeStampUltimoUpdate + UpdateRate <= timeStamp)
{
timeStampUltimoUpdate += UpdateRate;
if(timeStampUltimoReactToInput + UpdateRate * 6 <= timeStamp)
{
timeStampUltimoReactToInput += UpdateRate * 6;
controller.reactToInput();
}
controller.update();
}
In questo modo la sequenza che ha generato il log verrà sempre riprodotta...
Ad esempio questa è la sequenza delle chiamate:
Update1
Update2
Update3
Update4
Update5
ReactToInput1 -> eseguo MOVE_LEFT e scrivo nel log #1 MOVE_LEFT
Update6
Update7
Update8
Update9
Update10
Update11
ReactToInput2 -> eseguo ROTATE_CCW e scrivo nel log #2 ROTATE_CCW
Update12
Nella riproduezione:
Update1
Update2
Update3
Update4
Update5
ReactToInput1 -> nel turno #1 devo eseguire MOVE_LEFT
Update6
Update7
Update8
Update9
Update10
Update11
ReactToInput2 -> nel turno #2 devo eseguire ROTATE_CCW
Update12
Se vogliamo riprodurre esattamente l'input del utente mi pare sia l'unico modo.
Secondo me riprodurre l'input e' esattamente quello che non vogliamo fare, noi vogliamo riprodurre gli eventi di gioco che scaturiscono dall'input, per semplificare la gestione di questa lista di eventi.
Mi spiego meglio: se Jocchan decidesse che premdere due volte il tasto a destra in un turno causa lo spostamento della gemma alla fine della colonna di destra (esempio), quello che vogliamo registrare non e' la doppia pressione, ma l'evento "gemma si sposta alla fine della colonna a destra". Altro esempio: se la pressione del tasto destro e poi sinistro nello stesso tempo causa la generazione di nessun evento, noi non vogliamo registrare la pressione dei tasti, ma per l'appunto la generazione di nessun evento.
Quindi una lista di questo tipo:
Gameturn 1:
Spostamento a destra giocatore 1
Generazione nuova pair giocatore 2
Gameturn2:
Applicazione gravita' giocatore 1
Spostamento a destra giocatore 1
Spostamento a sinistra giocatore 1
Etc...
In questo modo la validazione degl'input viene fatta in locale e poi gli eventi risultati spediti in remoto, salvati nel log, o quant'altro ci venga in mente. Sostanzialmente voglio che questi eventi siano piu' ad alto livello dell'evento "pressione di un tasto".
Il replay sara' una cosa del tipo: leggi elenco degli eventi al gameturn x, applica gli eventi in ordine. Non serve InputReactor.
Hai guardato quelo che ho scritto nel post precedente ? Secondo me è l'unico modo per riprodurre fedelmente gli eventi...
Il replay sara' una cosa del tipo: leggi elenco degli eventi al gameturn x, applica gli eventi in ordine. Non serve InputReactor.
Va bene salvare le varie azioni degli event handler ?
Da abstractKeyEventHandler:
private void execute(InputReactor inputReactor)
{
if(isKeyPressed())
{
executeWhenPressed(inputReactor);
}
else
{
executeWhenReleased(inputReactor);
}
logger.writeEvent(this);
}
Oppure basta semplicemente andare a scrivere una stringa da ogni handler (dentro a executeWhenPressed/Released dei vari handler)...
Ad esempio pensando ad una applicazione KISS si potrebbe andare a scrivere il valore di this.getClass().getCanonicalName() sul file di log...
Per riprodurre un evento basterebbe riuscire a recuperare l'handle in base a questa stringa (con un map, o visto che gli handle sono pochi anche scorrendoli tutti e confrontando i vari nomi)...
Come siamo messi per i task di questa storia?
Come siamo messi per i task di questa storia?
Finché non finiamo i task di quello precedente non possiamo cominciare a smanettare con questo.
ciao ;)
Cerchiamo anche di riformulare bene il task 10.2.2...
Come abbiamo appurato non possiamo contare i gameLoop perchè non sono di durata costante... IMHO bisogna contare o i cicli di update o i cicli di reazione agli input...
Magari se vi fidate posso occuparmi del task 10.2.3 in pair... Se vi va bene contatto nihil84 come compagno di pair...
Oppure basta semplicemente andare a scrivere una stringa da ogni handler (dentro a executeWhenPressed/Released dei vari handler)...
Ad esempio pensando ad una applicazione KISS si potrebbe andare a scrivere il valore di this.getClass().getCanonicalName() sul file di log...
Per riprodurre un evento basterebbe riuscire a recuperare l'handle in base a questa stringa (con un map, o visto che gli handle sono pochi anche scorrendoli tutti e confrontando i vari nomi)...
Hmmm... prova. Non ho ancora chiaro il problema e il codice, perche' e' piu' di una settimana che non riesco a guardare il codice, quindi non saprei davvero dire di piu'. Ho in mente una soluzione ideale, o meglio quello che mi piacerebbe vedere, ma ha poco senso se non ho il codice davanti e se poi al codice la soluzione ideale non piace.
Per ora implementa la cosa piu' semplice che ti passa per la testa e farla funzionare. Appena torno dal tour de force faccio do' una ripassata al problema e vediamo che succede.
Dunque mi sono pigliato il primo task. Appena chiudiamo il ciclo precedente inizio.
cionci ti ho assegnato il secondo task se qualcuno si offre volontario bene altrimenti cominciamo anche da soli.
ciao ;)
Ok...ma io volevo fare il 3° :) Anche se il secondo è legato a doppio nodo al 3°...
Ok...ma io volevo fare il 3° :) Anche se il secondo è legato a doppio nodo al 3°...
Oops! :D
Correggo subito.
ciao ;)
Al limite posso fare anche quello in pair programming con nihil84...che tra l'altro ha accettato...quindi confermo il pair...
Dopo aver parlato con Cionci su MSN, ho segnato i task 2 e 3 in pair tra lui e nihil.
Per il task 10.2.2, acceptance test:
ogni volta che viene eseguita la GridController.reactToInput il contatore deve essere incrementato di 1
Task 1 completato anche se c'è ancora un po di duplicazione nel codice di TestGameLoop e GameLoop. Per ora lascio cosi anche se ho in mente un giochino con le interfaccie. :)
ciao ;)
Help...ragazzi serve un pair programmer... Io devo andare via e mi sa che non posso lavorarci per il fine settimana... C'è qualcuno che può aiutare nihil84 per completare il task ?
Fatevi sotto, che manca davvero poco.
Uff...
Ho finito... almeno credo :p Mi hanno lasciato solo tutti :cry:
La build e' verde ma prima di fare il commit voglio farvi vedere un esempio di esecuzione, se vi va bene rilascio, altrimenti faccio le modifiche richieste.
Ecco una porzione del log salvato:
seed 1140287050143313000
1
pair boxes/topaz boxes/sapphire
106
it.diamonds.handlers.DropCommandHandler 1
109
it.diamonds.handlers.DropCommandHandler 1
111
it.diamonds.handlers.DropCommandHandler 1
112
it.diamonds.handlers.DropCommandHandler 0
114
pair boxes/topaz ruby
116
it.diamonds.handlers.MoveLeftCommandHandler 1
ah, una domanda. Come faccio a spostare LogTest da it.diamonds.tests.ignore a it.diamonds.tests? ci dovrebbe essere un qualche metodo automatico, no?
Uff...
Ho finito... almeno credo :p Mi hanno lasciato solo tutti :cry:
La build e' verde ma prima di fare il commit voglio farvi vedere un esempio di esecuzione, se vi va bene rilascio, altrimenti faccio le modifiche richieste.
Ecco una porzione del log salvato:
seed 1140287050143313000
1
pair boxes/topaz boxes/sapphire
106
it.diamonds.handlers.DropCommandHandler 1
109
it.diamonds.handlers.DropCommandHandler 1
111
it.diamonds.handlers.DropCommandHandler 1
112
it.diamonds.handlers.DropCommandHandler 0
114
pair boxes/topaz ruby
116
it.diamonds.handlers.MoveLeftCommandHandler 1
ah, una domanda. Come faccio a spostare LogTest da it.diamonds.tests.ignore a it.diamonds.tests? ci dovrebbe essere un qualche metodo automatico, no?
Basta semplicemente trascirare il file da un package all'altro usando eclipse :)
ciao ;)
Da quello che ho capito il task è finito quaindi possiamo considerare anche il ciclo terminato. Confermate ?
ciao ;)
Sì...stiamo correggendo qualche bug e stiamo facendo un po' di refactoring...
Sì...stiamo correggendo qualche bug e stiamo facendo un po' di refactoring...
Perfetto. Buon lavoro. :)
ciao ;)
ho idea di fare un refactoring per creare un mockObject per il logFile, per pulizia e per eliminare un bruttissimo istanceof in logFile.
Aspe...lo stavo facendo anche io...ma il problema è che non si può rinunciare comunque al membro realWriter...
Aspe...lo stavo facendo anche io...ma il problema è che non si può rinunciare comunque al membro realWriter...
vero.
La mia idea era creare una classe abstract in cui implementare read e write, e dichiareare gli altri metodi abstract. Poi costruire il logFile, che crea e chiude il file, e il mockObject che crea un stringBuffer.
ps.
un altra idea piu semplice, e fare il mockObject estendendo LogFile e reimplementare la close(togliendo la close sul realwriter).
Un'idea buffa sarebbe implementare un MockFileWriter, derivando da StringWriter...ed implementando la close :)
Alle 14:00 faccio il tag e chiudiamo il ciclo. Se volete fare qualche commit averte 1 ora e 30 minuti. Per i task e il nuovo ciclo come al solito questa sera.
ciao ;)
VIC: gli ultimi update che hai fatto hanno rotto la build ;)
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.