درس 13 : آموزش پخش و effect گذاری بر روی فایل های Ogg,MIDI,Mp3

درس 13 : آموزش پخش و effect گذاری بر روی فایل های  Ogg,MIDI,Mp3 و ... (قسمت اول)

 

شاید 10-15 سال پیش کار بر روی صدا در بازی ها به اندازه ای که امروز اهمیت دارند اهمیت نداشت. ولی امروزه بیشتر تیم های بزرگ بازی ساز برای صدا گذاری مناسب اهمیت خاصی  قائل هستند تا انجا که مثلا در بازی های First Shooter می گویند: "بازیکن فقط میتواند 30 درصد صحنه را با چشمهای خود چک کند و بقیه صحنه را باید با گوش های خود چک کند ".

SDL هم برای خود زیر سیستمی بزای پخش صدا دارد ولی متاسفانه خیلی ضعیف هست.خوشبختانه یک برنامه نویس خوب به اسم Jonathan Atkins پیدا شد و بعد از اینکه متوجه این ضعف شد شروع به ساخت SDL_Mixer کرد واین ضعف SDL را کاملا بر طرف کرد.

 

قبل از ادامه کار شما باید فایل های SDL_Mixer را از سایت libsdl.org/projects دانلود کرده و طبق درس 3 نصب کنید.

 

SDL_Mixer در یک نگاه :

کار با  SDL_Mixer به 6  قسمت تقسیم می شود.

 

 O shit!!

Initialization :

دو تابع در این قسمت وجود دارد یکی برای باز کردن SDL_Mixer و دیگری یستن ان مطمئنا کار با این توابع سخت نیست !

Chunk :

همه effect های پس زمینه به عنوان chunk شناخته می شوند یک نکته دیگر هم این که ما هیچوقت با اشاره گر به chunk کاری نداریم بر خلاف SDL_Surface و ... .  همچنین chunk ها را می توانیم یا از فایل های روی دیسک یا از روی RAM بار گذاری کنیم و پسوندشان هم می تواند wav یا VOC باشد.

 

channels:

کانال ها به ما کمک می کنند بیشتر از یک chunk دریک زمان در پس زمینه پخش شود چند گزینه برای پخش chunk ها موجود است مثلا چند بار تکرار شود چه مدت وایا fade شود یا نشود.

 

Group:

شما میتوانید کانال ها را دسته بندی کنید مثلا شاید بخواهید effect های انفجار از effect های بازی جدا باشند. Group روشی برای مدیریت کانال های صدا است.

 

Music :

در یک زمان فقط یک music میتواند پخش شود که همان موزیک اصلی برنامه است که می توان از چندین فرمت استفاده کرد مثل Mp3,Ogg,Midi استفاده کرد.

شما می توانید یک اهنگ را پخش کنید یا pause یا stop یا fade in یا fade out کنید و همچنین مکان چخش را در اهنگ تغییر دهید.

 

Effect:

کسانی که می خواهند به طرز حرفه ای از mixer استفاده کنند این قسمت مخصوص انها است که علاوه بر داشتن توایع توکار خود نیز می توانید توابع خود را بسازید که در اخر ان درس توضیحاتی در این مورد خواهم داد.

 

همه توایع mixer با mix_ شروع می شوند و قبل از کار با انها باید زیر سیستم audio در sdl اماده شده باشد که همانطور که گفتم با فرستادن پارمتر SDL_INIT_EVERYTHING به تابع SDL_Init همه زیر سیستم ها نصب می شوند.

 

حالا وقت ان است که نگاهی دقیقتر به SDL_Mixer بیاندازیم

 

Initialization :

مانند همه کتابخانه های الحاقی دیگر برای استفاده از توابع mixer باید SDL_Mixer را اماده کرد ولی بر خلاف بقیه دیگر تابعی به نام Mix_Init وجود ندارد که به راحتی کار ما را اسان کند اولین کاری که باید بکنید فراخوانی تابع Mix_OpenAudio است :

int Mix_OpenAudio(int frequency, Uint16 format, int channels, int

chunksize);

4 پارامتری که می گیرد اولی frequency به عنوان فرکانس با مقیاس HZ است ودومی فرمت است که یکی از مقادیر زیر می تواند باشد

Constant Meaning

AUDIO_U8       Each channel consists of a stream of Uint8s.

AUDIO_S8       Each channel consists of a stream of Sint8s.

AUDIO_U16LSB      Each channel consists of a stream of little endian Uint16s.

AUDIO_U16MSB      Each channel consists of a stream of big endian Uint16s.

AUDIO_U16     This is the same as AUDIO_U16LSB.

AUDIO_U16SYS      Depending on the system, this might be either

AUDIO_U16LSB      or AUDIO_U16MSB.

AUDIO_S16LSB      Each channel consists of a stream of little endian Sint16s.

AUDIO_S16MSB      Each channel consists of a stream of big endian Sint16s.

AUDIO_S16     This is the same as AUDIO_S16LSB.

AUDIO_S16SYS      Depending on the system

پارامتر سوم تعداد کانال هاست 1 برای mono و2 برای صدای stereo است.

