درس ۱۳ : قسمت سوم

Music :

کار با موزیک در SDL_Mixer متفاوت از کار با صداهای معمولی است موزیک بر روی کانالها پخش نمی شود (در اصل خود برای خود یک کانال مخصوص دارد)

و هیچکدام از توابع کار با صدا با ان کار نمی کند ووقتی بخواهیم با موزیک کار کنیم باید با ساختار Mix_Music کار کنیم وهمیشه هم بوسیله اشاره گراز ان استفاده می کنیم.

 

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

 

برای بارگذاری یک موزیک می توانید از تابع :

Mix_Music *Mix_LoadMUS(const char *file);

این تابع یک رشته را به عنوان اسم فایل دریافت می کند و یک اشاره گر به شی Mix_Music برمی گرداند. در صورتی که مقدارNULL برگرداند یعنی اشتباهی رخ داده است.فعلا Mixer قادر به پخش فایل های Mp3,VOC,OGG,MOD,MIDI,Wave,IT,XM,S3M,669,AIFF هست.

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

void Mix_FreeMusic(Mix_Music *music);

یک اشاره گر به Mix_Music می گیرد و حافظه را خالی می کند.  مثال :

// load the MP3 file "music.mp3" to play as music

Mix_Music *music;

music=Mix_LoadMUS("music.mp3");

if(!music) {

  printf("Mix_LoadMUS("music.mp3"): %s ", Mix_GetError());

  // this might be a critical error...

}

Mix_FreeMusic(music);

music=NULL;

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

int Mix_PlayMusic(Mix_Music *music, int loops);

که یک اشاره گر به شیMix_Music می گیرد و همچنین مقدار تکرار را در پارامتر دوم که در صورت وارد کردن 1- بی نهایت بار و 0 هیچ بار پخش می شود.

 

دو تابع هم برای Fade in کردن :  

int Mix_FadeInMusic(Mix_Music *music, int loops, int ms);

int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position);

که ms مقدار زمان fade in هست و Position نقطه شروع Fade in است.

 

شبیه کانال ها شما می توانید عملیات pause,play,stopرا بر روی music انجام دهید هم چنین مکان پخش موزیک را تغییر دهید.همچنین می توانید از یک player خارجی برای پخش فایل های مختلف استفاده کنید.

 

ابتدا دو تابع Mix_PauseMusic و Mix_ResumeMusic را مورد بررسی قرار می دهیم.

void Mix_PauseMusic();

void Mix_ResumeMusic();

که کارشان بسیار واضح است.

اگر شما بخواهید یک موزیک را دوباره از ابتدا پخش کنید باید از تابع :

void Mix_RewindMusic();

استفاده کنید.

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

int Mix_SetMusicPosition(double position);

استفاده کنید که برای فرمت های مختلف استفاده از ان فرق دارد.

مثلا برای Ogg ثانیه مورد نظر را پخش می کند برای Mp3 تعداد ثانیه های جلو تر از مکان فعلی را پخش میکند ( به عقب بر نمی گردد ) برای MOD شماره pattern مورد نظر را پخش می کند و...

مثال : یک دقیقه اول اهنگ Mp3 را پخش نمی کند.

Mix_RewindMusic();

if(Mix_SetMusicPosition(60.0)==-1) {

        printf("Mix_SetMusicPosition: %s ", Mix_GetError());

}

اگر می خواهید volume را کم زیاد کنید باید از تابع :

int Mix_VolumeMusic(int volume);

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

 

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

int Mix_SetMusicCMD(const char *command)

برای مثال ما از mpg123 برای پخش استفاده می کنیم توجه داشته باشید Player حتما باید بتواند از خط فرمان پارامتر بگیرد.

Mix_Music *music=NULL;

if(Mix_SetMusicCMD("mpg123 -q")==-1) {

        perror("Mix_SetMusicCMD");

} else {

        // play some mp3 file

        music=Mix_LoadMUS("music.mp3");

        if(music) {

        Mix_PlayMusic(music,1);

        }

}

 

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

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

int Mix_HaltMusic();

int Mix_FadeOutMusic(int ms);

که ms در دومی مدت زمان fade out است.

 

