Newsgroup: it.comp.software.database
Titolo: MUD basato su DB

Salve a chi mi conosce e a chi non ha questa sfortuna ;-)
Sto valutando l'ipotesi di implementare un server per un gioco di ruolo
multiutente, puramente testuale, basato su un database SQL; il gioco sarebbe
in pratica un MUD (per chi conosce l'argomento), ma invece di mantenere
propri file di dati si baserebbe per mantenerli, sia a runtime che quando
inattivo, su un db.
Il programma dovrebbe essere multithread e avere la maggiore efficienza
possibile.
Prima ancora di iniziare, però, devo affrontare un problema non da poco: la
gestione della concorrenza degli accessi; cercherò di chiarire la situazione
con un esempio (semplificando un po').
Il server dovrebbe essere composto da singoli processi, ognuno dei quali
gestisce un giocatore connesso via telnet. Ad intervalli di tempo regolari
(alcune volte al secondo) legge i comandi del giocatore e li esegue mediante
query sul database e procedure di calcolo proprie. I comandi sono,
chiaramente, del tipo "get sword", "kill dragon" o "tell knight how are you
?".
Il database conterrebbe tutti i dati del gioco organizzati in tabelle,
quindi, ad esempio, potrebbe contenere qualcosa di simile (ridotto all'osso
per questo esempio):
[OBJECTS]
ID (key), NAME (varchar), TYPE (number), DESCRIPTION (varchar), OWNED_BY
(key)
[ROOMS]
ID(key),  NAME(varchar), DESCRIPTION(varchar), OBJECTS (list)*
[PLAYERS]
ID(key), NAME(varchar), OBJECTS (list)*, IN_ROOM (key)
Notare che (*) non conosco per ora un modo di memorizzare in un campo di una
tabella una lista di ID di oggetti, ma questo problema spero si possa
risolvere o comuqnue aggirare.
A questo punto, immaginiamo la seguente situazione.
Un giocatore dà il comando "get sword". Il relativo server lo interpreta e
stabilisce di effettuare le seguenti azioni:
1- SELECT per stabilire se c'è un oggetto nella stanza che risponde al nome
di "sword".
2- UPDATE per modificare il record dell'oggetto specificando chi è il nuovo
proprietario
3- UPDATE per modificare il record della stanza dicendo che non c'è più
l'oggetto
4- UPDATE per modificare il record del giocatore specificando che possiede
quell'oggetto
5- SELECT per ottenere l'elenco dei giocatori presenti nella stanza
6- istruzioni varie per inviare ad ogni giocatore il testo "XXXX has got a
sword".
Durante una qualunque di queste azioni, un altro processo può intervenire e
cercare di modificare uno qualunque dei record coinvolti, ad esempio un
altro giocatore può prendere l'oggetto dopo (1) ma prima della conclusione
(con risultato catastrofico), oppure un altro giocatore può andarsene dalla
stanza tra (5) e (6), e ricevere ugualmente il messaggio anche se non
dovrebbe.
Questo semplice esempio può facilmente essere esteso a situaizoni molto più
complicate, e comunque non risolvibili con una singola query SQL, e di
conseguenza non atomiche.
Una soluzione semplice ed efficace sarebbe creare un ulteriore processo
attivo sul server che riceve i comandi dei giocatori e per ognuno di essi
esegue le istruzioni necessarie, siano esse query o altro; ma questo
eliminerebbe qualsiasi forma di parallelismo dall'esecuzione.
Quello che mi servirebbe sarebbe un qualche meccanismo di locking,
possibilmente già implementato a livello di DB, o comunque qualcosa che mi
garantisca la consistenza dei dati coinvolti in una serie di transazioni
finché non sono state eseguite tutte; il tutto, possiblmente, senza bloccare
tutti gli altri accessi...
Sicuramente qualcuno avrà già incontrato problematiche del genere, sarei
grato per ogni consiglio o siggerimento.
Grazie per aver letto fin qui :-)
Massimo
P.S.
Non ho particolari preferenze sul DBMS da utilizzare, come linguaggio invece
preferirei il C o il C++; se possibile vorrei evitare Java, nonostante il
suo supporto ai DB e al multithread, per una serie di motivi che ora non mi
metto a spiegare.

 Massimo
Massimo "Octavius"?

tutti gli altri accessi...
Humm...se non mi sono perso qualcosa lungo la strada direi che puoi fare
tutto con qualunque database che supporti le transazioni e che ti permetta
di definire il livello di isolamento. Quindi tranne Mysql ci stanno dentro
tutti, da Access a Oracle.
In questo modo puoi passare da un livello di serialization che però
funziona da lock esclusivo quindi blocchi tutti ad un read uncommitted in
cui però rischi letture sporche. 
Ciao.
Faser
-- 
FABIO SERRA
*Questo testo deve essere valutato secondo il senso della RFC-3*
Per rispondere in e-mail sostituire "invalid" con "faser"
http://www.cfmentor.com
--------------------

 Massimo "Octavius"?
Sì, sempre io... ogni tanto mi ricordo che vorrei scrivere un MUD ;-)
Massimo

 cui però rischi letture sporche.
