- Menciptakan dan manajemen sesi permainan.
- Manajemen pemain dan grup dalam sesi.
- Pengiriman data antarpemain dalam sesi.
- Koneksi melalui lobby. Komunikasi suara antara pemain dalam sesi.
- Lebih sederhana karena tidak perlu menyediakan kode aplikasi yang berbeda. Tiap peer memiliki kode aplikasi yang sama dengan peer lain, termasuk peer yang bertindak sebagai host.
- Jika tidak diimplementasikan dengan benar, model ini berpotensi menghambat kinerja, terutama karena tiap peer yang bergabung menyebabkan jumlah trafik jaringan juga akan meningkat sangat drastis sehingga hanya cocok untuk aplikasi multiplayer dengan jumlah pemain sedikit.
- Keamanan lebih sulit dikontrol karena tiap peer dapat berkomunikasi langsung dengan peer lain.
- Model client-server menghasilkan skalabilitas yang lebih baik. Jika jumlah klien bertambah, jumlah trafik data naik secara linier karena tiap klien hanya berkomunikasi dengan server.
- Karena aplikasi server dan client terpisah, server dapat dibuat menggunakan sistem yang berdaya tinggi dan fokus ke bagaimana menangani pemrosesan logika, sedangkan aplikasi klien bisa dibuat fokus pada grafik dan suara yang menawan.
- Keamanan lebih baik karena tiap klien hanya berkomunikasi dengan server, sehingga lebih mudah dikontrol.
- Memudahkan penyusunan dunia dalam game, karena terpusat di server. Update dan bug fix terhadap kode yang menyusun game lebih mudah.
- Butuh usaha dan perencanaan lebih panjang.
- Lebih rumit karena aplikasi harus dibuat terpisah, untuk server dan client.
GUID pada DirectPlay digunakan untuk identifikasi aplikasi. Jika membuat aplikasi baru, sobat perlu membuat GUID baru untuk aplikasi tersebut. Sobat bisa membuat GUID pada saat runtime, namun hal ini tidak perlu. sobat bisa menghasilkan GUID di Delphi dengan menekan kombinasi keyboard shortcut Shift+Ctrl+G.
Operasi yang sobat kerjakan menggunakan DirectPlay dapat dibuat asynchronous atau synchronous. Pada operasi asynchronous, DirectPlay akan menjalankan operasi yang diminta dan segera mengembalikan kontrol ke aplikasi tanpa menunggu operasi tersebut selesai. DirectPlay akan mengembalikan nilai DPNSUCCESS_PENDING pada operasi asynchronous yang belum selesai.
Operasi asynchronous membuat aplikasi lebih responsif, namun prosesnya lebih rumit. Pada operasi synchronous, DirectPlay akan menunggu operasi hingga selesai baru kemudian mengembalikan kontrol ke aplikasi. Selama menunggu operasi selesai, aplikasi kita akan terlihat “hang” dan tidak responsif, namun prosesnya lebih sederhana.
Data Secara default, DirectPlay tidak menjamin data yang sobat kirim akan diterima oleh penerima. Paket data mungkin hilang pada saat dikirimkan ke jaringan dan ini sering kali terjadi. Untuk data yang tidak boleh hilang, sobat dapat memilih menggunakan pengiriman terjamin. Pada mode pengiriman terjamin, selama pemain masih terkoneksi, DirectPlay akan berusaha mengirim ulang paket-paket data yang drop sampai data lengkap terkirim.
Sisi buruknya, performa menurun karena membutuhkan waktu lebih banyak, terutama bila banyak paket data yang hilang. Pengiriman data juga tidak dienkripsi. Sobat dapat juga mengenkripsi data yang sobat kirim untuk mengamankan data tersebut. Enkripsi data juga menurunkan performa karena DirectPlay harus melakukan enkripsi data ketika mengirim dan dekripsi ketika menerimanya. Untungnya bagi kita, untuk memastikan data terkirim dan mengenkripsi data sangat mudah. Hanya masalah mengatur flag-flag yang sesuai.
Karena topik DirectPlay ini cukup luas, di artikel ini, admin akan membatasi pada penjelasan bagaimana menciptakan sesi peer-to-peer. Untuk menciptakan IDirectPlay8Peer, sobat menggunakan CoCreateInstance() yang dideklarasi di unit ActiveX.pas. Sebelum memanggil CoCreateInstance(), sobat perlu memastikan COM telah diinisialisasi dengan CoInitialize(). Contoh bagaimana mendapatkan instance IDirectPlay8Peer adalah seperti Listing 1.
Jika sukses, FPeerNet, seperti yang tercantum pada Listing 1, akan diisi dengan alamat ke instance IDirectPlay8Peer. Jika gagal, maka FPeerNet berisi nil. Bila sukses, sobat bisa melanjutkan proses inisialisasi peer-to-peer dengan memanggil metode Initialize() milik IDirectPlay8Peer (Listing 2).
Fungsi ini berguna untuk mendaftarkan fungsi callback yang akan dipanggil tiap kali instance IDirectPlay8Peer menerima pesan. Parameter pvUserContext adalah data yang akan dikirimkan melalui message handler yang alamatnya ada pada parameter pfn. dwFlags adalah flag inisialisasi seperti yang tercantum pada gambar dibawah ini.
Karena semua pesan yang dikirim jaringan akan melalui callback ini, fungsi callback ini sangat vital menentukan performa aplikasi sobat. Sangat tidak disarankan untuk meletakkan kode yang eksekusinya lambat dalam callback ini. Parameter pvUserContext pada Listing 3 adalah pointer ke data milik aplikasi kita. Isi parameter ini sama dengan isi parameter pvUserContext yang kita set pada saat pemanggilan Initialize() (Listing 2).
Parameter dwMessageType adalah tipe pesan yang dikirim DirectPlay ke aplikasi kita. Tabel 2 berisi tipe-tipe pesan yang dikirim ke aplikasi. Kita akan membahas bagaimana meng-handle beberapa pesan umum pada artikel bagian ke-3 di bagian “Menangani Pesan Jaringan”. Parameter pMessage adalah pointer yang menunjuk ke struktur data yang terkait dengan sebuah pesan.
Penamaan struktur data terkait sebuah pesan, sama dengan nama tipe pesannya, kecuali bahwa DPN_MSGID diganti dengan DPNMSG. Contoh, pesan DPN_MSGID_CREATE_PLAYER terkait dengan struktur data DPNMSG_CREATE_PLAYER. Sesuai konvensi di Delphi, struktur data tersebut dinamai ulang menjadi TDPNMsgCreatePlayer. Membuat callback untuk menangani pesan dari DirectPlay cukup mudah.
Sobat tinggal mendeklarasikan sebuah fungsi bertipe TFNDPNMessageHandler dan memproses masing-masing data berdasarkan tipe pesannya. Callback ini, meskipun sobat yang membuat implementasinya, tidak dipanggil oleh aplikasi sobat secara langsung. DirectPlay yang akan memanggil callback ini ketika pesan dari jaringan perlu diproses aplikasi.
Yang sobat harus pastikan ketika menyusun implementasinya adalah fungsi tersebut harus berupa fungsi biasa (bukan fungsi bagian dalam sebuah kelas), karena cara melewatkan parameternya berbeda, dan jangan lupa menggunakan direktif stdcall. Kecuali untuk tipe pesan tertentu, biasanya sobat cukup mengembalikan nilai S_OK sebagai return value.
Contohnya deklarasi callback seperti pada Listing 4. Fungsi callback akan dipanggil dalam thread yang terpisah dari thread aplikasi, sehingga fungsi callback ini harus threadsafe. Sobat mungkin perlu membuat mekanisme untuk mengontrol akses terhadap resource dalam callback ini.
Manajemen thread-thread tersebut ditangani oleh DirectPlay dan untuk tingkat tertentu, transparan bagi aplikasi kita. Namun karena thread utama aplikasi dan thread yang diciptakan DirectPlay berada dalam satu proses, maka thread-thread tersebut juga berada dalam memory yang sama. Oleh karena itu, threadthread tersebut harus berbagi resource di memory ini.
Hal ini bisa menjadi masalah. Katakanlah sobat memiliki dua thread yang harus mengakses sebuah variabel yang sama. Jika sebuah thread tersebut sedang membaca variabel tersebut dan thread lainnya berusaha menulis data ke variabel yang sama pada saat bersamaan, hasilnya mungkin tidak seperti yang diinginkan.
Untuk memecahkan masalah ini, kita bisa menggunakan critical section untuk melindungi suatu resource agar tidak dapat diakses oleh thread lain. Tiap kali sebuah thread hendak mengeksekusi bagian kode yang mengakses resource yang juga diakses oleh thread lain, akses ke resource tersebut kita blok dengan menguncinya.
Selama masih kita kunci dengan critical section, thread dapat dengan bebas memanipulasi isi resource tersebut. Thread lain yang hendak mengunci bagian kode yang ditandai sebagai critical section harus menunggu sampai critical section dibebaskan. Segera setelah selesai, penguncian kita buka, sehingga thread lain dapat menggunakan resource tersebut.
Untuk masuk dan keluar critical section, kita menggunakan EnterCriticalSection() dan LeaveCriticalSection() (Listing 5). Variabel csData pada Listing 5 bertipe TRTLCriticalSection. Sebelum digunakan, csData harus diinisialiasasi menggunakan InitializeCriticalSection(). Setelah critical section tidak lagi dibutuhkan, bisa dihapus dengan memanggil DeleteCriticalSection().
Apa yang kode sobat lakukan di antara pemanggilan EnterCriticalSection() dan LeaveCriticalSection() sebisa mungkin dipersingkat. Jika tidak, critical section bisa menghambat eksekusi thread lain sehingga menyebabkan turunnya performa aplikasi secara keseluruhan. Hal lain yang perlu dihindari adalah meletakkan critical section dalam critical section lain.
Sobat sebaiknya menghindari pemanggilan EnterCriticalSection() pada Listing 6. Kode pada Listing 6 sangat berpeluang menyebabkan deadlock. Jika thread A mencoba memasuki critical section csData2 yang sebelumnya sudah dimasuki thread B dan pada saat bersamaan thread B berusaha memasuki critical section csData yang sebelumnya sudah dimasuki thread A, kedua thread akan saling mengunci sehingga terjadilah deadlock.
Untuk bisa berkomunikasi dengan komputer-komputer lain, tiap komputer harus memiliki alamat unik. Alamat komputer di mana aplikasi berjalan disebut sebagai alamat device (device address), sedangkan untuk alamat komputer yang hendak dituju diistilahkan sebagai alamat host (host address).
DirectPlay menggunakan string URL untuk mengacu pada alamat suatu komputer yang dibungkus dalam interface IDirectPlay8Address. Untuk memanipulasi URL, sobat dapat menggunakan metode-metode yang disediakan interface ini.
Listing 7 berisi contoh bagaimana mendapatkan alamat instance IDirectPlay8Address. Kegunaan lain interface ini adalah untuk memilih service provider yang diinginkan menggunakan metode SetSP().
Deklarasinya adalah seperti Listing 8. pguidSP berisi GUID service provider. Sobat bisa menggunakan nilai-nilai yang sudah tertentu seperti tercantum pada Tabel 3.
Untuk tiap-tiap service provider, sobat mungkin perlu mengubah beberapa parameter terkait protokol yang dipilih.
Untuk TCP/IP atau IPX, sobat mungkin perlu mengubah port yang digunakan, atau untuk koneksi melalui serial, sobat mungkin ingin mengubah baud rate, stop bits, parity, dan fl ow control perangkat serial. Untuk mengubah parameter service provider, sobat menggunakan metode AddComponent() milik interface IDirectPlay8Address (Listing 9). pwszName berisi nama komponen yang akan ditambahkan.
Jika sudah ada, nilainya akan diubah. Sobat bisa menggunakan beberapa nilai-nilai terdefinisi seperti pada Tabel 4.
lpvData berisi pointer ke data. dwDataSize berisi ukuran data lpvData, sedangkan tipenya ditentukan parameter dwDataType. Tabel 5 berisi daftar tipe-tipe data. Listing 10 berisi contoh bagaimana mengubah port untuk TCP/IP.
FAddrNet diasumsikan bertipe IDirectPlay8Address dan FPort adalah variabel bertipe Dword yang menyimpan data nomor port yang diinginkan.
Informasi mengenai peer seperti nama dan data yang kita asosiasikan dengan suatu player perlu diset untuk identifi kasi bagi peer lain. Sobat harus mengatur deskripsi peer sebelum membuat sesi atau bergabung ke sesi yang sudah ada. Untuk mengatur deskripsi peer, sobat memanggil SetPeerInfo() milik IDirectPlay8Peer. Deklarasi fungsi ini ada pada Listing 11.
Deklarasi tipe TDPNPlayerInfo tercantum pada Listing 12. Field-fi eld tipe data ini adalah :
- dwSize, ukuran struktur data ini. Field ini harus sobat isi dengan sizeof(TDPNPlayerInfo).
- dwInfoFlags, flag berisi data apa yang dikandung. Ada dua nilai untuk flag ini :
1. DPNINFO_NAME, jika flag ini digunakan, field pwszName berisi data yang valid.
2. DPNINFO_DATA., field pvData berisi data yang valid.
- pwszName, nama player dalam format Unicode.
- pvData, data player.
- dwDataSize, ukuran yang tersimpan dalam pvData.
- dwPlayerFlags, flag status player apakah sebagai host atau player biasa. Isinya salah satu dari nilai berikut :
1. DPNPLAYER_LOCAL, player biasa.
Di sini kita perlu mengonversi string ke widestring agar dapat kita typecast ke PWideChar. Untuk mendapatkan nama player dengan akurat, sobat perlu memanggil GetPeerInfo() dua kali. Pertama untuk mendapatkan ukuran data player dan kedua untuk mengambil data player. Listing 14 berisi contoh bagaimana mendapatkan nama player menggunakan ID player (DPNID).
Di di bagian pertama artikel ini, sobat sudah memperoleh informasi tentang konsep dasar DirectPlay, bagaimana inisialisasi DirectPlay untuk model peer-to-peer pada bagian akhir sobat juga telah mengetahui bagaimana mengatur deksripsi peer. Pada artikel berikutnya, kita akan membahas bagaimana mengelola sesi permainan meliputi menciptakan dan bergabung dengan sebuah sesi.