همانند کانال ها در اینجا نیز توابعی برای بدست اوردن اطلاعات از وضع کنونی وجود دارد.

 

مثلا این که موزیک مورد نظر از چه نوع فرمتی است که برای تغییر مکان درست در موزیک مناسب است.

Mix_MusicType Mix_GetMusicType(const Mix_Music *music);

که مثال ان در زیر امده است

 

switch(Mix_GetMusicType(NULL))

{

        case MUS_NONE:

        MUS_CMD:

          printf("Command based music is playing. ");

          break;

        MUS_WAV:

          printf("WAVE/RIFF music is playing. ");

          break;

        MUS_MOD:

          printf("MOD music is playing. ");

          break;

        MUS_MID:

          printf("MIDI music is playing. ");

          break;

        MUS_OGG:

          printf("OGG music is playing. ");

          break;

        MUS_MP3:

          printf("MP3 music is playing. ");

          break;

        default:

          printf("Unknown music is playing. ");

          break;

}

3 تابع دیگر از این دست نیز وجود دارند.

int Mix_PlayingMusic();

int Mix_PausedMusic();

Mix_Fading Mix_FadingMusic();

که کارشان واضح است و مشخص می کنند که یک موزیک در حال pause است یا play و fade شده است یا نشده است.

 

 

درس 13 : قسمت دوم

Channels :

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

 

تخصیص کانال ها :

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

int Mix_AllocateChannels(int numchans);

این تابع تعداد کانال هایی که شما می خواهید تخصیص دهید را می گیرد و کانال های تخصیص یافته را بر می گرداند شما می توانید چند بار این تابع را فراخوانی کنید واگر مقدار کانال هارا کمتر بدهید کانال های اضافی متوقف می شوند مقدار صفر همه کانال ها را متوقف میکند.

 

پخش کانال ها :

تمام کار SDL_Mixer پخش صدا و میکس کردن درست ان است. Mixer 4 تابع برای پخش صداها دارد که اولین ان ها تابع Mix_PlayChannel است.

int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops);

این تابع 3 پارامتر می گیرد پارامتر اول شماره کانال است که اگر 1- وارد کنید Mixer می گردد و یک کانال خالی برای شما پیدا می کند پارامتر دوم یک اشاره گر به Mix_Chunk می گیرد که صدای مورد نظر ما است و پارامتر سوم تعداد تکرار را مشخص می کند که در صورت وارد کردن 0 یک بار و 1- بی نهایت بار اجرا می شود.

شما می توانید بوسیله تابع Mix_PlayChannelTimed مقدار خاصی از یک اهنگ را به میلی ثانیه پخش کنید.

int Mix_PlayChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ticks);

پارامترچهارم ticks مقدار زمان به میلی ثانیه است که اگر 1- وارد شود تا اخر اهنگ پخش می شود یک راه دیگر برای پخش کردن صدا استفاده از effect fade in است که به وسیله تابع Mix_FadeInChannel می توان اینکار را انجام داد.

int Mix_FadeInChannel(int channel, Mix_Chunk *chunk, int loops, int ms);

این تابع 4 پارامتر می گیرد که سه تای اولی مثل قبل است و پارامتر اخری هم مقدار زمان به میلی ثانیه است است که چه قدر طول بدهد که از Volume 0 به حداکثر Volume برسد.

یک تابع دیگر در این مورد که مقدار زمان پخش را تعیین می کند

int Mix_FadeInChannelTimed(int channel, Mix_Chunk *chunk, int loops,

int ms, int ticks);

فقط پارامتر پنجم  اضافه شده است که ان هم تایین کننده مقدار زمان پخش است.

 

Pause و Resume

هر API خوبی که برای پخش صدا وجود دارد باید 2 تابع Pause و Resume داشته باشد

void Mix_Pause(int channel);

void Mix_Resume(int channel);

یک کانال را به عنوان پارامتر می گیرد و Pause  یا Resume میکند در صورت وارد کردن 1- روی تمام کانال ها اثر می گذارد.

 

Stop

Mixer 4 تابع برای stop دارد که اولیش تابع Mix_HaltChannel است

int Mix_HaltChannel(int channel);

این تابع هم شماره کانال را می گیرد و در صورت وارد کردن 1- همه کانال ها را متوقف می کند.