Ehm, scusami ma qui non ti seguo, purtroppo non sono un professionista di
DB. Potresti spiegarti un po' meglio ? Grazie :-)
Massimo

 ha scritto nel messaggio
news:a431te$1g27$1...@newsreader1.mclink.it...
Io ti conosco;P
Giocavamo a LeU insieme=P
E anche insieme ad un'altra persona...
spiedo

 E anche insieme ad un'altra persona...
Mmmm... chi sei ?
E di chi parli ?
Massimo

 E di chi parli ?
olimpia?=P
(non dire il mio nome pero'=P)
spiedo

 (non dire il mio nome pero'=P)
Sei del nord, eh ?
Massimo

DB. Potresti spiegarti un po' meglio ? Grazie :-)
Voglio dire che tutti i database hanno il concetto di transazione. Quello
base si ha su una singola tabella, per cui se aggiorni 1000 record, ma dopo
300 si blocca qualcosa allora nessun record viene aggiornato. 
Poi le transazioni entrano in gioco quando fai più query, quindi se una
query non va a buon fine, tutte le query vengono annullate.
Infine le transazioni sono importanti per impostare la visibilità che hanno
i dati quando più utenti agiscono sulle stesse tabelle.
Devi raggruppare le query fatte da un utente all'interno di una transazione
e decidere se altri utenti possono vedere le modifiche fatte anche se la
transazione non è completata.
Es:
Utente 1
BEGIN WORK (inizio transazione)
INSERT INTO tabella VALUES('pippo')
--inserita 1 riga
SELECT * FROM tabella WHERE campo = 'pippo'
--trovati i dati
COMMIT WORK (finisco transazione)
Se l'utente 2 fa una SELECT per trovare pippo subito dopo la INSERT
dell'utente 1 non troverà nessun risultato perchè la transazione
dell'utente 1 non è conclusa.
Hai anche la possibilità di usare il Lock per evitare che due utenti
tentino di aggiornare la stessa riga.
Ad esempio in PG se fai un 
SELECT *
FROM tabella
WHERE campo = 'nuovo valore'
FOR UPDATE
Quella riga è bloccata e fino a quando non è terminato l'aggiornamento
nessun altro ci può scrivere. Alcuni db però non riescono a bloccare le
singole righe, ma bloccano tutta la tabella.
Non so se sono stato chiaro, comunque quello che dici tu si può fare, anzi
i database servono proprio a questo. L'unico problema che mi porrei nel tuo
caso è la velocità ed i tempi di risposta.


 mi porrei nel tuo caso è la velocità ed i tempi di risposta.
E' l'obiezione che avevo sempre fatto io a questo approccio alla
progettazione di un MUD, ma mi è sempre stato risposto che un DBMS serio può
gestire senza problemi migliaia di query al secondo...
Massimo

gestire senza problemi migliaia di query al secondo...
Effettivamente è vero, sono fatti per questo e soprattutto per mantenerti
una consistenza nei dati.


 una consistenza nei dati.
Proprio per questo l'approccio mi sembra interessante...
Massimo