Jump to content
Rpg²S Forum

Keroro

Utenti
  • Posts

    646
  • Joined

  • Last visited

Everything posted by Keroro

  1. Ecco un programmino scritto in 10 minuti per lo screen capture, utile per i rip dagli emulatori :D È GRATUITO e se volete costruirci su un tool sono contento (fate leggere la configurazione da un file di testo e gli create una interfaccia grafica :) ), da linux non è che me ne faccia molto :) (anzi già è tanto se sono riuscito a testarlo :D) Nota Salva in Bitmaps non compressi che pesano 2-3 mb l'uno, non eccedete con gli screen se non avete molto spazio Modifiche quante indica il numero di immagini da prendere refresh indica la pausa tra uno screen e l'altro (pausa di 1000/refresh millisecondi) valori più alti di 100 creano problemi visto che l'orologio di sistema non calcola tempi inferiori a 10 millisecondi e anzi ragiona in blocchi di 10 millisec alla volta base indica il prefisso degli screen che saranno seguiti da un numero da 0 a quante, in genere breve, se volete metterlo più lungo dovete modificare di conseguenza la dimensione dell'array title (che ho messo già sovrabbondante) Se piuttosto di tutto il desktop volete salvarvi la finestra di un emulatore (consigliato!) allora dovete associare a finestra l'handler dell'emu con FindWindow(NULL,"TitoloPrecisoDellaFinestra"); //potete o andare a tentativi o utilizzare i dati forniti dal task manager Come compilarlo: gcc -o screen.exe screen.c -lgdi32 -luser32 oppure con project manager selezionate compila come Finestra/Window/GUI e nel linker mettete lgdi32.lib e user32.lib se Visual C++ -lgdi32 -luser32 se Dev C++ (probabilmente manca qualche include di qualche lib di base, con dev c++ e usando gli include di base me la sono cavata) screen.c #include <windows.h> #include <stdio.h> //funzioni prese dall'msdn :D PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp); int CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi,HBITMAP hBMP, HDC hDC); int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { //----------------------modifica QUI----------------------- int quante = 10 int refresh = 10; char base[] = "screen"; char title[256]; //uno dei due HWND finestra = GetDesktopWindow(); //Desktop //HWND finestra = FindWindow(NULL,"NomeFinestraEmulatore"); //-------------------NON MODIFICARE E SE VUOI SMETTI DI LEGGERE :D--------------------------- int i,width, height; RECT dim; HDC source = GetWindowDC(finestra); HDC dest = CreateCompatibleDC(source); GetWindowRect(finestra,&dim); width = dim.right - dim.left; height = dim.bottom - dim.top; HBITMAP img = CreateCompatibleBitmap(source,width,height); for (i = 0;i<quante; i++){ SelectObject(dest,img); sprintf(title,"%s%3d.bmp",base,i); BitBlt(dest,0,0,width,height,source,0,0,SRCCOPY); CreateBMPFile(title,CreateBitmapInfoStruct(img),img,dest); Sleep(1000/refresh); } DeleteObject(img); DeleteDC(dest); ReleaseDC(finestra,source); return 0; } int CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) { HANDLE hf; // file handle BITMAPFILEHEADER hdr; // bitmap file-header PBITMAPINFOHEADER pbih; // bitmap info-header LPBYTE lpBits; // memory pointer DWORD dwTotal; // total count of bytes DWORD cb; // incremental count of bytes BYTE *hp; // byte pointer DWORD dwTmp; pbih = (PBITMAPINFOHEADER) pbi; lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); if (!lpBits) return 1; // Retrieve the color table (RGBQUAD array) and the bits // (array of palette indices) from the DIB. if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS)) { return 1; } // Create the .BMP file. hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD) 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hf == INVALID_HANDLE_VALUE) return 1; hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" // Compute the size of the entire file. hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage); hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; // Compute the offset to the array of color indices. hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD); // Copy the BITMAPFILEHEADER into the .BMP file. if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL)) { return 1; } // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), assolutamente (LPDWORD) &dwTmp, ( NULL))) return 1; // Copy the array of color indices into the .BMP file. dwTotal = cb = pbih->biSizeImage; hp = lpBits; if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) return 1; // Close the .BMP file. if (!CloseHandle(hf)) return 1; // Free memory. GlobalFree((HGLOBAL)lpBits); return 0; } PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp) { BITMAP bmp; PBITMAPINFO pbmi; WORD cClrBits; // Retrieve the bitmap color format, width, and height. if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) exit(1); // Convert the color format to a count of bits. cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); if (cClrBits == 1) cClrBits = 1; else if (cClrBits <= 4) cClrBits = 4; else if (cClrBits <= 8) cClrBits = 8; else if (cClrBits <= 16) cClrBits = 16; else if (cClrBits <= 24) cClrBits = 24; else cClrBits = 32; // Allocate memory for the BITMAPINFO structure. (This structure // contains a BITMAPINFOHEADER structure and an array of RGBQUAD // data structures.) if (cClrBits != 24) pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits)); // There is no RGBQUAD array for the 24-bit-per-pixel format. else pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)); // Initialize the fields in the BITMAPINFO structure. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = bmp.bmWidth; pbmi->bmiHeader.biHeight = bmp.bmHeight; pbmi->bmiHeader.biPlanes = bmp.bmPlanes; pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; if (cClrBits < 24) pbmi->bmiHeader.biClrUsed = (1<<cClrBits); // If the bitmap is not compressed, set the BI_RGB flag. pbmi->bmiHeader.biCompression = BI_RGB; // Compute the number of bytes in the array of color // indices and store the result in biSizeImage. // For Windows NT, the width must be DWORD aligned unless // the bitmap is RLE compressed. This example shows this. // For Windows 95/98/Me, the width must be WORD aligned unless the // bitmap is RLE compressed. pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight; // Set biClrImportant to 0, indicating that all of the // device colors are important. pbmi->bmiHeader.biClrImportant = 0; return pbmi; } allego il programma compilato :D screener.zip
  2. la mia curiosità va al sistema di ripping centesimale, vorrei sapere che api hai usato: le api di win32 per ottenere lo screen in bmp dell'intero desktop sono lente e non reggono i tempi da te segnati (uno screen ogni centesimo di secondo), nei miei test a malapena arrivano a una ogni decimo di secondo ma forse questo dipende dal fatto che io salvo tutto lo schermo e non solo la porzione dell'img. In effetti sarebbe più comodo cercarsi il pid e stampare solo quella ma non sono sicuro che le api di sistema abbiano funzioni così specializzate. Tu cosa utilizzi :? EDIT: sono tornato a casa e ho letto la reference su msdn e ho risolto!
  3. /me sbadiglia la fiera dell'ovvio sono felice che tu ti senta cresciuto ma ci vuole ben altro per fare di te un uomo
  4. per vedere che errore commetti esegui da console ruby file.rb copia incolla l'errore qui altrimenti se tutto funziona e il problema è solo che si chiude metti un gets alla fine
  5. evita i doppi post. i miei script funzionano visto che li ho testati con ruby 1.8, l'errore non sta nel codice ma come tu usi il computer :)
  6. per farlo partire devi chiamare main, non pensavo di doverlo scrivere :) per le combinazioni magari metterle tra parentesi e separate da virgole #!/usr/bin/ruby #usare il .to_i è inutile se non controlli l'output def comb(a,b) return "("+a+","+b+")" end def main print "\nInserisci il primo elemento: " a = gets.chomp print "\nInserisci il secondo elemento: " b = gets.chomp print "\nInserisci il terzo elemento: " c = gets.chomp print "\nCombinazioni "+ comb(a,b)+" "+comb(a,c)+" "+comb(b,c)+"\n" gets end main
  7. piuttosto che convertire ogni volta, fai così per avere nelle variabili dei valori considerati interi def main print "\nInserisci il primo addendo: " a=gets.chomp.to_i print "\nInserisci il secondo addendo: " b=gets.chomp.to_i print "\nRisultato: " +(a+b).to_s+"\n" end non capisco però perché nel codice hai creato una classe che non usi (oltre che programmata in modo scabroso)! parli di combinazioni ma presenti addizioni, sono cose diverse, per ottenere delle combinazioni devi prima di tutto creare un insieme (implementabile tramite array) e cavartela con qualche ciclo
  8. sono un credente convinto in dio ma l'idea che ho di lui non è sovrapponibile alle idee di divinità presente nelle religioni più diffuse, per inciso non credo siano esistiti suoi figli né che abbia comunicato con gli uomini precedentemente esistiti
  9. hanno letto in tanti ma risposto solo in 2 :( per quelli che hanno letto e vogliono fare prove potete giocare con hello.rb modificando le figure da disegnare sopra lo schermo :S. non so se fare un ulteriore tutorial sull'argomento quindi ditemi voi (anche via mp, msn o quello che volete) se lo volete :)
  10. sono pigro e non mi imbarcherò in un progetto che non riuscirò a portare a termine ;_; dopo il mio post di prima basato soprattutto su reminiscenza, mi sono documentato e ho ripreso a studiare le diverse tecnologie X (hot :Q_) e mi sono accorto con grande piacere che l'Xpath di cui non ricordavo niente è studiato per le ricerche e soprattutto che sopra questo (insieme ad XSLT e XLink) hanno creato XQuery 1.0 (diventato w3c raccomandation 1 anno e 5 giorni fa !!!) che viene presentato come il linguaggio per interrogare l'xml! (/me si lecca i baffi e inizia a studiare)
  11. Keroro

    Le nostre foto

    oddio mi ha shockato apprendere dal sito che sono il sosia perfetto di gary cooper XD (che avevo sentito solo di nome) e di alyssa milano o_o costa più che di geni si tratta di bilancio, a chi tanto a chi niente XD Jem posso creare un fan club in tuo onore :D?
  12. l'effetto è carino, la mappa però è un po' vuota quello che temo è che con gli oggetti in un 2d che si discosta tanto si crei un brutto effetto, il chara di ragnarok già stona un pochino
  13. l'Xml infatti è stato proposto come futuro formato di memorizzazione per tutti i database (da mysql a oracle) piuttosto che ISAM, MySam, InnoDB, Postgres e altri per offrire una completa portabilità e di considerare quindi i database solo come mezzo in cui compiere operazioni SQL o particolari proprie estensioni sugli Xml (proposta che però non raggiungerà mai perché ci fanno troppi soldi su). Ma evitando di sognare è possibile benissimo fare copie di backup dei database in Xml se vogliamo provare la via dei file L'XML pesa un po' di più in termini di kb rispetto ai file di testo ma ha il vantaggio di essere leggibile dall'uomo, standard e facilmente modificabile, inoltre esistono già migliaia di toolkit per leggerli (ora che gli hard disk costano poco è realistico adottare questa soluzione) avevo comprato un libro ora disperso a casa dei miei genitori, in cui si trattavano approfonditamente le tecnologie xml/xsl/xslt/xpointer/xbase e tanti altri! È una ottima soluzione per chi decidesse di non usare un database provvisto di SQL perché tramite la tecnologia XSLT è possibile estrarre informazioni in modo paragonabile ad una query. Mi sembra banale e infruttuoso mettermi a descrivere con minuziosità da cosa è costituito un file xml, se volete approfondire cercate in googles sempre rimanendo della filosofia KISS o Keep it simple vi dico come la vedo io: Formato xml (trascurando il DTD) <?xml version="1.0" encoding="UTF-8"?> <radice> <elemento1> <nome></nome> </elemento1> <elemento2> <nome></nome> <caratteristiche></caratteristiche> </elemento2> <blablabla></blablabla> </radice> La grandiosità dell'xml sta nel definire un tree (albero) che si può estendere con profondità infinita al contrario dei database canonici dove c'è solo database -> tabella -> campo. Un esempio notevole di albero sono le matrioske dentro ogni bambola può essere visibile 0 o 1 bambola Il concetto di albero invece è allargato si parte da un nodo radice dentro ogni nodo può esserci 0, 1 o più nodi Nella programmazione manipolare alberi ci permette di essere molto più veloci nelle operazioni di ricerca di informazioni XSL L'XSL è il linguaggio di descrizione del foglio di stile ed incorpora tre linguaggi: XSLT (per lo spiego dopo) XSL-FO (per conversione in pdf/latex) XPath (per boh?) XSLT Come già detto l'xslt è la tecnologia per estrarre informazioni, funziona più o meno in questo modo xml => |MOTORE| | XSLT | => xml (o xhtml o html) xls/xlst => |______| Esistono versioni di motori XSLT per tutti i linguaggi, sia di scripting e orientati al web come php, asp, python, ruby che in c o java quindi è plausibile usarla nei nostri giochi :) Una libreria che provai al tempo (5 anni fa) e che ora penso sia cresciuta è Libxslt Esempio di funzionamento Ho il mio database degli account in xml con qualche caratteristica striminzita <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="nomi.xsl"?> <lista> <account> <nome>Aldo</nome> <sesso>M</sesso> <stat> <hp>20</hp> <str>10</str> <dex>53</dex> </stat> </account> <account> <nome>Baglio</nome> <sesso>M</sesso> <stat> <hp>42</hp> <str>32</str> <dex>29</dex> </stat> </account> <account> <nome>Crucchio</nome> <sesso>M</sesso> <stat> <hp>232</hp> <str>32</str> <dex>69</dex> </stat> </account> <account> <nome>Sticchia</nome> <sesso>F</sesso> <stat> <hp>10</hp> <str>12</str> <dex>1</dex> </stat> </account> <account> <nome>Zaino</nome> <sesso>M</sesso> <stat> <hp>30</hp> <str>55</str> <dex>44</dex> </stat> </account> </lista> Costruisco un file (magari è meglio renderlo dinamico ovvero generarlo da codice e magari lasciarlo in memoria senza salvarlo sull'hd se non sono di fronte a richieste standard) per estrarre quello che mi serve. in questo caso mettiamo che mi servono tutti i nomi :) <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <!-- Applica questo template al nodo radice indicato dal carattere / --> <nomi> <xsl:apply-templates> <!-- Richiama e applica gli altri templates --> </xsl:apply-templates> </nomi> </xsl:template> <xsl:template match="account"> <!-- Quando trova un nodo account applica questa regola --> <xsl:value-of select="nome"></xsl:value-of> </xsl:template> </xsl:stylesheet> Ora che ho i due input mi manca il motore per generare l'output, ho fatto due prove, la prima, pallosissima da scrivere, l'ho fatta in C (non riporto il codice completo ma pezzi) Accenno in C e Libxslt La seconda invece l'ho fatta in Ruby mediante Ruby/XML e Ruby/XSLT (esempio FUNZIONANTE!!!) require 'xml/libxml' require 'xml/libxslt' elaboratore = XML::XSLT.new elaboratore.xml = "accounts.xml" elaboratore.xsl = "nomi.xsl" nomi = elaboratore.serve print nomi provate per credere, stamperà come ouput <?xml version="1.0"?> <nomi> Aldo Baglio Crucchio Sticchia Zaino </nomi> (se serve nell'xsl si può benissimo aggiungere un tag separatore ;) ) vi è piaciuto l'esempio :D? .......... Alternative a XML per il data storage Ci sono migliaia di alternative possibile al data storage tramite xml, una che mi sembra interessante è tpl che offre una serializzazione dei dati con la mission di avere le operazioni di lettura e scrittura veloci, ma comunque per un progetto importante conviene affidarsi a: propri standard di file testuali/binari xml databases ------------------------------------------ Sviluppo di un server MMORPG Tornando nel tema principale, come avete visto sono stato molto sbrigativo riguardo i modi in cui è possibile implementare il server, facendo l'elenco soltanto degli argomenti basilari ma di basso livello piuttosto che entrare in profondità su temi AVANZATI di networking. Adesso non ho tempo di riprendere la trattazione in profondità ma sfrutto l'argomento Xml per introdurre un nuovo metodo ad alto livello RPC RPC è la sigla che identifica la Remote Procedure Call e identifica la tecnologia per la quale da una data macchina possiamo chiamare una funzione/procedura di un'altra macchina. E' un'idea geniale ma comporta anche una notevole organizzazione soprattutto nel design dell'applicazione PERÒ bisogna ricordare che si tratta di meccanismi ad alto livello che mascherano il livello socket e spesso aumentano i problemi piuttosto che semplificarli,infatti è da usare con cautela perché aumenta il carico del server (la funzione viene eseguita dal server e l'output viene rimandato indietro) ed è quindi idoneo a chiamate con risposte precise come ad esempio in automatica mi sono trovato in un laboratorio dove il pc era collegato ad un sensore che misura la temperatura, avrei potuto usarlo come server per controllare l'esperimento da casa ed una RPC sarebbe stata l'ideale per controllare la temperatura Nel mondo degli mmorpg le rpc sono ideali soprattutto per smaltire il login, creando un login server che funga da filtro ovvero in caso di autenticazione fallita chiude la porta al client, nel caso di autenticazione riuscita reinderizza il client al vero e proprio programma server che può stare benissimo su un'altra macchina (o su più macchine) se siamo una software house e abbiamo a disposizione un cluster Implementazioni Ci sono diverse implementazioni delle RPC, una libreria famosa, meravigliosa ed economica è sicuramente la RakNet (che però è per c++ e non per c :( ) ma se ho deciso di parlarne è perché ho collegato l'Xml alla XML-RPC-c che implementa il discorso precedente tramite messaggi di richiesta e risposta in formato xml che viaggiano nella rete attraverso il protocollo http. Forse più tardi vi farò un esempio su XmlRpc Per ora ditemi che cosa ne pensate di questo tutorial :) e per domande/dubbi ci sono sempre, non scappo
  14. carino ma metterei la scenetta divertente con l'omino che cade giù :X a parte gli scherzi forse centrerei maggiormente lo sprite spostandolo lievemente verso destra di un paio di pixel prima di farlo muovere
  15. Molti di voi conoscono bene il ruby e magari si sentono limitati da rpgmaker xp o il vx o semplicemente vorrebbero creare qualcosa che sia completamente farina del proprio sacco e che magari si discosti molto da un rpg. Vorrei mostrarvi la libreria che uso e che amo tanto (ci programmo in C), della quale esiste da qualche anno un porting funzionante in Ruby Premessa Ammetto che stanotte mi ci sono imbattuto per sbaglio perché cercavo una libreria per scrivere kanji, SDLSKK (sì, sono strano =D ), destino ha voluto fosse ospitata nello stesso sito delle Ruby/SDL. Vorrei però cogliere l'occasione per aprire un nuovo filone di game making in questo sito ;D Ruby/SDL Che cos'è? SDL è una libreria cross-platform che permette di sviluppare facilmente un videogioco offrendo un accesso a basso livello all'audio, tastiera, mouse, joystick, 3d tramite OpenGL e funzioni 2d "veloci". L'implementazione ruby è più lenta (ma anche più semplice!) rispetto a quella C/C++, quindi l'autore del porting ruby consiglia di evitare il 3d mentre per il 2d va benissimo! Come la installo? Io sono culoso e scrivo da linux (ubuntu) per installarla è bastato scrivere sudo apt-get install libsdl-ruby1.8 I windowsiani trovano le istruzioni ed il download qui: http://www.kmc.gr.jp/~ohai/rubysdl_download.en.html Come potete notare quello è il sito ufficiale del porting ruby, era noto che i giapponesi avessero il pallino visto che questo linguaggio l'hanno creato loro ;) Se doveste avere problemi con l'installazione chiedete qui, e spero che qualche ruby windows guru risponderà :D Premessa2 Non conosco il ruby come le mie tasche perché non lo uso abitualmente (anzi se vedete nella sezione RGSS sono appena sopra la mezza sega :D), tutto il codice che scriverò sarà quindi evidentemente non ottimizzato, e non ha neanche la pretesa di esserlo visto che la sola pretesa è INSEGNARE qualcosa, sta poi a voi riunire in questo o quel modulo o in quella classe specifica. Utilizzerò il termine funzione piuttosto che metodo, del resto il metodo è una particolare funzione (perché non sono abituato a chiamarle metodi :Ok: ) Consiglio Programmate sempre con la documentazione/reference in background in modo programmare senza intoppi, trovate la reference delle Ruby/SDL alla pagina http://www.kmc.gr.jp/~ohai/rubysdl_doc.en.html ===== Hello World! ===== Il nostro primo programma non farà niente se non aprire una finestra in cui disegnamo qualche scarabocchio ;O; Penso sia necessario anche per notare la fondamentale differenza con le librerie TK. Le librerie TK sono a oggetti, mentre SDL è una libreria procedurale, seppur in Ruby costituita da classi e metodi. Questo vi permette molta libertà nella programmazione, libertà che sfrutto al volo perché non sono ancora ferrato con gli oggetti XD. Innanzitutto dobbiamo includere la libreria quindi nel nostro hello.rb metteremo require 'sdl' Poi dobbiamo inizializzare la libreria: il sistema SDL è costruito da più "sottosistemi" attivabili a nostro piacimento passandoli come argomento (combinati con l'OR |) alla funzione SDL.init SDL::INIT_AUDIO SDL::INIT_VIDEO SDL::INIT_CDROM SDL::INIT_JOYSTICK Penso siano autoesplicativi, vediamo un po' se indovinate cosa ci serve... Soltanto il sistema video! quindi da bravi amanuensi scriviamo SDL.init( SDL::INIT_VIDEO) NB: devi inizializzare la libreria prima di usare una qualsiasi funzione SDL! Adesso è ora di creare la nostra finestra ma prima un piccolo excursus Alla base di tutto c'è la classe SDL::Surface, essa rappresenta un'area di memoria che funziona come una immagine virtuale: ha una sua larghezza, altezza e profondità (bits per pixel) puoi disegnarci su tramite molti metodi ed in maniera diversa, puoi disegnare pixel o primitive quali linee, rettangoli, cerchi, puoi copiarci una immagine bmp, png, jpeg con o senza alpha channel e definire eventualmente un colore alpha (come rpgmaker 2000) puoi copia incollarla su altre Surface (così come le immagini in paint) Le SDL associano una Surface alla Finestra dell'applicazione ed il giocatore vedrà tutto e soltanto quello che disegnamo dentro questa Surface. Per crearla dobbiamo usare la funzione SDL.setVideoMode( width, height,bpp,flags) Analizziamo gli argomenti: width è la larghezza della finestra height è l'altezza della finestra bpp sono i bitsperpixel flags (si compongono sempre tramite operatore OR) SDL::SWSURFACE crea la Surface nella Memoria di Sistema, da evitare come la peste (è per i pc senza scheda video) SDL::HWSURFACE crea la Surface nella Memoria Video (ovvero accellerazione hardware!) SDL::FULLSCREEN abilita il fullscreen SDL::SDL_DOUBLEBUF abilita il doppio buffer (RULES!) La funzione restituisce l'istanza della SDL::Surface associata alla Finestra ho creato anche delle variabili (che potete benissimo inglobare in una classe) per semplificare il tutto: width = 640 height = 480 bpp = 32 schermo = SDL.setVideoMode(width,height,bpp,SDL::HWSURFACE|SDL::DOUBLEBUF) nota: il signore tra SDL::HWSURFACE e SDL::DOUBLEBUF è l'operatore OR e si ottiene con la combinazione di tasti SHIFT + \ (quella a sx dell'1) abbiamo creato la nostra finestra e dobbiamo porre particolare attenzione a schermo visto che come ho nerettizzato prima vedremo solo quello che disegneremo dentro questa SDL::Surface!! (ennesima volta che lo scrivo) Modifichiamo qualche parametro estetico così prendete familiarità con le classi #SDL::WM.Caption stringa1, stringa2 Imposta il titolo della finestra a stringa1 e il titolo nella icon bar a stringa2 Il # indica il commento, lo metto in modo che un eventuale neofita non mi chieda perché non funziona perché ruby non trova stringa1 SDL::WM.Caption "Hello World!", "Hello World!" e nascondiamo il mouse SDL::Mouse.hide Ora arriva un passo importante ed un concetto difficile: Controllo degli eventi La programmazione delle SDL è fatta ad eventi, che significa? La pressione di un tasto genera un evento, il rilascio di un tasto un altro evento, la chiusura della finestra, il movimento del mouse, la pressione ed il rilascio dei tasti del mouse, altri eventi, una caterva di eventi sul joystick e TANTI ALTRI. Che cosa deve fare la nostra applicazione o gioco? Reagire agli eventi, magari :) Come reagire lo decidiamo noi (se preme la freccetta si muove in basso o se premo esc esco dal gioco), ma per reagire bisogna accorgercene! E come ce ne accorgiamo con le SDL/Ruby? Ci sono diversi metodi, per ora ho provato solo il metodo classico (e presente nella controparte C) mentre esiste un metodo ancora più semplice (ma che non ho voglia di vedere come funzioni, per gli interessati nella Ruby/SDL reference la classe è la SDL::Event2) Metodo classico: Uso di SDL::Event Praticamente creiamo questo evento generico che sarà da ricettacolo a tutti gli eventi che si manifestano e che estrarremo, uno alla volta, con il metodo poll, una volta estratto vediamo che tipo di evento si tratta e agiamo di conseguenza Ma non basta! Vogliamo che il gioco continui ad andare fino a quando lo vogliamo noi, quindi ci serve prima di tutto un ciclo quasi infinito, che io abitualmente creo con fine = 0 while (fine == 0) #controllo gli eventi #aggiorno il mondo (le variabili) #disegno tutto #flippo il backbuffer end Come vedete metterò tutto il mio programma dentro al while Per uscire basterà porre fine = 1 o brutalmente un break e grazie a Dio (per ora) non serve altro dopo il while per deinizializzare la libreria perché il porting Ruby si deinizializza da solo! Torniamo ad i nostri eventi, questa volta analizziamo in pseudocodice -Finché poll mi restituisce un evento -controllo l'evento, se è tizio faccio questo, se è caio faccio quest'altro Passiamo al codice, vi avverto, per i neofiti (e per me lo è stato) può essere abbastanza spaventoso, il while dentro il while (DEN DEN DEN!) evento = SDL::Event.new #creo l'istanza prima dei while! while (fine == 0) #già inserito #controllo gli eventi while (evento.poll != 0) if (evento.type == SDL::Event::QUIT) then fine = 1 break end if (evento.type == SDL::Event::KEYDOWN) then if (evento.keySym == SDL::Key::Escape) then fine = 1 break end end end #aggiorno il mondo (le variabili) #disegno tutto #flippo il backbuffer end >>>Riprendo la trattazione Come avete visto il metodo type restituisce il tipo di evento che la nostra applicazione ha INTERCETTATO Tipi di Evento SDL::Event::ACTIVEEVENT non trovo documentazione in ruby, in c è quando il mouse entra/esce nella finestra dell'applicazione o quando l'applicazione è minimizzata (penso che da minimizzata continui ad andare, se volete frozzare il gioco mettete un flag dopo aver ricevuto questo evento e controllato sia in state == SDL::APPACTIVE, gain = 0 se minimizzata, gain = 1 se ripristinata) SDL::Event::KEYDOWN un qualsiasi tasto premuto SDL::Event::KEYUP un qualsiasi tasto rilasciato SDL::Event::MOUSEMOTION movimento del mouse SDL::Event::MOUSEBUTTONDOWN pressione di un tasto del mouse SDL::Event::MOUSEBUTTONUP rilascio di un tasto del mouse SDL::Event::JOYAXISMOTION movimento di un asse del joystick SDL::Event::JOYBALLMOTION movimento di palla XD del joystick SDL::Event::JOYHATMOTION movimento dell'hat del joystick (non ho idea di cosa sia ;D) SDL::Event::JOYBUTTONDOWN pressione bottone joystick SDL::Event::JOYBUTTONUP rilascio bottone joystick SDL::Event::QUIT quando si preme alt+f4 o si clicca sulla croce in alto a destra SDL::Event::SYSWMEVENT dipende dal windows manager del sistema (ovvero intercetti chiamate a eventi aggiuntivi, sapete che tutti i wm funzionano a eventi?) (è il modo per implementare il copia incolla, ad esempio), se volete una applicazione crossplatform e non sapete come individuare il sistema di riferimento, ignoratelo SDL::Event::VIDEORESIZE se ridimensionano la tua finestra, ma puoi ottenere questo evento solo se metti in setVideoMode la flag SDL::RESIZABLE (e perché in un gioco dovremmo farlo? anzi non ho messo il fullscreen per non essere invadente, lo lascio restringere per distruggermi tutte le proporzioni :D? avrebbe senso soltanto se l'applicazione fosse 3d :) ) Come vedete gli eventi di pressione e rilascio sono generici e dobbiamo vedere con altri metodi che tipo di tasto/bottone è stato premuto, per quanto riguarda la tastiera si fa con il metodo keySym che restituisce il codice corrispondente al tasto premuto (per una lista di tasti guardate la reference ;) ) Il ruby per quanto riguarda gli eventi è ridondante perché permette diverse vie per controllare l'input, che vedremo nel prossimo tutorial/esempio ;) Come vedete nel codice sopra non ho fatto che dire: se tentano di chiudere la finestra o se premono esc, chiuditi Andiamo avanti, per ora non abbiamo nessuna istanza che si muove o viene aggiornata quindi la parte di aggiorno il mondo la ignoriamo, altrimenti cosa ci andrebbe? Qui non esistono variabili predefinite per la velocità degli oggetti, ogni deltat dovete sommarla voi (e qui si entra in un discorso complicato, l'esecuzione non va alla stessa velocità su tutti i computer, un pentium 2 con scheda video cacca magari impiega 1 secondo per girare il while mentre un computer decente di 5 anni fa con scheda video carina ce ne impiega 0.05) quindi quando ci toccherà muovere un evento prima dovremo calcolare il tempo intercorso tra un while ed un altro e poi incrementare le variabili controllate in proporzione al tempo (ma questa è un'altra storia) Siamo finalmente arrivati alla fine, disegnamo sullo schermo! In teoria qui dovremmo gestire tutte le Surface e riversarle (copiarle) come vogliamo sullo schermo (immagini che fungono da sprite, background ecc...) ma visto che non ne abbiamo possiamo disegnare direttamente sullo schermo! Piuttosto che disegnare qualche funzione malata pixel per pixel preferisco che usiate i molteplici metodi offerti dalle SGE (una parte di Ruby/SDL) (che trovate nella reference nella sezione SDL::Surface) #qualche mio disegnino a caso, volevo fare una faccina schermo.fillRect 0,0,640,480,0xF0F6FA schermo.fillRect 50,50,50,50,0xCACCAA #questo colore mi fa morire XD schermo.drawRect 540,50,50,100,0x215571 #per creare un rettangolo più spesso schermo.drawRect 541,51,48,98,0x215571 #ne disegno due uno dentro l'altro schermo.drawLine 340,100,300,200,0xFF0000 schermo.drawLine 300,200,330,220,0xFF0000 schermo.drawLine 300,200,330,201,0xFF0000 schermo.drawLine 330,220,330,201,0xFF0000 schermo.drawCircle 250,150,40,0x0 schermo.drawFilledCircle 400,150,30,0x000090 schermo.drawFilledCircle 400,150,10,0x0 (spiego sotto questa sezione) bisogna poi aggiungere il flip del backbuffer, che cos'è il backbuffer? praticamente piuttosto che scrivere direttamente in faccia al monitor, al monitor sono associate due Surface, una davanti e una dietro. Noi possiamo disegnare e fare quello che ci pare su quella dietro (sul di dietro :Q_) mentre l'utente continuerà a vedere quella avanti, dopo che abbiamo finito di disegnare possiamo versare la faccia di dietro su quella davanti tramite questo semplice comando! #fine schermo.flip end #già scritto, è quello del while (fine == 0) !!!!! Così siamo arrivati alla fine, provate il gioco con ruby hello.rb e vedrete gli scarabocchi che facevo stanotte (belli no?) adesso indaghiamo su quel mucchio di funzioni grafiche e intellegibili che ho usato Metodi di SDL::Surface fillRect x,y,width,height,colore crea un rettangolo pieno colorato di dimensioni widthxheight in (x,y) drawRect x,y,width,height,colore crea un rettangolo vuoto ma come sopra drawLine x0,y0,x1,y1,colore disegna una linea drawCircle x,y,raggio,colore disegna una circonferenza drawFilledCircle x,y,raggio,colore disegna un cerchio Tutte molto isy e sul manuale ne trovate altre ;D Allego il file hello.rb per chi si fosse perso fatemi sapere che ne pensate per domande/approfondimenti nel prossimo tutorial creeremo una istanza che si muoverà con le freccette :)
  16. il socket è maschile Mazus: il programma per le modifiche immediate non influisce sulla velocità del server! E' possibile crearne uno anche per un qualsiasi mio formato di file di testo, quindi non vedo dove sia l'innovazione! Che sia molto più comodo lavorare con il database, non ci piove, anche perché esistono già migliaia di tool vista la diffusione e gli standard proposti mentre usare i file significa crearsi il proprio standard È l'azione di ottenere le informazioni da parte del server che è molto più veloce usando le funzioni di filesystem piuttosto di query sql In c te la cavi con 3 funzioni: fopen() fgets() sscanf() L'sql è un linguaggio interpretato, oltre ad inviare la richiesta (delta t1), interpretare la richiesta (delta t2) al tempo che impiego a leggere da disco i dati, che siano poi organizzati con ISAM o InnoDB è trascurabile in questo paragone (delta t3) e poi inviartela tramite socket (delta t4). Con le funzioni standard di input/output se programmi da cane al massimo impieghi qualcosina in più di delta t3, ma gli altri delta dove li metti? Pensi che la query sia più veloce perché vieni dall'ambiente di visual basic e quindi ti ritrovi ad affrontare un linguaggio lento ma io parlo di C! Approfondimento: nei benchmark migliori è risultato che un exe vb impiega il triplo del tempo per le operazioni di Input/Output di uno in c compilato con gcc che usa le window lib c, differenza che diventa abissale con le glibc e OS linux =) Ora puoi anche non aver trovato lag nelle tue prove, non te lo contesto, ma prova a trovare il numero minimo (o limite) di utenti che ti causa una fissata lag e confrontalo con un server (necessariamente in c/c++) che usa i file di testo, noterai che regge come minimo altri 100 utenti senza arrivare a quella lag Ribadisco, non dico che usare i file sia migliore in assoluto ma ignorare la componente di calcolo del middleware è stupido (l'azione, non la persona, non mi permetterei mai)
  17. SQL è una questione che ho trascurato volutamente, sono consapevole che i database siano largamente usati, ma non aveva senso mettermi a spiegare questo se non si sa neanche come organizzare il resto :) anche perché dovrei iniziare a parlare del linguaggio SQL, delle selezioni, della possibilità di avere più database e quindi di fare join mirati ad ottenere tutti i dati che servono, ma esulerei troppo dal discorso! Lasciando quindi perdere i dettagli intimi che chiunque potrebbe leggere in un manuale, dedicherò (o forse anticiperò la trattazione, nella mia testa non so se e quando sarebbe avvenuta) un po' di tempo ad i database SQL, dopo averti risposto :D non mi pare di essermi espresso male con la faccenda delle struct del client non parlando esplicitamente di database poiché in memoria una struct per il client SERVE quando devi manipolare dei dati, lavorare direttamente facendo query SQL è dannatamente lento! Legenda < significa riceve in input > significa invia come output ? posto vicino ad operatore significa eventualmente db significa database ::tiziocaio combatte con slime server < tiziocaio da query sql db dei personaggi (joinato magari con quello degli account) (se non già presente in memoria!) server < slime da query sql db dei mostri (e se ci sono solo 10 tipi di mostro, concordi anche tu che fare un database un po' mi puzza ;) ) ::finché la battaglia imperversa server > calcoli al client client > aggiorna il mondo al giocatore client <? riceve input giocatore server <? cambio di strategia del client ::fine battaglia server ?> exp al client o messaggio di morte o fuga dalla battaglia (una battaglia può finire in tanti modi) client <? riceve exp e/o aggiorna dati interni server ?> aggiorna tiziocaio in db è su questo che mi soffermerei, non è così immediato da capire che io non devo aggiornare il database dopo ogni scontro perché è una operazione esosa (se aggiorno e il client compie un'altra azione 1 secondo dopo devo riscaricarmi tutti i dati, di nuovo) e preferisco spesso lasciarmi i dati in memoria (con delle funzioni che tengono traccia magari del timeout e di quanta ram è libera) e magari aggiornare il database ogni 5/15/20/30 minuti o 2 ore (se mi sento spavaldo XD). Certo ribadirai che per questo piuttosto che tenere in memoria in una struct pg io possa direttamente estrarlo e metterlo in delle struct fatte appositamente per la battaglia ma capisci che questo esula dalla propositività di for dummies e ci si dovrebbe addentrare prima in discussioni tipo: Ha senso usare dei database? Ha molto senso perché sono tanto facili da gestire (sviluppo sostenibile) e sono consigliati per server che mirano a più di 100 utenti connessi contemporaneamente. Ma quale database devo usare? C'è l'imbarazzo della scelta a cominciare da MySQL (gratuito per le applicazioni opensource ma penso a pagamento per le applicazioni commerciali), MSSQL, Oracle (se si hanno le possibilità economiche...) o alternative gratuite come PostgreSQL (il migliore tra i free software) o SQLite (un minidatabase piccolo e veloce, naturalmente ha anche lati negativi). Io consiglio MySQL per la diffusione e se siete alle prime armi, mentre se avete in mente di fare un progettino serio dovete vedere bene i dettagli delle licenze di MySQL e al massimo optare per PostgreSQL Che alternative ho ai database? Se mi fate questa domanda è perché finora sono stato poco chiaro :) Un database è una tecnologia dove immagazzinare ordinatamente le informazioni sensibili, a cui possiamo accedere tramite una interfaccia software, ovvero l'elaboratore del linguaggio SQL che interpreta il nostro codice e ci restituisce un risultato. Dovete immaginare il database come se fosse un altro client attaccato persistentemente al server client <==> server <=!=> database <==> dati dove quel punto esclamativo indica che il legame non si può rompere perché funzionano "a braccetto". Quello che ci importa quindi è un sistema di accesso ai dati! E allora potremmo benissimo usare della alternative, dai semplici fogli di testo formattati come ci pare a minidatabase xml. Ma qui compaiono altre problematiche, perché client <==> server <==> BUCO <==> dati l'interfaccia di accesso ai dati la dobbiamo costruire noi, o tramite le funzioni di sistema o tramite altre librerie e spesso se non ci si pone abbastanza attenzione abbiamo funzioni ancora più pesanti del database o un design cattivo per i dati e per ottenere le informazioni che ci servono o solo paragonarle dobbiamo fare i salti mortali. Un esempio chiarificatore potete averlo guardando i miei due emulatori preferiti, eathena_SQL e eathena_TXT e vedere come nel TXT ci si incasina notevolmente e per inserire dati o si smania con il txt oppure bisogna scriversi una applicazione mentre nell'alternativa SQL basta una query fatta o dal gestore interno del database, o da un nostro programmino o perfino da uno script in php/asp (e questo velocizza moltissimo lo sviluppo) Spero di aver dileguato altri dubbi, stavolta trattazione breve, non amo particolarmente i database perché scrivo SQL contro voglia XD, eventualmente ci ritornerò una volta approfondito ulteriormente il corpo del server Risposte in sintesi Mazus, come spero di averti fatto capire non sono un neofita nei database ma permettimi di dissentire su i database RALLENTANO la modifica delle informazioni A FAVORE della semplicità (vuoi comparare le system calls read() write() al lavoro che fa un database? se vuoi scrivo un programmino ad hoc e facciamo un benchmark ma spero che te ne renda conto da solo :) ). Apprezzo che tu abbia già esperienza quindi spero revisionerai il lato database dei miei post e magari offrirai altri spunti ;) ProGM, era solo un accenno, nel prossimo post lo tratterò in dettaglio ;) Kamahl tipico errore delle università il non spiegare accuratamente la struttura degli OS! Winsocket! i socket non sono una astrazione di java ma un'astrazione offerta già nel sistema operativo! esistono due tipi di implementazioni per i socket: Berkeley (spero di averlo scritto bene) e Winsock le api Berkeley sono implementate in tutti gli OS unix/linux/BSD le api Winsock sono implementate su Windows a partire da non so che data, forse nel 3.1 erano ancora un pacchetto opzionale, nel 95/98 dovrebbero essere presenti di default Quindi su windows per avere i socket non devi fare altro che interfacciarti a queste API! In pillole, se programmi in C per win32, le cose da fare sono queste: #include <winsock2.h> dare in pasto al linker la libreria ws2_32.lib (o libws2_32.a se usi gcc, se ti manca ti posso spiegare come ottenerla) in questi post io parlerò principalmente avendo come riferimento le Berkeley (sulle quali ho esperienza) tornando di tanto in tanto ad evidenziare le minuscole differenze principalmente SINTATTICHE con Winsock. La differenza sostanziale è che la libreria (dll) WinSock va Inizializzata (prima) e Deinizializzata (dopo l'uso) All'inizio del programma, dovrai fare qualcosa tipo: WSADATA wdata; if (WSAStartup(MAKEWORD(2,2),&wdata)){ //in genere per le piccole applicazioni tratto gli errori così: //dopo aver definito lo stream error se non definito (non ricordo più com'è in windows XD) //per maggiori dettagli si può voler chiamare la WSAGetLastError(); fprintf(stderr,"Errore, Impossibile inizializzare libreria Winsock2!"); exit(1); } if (! (LOBYTE(wdata.wVersion) == 2 && HIBYTE(wdata.wVersion) == 2)) { WSACleanup(); fprintf(stderr,"Impossibile caricare Winsock v 2.2!"); exit(2); } Il primo codice inizializza la libreria winsock versione 2.2, il secondo invece controlla che sia stata davvero caricata in memoria la 2.2, è un check da fare perché le winsock sono bastarde (sotto alcuni punti di vista) e se non hanno a disposizione la versione 2.2 caricano l'ultima versione disponibile (che può essere la 2.0) che magari difetta di qualche funzione o è buggata MA in windows xp e penso superiori (ma l'idea di Vista mi disgusta ;D) è sicuramente presente la libreria aggiornata (quindi non dovete farvi troppe pippe mentali) Per deinizializzare dll e sue strutture basta fare: WSACleanup(); (eventualmente studiare l'output se si è maniacali :) ) Conclusione Apprezzo molto i vostri commenti positivi :) (e ne aspetto altri anche di altri XD) Continuate a chiedere approfondimenti e curiosità, presto continuerò il discorso :)
  18. Sono insonne, sono le 2.30, e quindi scrivo qualche parola di getto piuttosto che perdere tempo sui siti hot, magari quello che scrivo può essere utile a qualcuno, mi sento abbastanza ispirato. Ciò non toglie che domani da riposato e sano non possa correggere e magari estendere ciò che scriverò :D Molti vorrebbero creare il loro mmorpg o anche solo un gioco online ma sono spaventati da un mondo che non conoscono completamente, la network programming! Cercherò quindi in questa miniguida di illustrare i concetti principali della programmazione dei giochi web, frutto di diverse mie prove che per motivazioni diverse non sono mai state concretizzate. Innanzitutto la motivazione, perché si vuole creare il gioco online? Questa domanda ha una sola risposta, a cui si può dare una interpretazione positiva o negativa a seconda della visione del mondo. Innanzitutto la risposta è... (provate a indovinare) Il giocatore si sente vivo a ownare un'altra persona, è un meccanismo istintivo, naturale e MOOOLTO soddisfacente. Ma perché è così soddisfacente? a) Siamo/Siete una sega a creare IA appaganti/equilibrate b) Il giocatore si troverà di fronte sempre nuove sfide/avversari mentre in un gioco offline finita la storia finito il divertimento => gioco nel cestino Esistono, al giorno d'oggi, 3 alternative per creare un gioco online a) Via Client/Server b) Via Browser c) Ibrido Appunti: I sistemi b e c offrono o una grafica scarna o una dinamica lenta ma maggior semplicità di realizzazione Nota: Non lasciatevi incantare, le applet java ricadono nel meccanismo client/server Data la mia poca esperienza con il php nel gestire alternative alle sessioni (qui bisognerebbe far intervenire esperti tipo dax) vi parlerò solo dell'alternativa a Meccanismo Client/Server Esistono migliaia di giochi famosissimi che rientrano in questa famiglia, ad esempio Wow, Ragnarok Online, Gunbound e il 99.9% degli MMORPG che non sto a citare perché non mi frega, ma anche tutti quei giochi fatti per 2, 4, 8, 10 giocatori. Ora dovrei entrare in modalità pallosa e spiegare i vari tipi di reti ad anello, ecc.. ma lo evito volentieri La maggior parte dei giochi online commerciali funziona in questo modo: esiste da qualche parte nel mondo una farm, ovvero una serie di computer collegati tra di loro, che fungono da server (per giochi amatoriali basta un pc :) ) un giocatore invece è un client La regole classica di costruzione di un gioco online, favorita anche dall'aumento negli anni della banda di down/up (oltre e soprattutto per logiche di sicurezza), sono che il client può dialogare solo ed esclusivamente con un/i server e quindi mai direttamente su altri client. I messaggini privati che mandate al vostro amico tramite ragnarok passano comunque dal server. Il server quindi sarà un programma che gestisce ogni canale di comunicazione con i client e da bravo postino dovrà reindirizzare i messaggi Il client invece dovrà essere abbastanza aggiornato dal server da permettere una buona esperienza di gioco, e ogni tanto dovrà inviare sue notizie Tipo di comunicazione e dinamiche di gioco Come al solito ci troviamo di fronte ad una vasta letteratura che non fa che confondere l'apprendista, se devo creare client e server a che santo mi devo votare? al protocollo tcp/ip o al protocollo udp? seppure vi affidate ad una libreria che automatizza gran parte delle procedure, la scelta stilistica ha delle ripercussioni molto profonde sulla giocabilità! Innanzitutto se non sapete neanche cosa sono i protocolli tcp/ip e udp vi do qualche pillola. Prendete come assioma che l'informatica va avanti a standard, perché senza standard non sarebbero possibili forme di comunicazione (l'italiano è un esempio di standard). Un protocollo è un insieme di regole formali (o ben-definite ergo comprensibil'inequivocabili) che in questo caso regola la trasmissione (invio e ricezione) di dati attraverso due nodi della rete (sia essa locale o globale). La comunicazione vuole un mittente, un destinatario ed un messaggio. Nel caso del protocollo tcp/ip il mittente e il destinatario sono in qualche modo connessi virtualmente nei due sensi (ovvero entrambi possono agire sia da mittente che da destinatario) ed i messaggi arrivano sempre e comunque (eccetto perdite di pacchetto che hanno una distribuzione poissoniana come modellizazione, quindi molto rare), ed arrivano nell'ordine in cui sono stati inviati (cosa molto importante!), Il difetto del protocollo tcp/ip è la pesantezza degli header (intestazioni) dei pacchetti (la distribuzione dei messaggi avviene per frazioni dette pacchetto di non so, qualche centinaio di kb di lunghezza) che provoca un carico in più per la rete (simulare la connessione tra le due macchine richiede risorse, la non fallibilità del sistema richiede pacchetti aggiuntivi di conferma di avvenuta ricezione) Il protocollo udp invece è più snello nel senso che non avviene una connessione tra mittente e destinatario, ogni messaggio inviato funziona come un aeroplanino di carta, non c'è bisogno che tu e il destinatario abbiate un canale di comunicazione, non è garantito che il messaggio arrivi e se mandi più messaggi o aeroplanini, non è detto che arrivino in ordine, il vento può averli mischiati nel frattempo. Un bel dito in culo, noterete voi :Ok: . Chiaramente sì, ma offre dei vantaggi considerevoli quali velocità e non carica eccessivamente la rete. Come usare udp? Sta al programmatore client/server definire delle regole per marchiare i messaggi udp per poterli riordinare e magari richiedere se smarriti (e qui si aprirebbe una larga parentesi sulle strategie per marchiare un pacchetto e al contempo avere una comunicazione "sicura") Perché usare udp? A questa domanda personalmente risponderei per quasi nessun motivo, perché al giorno d'oggi con la banda down/up disponibile a meno di giochi next gen programmati col culo usare udp non ha particolari vantaggi, la teoria invece vuole che udp sia usato per tutti quei processi in cui il tempo di risposta è critico, quindi quando la dinamica del gioco è molto veloce (es. sparatutto o mmorpg) se non fosse che la maggior parte degli mmorpg commerciali, seppur con centinaia di migliaia di utenti, tipo WOW, usi il protocollo tcp/ip (ma loro hanno una mega farm ._.) (anche Ragnarok Online usa tcp/ip ma l'emulatore server ovvero eathena è programmata un po' male quindi spesso e volentieri lagga (non parliamo della cacca nota come aegis :D)) Quindi ci conviene usare solo tcp/ip? La risposta è NI. La soluzione ideale sarebbe un modello ibrido sia tcp/ip che udp, su connessione tcp/ip si fanno passare i dati sensibili mentre su udp e con maggiore frequenza si inviano i dati a dinamica veloce (esempio posizione+velocità del personaggio), ma è consigliato solo per server con taaaaaaaante persone visto che lo sbatti è considerevole! Per server con una stima di giocatori inferiore a 500 giocatori mi prendo la responsabilità di dire che basta l'uso del protocollo tcp/ip (oltre che un cervello acceso). Bene, ho scelto (volontariamente XD) di usare il protocollo tcp/ip, ma come funziona in dettaglio? Iniziamo col dire che, trascurando le reti locali, ogni macchina è identificata da un indirizzo ip ed ogni macchina ha a disposizione 65536 porte. L'indirizzo ip (v4, l'ipv6 può andarsi a farsi friggere) può essere rappresentato da una serie di 4 numeri grandi un byte (da 0 a 255) separati da un . es. 214.0.232.12 Cos'è una porta? Immaginate una porta. Ecco... Il server piazza uno dei suoi nipoti dietro una prefissata porta ad ascoltare, Arriva il giocatore/client, che ha bisogno di qualcosa, all'indirizzo ip trovato sul biglietto da visita (homepage) del videogioco, bussa alla porta prefissata, il nipote del server, che aveva ascoltato, gli apre (è un po' pollo, per filtrare a priori servono le iptables) e, in genere, il giocatore parla per magari presentarsi. Se al server sta bene quello che dice magari risponde con un saluto ed un piccolo aggiornamento e lascia la porta aperta per ulteriori discussioni, altrimenti può benissimo mandarlo a cagare se è un testimone di geova. Se ci fosse un solo giocatore con un nipote me la caverei bene... Il problema è che i giocatori sono molteplici e i figli del server non hanno trombato come conigli (oltre al fatto che ho appena 65k porte ) quindi io server non avendo un nipote per ogni porta, decido di usare un'unica porta (o un paio ma dimenticatelo) per l'accettazione (ovvero come portineria) ma creo, nel momento in cui accetto la visita, un collegamento psichico diretto, tra me e il client, a cui do il nome di Socket, che in soldoni non è altro che una comoda astrazione che simboleggia il percorso tra mittente e destinatario o viceversa. (il doppio collegamento sopra citato nella spiegazione di tcp/ip) Ricapitolando da server: Metto un nipote in ascolto su una determinata porta; Se bussano alla porta dell'accettazione, creo un collegamento con loro (Socket); Vedo chi sono: se non mi piacciono, li butto la porta in faccia distruggendo il Socket al volo altrimenti conservo in qualche modo le informazioni che mi danno, magari associando il nome con cui si sono presentati (e che ho ritenuto valido) con il Socket, in modo che nelle successive comunicazioni non avrò problemi ad identificare da chi ricevo il messaggio o a chi lo mando. In programmatichese questo si fa in molteplici modi, a partire da due array, uno di Socket e uno di Stringhe dove a stesso indice corrisponde stesso utente, o con UNA o PIÙ liste, dinamiche o meno, di struct ad esempio (adattato da un mio sorgente in c, ultimamente sono su linux, che usa gli int per identificare i socket): #ifndef WIN32 typedef int Socket; #endif typedef struct client{ Socket * sock; char * nome; struct client * next; }Client; WOW del codice inutile! :) Secondo grande dilemma Siamo arrivate al secondo grande dilemma, dopo la scelta forzata di tcp/ip XD Qui però non esiste una strada giusta ed una sbagliata... Il problema è, io SERVER ho capito che devo memorizzare ogni collegamento e magari associarlo ad una struttura dati che memorizza anche informazioni (che in un mmorpg possono essere anche posizione, razza, sprite, stato, ecc...) ma esistono diversi TIPI di collegamento e di conseguenza diversi MODI di implementarlo (pur rimanendo entro i confini del tcp/ip) Compaiono quindi nuovi paroloni quali sincrono, asincrono, threaded. Cosa significano? Sincrono ed asincrono si riferiscono al modo in cui il destinatario riceve il messaggio. La modalità sincrona significa che il destinatario rimane con la bocca aperta e saliva calante (aka modalità Homer&Donuts) fino a quando non riceve il messaggio da parte del destinatario, e se non arriva è possibile che rimanga per le mani in mano fino alla fine del programma! La modalità asincrona significa invece che faccio una corsetta fino al Socket (il mio collegamento visto come corridoio virtuale), vedo se c'è un pacchetto fuori dalla porta, se non c'è continuo la mia corsetta nel sbrigare le altre faccende di casa (magari controllando gli altri socket). E' chiaro che, se non avessimo a disposizione particolari tecnologie, l'unico modo per fare un gioco sarebbe tramite socket asincroni perché avendo più client non possiamo rimanere incantati su un solo Socket! Esiste però una parola magica chiamata thread, che voi di rpgmaker potete immaginare come un processo parallelo che non rallenta tutto perché è DAVVERO parallelo, una sorta di schiavetto, che al contrario di nostro nipote non ci viene a chiamare appena bussa qualcuno ma che esegue in autonomia quello che c'è da fare, possibilmente senza intralciarci, e che ha a disposizione la nostra casuccia (le variabili globali) Un programma quindi può creare tanti thread o sottoprocessi che andranno ad occupare una area ben definita in memoria (quindi come limite al numero di thread da creare dovete considerare la memoria ram e la velocità della cpu del server) e eseguiranno operazioni contemporaneamente al server, possibilmente senza scornarsi ovvero dovranno cercare di non accedere contemporaneamente alla memoria condivisa (per questo esistono i semafori, mutex e altre diavolerie). Esistono quindi tante vie di implementare un server: 1a) Usare socket asincroni, creo il mio bel listone giordano e controllo 1b) Usare socket asincroni, immagazzinati in strutture e funzioni che fanno il lavoro per me (select() ) 2) Usare socket sincroni+ thread, ogni thread è un bocc'aperta 3) MIX (provate a pensarci) La soluzione 1a è la più usata in applicazioni commerciali che hanno a disposizione farm La soluzione 1b è il modo più semplice per ottenere un buon server < 400 utenti (butto la cifra a naso, poi dipende da quanto si è programmato bene) La soluzione 2 porta ad un grande spreco di memoria ed è fatta solo dai ragazzini :) La soluzione 3 è l'unico modo per avere un server competitivo con le applicazioni commerciali senza dover gestire una farm di server [-----------------] Per stanotte è tutto, si sono fatte le 6 e ho passato 3 ore e rotti qui su :) Mi scuso per il linguaggio volgare, ma volevo arrivare a tutti. Penso che ci sarà un seguito presto, sia per approfondire il MIX lasciato in sospeso sia per introdurre nuovi argomenti, il tutto dettato anche in base al vostro feedback (ovvero risposte a questo topic), spero di guadagnarmi qualche rens così posso aprirmi una botteguccia :E Se non capite qualcosa o volete ulteriori semplificazioni sugli argomenti trattati, non avete che da chiedere, più avanti magari metterò qualche codice più esplicativo ma volevo rimanere caotico neutrale perché non è detto che dobbiate scendere fino a livello socket per fare un gioco online poiché potete benissimo usare una libreria di terzi, ma mi premeva darvi almeno un rudimentale metro di giudizio ByeZ
  19. chiama l'update solo quando gli hp o sp diminuiscono o crescono piuttosto che ogni volta
  20. Keroro

    GFM Lezione1

    carino! non pensavo ci fossero queste tecniche, ho tanto da imparare :) per fortuna non si usa qualche filtro particolare per cui con gimp si può ottenere un risultato equivalente, mi metto a fare qualche prova!
  21. Keroro

    Le nostre foto

    la liz vicious italiana :Q______
  22. ma capisci che in una grande realtà tu hai delle competenze limitate e se ti assumono come programmatore junior del game design se ne occupano altri :)
  23. programmare un videogioco è un'operazione semplice quali possono essere le difficoltà? imparare un nuovo linguaggio, ma se si sa programmare sia "proceduralmente" sia a oggetti si tratta solo di assegnare nuova sintassi ai concetti noti, te la cavi in tempo brevissimo e al massimo ti toccherà qualche ottimizzazione in asm dell'architettura obiettivo (anche questa operazione banale se si sa quello che si sta facendo :) ) Il 3d può essere uno scoglio all'inizio ma con l'algebra vettoriale e l'esperienza ne esci fuori, inoltre al giorno d'oggi ci troviamo ad un livello di astrazione tale (es. OpenGL) e così tanta documentazione che in breve tempo riesci a ottenere risultati considerevoli Hai quei 3-4 algoritmi per ottimizzare, ordinare e ricercare nei vettori, grafi, ecc.. Il mondo è pieno di sdk per le architetture, al massimo devi crearti hardware per scrivere il bios sul processore obiettivo (banale, basta un corso di elettrotecnica e tanta passione :D) Modellare il problema? chiunque con un po' di sale in zucca/esperienza dotato di abbastanza tempo riesce a trovare diverse soluzioni e a scegliere magari la migliore (o a farsi cazziare e licenziare dal capo) ricapitolando è una mansione che ad alti livelli non è niente di creativo e studiare ingegneria informatica per ambire a lavorare in una software house è come dire... sprecare un talento quando quel posto lo potrebbe occupare uno uscito da ragioneria per programmatori o anche da un laureato in informatica, in parole povere io vedo l'ingegneria come un modo per innovare ma trovare l'innovazione in una software house è durissimo se non impossibile. Naturalmente ora mi verrete tutti addosso ribadendo che è difficile, purtroppo senza avere cognizione di causa perché molti di voi si trovano alle superiori o in altre facoltà o non in ingegneria informatica in una facoltà prestigiosa/evoluta come il Politecnico di Milano (e ho tutto il diritto di sboronarmi).
  24. ma ci vogliono anche competenze banali per lavorare in una software house, studio da ingegnere informatico e ambisco a molto di più =)
×
×
  • Create New...