تابع بعدی هم با تاخیر کانال را متوقف میکند

int Mix_ExpireChannel(int channel, int ticks);

که ticks مقدار تاخیر به میلی ثانیه است.

همچنین شما می توانید یک صدا را در کانال Fade out  کنید به وسیله :

int Mix_FadeOutChannel(int which, int ms);

که ms مقدار زمان تاخیر است.

و تابع اخر

void Mix_ChannelFinished(void (*channel_finished)(int channel));

که یک اشاره گر به تابع می گیرد به منظور این که بداند وقتی یک کانال متوقف شد چه تابعی را اجرا کند در مستندات mixer ذکر شده که هیچوقت از تابع های خود mixer و همچنین تابع SDL_LockAudio در این تابع استفاده نکنید.

 

توابعی در Mixer وجود دارد برای یافتن اطلاعاتی در مورد کانال ها وجود دارد.

مثل :

int Mix_Playing(int channel);

این تابع شماره کانال را می گیرد و در صورتی که کانال در حال پخش باشد مقدار 1 برمی گرداند در غیر اینصورت 0 و در صورتی که مقدار 1- را به تابع بدهیم تعداد کانال های که در حالت پخش هستند را می گرداند.

int Mix_Paused(int channel);

این تابع هم می گوید ایا کانال pause شده است یا نشده است در صورت وارد کردن 1- تعداد کانال های pause شده را بر می گرداند.

 

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

Mix_Fading Mix_FadingChannel(int which);

این تابع شماره کانال را می گیرد و3 مقدار ثابت بر می گرداند MIX_NO_FADING به معنی اینکه کانال fade نشده است 

MIX_FADING_OUT به معنی اینکه کانال fade out شده است

MIX_FADING_IN به معنی اینکه کانال fade in شده است

 

و تابعی دیگری هم برای اینکه بفهمیم چه chunk در حال اجرا است موجود است.

Mix_Chunk* Mix_GetChunk(int channel);

باید کانال مورد نظر در حالت play باشد.

 

Groups :

به نظر هوشمندانه می اید که کانال های صدا را گروه بندی کنید مثلا یک گروه برای effect های انفجار یک گروه برای صداهای بازیکنان و ...

 

برای اینکار اول باید به وسیله تابع

int Mix_ReserveChannels(int num);

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

سپس با تابع

int Mix_GroupChannel(int which, int tag);

به صورت دستی صدا ها را گروه بندی کنیم which مشخص کننده کانال مورد نظر و tag مشخص کننده گروه مورد نظر است مثل مثال زیر :

Mix_ReserveChannels(8);//reserve eight channels

Mix_GroupChannel(0,1);//group 1 is explosion sounds

Mix_GroupChannel(1,1);

Mix_GroupChannel(2,1);

Mix_GroupChannel(3,1);

Mix_GroupChannel(4,2);//group 2 is players sounds

Mix_GroupChannel(5,2);

Mix_GroupChannel(6,2);

Mix_GroupChannel(7,2);

یا از تابع زیر استفاده کنید :

int Mix_GroupChannels(int from, int to, int tag);

که from مشخص کننده کانال شروع است و to کانال پایانی است و tag هم شماره گروه معادل دستور بالا دستور زیراست.

Mix_ReserveChannels(8);//reserve eight channels

Mix_GroupChannels(0,3,1);// group 1 is explosion sounds Mix_GroupChannels(4,7,2);// group 2 is players sounds

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

int Mix_GroupCount(int tag);

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

اگر 1- وارد کنیم تعداد کل کانال ها را بر می گرداند.

تابع :

int Mix_GroupAvailable(int tag);

این تابع تعداد کانال های قابل استفاده در گروه را بر میگرداند.اگر هیچ کدام قابل پخش نبودند مقدار 1- را برمیگرداند.

بالاخره دو تابع بدرد بخور که مسئولیت توقف همه اعضا موجود در گرئه را دارند.

int Mix_FadeOutGroup(int tag, int ms);

شماره گروه را میگیرد و ms هم زمان fade out به میلی ثانیه است

و تابع:

int Mix_HaltGroup(int tag);

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

 

درس 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 از کلاینت خارج می شود.
شیشسی
شیب