Senin, 30 Desember 2013

revisi: libao + libmpg123 mingw gcc + code block sample...

Pada artikel sebelumnya saya telah memberikan code sumber untuk memainkan file mp3 dengan menggunakan library dari libmpg123 dan libao. kode sumber tersebut adalah oprekan dari visual studio 2013. disana menggunakan fungsi reinterpret_cast <char*>(buffer) sebagai casting type data yang berbeda antara keluaran libmpg123 (unsigned char) dengan masukan libao (char). Nah ternyata fungsi reinterpret_cast<>() tersebut tidak dapat berjalan di gcc. Jadi ngulik-ngulik terus berlanjut agar ide tersebut dapat berjalan dilingkungan mingw gcc. ulikan dilakukan dengan menggunakan codeblok sebuah ide c++ dari lingkungan open source. Berikut cuplikan kode sumber yang terdahulu.
Code: play.cpp
while (mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK)
{
ao_play(dev, reinterpret_cast<char*>(buffer), done);
fprintf(stderr, "Frame: %li\n", mpg123_tellframe(mh));
}
//===========reinterpret_cast<char*>(buffer) : digunakan untuk mengkonversikan type buffer unsigned char menjadi char.



cuplikan kode tersebut jika dikompilasi menggunakan mingw gcc akan error, karena gcc tidak mengenal fungsi reinterpret_cast tersebut.

jika diinginkan dapat dicari cara lain untuk melakukan type casting dari unsigned char menjadi char, namun setelah fungsi ao_play() dioprek langsung dari kode sumber libao, maka kita dapat merubah type data char yang digunakan menjadi unsigned char, sehingga tidak perlu melakukan terlalu banyak effort.