و پارامتر اخر هم اندازه chunk هست مستندات SDL_Mixer به ما پیشنهاد می کند که از مقدار 4096 استفاده کنیم. در صورت اینکه درست اجرا شود مقدار 1- وگرنه 0 را بر می گرداند.

وقتی کارمان با mixer تمام شد با استفاده از تابع Mix_CloseAudio می توانیم حافظه را خالی کنیم.

 

Chunks :

Chunk ها effect های پس زمینه هستند با ساختاری به شرح زیر :

typedef struct {

int allocated;

Uint8 *abuf;

Uint32 alen;

Uint8 volume;

} Mix_Chunk;

که ساختار ساده ایست شامل یک flag به نام allocated که مشخص می کند chunk تخصیص یافته یا نه, یک بافر از نوع Uint8 که نگه دارنده اطلاعات صوتی است.یک متغییر از نوع Uint32 که مشخص کننده اندازه بافر است و یک متغییر از نوع Uint8 که اندازه Volume صدا را تعیین می کند.

شما معمولا به صورت مستقیم از Chunk استفاده نمی کنید بلکه بیشتر با اشاره گر ها با chunk ها سر و کار دارید البته اگر شما بخواهید می توانید به صورت مستقیم با انها کار کنید بیشتر وقتها شما بوسیله تابع Mix_LoadWav یک فایل wav را به عنوان chunk بار گذاری میکنیم .

Mix_Chunk *Mix_LoadWAV(char *file);

این تابع یک رشته به عنوان مسیر می گیرد و یک اشاره گر به Mix_Chunk بر می گرداند.اگر مشکلی پیش اید NULLبر می گرداند.

همچنین می توانید یک فایل Wav که قبلا در حافظه ذخیره شده است را بارگزاری کنید

Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem);

Mem یک اشاره گر از نوع Uint8 می باشد که به محلی در حافظه که فایل wav در ان است اشاره می کند تا وقتی که خیلی به خودتان مطمئن نیستید از این تابع استفاده نکنید.

یک تابع حرفه ای دیگر:

Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len);

که mem اشاره گر به داده های Raw در حافظه است و len اندازه ان است ( فقط در صورت این که می دانید دارید چه کار می کنید از اینها استفاده کنید )

 

هر وقت کارتان با یک chunk تمام شد می توانید به وسیله دستور Mix_FreeChunk حافظه را خالی کنید.

void Mix_FreeChunk(Mix_Chunk *chunk);

برای تنظیم volume یک chunk هم می توانید از تابع

int Mix_VolumeChunk(Mix_Chunk *chunk, int volume);

استفاده کنید که یک اشاره گر به Chunk میگیرد و volume که از 0  است تا

MIX_MAX_VOLUME که برابر 128 است.

 

 

شرمنده اگر یه چند روز وبلاگ update نکردم!!
چون سخت درگیر نوشتن این بازی ام
باید سریعتر تمومش کنم از امرور باید بیشتر از روزی 4 ساعت رو بازی کار کنم !!!!! O My GOD
قول میدهم تا 25 روز دیگر source code کامل ورژن یک بازی بزارم واسه دانلود.
یه خوبی دیگه این بازی اینه که علاوه بر ویندوز رو لینوکس ام اجرا می شود
و اگر کسی Mac OS X داره می تونه به ما برای پورت کردن این بازی روی OS x کمک کند
و همچنین FreeBSD

خوبی OpenGL و SDL که بر پایه اونه پورتابل بودنشونه از PlayStation 2 گرفته تا سیمبین نوکیا همه گی در دستان شما!

موفق باشید

درس 12 : نوشتن بازیهای تحت شبکه با SDL_net قسمت چهارم

استفاده از UDP :

بازی ها از پرتکل UDP استفاده می کنند چون UDP از TCP سریعتر است و

البته هزینه این سرعت را هم باید پرداخت و این هزینه نامطمئن بودن این پروتکل است

 همانطور که قبلا توضیح دادم.

 

قسمت سرور :

1.اماده کردن کتابخانه SDL_net

2.باز کردن یک سوکت بر روی یک پورت مشخص

3.تخصیص دادن حافظه برای بسته های اطلاعات

4.منتظر ماندن برای بسته های ارسالی از کابران

5.انجام دادن  کاری با بسته ها

6.ازاد کردن حافظه و خروج از برنامه.

 

قسمت کلاینت :

1.اماده سازی کتابخانه SDL_net .

2.باز کردن یک سوکت روی یک پورت قابل استفاده به صورت شانسی .

3.تایین کردن آدرس سرور

4.تخصیص حافظه برای بسته ها

5.پر کردن و فرستادن بسته ها

6.آزاد سازی حافظه و خروج از برنامه

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#include "SDL_net.h"

 

int main(int argc, char **argv)

