Flusso delle informazioni
Essendo NetGame un ambiente distribuito,
si desidera che quando un utente compie una certa azione questa
si manifesti su tutta la rete, cioè è necessario
che la informazione generata dall'utente sia trasmessa a tutti
gli altri client che sono connessi alla rete. Perchè
ciò sia possibile, l'informazione deve attraversare
vari stadi prima di arrivare a destinazione. Si analizza quindi
ora il percorso che devono seguire queste informazione. Come
esempio di azione si prenda l'invio di un messaggio pubblico
di chat, cioè un messaggio inviato da un utente che
sarà visibile a tutte le persone che si trovano nella
stessa stanza di colui che l'ha inviato (eventualmente anche
su server diversi).
Il punto di partenza è chiaramente l'utente
che agendo sull'interfaccia grafica del suo client (MainClientUI )
dà il comando di invio del messaggio. L'interfaccia grafica quindi
cattura tale evento e comunica al client di chat (RoomClient )
che è stato generato un messaggio pubblico. Quest'ultimo a sua
volta comunicherà l'azione al nodo server per mezzo dell'agente
che si occupa delle comunicazioni (ClientAgent ). Dal ClientAgent
l'informazione viene passata al server di stanza (RoomServer )
che deve provvedere a ritrasmettere a tutti il messaggio che ha appena
ricevuto. Deve quindi inviare il messaggio a tutti gli altri utenti
che sono sulla rete. Chiaramente, al fine di ridurre il traffico in
rete, il RoomServer non comunica direttamente con tutti
gli utenti della rete, ma soltanto con gli utenti locali connessi al
suo nodo. Inoltre comunica con i tutti i RoomServer remoti
analoghi ad esso (ovvero relativi allo stesso gioco), e saranno poi
questi ultimi ad avvisare i loro rispettivi clienti. La comunicazione
tra il server di stanza ed il client avviene ancora una volta tramite
il ClientAgent . Una volta che il client riceve il messaggio,
questo provvede ad aggiornare l'interfaccia grafica rendendo l'azione
compiuta visibile all'utente. In questo modo il ciclo si richiude. Va
evidenziato il fatto che anche l'utente che ha inviato il messaggio
viene trattato al pari di tutti gli altri, ovvero l'aggiormento della
sua interfaccia grafica avverrà soltanto dopo tutti i passaggi
appena visti. Questo in un primo momento potrebbe sembrare una complicazione
inutile, ma è invece necessaria in quanto ci sono anche azioni
che potrebbero fallire, per cui si deve attendere la risposta dal server
prima di poter aggiornare lo stato del client (si vedano le possibili
operazioni in conflitto).
Introduzione degli eventi
Il precedente sistema di comunicazione
presenta un paio di inconvenienti:
- Il processo che si occupa di gestire gli eventi legati
all'interfaccia grafica è costretto ad attendere
la risposta da parte del server ed in questo modo l'interfaccia
grafica rimane bloccata.
- Il server di stanza comunica in modo sequenziale con tutti
i suoi clienti e con i server remoti, e quindi ogni comunicazione
deve attendere il termine di quella precedente, con un notevole
rallentamento.
Per risolvere questi problemi è quindi
necessario disaccoppiare le attività, cioè fare in modo
che sia possibile eseguire più azioni contemporaneamente. Questo
può essere fatto adottando una programmazione basata sugli eventi,
in cui ci sono diversi gestori di eventi che sono in esecuzione contemporaneamente.
Un gestore di eventi (EventHandler ) è quindi un
processo a cui è associata una coda di eventi. Il gestore rimane
in attesa che qualche altro processo metta nella sua coda un evento
per poi prelevarlo ed eseguirlo. Naturalmente l'esecuzione dell'operazione
dipenderà dal tipo di evento e di seguito si vedrà una
descrizione dei diversi tipi di evento presenti nell'ambiente NetGame.
Con questa tecnica quindi alcuni componenti del
sistema visti in precedenza divengono a tutti gli effetti dei gestori
di eventi. Ad esempio, il RoomClient ha una coda in cui
gli eventi vengono aggiunti dalle routine del framework che gestisce
l'interfaccia grafica. Un caso particolare è costituito dal RoomServer
in cui non c'è una sola coda di eventi, ma due distinte. Questo
perchè il compito del RoomServer è quello
di ritrasmettere il messaggio a tutti i suoi client ed a tutti server
remoti. La gestione degli eventi in questo caso quindi consiste semplicemente
nel prendere un messaggio dalla coda e ridistribuirlo mettendolo nelle
code di tutti i ClientAgent e RoomServerAgent ,
i quali provvederanno a gestire tale messaggio e ad effettuare la realtiva
chiamata remota. A tal fine si è quindi prevista la creazione
di una classe apposita per la distribuzione degli eventi EventDispatcher
che specializza la più generica EventHandler .
La trattazione fin qui fatta fa riferimento alla
comunicazione tra RoomClient e RoomServer
(e viceversa), ma in maniera del tutto analogo si può considerare
la comunicazione tra MainClient e MainServer .
Classificazione degli eventi
Una volta chiarita la necessità di una
gestione ad eventi delle comunicazioni tra le varie parti del sistema,
si vede ora quali sono i possibili eventi che possono essere generati.
Una prima distinzione va fatta tra eventi generati da utenti (UserEvent )
ed eventi generati dai server (ServerEvent ).
Eventi generati
dai client
Gli eventi generati dai client possono
essere suddivisi in quattro categorie, riguardanti:
- Ingresso e uscita dal sistema (
LogEvent )
- Gestione delle partite (
MatchEvent )
- Scambio di messaggi (
MessageEvent )
- Ingresso e uscita da una stanza (
RoomEvent )
LogEvent |
LoginEvent |
Ingresso di un utente nel sistema |
LogoutEvent |
Uscita di un utente dal sistema |
MatchEvent |
CreateMatchEvent |
Creazione di una nuova partita |
DeleteMatchEvent |
Eliminazione di una partita |
EnterMatchEvent |
Ingresso in una partita |
ExitMatchEvent |
Uscita da una partita |
FinishMatchEvent |
Terminazione di una partita |
StartMatchEvent |
Avvio di una partita |
MessageEvent |
PrivateMessageEvent |
Invio di un messaggio privato ad un altro utente |
PublicMessageEvent |
Invio di un messaggio pubblico nella stanza |
RoomEvent |
JoinRoomEvent |
Ingresso di un utente in una stanza |
LeaveRoomEvent |
Uscita di un utente da una stanza |
Eventi generati
dai server
Gli eventi generati dal server sono
sostanzialmente legati alle problematiche di sincronizzazione
discusse in precedenza, ed in particolare alla realizzazione
del protocollo 2PC. Oltre a questo, anche la gestione
dei guasti viene fatta ad eventi. Alla luce di tutto ciò
possiamo distinguere vari tipi di eventi:
- Realizzazione del 2PC a livello di MainServer (
MainServerEvent )
- Realizzazione del 2PC a livello di RoomServer (
RoomServerEvent )
- Realizzazione del 2PC per la gestione della partite (
MatchEvent )
- Realizzazione del 2PC per il login degli utenti (
LogEvent
- Disconnessione di un server (
ServerDisconnectedEvent )
- Disconnessione di un client (
ClientDisconnectedEvent )
MainServerEvent |
AbortConnectEvent |
Connessione di un server (II^ fase - insuccesso) |
ConnectEvent |
Connessione di un server (II^ fase - successo) |
PrepareConnectEvent |
Connessione di un server (I^ fase) |
RoomServerEvent |
AbortConnectRoomEvent |
Connessione di un server (II^ fase - insuccesso) |
ConnectRoomEvent |
Connessione di un server (II^ fase - successo) |
PrepareConnectRoomEvent |
Connessione di un server (I^ fase) |
MatchEvent |
AbortCreateMatchEvent |
Creazione di una partita (II^ fase - insuccesso) |
AbortEnterMatchEvent |
Ingresso in una partita (II^ fase - insuccesso) |
AbortExitMatchEvent |
Uscita da una partita (II^ fase - insuccesso) |
AbortStartMatchEvent |
Avvio di una partita (II^ fase - insuccesso) |
PrepareCreateMatchEvent |
Creazione di una partita (I^ fase) |
PrepareEnterMatchEvent |
Ingresso in una partita (I^ fase ) |
PrepareExitMatchEvent |
Uscita da una partita (I^ fase) |
PrepareStartMatchEvent |
Avvio di una partita (I^ fase) |
LogEvent |
AbortLoginEvent |
Ingresso di un utente (II^ fase - insuccesso) |
PrepareLoginEvent |
Ingresso di un utente (I^ fase) |
Eventi di
disconnessione |
ServerDisconnectedEvent |
Rilevata disconnessione di un MainServer |
ClientDisconnectedEvent |
Rilevata disconnessione di un client |
N.B. Per alcuni eventi del protocollo 2PC riguardanti
la seconda fase in caso di successo si sono usati gli stessi eventi
riportati nella tabella precedente. Ad esempio: LoginEvent ,
CreateMatchEvent , ecc.
|