berikut ini prosedurnya:
1. extract kode sumber libao (<a href="http://downloads.xiph.org/releases/ao/libao-1.1.0.zip">http://downloads.xiph.org/releases/ao/libao-1.1.0.zip</a>) dan explore kedalam folder src.
2. edit file audio_out.c dengan codeblock dan silahkan cari (ctrl+f) ao_play.
3. didalam fungsi ao_play tersebut lakukan persesuaian type data

int ao_play(ao_device *device, char* output_samples, uint_32 num_bytes)
{
char *playback_buffer;

if (device == NULL)
return 0;

if (device->swap_buffer != NULL) {
int out_bytes = num_bytes*device->output_channels/device->input_channels;
if (_realloc_swap_buffer(device, out_bytes)) {
int i;
int swap = (device->bytewidth>1 &&
device->client_byte_format != device->driver_byte_format);
for(i=0;i<device->output_channels;i++){
int ic = device->inter_permute ? device->inter_permute[i] : i;
if(ic==-1){
_buffer_zero(device->swap_buffer,i,device->bytewidth,device->output_channels,
 out_bytes);
}else if(swap){
_buffer_permute_swap(device->swap_buffer,i,device->bytewidth,device->output_channels,
 out_bytes, output_samples, ic, device->input_channels);
}else{
_buffer_permute(device->swap_buffer,i,device->bytewidth,device->output_channels,
out_bytes, output_samples, ic, device->input_channels);
}
}
playback_buffer = device->swap_buffer;
num_bytes = out_bytes;
} else
return 0; /* Could not expand swap buffer */
} else
playback_buffer = output_samples;

return device->funcs->play(device, playback_buffer, num_bytes);
}


4. lihat argumen output_samples disana didefinisikan sebagai char* rubahlah menjadi unsigned char* output_samples.
5. lihat deklarasi playback_buffer ia juga didefinisikan sebagai char* rubahlah menjadi unsigned char*.

berikut perubahannya:
int ao_play(ao_device *device, unsigned char* output_samples, uint_32 num_bytes)
{
unsigned char *playback_buffer;

if (device == NULL)
return 0;

if (device->swap_buffer != NULL) {
int out_bytes = num_bytes*device->output_channels/device->input_channels;
if (_realloc_swap_buffer(device, out_bytes)) {
int i;
int swap = (device->bytewidth>1 &&
device->client_byte_format != device->driver_byte_format);
for(i=0;i<device->output_channels;i++){
int ic = device->inter_permute ? device->inter_permute[i] : i;
if(ic==-1){
_buffer_zero(device->swap_buffer,i,device->bytewidth,device->output_channels,
 out_bytes);
}else if(swap){
_buffer_permute_swap(device->swap_buffer,i,device->bytewidth,device->output_channels,
 out_bytes, output_samples, ic, device->input_channels);
}else{
_buffer_permute(device->swap_buffer,i,device->bytewidth,device->output_channels,
out_bytes, output_samples, ic, device->input_channels);
}
}
playback_buffer = device->swap_buffer;
num_bytes = out_bytes;
} else
return 0; /* Could not expand swap buffer */
} else
playback_buffer = output_samples;

return device->funcs->play(device, playback_buffer, num_bytes);
}


6. Simpan perubahan tersebut, dan silahkan buka folder include, edit file ao/ao.h, lakukan pencarian (ctrl+f) ao_play juga, dan lakukan persesuaian lagi.
int ao_play(ao_device *device,
char *output_samples,
uint_32 num_bytes);


sesuaikan menjadi:

int ao_play(ao_device *device,
unsigned char *output_samples,
uint_32 num_bytes);


7. Simpan dan kompilasi ulang library libao tersebut (./configure via msys dan mingw32-make).
8. Jadikan library libao hasil kompilasi menjadi opsi linker yang digunakan oleh codeblok / visual studio.

9. Sedangkan source kode player kita akan menjadi sbb:
#include <mpg123.h>
#include <ao/ao.h>
#include <malloc.h>
#include <stdlib.h>
#include <windows.h>

#define BITS 8

int main(int argc, char *argv[])
{
mpg123_handle *mh;
unsigned char *buffer;
size_t buffer_size;
size_t done;
int err, driver, channels, encoding;
ao_device *dev;
ao_sample_format format;
long rate;
ao_initialize();
driver = ao_default_driver_id();

// ========= inisial library =============

mpg123_init();
mh = mpg123_new(NULL, &err);
buffer_size = mpg123_outblock(mh);
buffer = (unsigned char*)malloc(buffer_size * sizeof(unsigned char));
mpg123_param(mh, MPG123_RESYNC_LIMIT, -1, 0);
char* filename=argv[1];
mpg123_open(mh, filename);
mpg123_getformat(mh, &rate, &channels, &encoding);

// ************************************ 

format.bits = mpg123_encsize(encoding) * BITS;
format.rate = rate;
format.channels = channels;
format.byte_format = AO_FMT_NATIVE;
format.matrix = 0;
double secs;
dev = ao_open_live(driver, &format, NULL);

while (mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK)
{
ao_play(dev, buffer, done);
fprintf(stderr, "Frame: %li\n", mpg123_tellframe(mh));
mpg123_position(mh, 0, 0, NULL, NULL, &secs, NULL);
fprintf(stderr,"[%d:%02d] Decoding of %s finished.\n", (int)(secs / 60), ((int)secs) % 60, filename);
}

/* clean up */

free(buffer);
ao_close(dev);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
ao_shutdown();

return 0;
}
nah kode diatas sudah tidak menggunakan reinterpret_cast&lt;&gt;() lagi, ini dapat dikompilasi oleh mingw gcc. selamat mencoba...

Minggu, 29 Desember 2013

mengexport symbol dari file dll opensource untuk digunakan oleh visual studio

Banyak library yang disediakan oleh opensource untuk digunakan di lingkungan ms-windows, namun kadang kita hanya mendapatkan file dll nya saja.berikut ini adalah cara yang saya gunakan untuk mengexport symbol dari file dll open source dan kemudian digunakan oleh visual studio.

pertama kita membutuhkan:
a. mingw dan msys (msys: optional) : gendef.exe
b. visual studio 2013 (boleh versi sebelumnya) : lib.exe

nah anggaplah kita telah mempunyai file libmpg123-0.dll yang kita peroleh dari suatu tempat opensource, dan tersimpan di c:\myvslib\bin.

prosedurnya:
1. extract symbol atau function definition dari file libmpg123-0.dll tersebut dengan menggunakan gendef.exe.
buka console terminal cmd
c:
cd \myvslib\bin
gendef.exe  libmpg123-0.dll
* [libmpg123-0.dll] Found PE image

sampai disini maka akan tersedia file libmpg123-0.def di folder tersebut. isi dari libmpg123-0.def adalah daftar function yang tersedia didalam file libmpg123-0.dll.
;
; Definition file of libmpg123-0.dll
; Automatic generated by gendef
; written by Kai Tietz 2008
;
LIBRARY "libmpg123-0.dll"
EXPORTS
mpg123_add_string
mpg123_add_substring
mpg123_chomp_string
mpg123_clip
mpg123_close
mpg123_copy_string
mpg123_current_decoder
mpg123_decode
mpg123_decode_frame
mpg123_decode_frame_32
mpg123_decoder
mpg123_decoders
mpg123_delete
mpg123_delete_pars
mpg123_enc_from_id3
mpg123_encodings
mpg123_encsize
mpg123_eq
mpg123_errcode
mpg123_exit
mpg123_feature
mpg123_feed
mpg123_feedseek
mpg123_feedseek_32
mpg123_fmt
mpg123_fmt_all
mpg123_fmt_none
mpg123_fmt_support
mpg123_format
mpg123_format_all
mpg123_format_none
mpg123_format_support
mpg123_framebyframe_decode
mpg123_framebyframe_decode_32
mpg123_framebyframe_next
mpg123_framedata
mpg123_framepos
mpg123_framepos_32
mpg123_free_string
mpg123_geteq
mpg123_getformat
mpg123_getpar
mpg123_getparam
mpg123_getstate
mpg123_getvolume
mpg123_grow_string
mpg123_icy
mpg123_icy2utf8
mpg123_id3
mpg123_index
mpg123_index_32
mpg123_info
mpg123_init
mpg123_init_string
mpg123_length
mpg123_length_32
mpg123_meta_check
mpg123_meta_free
mpg123_new
mpg123_new_pars
mpg123_noise
mpg123_open
mpg123_open_32
mpg123_open_fd
mpg123_open_fd_32
mpg123_open_feed
mpg123_open_handle
mpg123_open_handle_32
mpg123_outblock
mpg123_par
mpg123_param
mpg123_parnew
mpg123_plain_strerror
mpg123_position
mpg123_position_32
mpg123_rates
mpg123_read
mpg123_replace_buffer
mpg123_replace_reader
mpg123_replace_reader_32
mpg123_replace_reader_handle
mpg123_replace_reader_handle_32
mpg123_reset_eq
mpg123_resize_string
mpg123_safe_buffer
mpg123_scan
mpg123_seek
mpg123_seek_32
mpg123_seek_frame
mpg123_seek_frame_32
mpg123_set_filesize
mpg123_set_filesize_32
mpg123_set_index
mpg123_set_index_32
mpg123_set_string
mpg123_set_substring
mpg123_spf
mpg123_store_utf8
mpg123_strerror
mpg123_strlen
mpg123_supported_decoders
mpg123_tell
mpg123_tell_32
mpg123_tell_stream
mpg123_tell_stream_32
mpg123_tellframe
mpg123_tellframe_32
mpg123_timeframe
mpg123_timeframe_32
mpg123_tpf
mpg123_volume
mpg123_volume_change

2. link file libmpg123-0.def menjadi libmpg123-0.lib dengan menggunakan perintah lib.exe dari visual studio. masih didalam directory c:\myvslib\bin, gunakan lib.exe untuk mengkonversikan file libmpg123-0.def tersebut menjadi file .lib yang kelak akan digunakan oleh visual studio.

lib.exe /def:libmpg123-0.def /OUT:libmpg123-0.lib

Microsoft (R) Library Manager Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.

LINK : warning LNK4068: /MACHINE not specified; defaulting to X86
   Creating library libmpg123-0-d.lib and object libmpg123-0-d.exp

maka didalam folder akan terdapat file libmpg123-0-d.lib dan libmpg123-0-d.exp. kedua  file tersebutlah yang kelak akan digunakan di visual studio sebagai library tambahan dalam suatu program.

oh ya.. sebelum melakukan langkah tersebut silahkan pastikan terlebih dahulu bahwa situs mpg123 tersebut memang tidak menyediakan developer version dari library tersebut. karena bagaimanapun juga kita lebih baik menggunakan versi developer dari pemilik library tersebut. disana masih dibutuhkan header file untuk dapat menggunakan library tersebut dalam visual studio.

Sabtu, 21 Desember 2013

SFML Network dengan Thread untuk client server [bagian 5]

Setelah kita mengetahui bagaimana hubungan antara object server.cpp dengan client.cpp, sekarang perhatikan code berikut yang terdapat didalam kode server.cpp:
if(Listener.accept(Clients[OpenSlot-1]->Socket) != sf::Socket::Done) {…}
fungsi jika dalam baris tersebut memangil fungsi Listener.accept(dengan parameter nilai kembalian dari Client[OpenSlot-1] object member Socket) Listener itu sendiri adalah object turunan dari sfml-network yaitu sf::tcplistener yang dideklarasikan diawal kode server.cpp:
sf::TcpListener Server::Listener = sf::TcpListener();
mari kita anggap kondisi jika adalah false maka proses yang dijalankan adalah bagian:
else
{      {
      std::wstringstream ss; ss << L"{GDClient connected} on port {YD" << OpenSlot+Port << L"}.";
      writeToLog(ss.str());
      }
      Clients[OpenSlot-1]->Dead = false;
      Clients[OpenSlot-1]->listenThread = new sf::Thread(&Client::listen, Clients.at(OpenSlot-1));
      Clients[OpenSlot-1]->listenThread->launch();
}
Proses akan menulis dilayar tentang std::wstringstream ss; ss << L"{GDClient connected} on port {YD" << OpenSlot+Port << L"}."; writeToLog(ss.str()); kemudian memberi nilai false kepada Client[OpenSlot-1] object member:Dead, kemudian membuka lagi thread baru untuk koneksi client berikutnya:
Clients[OpenSlot-1]->listenThread = new sf::Thread(&Client::listen, Clients.at(OpenSlot-1));
Clients[OpenSlot-1]->listenThread->launch();

Nah dengan demikian setiap client akan terhubung kepada server pada port mereka masing-masing dimana setiap koneksi client tersebut akan berada dalam thread yang berbeda-beda.

Jumat, 20 Desember 2013

SFML Network dengan Thread untuk client server [bagian 4]

mari kita lanjutkan journey kita ke bagian code Server::runAccepter() yang masih bagian  dari server.cpp, berikut ini adalah codenya:
void Server::runAccepter() {
    if(!isFull()) {
        sf::Uint16 OpenSlot = 0;
        for(sf::Uint16 i = 1; OpenSlot == 0; i++) {
            if(isSlotOpen(i)) OpenSlot = i;
        }
        {
        std::wstringstream ss; ss << L"Started listen on port {YD" << Port+OpenSlot << L"}.";
        writeToLog(ss.str());
        }
        if(Listener.listen(Port+OpenSlot) != sf::Socket::Done) {
            {
            std::wstringstream ss; ss << "{RDFailed to listen on port} {YD" << Port+OpenSlot << L"}";
            writeToLog(ss.str());
            }
        } else {
            clientAccess.lock();
            delete Clients[OpenSlot-1];
            Clients[OpenSlot-1] = new Client();
            clientAccess.unlock();

            if(Listener.accept(Clients[OpenSlot-1]->Socket) != sf::Socket::Done) {
                {
                std::wstringstream ss; ss << L"{RDFailed to accept incomming connection on slot} {YDno. " << OpenSlot << L"}.";
                writeToLog(ss.str());
                }
            } else {
                {
                std::wstringstream ss; ss << L"{GDClient connected} on port {YD" << OpenSlot+Port << L"}.";
                writeToLog(ss.str());
                }
                Clients[OpenSlot-1]->Dead = false;
                Clients[OpenSlot-1]->listenThread = new sf::Thread(&Client::listen, Clients.at(OpenSlot-1));
                Clients[OpenSlot-1]->listenThread->launch();
            }
            Listener.close();
        }
    }
    Listening = false;
}

mari kita bahas kode Server::runAccepter(), yang pertama kali dijalankan adalah if(!isFull()) dimana fungsi isFull() adalah rangkaian dari beberapa fungsi yang dipanggil secara berurutan yaitu:


sf::Uint16 Server::calcAvgPing() { //Weighted average
    sf::Uint16 temp(0), count(0);
    for(sf::Uint16 i = 0; i < Clients.size(); i++) {
        if(!Clients.at(i)->isDead()) {temp+=Clients.at(i)->getPing(); count++;}
    }
  
    if(count != 0) {
        return temp/count;
    } else {
        return 0;
    }
}


bool Server::isSlotOpen(sf::Uint16 slot) {
    if(slot>0 && slot<=config.max_connections) {
        sf::Lock lock(clientAccess);
        return Clients[slot-1]->isDead();
    } else {
        writeToLog(L"{RDInvalid slot request.}");

        return false;
    }
}

sf::Uint16 Server::getSlotsOpen() {
    sf::Uint16 open = 0;
  
    for(sf::Uint16 i = 0; i < config.max_connections; i++) {
        if(isSlotOpen(i+1)) open++;
    }

    return open;
}

bool Server::isFull() {
    return (getSlotsOpen() == 0);
}


Begini singkat ceritanya: fungsi isFull() memanggil fungsi getSlotOpen() kemudian fungsi getSlotOpen() memanggil sf::Uint16() dan fungsi isSlotOpen() untuk mendapatkan apakah status slot open atau close.


Nah kembali ke kode Server::runAccepter() jadi nilai kembalian dari fungsi isFull() jika tidak true maka deklarasikan variable openslot sebagai sf::uint16 dengan nilai awal = 0. Kemudian baris berikutnya merupakan loop dengan for(sf::Uint16 i = 1; OpenSlot == 0; i++) sejauh incremental dari nilai i masih memenuhi kondisi openSlot==0 maka perintah if dibawahnya akan dijalankan yaitu if(isSlotOpen(i)) OpenSlot = i; berarti jika nilai kembalian fungsi isSlotOpen(dengan parameter nilai incremental i) adalah true maka variable OpenSlot akan diisi dengan nilai incremental i tersebut. Selesai dari loop, dua baris perintah berikutnya adalah untuk penulisan ke layar dan file log.
 

Nah pada kode berikutnya yaitu:
if(Listener.listen(Port+OpenSlot) != sf::Socket::Done)
disinilah mode “listen to network” dijalankan oleh server ini didalam thread yang berbeda, bagaimana caranya: ingat dibagian server.init sebelumnya, pemanggilan kode AccepterThread = new sf::Thread(&Server::runAccepter); disanalah deklarasi atas Server::runAccepter yang kita bahas ini dilakukan sebagai sf::Thread baru yang independent dari thread lainnya. Listener itu sendiri adalah penurunan inherit dari sf::TcpListener(); yang dideklarasikan diawal kode server.cpp.
 

sf::TcpListener Server::Listener = sf::TcpListener();
disitu jelas terlihat bahwa Server::Listener merupakan Tcp listener jadi sekarang kita telah mengaktifkan listener pada tcp socket bukan pada udp socket. Nilai port/slot yang dibuat untuk tcp listener ini adalah udp socket + 1, dan setiap kali terjadi koneksi udp socket baru dari para client, maka nilai tcp socket +1 akan berlaku untuk seterusnya hingga max.connection terpenuhi fungsi isFull() akan true.
 

Selanjutnya marilah kita abaikan kondisi true dari if(Listener.listen(Port+OpenSlot) != sf::Socket::Done) maka baris setelah else akan dieksekusi yaitu:
else {
    clientAccess.lock();
    delete Clients[OpenSlot-1];
    Clients[OpenSlot-1] = new Client();
    clientAccess.unlock();

    if(Listener.accept(Clients[OpenSlot-1]->Socket) != sf::Socket::Done)
{
    {std::wstringstream ss; ss << L"{RDFailed to accept incomming connection on slot} {YDno. " << OpenSlot << L"}.";
    writeToLog(ss.str());}
    }
else {
    {std::wstringstream ss; ss << L"{GDClient connected} on port {YD" << OpenSlot+Port << L"}.";
    writeToLog(ss.str());}
    Clients[OpenSlot-1]->Dead = false;
    Clients[OpenSlot-1]->listenThread = new sf::Thread(&Client::listen, Clients.at(OpenSlot-1));
    Clients[OpenSlot-1]->listenThread->launch();}
    Listener.close();
    }


Bagian kode ini berfungsi untuk menyediakan, mengunci dan membuka kuncian terhadap setiap thread yang terjadi setiap kali terdapat client yang terkoneksi.
Ini berarti pada awal koneksi client kepada server akan dilakukan secara UDP dan setelah sukses server akan membuka satu port/slot baru di TCP socket sebagai thread yang berbeda dengan UDPnya untuk digunakan selanjutnya oleh para client. Masing-masing Client akan terhubung kepada tcp socket yang berbeda.
Untuk memahami kode diatas mari kita pelajari kode sumber client.h:
 

//=============client.h===========

struct ClientData {
    wchar_t * name;

    ClientData();
};



class Client {

    void disconnect();

    sf::Mutex dataMutex;
    sf::Mutex cMutex;

    sf::Uint16 ping;

    ClientData data;

public:
    bool Dead;
    sf::Uint16 getPing();
    void setPing(sf::Uint16 Ping);
    bool handleTCPPacket(sf::Packet& packet);
    sf::TcpSocket Socket;
    sf::Thread * listenThread;
    bool isDead();
    ClientData& getData();
    void freeDataAccess();
    void listen();
    Client();
    ~Client();
};

Seperti telah dijelaskan sebelumnya bahwa C++ header file adalah berisikan symbol-symbol yang bertautan terhadap fungsi-fungsi didalam file object c++. Dalam hal ini file object tersebut adalah hasil kompilasi c++ terhadap file kode sumber. File client.h tersebut adalah header atas file client.cpp sebagai berikut:
 

//===========client.cpp============
#include <iostream>
#include <SFML\Network.hpp>
#include <vector>
#include <deque>
#include <sstream>
#include "config.h"
#include "client.h"
#include "server.h"
#include "packetHandling.h"
#include "ui.h"

ClientData::ClientData() {
    name = new wchar_t[MAX_CLIENTNAME_LENGTH];
}

Client::Client() : Socket(), Dead(true), listenThread(0), dataMutex(), cMutex(), ping(0), data() {
    //Socket.setBlocking(false); If I want a non-blocking socket, I will have to make a thread which sends packets in a non-blocking way (NOTREADY handling)
}

Client::~Client() {
    if(listenThread!=0) listenThread->wait();
    delete listenThread;

    Socket.disconnect();
}

ClientData& Client::getData() {
    dataMutex.lock();
    return data;
}

void Client::freeDataAccess() {
    dataMutex.unlock();
}

bool Client::isDead() {
    sf::Lock lock(cMutex);
    return Dead;
}

sf::Uint16 Client::getPing() {
    sf::Lock lock(cMutex);
    return ping;
}

void Client::setPing(sf::Uint16 Ping) {
    cMutex.lock();
    ping = Ping;
    cMutex.unlock();
}

void Client::disconnect() {
    cMutex.lock();
    Dead = true;
    cMutex.unlock();

    std::wstringstream ss; ss << "Socket {YDno. " << Socket.getLocalPort()-Server::Port << "} {RDdisconnected}.";
    writeToLog(ss.str());

    Socket.disconnect();
}

void Client::listen() {
    cMutex.lock();
    bool d = Dead;
    cMutex.unlock();
  
    while(!d) {
        sf::Packet received;
        sf::Socket::Status status = Socket.receive(received);
      
        if(status == sf::Socket::Disconnected) {
            disconnect();
        } else if(status == sf::Socket::Error) {
            std::wstringstream ss; ss << "Error on socket {YDno. " << Socket.getLocalPort()-Server::Port << "}. {RDDisconnecting it}.";
            writeToLog(ss.str());
          
            disconnect();
        } else if(status == sf::Socket::Done) {
          
            if(handleTCPPacket(received)) {
                //Success
            } else {
                std::wstringstream ss; ss << L"Packet error on slot {YDno. " << Socket.getLocalPort()-Server::Port << L"}.";
                writeToLog(ss.str());
            }
        }

        cMutex.lock();
        d = Dead;
        cMutex.unlock();
    }
}

Sekarang mari kita lihat bagaimana hubungan antara object client.cpp ini dengan server.cpp yang kita bahas sebelumnya. Perhatikan dua object berikut ini:


clientAccess.lock();
delete Clients[OpenSlot-1];
 

clientAccess.lock() merupakan turunan daripada object mutex yang didefinisikan diawal kode sumber server.cpp, yaitu: sf::Mutex Server::clientAccess = sf::Mutex(); sedangkan object Client[] merupakan bagian dari object vector yang juga didefinisikan diawal kode sumber server.cpp yaitu: std::vector<Client *> Server::Clients = std::vector<Client *>(); .
 

jadi sekarang kita telah mengetahui kemana pointer akan pergi saat kita memanggil clientAccess.lock() pointer akan pergi kepada object sf::mutex, dan saat object Client[] kita panggil maka pointer akan pergi kepada object std::vector.
 

Sekarang perhatikanlah file client.h diatas:
class Client {}
 

disanalah terjadinya hubungan antara object server.cpp dengan client.cpp, sedangkan mutex akan digunakan oleh kedua object tersebut sebagai perantara, perhatikan baris berikut:
class Client {

    void disconnect();

    sf::Mutex dataMutex;
    sf::Mutex cMutex;

    sf::Uint16 ping;

    ClientData data; …..}
 

Jadi kelas dengan nama Client ternyata juga meregistrasikan membernya kepada pointer sf::Mutex.
 

...tunggu lanjutannya...

Selasa, 17 Desember 2013

SFML Network dengan Thread untuk client server [bagian 3]

sekarang mari kita lihat bagian kode berikut:

if(config.port==0) {
        Socket.bind(sf::Socket::AnyPort);
        Port = Socket.getLocalPort();
    } else {
        Port = config.port;
        if(Socket.bind(config.port) != sf::Socket::Done) {
            std::wstringstream ss; ss << L"{RDError:} Could not bind to port {YD" << config.port << L"}.";
            writeToLog(ss.str());
            return false;
        }
    }


saya translasikan kedalam bahasa indonesia menjadi begini:


jika (config.port adalah 0) maka jalankan
{
  Socket.bind(dengan parameter yang diambil dari object sf::Socket::AnyPort);

  dan Variable Port diisi dengan nilai dari Socket.getLocalPort();
}
jika tidak demikian maka jalankan
{
  variable Port diisi dengan nilai config.port;
  jika (Socket.bind(dengan parameter dari config.port) tidak sama dengan nilai sf::Socket::Done) maka jalankan
  {
    definisikan std::wstringstream sebagai ss;
    ss diisi dengan "{RDError:} Could not bind to port {YD" ditambah lagi dengan nilai dari config.port;
    jalankan writeToLog(dengan nilai ss.str());
    kembalian adalah salah;
  }
}


dari kode diatas dapat dijelaskan bahwa pada awalnya Server.init() akan memeriksa nilai didalam object config.port, jika nilainya 0 maka perintah socket.bind akan dijalankan dengan nilai port yang diambil secara acak oleh object sf::Socket::AnyPort. hal ini tentunya tidak akan pernah terjadi sehubungan kita telah menetapkan nilai bukan 0 melainkan 8888 untuk config.port pada main.cpp. jadi yang dieksekusi adalah kemungkinan kedua.


pada kemungkinan kedua terlihat bagaimana variable Port diisi dengan nilai config.port dan selanjutnya dilakukan kembali pemanggilan sekaligus pengecekan terhadap Socket.bind dengan menggunakan nilai dari config.port, jika status pemanggilan Socket.bind tersebut tidak Done maka ss akan diisi dengan "{RDError:} Could not bind to port {YD" ditambah lagi dengan nilai dari config.port, pesan tersebut akan ditulis ke file.log dan program akan memberi nilai kembalian false kepada pemanggilnya - main.cpp yang mana akan menghentikan program untuk berlanjut.


nah jika Socket.bind sukses maka kode berikutnya adalah:


    AccepterThread = new sf::Thread(&Server::runAccepter);
    MainThread = new sf::Thread(&Server::run);
    MainThread->launch();
    PacketQueueThread = new sf::Thread(&Server::handlePacketQueue);
    PacketQueueThread->launch();
    return true;


definisikan AccepterThread sebagai clone dari sf::Thread(dan registrasikan &Server::runAccepter sebagai bagian modul didalam sf::thread);


definisikan MainThread sebagai clone dari sf::thread(dan registrasikan &Server::runsebagai bagian modul didalam sf::thread);
panggil fungsi launch() yang merupakan member dari MainThread;
definisi seperti diatas dilakukan juga terhadap PacketQueueThread. kelima pendefinisian ini bisa dilakukan karena adanya: sf::Thread * Server::AccepterThread = 0; sf::Thread * Server::MainThread = 0; dan sf::Thread * Server::PacketQueueThread = 0; pada baris awal code server.cpp.


dengan dipanggilnya MainThread->launch(); dan PacketQueueThread->launch(); maka server::init sudah running dan memberikan nilai kembalian true kepada int main(...) di main.cpp. proses akan dikembalikan kepada int main(...).


namun mari kita lihat bagian ini: MainThread = new sf::Thread(&Server::run); MainThread->launch(); ketika MainThread->launch() dipanggil, maka sf::Thread akan mengeksekusi &Server::run dan berikut ini adalah kode dari fungsi &Server::run:


void Server::run() {
    writeToLog(L"Main Network Thread started.");
    sf::Clock clock;
    while(Run) {
        if(!isFull() && !Listening) {
            Listening = true;
            AccepterThread->launch();
        }
        sf::Packet packet; sf::IpAddress ip; sf::Uint16 port;
        if(Socket.receive(packet, ip, port) == sf::Socket::Done) {
            if(handleUDPPacket(packet, ip, port)) {
                //Success
            } else {
                std::wstringstream ss; ss << L"Packet error on {YDUDP Socket}.";
                writeToLog(ss.str());
            }}}} // untuk memperpendek baris kode

fungsi writeToLog dipanggil untuk menulis kata "Main Network Threa started" di layar. fungsi writeToLog adalah fungsi yang terdapat di dalam ui.cpp yang dalam hal ini tidak perlu dibahas dalu. kemudian memanggil fungsi sf::Clock melalui inheritnya dengan nama clock.
loop dilakukan selama nilai kembalian Run adalah true, Run sendiri merupakan fungsi private dari  class Server sendiri yang didefinisikan diawal code server.cpp:
bool Server::Run = true;
 

dan merujuk langsung pada definisi symbol yang terdapat di file server.hpp yaitu:
static bool Run;

proses yang di-loop adalah:
1. if(!isFull() && !Listening) {Listening = true; AccepterThread->launch();} --jika nilai kembalian fingsi isFull() tidak true dan nilai Listening juga tidak true, maka jalankan {Listening diisi nilai true; eksekusi AccepterThread->launch();}
jadi selama proses loop terus berlangsung, maka nilai Listening akan selalu dibuat true dan fungsi AccepterThread->launch(); selalu dijalankan.


2. sf::Packet packet; sf::IpAddress ip; sf::Uint16 port;
Ini adalah deklarasi atas sf::packet sebagai packet; sf::IpAddress sebagai ip; dan sf::Uint16 sebagai port;


3. if(Socket.receive(packet, ip, port) == sf::Socket::Done) {if(handleUDPPacket(packet, ip, port)) {//Success} else {std::wstringstream ss; ss << L"Packet error on {YDUDP Socket}.";    writeToLog(ss.str());}}


jika (fungsi Socket.receive dipanggil (dengan parameter:packet, ip, dan port) memberikan nilai kembalian sama dengan nilai kembalian dari sf::Socket::Done) maka jalankan proses berikut:
{
jika (fungsi handleUDPPacket dipanggil (dengan parameter:packet,ip, dan port) menghasilkan nilai kembalian true) maka jalankan {abaikan saja..} namun jika tidak demikian maka jalankan {deklarasikan std::wstringstream sebagai ss; isi ss dengan kata: "Packet error on {YDUDP Socket}."; jalankan fungsi writeToLog(dengan parameter adalah nilai kembalian dari fungsi ss.str());}
}
...tunggu lanjutannya...

SFML Network dengan Thread untuk client server [bagian 2]

mari kita lihat kembali pada main.cpp kode sumbernya hanya terdiri dari satu fungsi standar c++ yaitu int main{...}:

int main(int argc, char * argv[]) {
//Retrieve IP-adress
    config.port = UINT16(8888);
    config.max_connections = 6;
    config.name = std::wstring(L"SPIRO");
    config.password = std::wstring(L"qwerty");

    //config.PublicIP =
    config.LocalIP = "192.168.1.2";
    config.PublicIP = "192.168.1.2";
    if(Server::init()) {
        sf::Thread uiThread(uiLoop);
        uiThread.launch();
        logMutex.lock();
        output.push_front(L"Server successfully started!");
        for(sf::Uint16 i=0; i<4; i++) {output.push_front(L"");}
        logMutex.unlock();
        // This blocks the 'main' thread untill the uiThread quits
        uiThread.wait();
    } else {
        writeToLog(L"Error, shutting down. Press any key to continue.");
    }
    Server::terminate();
    return 0;
}


setelah mendefinisikan berbagai variable untuk config, maka Server::init() di panggil. ini adalah perintah yang sebenarnya terdapat didalam header file server.h yang di include kan diawal baris code.


//==============server.h==========
#include <iostream>
#include <SFML\Network.hpp>
#include <vector>
#include <deque>
#include <sstream>
extern sf::Mutex logMutex;
extern std::deque<std::wstring> output;
//Pure static
class Server {
private:
    static sf::UdpSocket Socket; // The socket which recieves game info
    static sf::TcpListener Listener; // Accepts new clients
    static sf::Thread * AccepterThread;
    static sf::Thread * MainThread;
    static sf::Thread * PacketQueueThread;
    static bool Run;
    static bool Listening;
    static sf::Mutex clientAccess;
    static void runAccepter();
    static void run();
    static void handlePacketQueue();
public:
    static bool handleUDPPacket(sf::Packet& packet, const sf::IpAddress& ip, const sf::Uint16 port);
    static sf::Uint16 Port;
    static std::vector<Client *> Clients;
    static bool init();
    static void terminate();
    static sf::Uint16 calcAvgPing();
    static bool isSlotOpen(sf::Uint16 slot);
    static sf::Uint16 getSlotsOpen();
    static bool isFull();
};
//==============server.h==========


header server.h merupakan definisi symbol-symbol yang ada didalam server.cpp. server.h berisikan class object Server{...} dengan member private, public, dan constanta. setiap member tersebut akan mewakili sekaligus triger bagi fungsi-fungsi yang ada didalam server.cpp. dengan kata lain kita mengeksekusi fungsi-fungsi didalam server.cpp dengan memanggil symbol yang mewakili fungsi tersebut didalam server.h.
berikut source code server.cpp


//==============server.cpp==========
#include "config.h"
#include "client.h"
#include "server.h"
#include "packetHandling.h"
#include "ui.h"
/// SERVER
sf::UdpSocket Server::Socket = sf::UdpSocket();
sf::TcpListener Server::Listener = sf::TcpListener();
sf::Thread * Server::AccepterThread = 0;
sf::Thread * Server::MainThread = 0;
sf::Thread * Server::PacketQueueThread = 0;
sf::Mutex Server::clientAccess = sf::Mutex();
bool Server::Run = true;
bool Server::Listening = false;
std::vector<Client *> Server::Clients = std::vector<Client *>();
sf::Uint16 Server::Port = sf::Uint16();
bool Server::init() {
    Socket.setBlocking(false);
    if(config.port==0) {
        Socket.bind(sf::Socket::AnyPort);
        Port = Socket.getLocalPort();
    } else {
        Port = config.port;
        if(Socket.bind(config.port) != sf::Socket::Done) {
            std::wstringstream ss; ss << L"{RDError:} Could not bind to port {YD" << config.port << L"}.";
            writeToLog(ss.str());
            return false;
        }
    }
    std::wstringstream ss; ss << L"Server assigned to port {YD" << Port << L"}";
    writeToLog(ss.str());
    Clients.resize(config.max_connections);
    for(sf::Uint16 i = 0; i<Clients.size(); i++) {
        Clients[i] = new Client();
    }
    AccepterThread = new sf::Thread(&Server::runAccepter);
    MainThread = new sf::Thread(&Server::run);
    MainThread->launch();
    PacketQueueThread = new sf::Thread(&Server::handlePacketQueue);
    PacketQueueThread->launch();
    return true;
}
void Server::terminate() {
    Run = false;
    AccepterThread->terminate();
    PacketQueueThread->wait();
    MainThread->wait();
    for(sf::Uint16 i = 0; i<Clients.size(); i++) {
        delete Clients[i]; //This deadlocks the process when clients are connected due to the Client's TCP socket being set to Blocking mode. It then listens for packets to arrive, and calls wait() on the listenThread
    }
    if(config.logToFile) {config.logFile << std::endl; config.logFile.close();}
}
void Server::runAccepter() {
    if(!isFull()) {
        sf::Uint16 OpenSlot = 0;
        for(sf::Uint16 i = 1; OpenSlot == 0; i++) {
            if(isSlotOpen(i)) OpenSlot = i;
        }
        {
        std::wstringstream ss; ss << L"Started listen on port {YD" << Port+OpenSlot << L"}.";
        writeToLog(ss.str());
        }
        if(Listener.listen(Port+OpenSlot) != sf::Socket::Done) {
            {
            std::wstringstream ss; ss << "{RDFailed to listen on port} {YD" << Port+OpenSlot << L"}";
            writeToLog(ss.str());
            }
        } else {
            clientAccess.lock();
            delete Clients[OpenSlot-1];
            Clients[OpenSlot-1] = new Client();
            clientAccess.unlock();
            if(Listener.accept(Clients[OpenSlot-1]->Socket) != sf::Socket::Done) {
                {
                std::wstringstream ss; ss << L"{RDFailed to accept incomming connection on slot} {YDno. " << OpenSlot << L"}.";
                writeToLog(ss.str());
                }
            } else {
                {
                std::wstringstream ss; ss << L"{GDClient connected} on port {YD" << OpenSlot+Port << L"}.";
                writeToLog(ss.str());
                }
                Clients[OpenSlot-1]->Dead = false;
                Clients[OpenSlot-1]->listenThread = new sf::Thread(&Client::listen, Clients.at(OpenSlot-1));
                Clients[OpenSlot-1]->listenThread->launch();
            }
            Listener.close();
        }
    }
    Listening = false;
}
void Server::run() {
    writeToLog(L"Main Network Thread started.");
    sf::Clock clock;
    while(Run) {
        if(!isFull() && !Listening) {
            Listening = true;
            AccepterThread->launch();
        }
        sf::Packet packet; sf::IpAddress ip; sf::Uint16 port;
        if(Socket.receive(packet, ip, port) == sf::Socket::Done) {
            if(handleUDPPacket(packet, ip, port)) {
                //Success
            } else {
                std::wstringstream ss; ss << L"Packet error on {YDUDP Socket}.";
                writeToLog(ss.str());
            }
        }
    }
}
sf::Uint16 Server::calcAvgPing() { //Weighted average
    sf::Uint16 temp(0), count(0);
    for(sf::Uint16 i = 0; i < Clients.size(); i++) {
        if(!Clients.at(i)->isDead()) {temp+=Clients.at(i)->getPing(); count++;}
    }
   
    if(count != 0) {
        return temp/count;
    } else {
        return 0;
    }
}
bool Server::isSlotOpen(sf::Uint16 slot) {
    if(slot>0 && slot<=config.max_connections) {
        sf::Lock lock(clientAccess);
        return Clients[slot-1]->isDead();
    } else {
        writeToLog(L"{RDInvalid slot request.}");
        return false;
    }
}
sf::Uint16 Server::getSlotsOpen() {
    sf::Uint16 open = 0;
    for(sf::Uint16 i = 0; i < config.max_connections; i++) {
        if(isSlotOpen(i+1)) open++;
    }
    return open;
}
bool Server::isFull() {
    return (getSlotsOpen() == 0);
}
//==============server.cpp==========

SFML Network dengan Thread untuk client server [bagian 1]

SFML Network Server dengan Thread
ini adalah jurney saya tentang SFML, khusus untuk bagian sfml::net dan sf::thread. 


membuat program untuk multi user client server tidaklah mudah karena menyangkut akses konkurent dari para user. SFML memberikan fasilitas thread untuk memudahkan pemrograman aplikasi client server.
berikut ini contoh kodenya:


//=======main.cpp========
#include <iostream>
#include <time.h>
#include <sstream>
#include <string>
#include <cstdio>
#include <fstream>
#include <SFML\Network.hpp>
#include "config.h"
#include "client.h"
#include "server.h"                                   
#include "ui.h"
sf::Mutex logMutex;
Config config;
std::deque<std::wstring> output;
const sf::Uint16 MAX_SERVERNAME_DISPLAY_LENGTH = 25;
const sf::Uint32 MAX_LOG_LENGTH = 200;
const sf::Uint16 LOG_INDENT = 3;
const sf::Uint16 MAX_CLIENTNAME_LENGTH = 32;

int main(int argc, char * argv[]) {
//Retrieve IP-adress
    config.port = UINT16(8888);
    config.max_connections = 6;
    config.name = std::wstring(L"SPIRO");
    config.password = std::wstring(L"qwerty");

    //config.PublicIP =
    config.LocalIP = "192.168.1.2";
    config.PublicIP = "192.168.1.2";
    if(Server::init()) {
        sf::Thread uiThread(uiLoop);
        uiThread.launch();
        logMutex.lock();
        output.push_front(L"Server successfully started!");
        for(sf::Uint16 i=0; i<4; i++) {output.push_front(L"");}
        logMutex.unlock();
        // This blocks the 'main' thread untill the uiThread quits
        uiThread.wait();
    } else {
        writeToLog(L"Error, shutting down. Press any key to continue.");
    }
    Server::terminate();
    return 0;
}
//=======main.cpp========


penjelasan kode sumber:


selain dari: #include <SFML\Network.hpp>, #include "config.h", #include "client.h", #include "server.h", dan #include "ui.h"
adalah header standar dari c++, sudah disediakan oleh compiler. kelima #include tersebut digunakan untuk memberitahu kompiler agar membuka kelima file header tersebut sebelum kode dibawahnya dibaca oleh kompiler.


biasanya suatu program c++ akan dibentuk oleh banyak object-object yang terpisah dan di link kedalam satu file executable/dll. nah kebanyakan fungsi dari header file adalah memberikan symbol-symbol dari tiap tiap object kepada source code yang memanggilnya. sebagai contoh pemberian perintah #include <SFML\Network.hpp> akan memberitahukan kompiler bahwa modul object yang terdapat didalam file sfml-network.dll dapat dipanggil dengan menggunakan symbol-symbol yang terdapat didalam SFML\Network.hpp.
Selain module SFML yang memang sudah tersedia, mari kita bahas #include "config.h" berikut ini kode sumbernya:


//=======config.h==========
#include <fstream>
#include <windows.h>
#include <SFML\Network.hpp>
struct Config {
    // Run-time config
    sf::IpAddress LocalIP;sf::IpAddress PublicIP;std::fstream logFile;COORD bufferSize;
    //IPConfig
    sf::Uint16 port;sf::Uint16 max_connections;sf::Uint32 timeout;
    //Server
    std::wstring name;std::wstring password;
    //UI
    sf::Uint16 verbose_level;
    bool logToFile;
    Config() : port(0), max_connections(0), timeout(), name(), password(), LocalIP(sf::IpAddress::None), PublicIP(sf::IpAddress::None), verbose_level(0), logToFile(false), logFile(), bufferSize() {}
};
extern Config config;
extern const sf::Uint16 MAX_SERVERNAME_DISPLAY_LENGTH;
extern const sf::Uint32 MAX_LOG_LENGTH;
extern const sf::Uint16 LOG_INDENT;
extern const sf::Uint16 MAX_CLIENTNAME_LENGTH;
//======config.h========


source code config.h berisikan perintah struct Config{...}  perintah ini menyatakan agar kompiler membuat struktur data di memory dengan inisial Config. Perintah standar c++ untuk struct{} adalah:


struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
} object_names;


kemudian perhatikan baris : extern Config config;
perintah extern akan membuat structure data "Config" dapat dipanggil sebagai symbol "config" oleh object lain diluar dirinya sendiri. inilah kemudian menjadi #include pada kode sumber lainnya.


struktur Config terdiri dari member-member yang kelak dapat dipanggil dengan pernyataan seperti config.LocalIP, config.port dan sebagainya sesuai dengan member name yang ada didalam struktur tersebut. sebagai contoh: sf::IpAddress LocalIP member didalam structur Config. baris tersebut menyiratkan bahwa jika "LocalIP" dipanggil, maka "sf::IpAddress" akan melakukan tugasnya dan jika ada nilai kembalian dari "sf::IpAddress" maka nilai tersebut adalah nilai bagi  "LocalIP".


catatan: "sf::IpAddress" adalah fungsi yang ada didalam modul SFML yang akan meretrieve nilai alamat IP yang aktif didalam operating system.


sampai disini maka program main.cpp kita akan dapat mengenali suatu object data buatan dengan nama Config. lihat pada main.cpp disana terdapat definisi atas Config sebagai config dan selanjutnya digunakan didalam int main{} dengan syntax:

config.LocalIP = xxxxxxx
config.port = UINT16(8888);
config.max_connections = 6;
config.name = std::wstring(L"SPIRO");
config.password = std::wstring(L"qwerty");



Minggu, 15 Desember 2013

msvc visual studio expres 2013 + polycode C++ [Bagian 3]

nah sekarang tinggal build polycode framework nya. tutup visual studio bila masih terbuka atau aktif. silahkan explorer ke c:\polycodemaster, dan buatlah folder Build disana. panggil lagi cmake-gui dan sesuaikan berbagai isian.

click tombol finish, click configure, click generate. silahkan buka (dobel click) file "Polycode.sln" di folder "c:\polycodemaster\Build" dan lakukan build pada ALL_BUILD di solution explorer visual studio.

*Note: jangan lakukan build pada Polycode project tapi lakukan build pada ALL_BUILD.

untuk membuat versi releasenya silahkan ganti ke mode release dan lakukan build pada ALL_BUILD kembali.


jika build success dan ingin membuat paket polycode dalam zip file, click kanan pada PACKAGE di solution Explorer dan pilih build.

jika ingin menginstall paket polycode silahkan click kanan pada INSTALL di solution explorer dan pilih build.




msvc visual studio expres 2013 + polycode C++ [Bagian 2]



silahkan buka (double click) file "PolycodeDependencies.sln" yang adalah file ms visual studio. selanjutnya ms visual studio akan loading dengan project Polycode Dependencies tersebut.

lakukan build solution. maka visual studio akan mengkompilasi per library yang dibutuhkan oleh polycode. Proses ini terdiri dari:
1. otomatis download source code per library
2. extract library yang akan di build
3. build library tersebut.

ketiga proses tersebut dilakukan per library sesuai dengan urutan yang ditentukan oleh konfigurasi cmake.

*Note:
Jika terjadi gagal download maka visual studio akan mencoba ulangi proses. atau silahkan ulangi secara manual dengan click kanan per library yang terdaftar di solution explorer.

*Note: OpenAL error:::
dalam kasus saya build akan terhenti pada error di paket openal. ini disebabkan adanya kode:

#ifdef _MSC_VER
static double round(double val)
{
    if(val < 0.0)
        return ceil(val - 0.5);
    return floor(val + 0.5);
}
#endif

perbaikan:
keluar dari visual studio, kemudian silahkan explore ke folder "C:\Polycodemaster\Dependencies\Build\openal\src\openal-build" dan doble click pada file "OpenAL.sln" ini akan memanggil visual studio kembali untuk project OpenAL saja. silahkan buka makehrtf.c dari solution explorer dan hapus kode tersebut diatas dan simpan project.

coba click kanan pada makehrtf root di solution explorer dan pilih project only -- build makehrtf jika error sudah tidak muncul atau kompilasi success, maka simpan project dan keluar dari visual studio.

kembali buka file "PolycodeDependencies.sln" dan lakukan build solution. jika sukses maka didalam folder "C:\Polycodemaster\Dependencies\Release\windows" akan terdapat folder framework dengan isi: folder Core, folder modules, dan folder tools. didalam folder core akan terdapat folder dependencies yang isinya folder bin, include, lib. inilah yang menjadi inti dari polycode framework. silahkan lihat isi folder lib dan pastikan library .lib sudah tersedia untuk semua paket library.

*Note:
Build solution  "PolycodeDependencies.sln" harus dilakukan untuk dua mode: debug dan release.

ke bagian3 >

msvc visual studio expres 2013 + polycode C++ [Bagian 1]

Polycode adalah framework /API untuk C++ yang dibuat oleh ivan safrin (polycode.org) yang sepenuhnya menggunakan library dari opensource, library tersebut adalah :
* [Lua](http://www.lua.org/)
* [Freetype](http://www.freetype.org/)
* [zlib](http://www.zlib.net/)
* [libpng](http://www.libpng.org/pub/png/libpng.html)
* [PhysicsFS](http://icculus.org/physfs/)
* [Ogg Vorbis](http://www.vorbis.com/)
* [OpenAL](http://www.openal.org/)
* [SDL](http://www.libsdl.org/)
* [Box2D](http://www.box2d.org/)
* [Bullet Physics](http://bulletphysics.org/)
* [Assimp](http://assimp.sourceforge.net/)
dapat digunakan untuk membuat program-program digital multi media interaktif.

polycode dapat di download di: https://github.com/ivansafrin/Polycode

silahkan cari tombol "download as zip"

setelah selesai download, untuk mem-build polycode dibutuhkan :
* cmake
* Visual Studio Express (versi yang saya gunakan VS2013 tapi dapat juga dilakukan dengan versi dibawahnya)

prosedur :

1. terlebih dahulu paket yang merupakan dependencies bagi polycode di build.
2. polycode basic di build.

extract polycodemaster.zip ke folder misalnya c:\polycodemaster. pindah kedalam folder tersebut, dan masuk ke folder dependencies. buatlah folder dengan nama "Build". panggil cmake-gui, set where is the source code : ke folder "c:\polycodemaster\dependencies" dan where to build binaries: ke folder "c:\polycodemaster\dependencies\Build" lalu tekan tombol configure dan arahkan "specify the generator for this project" ke visual studio 12 lalu click finish. cmake akan mendownload semua kebutuhan library tersebut diatas kefolder Downloads.


dan akan memeriksa konfigurasi dasar untuk keperluan build source code.


kemungkinan cmake akan menampilkan daftar environment build dengan warna merah... meskipun tidak apa-apa, namun ada baiknya untuk variable "POLYCODE_RELEASE_DIR" diperbaiki menjadi : "../release/windows", dan click tombol configure lagi, kemudian click tombol generate.

sekarang didalam folder dependencies telah terdapat folder Downloads dan Release. masuklah kedalam folder build, disana sekarang sudah terdapat banyak file-file dan folder untuk MS visual studio.




ke bagian2>>