{

            UDPsocket sd;       /* Socket descriptor */

            UDPpacket *p;       /* Pointer to packet memory */

            int quit;

 

            /* Initialize SDL_net */

            if (SDLNet_Init() < 0)

            {

                        fprintf(stderr, "SDLNet_Init: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا sd همان سوکت سرورمان است و p اشاره گری است

 به بسته حاوی اطلاعات در حافظه است .

            /* Open a socket */

            if (!(sd = SDLNet_UDP_Open(2000)))

            {

                        fprintf(stderr, "SDLNet_UDP_Open: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا هم یک سوکت بر روی پورت 2000 باز می کنیم.

            /* Make space for the packet */

            if (!(p = SDLNet_AllocPacket(512)))

            {

                        fprintf(stderr, "SDLNet_AllocPacket: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا هم 512 بایت حافظه را به بسته های ارصالی تخصیص می دهیم.

            /* Main loop */

            quit = 0;

            while (!quit)

            {

                        /* Wait a packet. UDP_Recv returns != 0 if a packet is coming */

                        if (SDLNet_UDP_Recv(sd, p))

                        {

                                    printf("UDP Packet incoming ");

                                    printf(" Chan:    %d ", p->channel);

                                    printf(" Data:    %s ", (char *)p->data);

                                    printf(" Len:     %d ", p->len);

                                    printf(" Maxlen:  %d ", p->maxlen);

                                    printf(" Status:  %d ", p->status);

                                    printf(" Address: %x %x ", p->address.host, p->address.port);

 

                                    /* Quit if packet contains "quit" */

                                    if (!strcmp((char *)p->data, "quit"))

                                                quit = 1;

                        }                     

            }

 

            /* Clean and exit */

            SDLNet_FreePacket(p);

            SDLNet_Quit();

            return EXIT_SUCCESS;

}

در اینجا هم در صورت دریافت هرگونه بسته اطلاعاتی

 تابع SDLNet_UDP_Recv مقدار غیر صفر بر می گرداند.

و p هم که اشاره گری از نوع UDPpacket است.

ساختار UDPpacket طبق شکل مقابل است.

typedef struct {

    int channel;          /* The src/dst channel of the packet */

    Uint8 *data;          /* The packet data */

    int len;              /* The length of the packet data */

    int maxlen;           /* The size of the data buffer */

    int status;           /* packet status after sending */

    IPaddress address;    /* The source/dest address of an

                                   incoming/outgoing packet */

} UDPpacket;

و اما برای سمت کلاینت :

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#include "SDL_net.h"

 

int main(int argc, char **argv)

{

            UDPsocket sd;

            IPaddress srvadd;

            UDPpacket *p;

            int quit;

 

            /* Check for parameters */

            if (argc < 3)

            {

                        fprintf(stderr, "Usage: %s host port ", argv[0]);

                        exit(EXIT_FAILURE);

            }

           

            /* Initialize SDL_net */

            if (SDLNet_Init() < 0)

            {

                        fprintf(stderr, "SDLNet_Init: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا نیز sd سوکت ما و srvadd ادرس سرور و p اشاره گری از نوع UDPpacket است

و در اول کار چک می کند که حتما دو ارگومان به عنوان ورودی از خط فرمان بگیرد.

            /* Open a socket on random port */

            if (!(sd = SDLNet_UDP_Open(0)))

            {

                        fprintf(stderr, "SDLNet_UDP_Open: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

یک سوکت بر روی یک پورت شانسی ایجاد می کند.

            /* Resolve server name  */

            if (SDLNet_ResolveHost(&srvadd, argv[1], atoi(argv[2])))

            {

                        fprintf(stderr, "SDLNet_ResolveHost(%s %d): %s ", argv[1], atoi(argv[2]), SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

آدرس سرور را در srvadd می ریزد.

 

            /* Allocate memory for the packet */

            if (!(p = SDLNet_AllocPacket(512)))

            {

                        fprintf(stderr, "SDLNet_AllocPacket: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

 

            /* Main loop */

            quit = 0;

            while (!quit)

            {

                        printf("Fill the buffer >");

                        scanf("%s", (char *)p->data);

                       

                        p->address.host = srvadd.host;    /* Set the destination host */

                        p->address.port = srvadd.port;    /* And destination host */

 

                        p->len = strlen((char *)p->data) + 1;

                        SDLNet_UDP_Send(sd, -1, p); /* This sets the p->channel */

 

                        /* Quit if packet contains "quit" */

                        if (!strcmp((char *)p->data, "quit"))

                                    quit = 1;

            }

 

            SDLNet_FreePacket(p);

            SDLNet_Quit();

 

            return EXIT_SUCCESS;

}

سپس کار تخصیص فضا را انجام می دهد و منتظر ورودی کاربر می شود و اطلاعات را به سمت سرور می فرستد.

کلاینت :

 

 شمخخ

سرور :

شسی

درس 12 : نوشتن بازیهای تحت شبکه با SDL_net قسمت سوم

قسمت 3 : ساخت یک سرور و یک کلاینت ساده

 

استفاده از TCP :

اگر شما یک رتباط مطمئن ولی کند می خواهید TCP نیاز شما را تامین می کند (البته این را بدانید که وقتی فایل

مثلا PDF یا ZIP یا ... دانلود می کنید شما از این پروتکل استفاده می کنید )  خصوصیت این ارتباط این است

که در آنور چک می کند که فایل سالم رسیده یا نه و این سرعت کار را پایین می آورد ولی UDP دقیقا عکس این

 خصوصیات را دارد و از این لحاظ به درد انتقال فیلم و صدا می خورد مثلا حذف شدن چند فرم از فیلم برای شما مهم نیست .

در ساخت ساده ترین برنامه کلاینت -  سرور ما  کارهای زیر را انجام می دهیم.

 

ابتدا ساخت قسمت سرور :

1.     اماده سازی کتابخانه SDL_net

2.     انتخاب کردن پورتی که سرور روی ان گوش بایستد.

3.     ساخت سوکتی برای سرور

4.     قبول کردن اتصال ( فرض کنید سرور صاحب خانه است و خونه اش 65536  تا در داره

 و هر در یک شماره دارد و جلوی هر در که بایستد با در های دیگر فاصله زیادی دارد در مرحله

      2 ما در را انتخاب می کنیم که انجا منتظر مهمان می شویم و در این مرحله ما باید  به مهمان ها اجازه ورود بدهیم )

 

5.     دریافت اطلاعات

6.     انجام دادن اعمالی با داده ها (مثلا نمایش انها )

7.     قطع ارتباط و خروج از برنامه

 

در قسمت کلاینت (مشتری {مهمان خودمان} ):

1.اماده کردن کتابخانه SDL_net

2. گرفتن ادرس سرور

3.ساختن یک سوکت

4.گرفتن اطلاعات از کاربر

5.فرستادن اطلاعات

6.قطع ارتباط و خروج

 

البته توجه کنید در این مثال سرور ما فقط یک کانکشن را قبول می کند

( با استفاده از thread ها می توان اتصالات زیادی را مدیریت کرد)

 

#include

#include

#include

 

#include "SDL_net.h"

 

int main(int argc, char **argv)

{

            TCPsocket sd, csd; /* Socket descriptor, Client socket descriptor */

            IPaddress ip, *remoteIP;

            int quit, quit2;

            char buffer[512];

            if (SDLNet_Init() < 0)

            {

                        fprintf(stderr, "SDLNet_Init: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در ابتدا ما متغییرها را تعریف می کنیم

Sd سوکت سرور ما برای انتقال اطلاعات است csd سوکت برای کلاینت هایی است که می ایند

ip ساختار ادرس سرور ماست و remoteIP هم ساختار ادرس کلاینت ماست

quit برای کل برنامه هست

quit2 هم برای این است که کلاینت کارش تمام شد دوباره سرور listen کند

Buffer هم نگه دارنده متنی است که کلاینت برای ما می فرستد.

            /* Resolving the host using NULL make network interface to listen */

            if (SDLNet_ResolveHost(&ip, NULL, 2000))

            {

                        fprintf(stderr, "SDLNet_ResolveHost: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

 

در اینجا با تنظیم کردن پارامتر دوم به NULL و سوم به 2000 مشخص می کنیم

 که ساختار ip باید یک سرور باشد و بر روی پورت 2000 گوش بایستد.

            /* Open a connection with the IP provided (listen on the host's port) */

            if (!(sd = SDLNet_TCP_Open(&ip)))

            {

                        fprintf(stderr, "SDLNet_TCP_Open: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا سوکت سرور را با ادرس ip می سازد و شروع می کند به listen کردن روی پورت2000

            /* Wait a connection, send data and term */

            quit = 0;

            while (!quit)

            {

                        /* This check the sd if there is a pending connection.

                        * If there is one, accept that, and open a new socket for communicating */

                        if ((csd = SDLNet_TCP_Accept(sd)))

                        {

 

حلقه اصلی برنامه و پس از ان تابع SDLNet_TCP_Accept(sd) چک میکند که کلاینتی

 خواهان وصل شدن است و اگر بود او را قبول می کند و یک سوکت جدیدی را برای او اماده می کند

                                    /* Now we can communicate with the client using csd socket

                                    * sd will remain opened waiting other connections */

 

                                    /* Get the remote address */

                                    if ((remoteIP = SDLNet_TCP_GetPeerAddress(csd)))

                                                /* Print the address, converting in the host format */

                                                printf("Host connected: %x %d ", SDLNet_Read32(&remoteIP->host), SDLNet_Read16(&remoteIP->port));

                                    else

                                                fprintf(stderr, "SDLNet_TCP_GetPeerAddress: %s ", SDLNet_GetError());

 

حال ما می توانیم با سوکت csd با کلاینب ارتباط برقرار کنیم

مامی توانیم با استفاده از تابع SDLNet_TCP_GetPeerAddress و دادن سوکت مورد نظر

 ساختار ادرس کلاینت را بگیریم که شامل IP->hostو IP->remoteIP است

quit2 = 0;

                                    while (!quit2)

                                    {

                                                if (SDLNet_TCP_Recv(csd, buffer, 512))

                                                {

                                                            printf("Client say: %s ", buffer);

 

                                                            if(strcmp(buffer, "exit") == 0)     /* Terminate this connection */

                                                            {

                                                                        quit2 = 1;

                                                                        printf("Terminate connection ");

                                                            }

                                                            if(strcmp(buffer, "quit") == 0)    /* Quit the program */

                                                            {

                                                                        quit2 = 1;

                                                                        quit = 1;

                                                                        printf("Quit program ");

                                                            }

                                                }

                                    }

 

                                    /* Close the client socket */

                                    SDLNet_TCP_Close(csd);

                        }

            }

 

            SDLNet_TCP_Close(sd);

            SDLNet_Quit();

 

            return EXIT_SUCCESS;

}

 

تابع SDLNet_TCP_Recv(csd,buffer,512) در صورتی که چیزی از کلاینت دریافت کند

 مقدار غیر صفر بر می گرداند و پارامتر اول که سوکت کلاینت است در buffer هم اطلاعات فرستاده

شده ذخیره می شود و پارامترسوم هم حداکثر مقدار اطلاعاتی است به بایت که باید دریافت کند.

و بعد از ان هم با printf داده فرستاده شده را نمایش می دهد.در صورت وارد کردن quit برنامه سرور بسته می شود

و اگر کلاینت کلمه exit را بفرستد سرور این کلاینت را رها کرده و دوباره به listening  می پردازد.

 

و حالا کد کلاینت :

#include

#include

#include

 

#include "SDL_net.h"

 

int main(int argc, char **argv)

{

            IPaddress ip;                        /* Server address */

            TCPsocket sd;                      /* Socket descriptor */

            int quit, len;

            char buffer[512];

 

            /* Simple parameter checking */

            if (argc < 3)

            {

                        fprintf(stderr, "Usage: %s host port ", argv[0]);

                        exit(EXIT_FAILURE);

            }

            if (SDLNet_Init() < 0)

            {

                        fprintf(stderr, "SDLNet_Init: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

 

 

در اینجا ip ساختار آدرس سرور است و TCPsocket هم سوکتی است که ما با ان می خواهیم

 با سرور ارتباط برقرار کنیم quit برای کنترل خرج از برنامه است و len هم طول رشته ارصالی است

 و buffer هم رشته که می خواهیم بفرستیم. سپس چک می کنیم که کاربر حداقل دو پارامتر را وارد کرده

 باشد یکی ip سرور و دیگری پورت باز بر روی سرور است.

            /* Resolve the host we are connecting to */

            if (SDLNet_ResolveHost(&ip, argv[1], atoi(argv[2])))

            {

                        fprintf(stderr, "SDLNet_ResolveHost: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا هم ip و port سرور را به ساختار IPaddress تبدیل می کنیم.

            /* Open a connection with the IP provided (listen on the host's port) */

            if (!(sd = SDLNet_TCP_Open(&ip)))

            {

                        fprintf(stderr, "SDLNet_TCP_Open: %s ", SDLNet_GetError());

                        exit(EXIT_FAILURE);

            }

در اینجا هم با ساختار ip یک سوکت برای ارتباط با سرور می سازیم
 

            /* Send messages */

            quit = 0;

            while (!quit)

            {

                        printf("Write something: >");

                        scanf("%s", buffer);

 

                        len = strlen(buffer) + 1;

                        if (SDLNet_TCP_Send(sd, (void *)buffer, len) < len)

                        {

                                    fprintf(stderr, "SDLNet_TCP_Send: %s ", SDLNet_GetError());

                                    exit(EXIT_FAILURE);

                        }

 

                        if(strcmp(buffer, "exit") == 0)

                                    quit = 1;

                        if(strcmp(buffer, "quit") == 0)

                                    quit = 1;

            }

 

            SDLNet_TCP_Close(sd);

            SDLNet_Quit();

 

            return EXIT_SUCCESS;

}
در ابتدا با scanf یک رشته را می خواند سپس طول رشته را بدست می اورد و
 سپس با SDLNet_TCP_Send رشته را می فرستد
 و در صورت وارد کردن exit و quit از کلاینت خارج می شود.
شیشسی
شیب

 

درس 12 : نوشتن بازیهای تحت شبکه با SDL_net قسمت دوم

حال قدری عمیق تر به SDL_net نگاه می کنیم .

 

شبیه SDL , SDL_net هم باید قبل از استفاده اماده شود بوسیله تابع SDLNet_Init :

int SDLNet_Init(void);

شما در صورت درست آماده شدن مقدار غیر صفر بر می گرداند.

 

و اما ساختار IPaddress :

typedef struct {

Uint32 host; /* 32-bit IPv4 host address */

Uint16 port; /* 16-bit protocol port */

} IPaddress;

Host به عنوان یک Uint32 تعریف شده و نگه دارنده 4 بایت شناسه یک کامپیوتر است.البته یک مقدار ثابتهایی هم براش در نظر گرفته شده است مثل :

 INADDR_ANY(0) و INADDR_NONE(0xFFFFFFFF)

شما از INADDR_ANY برای سوکت های سرور استفاده می کنید و از INADDRE_NONE هم برای موقعی که نمی خواهید در شبکه دیده شوید.

 

Port هم به عنوان یک Uint16 تعریف شده است که نمایند مدخل ورود و خروج اطلاعات بر روی کامپیوتر است شبیه یک در در نظر بگیرید که روش یه شماره دارد وشماره چون 2 بایتی پس از صفر می تواند باشد تا 65535.

 

حال دو تابعی که با IP ادرس کار می کنند را به شما می گم

اولیش SDLNet_ResolveHost است شما از این تابع برای پیدا کردن IP سرور ی که می خواهید به ان وصل شوید استفاده می کنید مثلا اسمش و دارید gamedev.net می خواهید IP اش را بدانید . یا می توان با این تابع یک سرور بسازیم

int SDLNet_ResolveHost(IPaddress *address, char *host, Uint16 port);

این تابع یک int بر می گرداند در صورت پیش امدن اشکال مقدار صفر بر می گرداند پارامتر اول یک ساختار IPaddress می گیرد و ان را پر می کند پارامتر بعدی یک رشته بع عنوان اسم می گیرد اگر Lan بود مثلا 192.168.0.1 یا اینترنت phyton.org به هر معرفی کننده جایی که می خوایم به ان وصل شویم و پارامتر اخر هم مشخص کننده مدخلی است که سرور برای ما باز گذاشته است و خود داره روش Listen می کند.

وقتی بخواهید یک سوکت سرور بنویسید شما باید host را NULL قرار دهید معادل قرار دادن هاست به INADDR_ANY است

اگر شما IP ماشینی را دارید و اسمش را بخواهید کافی است IP ان را به یک اشاره گر به ساختار IPaddress بدهید وسپس از این تابع استفاده کنید.

char * SDLNet_ResolveIP(IPaddress *ip);

اگر host را به INADDR_ANY ست کنید اسم کامپیوتر خود را بر می گرداند در غیر اینصورت می گردد و اسم را پیدا می کند.

یک sample از اینچیزای که گفتم تا اینجا  (اسم کامپیوتر من Anothername !):

 

#include "sdl/sdl.h"

#include "sdl/sdl_net.h"

#include

int main(int argc,char* argv[])

{

            if(SDL_Init(SDL_INIT_EVERYTHING)==-1)

            {

            return(0);

            }

            atexit(SDL_Quit);

            if(SDLNet_Init())

            {

            return(0);

            }

            atexit(SDLNet_Quit);

            IPaddress ip;

            SDLNet_ResolveHost(&ip,NULL,16);

            fprintf(stdout,"Local Host: %s ",SDLNet_ResolveIP(&ip));

            SDLNet_ResolveHost(&ip,"www.gamedev.net",16);

            fprintf(stdout,"Remote Host: %s ",SDLNet_ResolveIP(&ip));

            return(0);

}

 

 

TCPScocket:

یک TCPsocket یک اشاره گر است به ساختار _TCPsocket :

typedef struct _TCPsocket *TCPsocket;

تعریف ساختار _TCPsocket از برنامه نویس ها مخفی شده است چون نیازی به دانستن کارش نیست اگرم خواستین می توانید source code , SDL_net را از libsdl.org/projects دانلود کنید و ببینید.

قبل از این که بتوانید اطلاعاتی را به کامپیوتر های دیگر بفرستید باید اول یک سوکت باز کنید و اون کامپیوتری که روش سوکت باز می کنید باید یک سوکت سرور هم داشته باشد  پس در یک زمان شما نیازمند باز کردن یک سوکت سرور هم هستید شما با استفاده از یک تابع هر دو تا کار و انجام می دهید.

TCPsocket SDLNet_TCP_Open(IPaddress *ip);

این تابع یک اشاره گر به IP ادرس می گیرد و یک TCPSocket می سازد اگر NULL برگرداند یعنی اشتباهی رخ داده است. اگر ip.host به INADDR_ANY یا INADDR_NONE تنظیم شده باشد یک سوکت سرور می سازد و اگر نه تلاش می کند که به ان سرور وصل شود.

 

معمولا اگر یک سوکت باز کنید بعدا باید ببندینش تابع انجام دادنش هم :

void SDLNet_TCP_Close(TCPsocket sock);

حالا چند کلمه در مورد فرق بین سوکت های سرور و سوکت های کلاینت فرض کنید مثلا بخواهید یک برنامه چت بنویسید که می تونید انتخاب کنید برنامه تون سرور باشد یا به یک سرور وصل شود برای قسمت سرور شما لازمه یک سوکت سرور داشته باشید به تعداد سوکت های کلاینت که به سرور شما وصل شده اند و در نقش یک کلاینت شما فقط نیاز به یک سوکت کلاینت دارید برای ارتباط با سرور, سوکت سرور در حال listen کردن کامپیوترهای دیگه هست که به session اضافه می وند و شما یک سوکت سرور را برای send و receive اطلاعات لازم نداریدوقتی یک سوکت سرور اطلاعاتی اماده خوندن دارد شما از تابع SDLNet_TCP_Accept استفاده می کنید.

TCPsocket SDLNet_TCP_Accept(TCPsocket server);

این تابع یک سوکت TCP می گیرد و حتما هم باید از نوع سرور باشد و یک TCPSocket بر می گرداند که مقدار برگشتی یک کانکشن است به کامپیوتر مقصد که از SDLNet_TCP_Open استفاده کرده بودیم که با ان ارتباط بر قرار کنیم.

بعد از اینکه این کار را برای اتصال به یک ماشین جدید کردیم می توانیم IP کامپیوتر را به وسیله تابع SDLNet_GetPeerAddress بدست اوریم.

IPaddress * SDLNet_TCP_GetPeerAddress(TCPsocket sock);

و تابعی دیگر که برای ارصال اطلاعات از ان استفاده می شود SDLNET_TCP_SEND است.

int SDLNet_TCP_Send(TCPsocket sock, void *data, int len);

این تابع یک TCPsocket غیر سرور می گیرد یک Void * هم که اشاره دارد به دادهای ما و طول داده ومقدار داده ای که فرستاده شد را بر می گرداند که اگر برابر باlen نباشد یعنی خطایی رخ داده است.

تابعی هم برای  دریافت اطلاعات هست :

int SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen);

که مثل بالای است ومقدار بازگشتی 1 یعنی کار درست انجام شده است.

باور بکنید یا نه SDL_net با 8 تابع و 2 ساختار می تواند تقریبا هر نوع برنامه شبکه ای را بنویسد.

 

SDL_net توابعی برای UDP هم دارد که من در اینجا توضیح نمی دم چون فکر می کنم TCP همه کارهای شما را می تواند راه بیاندازد.

 

یک ساختار SDLNET_SocketSet هم هست برای نگه داری ارایه از سوکت ها اول باید با دستور SDLNET_AllocSocketSet اماده شود.

Net_SocketSet SDLNet_AllocSocketSet(int maxsockets);

برای add کردن سوکت :

int SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);

برای پاک کردن سوکت :

int SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);

و دستور :

int SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout);

 

Resource Focus On SDL

ISBN :  1592000304 

درس 12 : نوشتن بازیهای تحت شبکه با SDL_net قسمت اول

درس 12 : نوشتن بازیهای تحت شبکه با SDL_net

 

در این درس شما  با SDL_net اشنا می شوید کتابخانه الحاقی مورد علاقه من. حتی اگر با برنامه نویسی شبکه اشنا نیستید می توانید sdl_net را به عنوان یک کتابخانه بشناسید ولی پیشنهاد من این است که قبل از خواندن این بخش اطلاعاتی در مورد شبکه پیدا کنید

 

همانطور که با دانستن اینکه چگونه می توانید یک pixel را در صفحه مقدار دهی کنید می توانید تمام دنیای گرافیک (بر روی مانیتور خود) را در دستان خود بگیرید از 2 بعدی تا 3 بعدی (3 بعدی هم نوعی دو بعدی است بر روی مانیتور)  در شبکه هم وقتی فهمیدید چگونه می توانید packet (بسته)(منظور چند بایتی است که به عنوان اطلاعات در نظر گرفته می شود) را از کامپیوتری به کامپیوتر دیگر منتقل کنید تمام دنیای شبکه در دستان شماست!! .

 

مقدمات برنامه نویسی شبکه :

چند تا چیز هست که قبل از شروع با SDL_net باید یاد بگیرید. اگر شما معتاد اینترنت باشید یا با بازیهای تحت اینترنت خودتون سرگرم میکنید ( مثل من!) بعضی واژه ها باید واسه شما اشنا باشد.

 

اولیشون واژه IP , IP مخفف کلمه Internet Protocol و فقط مشخص کننده یک راه استاندارد و مورد تایید همه است برای مشخص کردن یک کامپیوتر بر روی اینترنت است ip شما مجموعه ای از 4 شماره است که با نقطه از هم جدا شدند شبیه 127.0.0.1 هر شماره بین دو عدد 0 تا 255 است این به این معنی که هر IP ادرس حاوی 4 بایت است هر عدد یک بایت IP شما مشخص کننده ادرس شماست بر روی شبکه واین که شما کجای این کره خاکی هستید.

 

دومی اش واژه Socket است یک سوکت یک کانکشن ساده است است از یک کامپیوتر به کامپیوتر دیگر یا از یک IP به IP دیگر بر قرار می شود . سوکت به شما اجازه می دهد که با کامپیوتر های دیگر ارتباط برقرار کنید و به گشت و گزار در اینترنت بپردازید ( راستی این اینترنت ملی چی شد!؟ )

 

2 تا واژه بعدی واژه هم Client و Server هستند اینها هم در اصل مشخص می کند کامپیوتر شما چه رولی را در بازی ایفا می کند معمولا پشت سرور های بازی کسی نمی شیند و بازیکنا پشت کلاینت می شینند. نقش سرور پروسس کردن وعکس العمل نشان دادن نسبت به اطلاعاتی است که کاربران می فرستند client هم همان کامپیوتر کاربر هست که اطلاعات به سرور ارسال میکند

 

کلمه اخر هم کلمه Host است هاست ماشینی است که اطلاعات ارسالی از کلاینت ها را در خود هاست می تواند خود یک کلاینت باشد.

 

خوب حالا می رویم به سراق اینکه چگونه شبکه ها کار می کنند ساده ترین نوع شبکه شبکه ایست که در زیر می بینید که شامل دو کامپیوتر A و B است که به هم وصل شده اند

Yoho

 

در شکل بالا AوB می توانند هر دو یک سرور باشند یا کلاینت باشند وفقط یک اتصال بین این دو است که تمام نیاز هارا بر طرف می کند برای سادگی فرض می کنیم هر دو کلاینت باشند بنابر این یک ارتباط P2P (Peer-to-Peer ) یا نقطه به نقطه به ما می دهد که عبارت است یک ارتباط ساده که سروری در ان وجود ندارد شما می تونید کامپیوتر های بیشتری هم به این شبکه اضافه کنید مثل شکل زیر که شدند 4 تا کامپیوتر A,B,C و   D

 

 

همانطور که می بینید هر کامپیوتر با 3 کامپیوتر دیگر ارتباط جداگانه ای دارد بوسیله 6 کانکشن جدا از هم هر کامپیوتر دیگری که به این 4 تا اضافه شود کانکشن های زیادی باید به این شبکه باید اضافه شود پس به ازای N تا ماشین N * (N-1) / 2  تا کانکشن لازم داریم مثلا 20 تا ماشین می شود 190 تا کانکشن پس واسه تعداد بالا این مدل جواب نمی دهد .

  

برای شبکه های بزرگتر بهترین کار استفاده از مدل کلاینت سرور هست مثل عکس زیر :

alooo

 

در دریاگرام بالا کامپیوتر A به عنوان سرور است و بقیه کلاینت هستند . کلاینت اطلاعات به کامپیوتر A می فرستد و سرور هم اطلاعات بقیه کامپیوتر ها را در صورت لزوم Update می کند در اینجا 6 کامپیوتر هست و 5 کانکشن اگر Peer to peer بود می شد 15 تا کانکشن.

ولی از این طرف هم برای ارصال یک پیغام از ماشینی به ماشین دیگر باید یک بار  به سرور فرستاده شود و دفعه بعد به ماشین دیگر که می شود 2 تا .  قبلی بود یکی . که این یک مقدار کارایی را پایین می اورد البته در اینجا هاست ما کامپیوتر سرور است اگر خراب شود کل شبکه از کار می افتد به هر حال در این نوع شبکه بندی کامپیوتر های سرور باید قوی انتخاب شوند

 

شما باید نوع خودتون را انتخاب کنید برای بازیهای که دو نفر تحت اینترنت یا شبکه محلی به هم وصل می شوند مثل بیلیارد بهترین انتخاب P2P است و بازیهای مثل counter-strike بهتره از نوع کلاینت سرور باشند .

 

نصب :

برای نصبش هم به درس 3 مراجعه کنید مثل SDL_image است

از قسمت پروژه ها دانلود کنید

http://libsdl.org/projects

 

SDL_net در یک نگاه:

4 قسمت جداگانه در SDL_net وجود دارد که هر کدام ساختاری جدا گانه ای دارد این 4 تا عبارتند از : آدرس IP  ,   سوکت TCP , سوکت UDP و Set Socket .

 

IP Address:

مشخص کننده آدرس کامپیوتر هایی است که با هم ارتباط برقرار می کنند هر کامپیوتر با یک IP   که یک شناسه 4 بایتی است و port هم که یک شناسه 16 بیتی است ( که مشخص کننده این است که از طریق کدام مدخل باید ارتباط برقرار شود مثلا مدخل 80 یا همان پورت 80 ) شناخته می شود

که در SDL_net با استراکچر IPadreess شناخته می شود.

 

TCP Socket:

شما از سوکت TCP (Transfer Control Protocol ) برای ساخت یک کانکشن بین دو کامپیوتر استفاده می کنید TCP یک پروتکل ( شما فکر کنید یک زبان استاندارد مثل عربی ) برای انتقال اطلاعات است ما دو نوع سوکت TCP داریم سرور و کلاینت . استفاده از پرتکل TCP تضمین می کند که اطلاعاتی که شما می فرستید حتما به مقصد درست می رسد سوکت TCP بوسیله ساختار TCPSocket تعریف می شود که بیشتر به عنوان یک اشاره گر تعریف می کنیم.

 

UDP Socket:

UDP(User Datagram Protocol) سوکتی شبیه به سوکت TCP است.فرقشان این است که تضمین نمی کند که اطلاعات سالم برسند ولی سرعت بیشتری دارند چون اطلاعات چک نمی شوند.

مثلا واسه ارسال ویدئو مناسب است. با ساختار UDPSocket شناسایی می شود و معمولا به عنوان اشاره گر تعریف می شوند.

 

Socket Sets:

عبارت است از مجموعه از سوکت ها و معمولا توسط سرور ها استفاده می شود برای دریافت اطلاعات ورودی از کلاینت ها و بوسیله ساختار SDLNet_SocketSet شناسایی می شوند

 و معمولا به عنوان اشاره گر تعریف می شوند.