Yea, thats the place when we can have some problems. But don’t worry, you can handle everything what you touch :p
At start, we have to declare our zoidcom classes. Because this is server application we start with the Server class.
Before that, we must add some declarations:
void put_string(char*, char*); //because we use it in Server class ZCom_ConnID other_id //variable to store id of computer //which connects to ours.
Then, let’s go with class Server. First function we would like to add is connection request
class Server : public ZCom_Control { // someone tries to connect bool ZCom_cbConnectionRequest(ZCom_ConnID _id, ZCom_BitStream &_request, ZCom_BitStream &_reply ) { put_string("Requested connection...", ""); return true; //Always returnig true will allow all users to connect } };
Next, we should add a function which informs us about connection spawn and invite the other person to chat with us.
// someone has connected void ZCom_cbConnectionSpawned( ZCom_ConnID _id ) { put_string("Connection made...you can now write", ""); other_id = _id; //Storing the id of other computer to later use ZCom_BitStream *welcome = new ZCom_BitStream(); welcome->addString(""); //There should be nick but we can //treat this as system message //so i left it blank welcome->addString("Connection made, you can now write"); ZCom_sendData(other_id, welcome); //Sending message }
Now, we will add function which inform us about closing connection. Simple...
void ZCom_cbConnectionClosed( ZCom_ConnID _id, eZCom_CloseReason _reason, ZCom_BitStream &_reasondata ) { put_string("Connection closed.", ""); }
Most important thing is to read data send to us. It is handled by other function
// someone has sent data void ZCom_cbDataReceived(ZCom_ConnID _id, ZCom_BitStream &_data) { put_string(_data.getString(), _data.getString()); }
We must also write all unused functions to make zoidcom work. (just copy paste it)
void ZCom_cbConnectResult( ZCom_ConnID _id, eZCom_ConnectResult _result, ZCom_BitStream &_reply ) {} bool ZCom_cbZoidRequest( ZCom_ConnID _id, zU8 _requested_level, ZCom_BitStream &_reason ) {return false;} void ZCom_cbZoidResult( ZCom_ConnID _id, eZCom_ZoidResult _result, zU8 _new_level, ZCom_BitStream &_reason ) {} void ZCom_cbNodeRequest_Dynamic( ZCom_ConnID _id, ZCom_ClassID _requested_class, ZCom_BitStream *_announcedata, eZCom_NodeRole _role, ZCom_NodeID _net_id ) {} void ZCom_cbNodeRequest_Tag( ZCom_ConnID _id, ZCom_ClassID _requested_class, ZCom_BitStream *_announcedata, eZCom_NodeRole _role, zU32 _tag ) {} bool ZCom_cbDiscoverRequest( const ZCom_Address &_addr, ZCom_BitStream &_request, ZCom_BitStream &_reply ) {return false;} void ZCom_cbDiscovered( const ZCom_Address & _addr, ZCom_BitStream &_reply ) {}
And this is enough for Server class, now needed declarations (after class description):
ZoidCom* zcom = NULL; Server* server = NULL;
By the way you can add two lines to shutdown() function:
delete server; delete zcom;
Now, we have to get back to start() function and on bottom of it add some stuff:
zcom = new ZoidCom("netlog.txt"); //creating new object //netlog.txt is a file //where zoidcom will save his log if (!zcom || !zcom->Init()) exit(255); //if initialization fails -> quit //Important thing, timeout, after this time (in miliseconds), //if the other machine isn't respondig we break connection //and func ZCom_cbConnectionClosed() is executed. // 10 seconds zcom->setConnectionTimeout(10000); //Our server object based on class we defined eariler server = new Server(); server->ZCom_setDebugName("Chatic Server"); // port 10000 server->ZCom_initSockets(true, 10000, 0);
Yeah, now we are almost done. Last thing is to use sts variable, remember it? Great, look at Process() func:
void Process() { if(sts) { sts = false; for(int u = 0; u < 256; u++) message[u] = 0; } }
And make it look like this
void Process() { if(sts) { ZCom_BitStream *reply = new ZCom_BitStream(); //create stream reply->addString(nickname); //add your nick to stream reply->addString(message); //add your message to stream server->ZCom_sendData(other_id, reply); //send message to id //stored on start sts = false; //there is nothing more to send for(int u = 0; u < 256; u++) message[u] = 0; //clear message array } server->ZCom_processInput(); //zoidcom func processing all input server->ZCom_processOutput();//zoidcom func processing all output zcom->Sleep(1); //give rest of time to cpu }
Now, we have complete server application. All we have to do, is to save it somewhere, copy somewhere and make client application from the copy.
Complete source code for server app:
#include <zoidcom.h> //Zoidcom library include #include <allegro.h> //Allegro library include #include <conio.h> //Windows library for getch() func BITMAP* buffer; BITMAP* talk_screen; BITMAP* talk_screen_temp; bool exit_chat = false; char* nickname; char message[256]; //Message array char ch = 0; //Single char for reading int index = 0; //index to message array bool sts = false; //Something to send variable //It tells program is there anything to send void put_string(char*, char*); ZCom_ConnID other_id; class Server : public ZCom_Control { bool ZCom_cbConnectionRequest(ZCom_ConnID _id, ZCom_BitStream &_request, ZCom_BitStream &_reply ) { put_string("Requested connection...", ""); return true; } void ZCom_cbConnectionSpawned( ZCom_ConnID _id ) { put_string("Connection made...you can now write", ""); other_id = _id; ZCom_BitStream *welcome = new ZCom_BitStream(); welcome->addString(""); welcome->addString("Connection made, you can now write"); ZCom_sendData(other_id, welcome); } void ZCom_cbConnectionClosed( ZCom_ConnID _id, eZCom_CloseReason _reason, ZCom_BitStream &_reasondata ) { put_string("Connection closed.", ""); } void ZCom_cbDataReceived(ZCom_ConnID _id, ZCom_BitStream &_data) { put_string(_data.getString(), _data.getString()); } void ZCom_cbConnectResult( ZCom_ConnID _id, eZCom_ConnectResult _result, ZCom_BitStream &_reply ) {} bool ZCom_cbZoidRequest( ZCom_ConnID _id, zU8 _requested_level, ZCom_BitStream &_reason ) {return false;} void ZCom_cbZoidResult( ZCom_ConnID _id, eZCom_ZoidResult _result, zU8 _new_level, ZCom_BitStream &_reason ) {} void ZCom_cbNodeRequest_Dynamic( ZCom_ConnID _id, ZCom_ClassID _requested_class, ZCom_BitStream *_announcedata, eZCom_NodeRole _role, ZCom_NodeID _net_id ) {} void ZCom_cbNodeRequest_Tag( ZCom_ConnID _id, ZCom_ClassID _requested_class, ZCom_BitStream *_announcedata, eZCom_NodeRole _role, zU32 _tag ) {} bool ZCom_cbDiscoverRequest( const ZCom_Address &_addr, ZCom_BitStream &_request, ZCom_BitStream &_reply ) {return false;} void ZCom_cbDiscovered( const ZCom_Address & _addr, ZCom_BitStream &_reply ) {} }; ZoidCom* zcom = NULL; Server* server = NULL; void put_string(char* msg, char* nick) { static int y_pos = 30; //Position where our message textprintf_ex(talk_screen, font, 1, y_pos, makecol(255,255,255), -1, "%s> %s", nick,msg); y_pos+=15; //Scroling if(y_pos>330) { blit(talk_screen, talk_screen_temp, 0, 0, 0, 0, talk_screen_temp->w, talk_screen_temp->h); clear_to_color(talk_screen, makecol(128, 128, 128)); blit(talk_screen_temp, talk_screen, 0, 105, 0, 0, talk_screen_temp->w, talk_screen_temp->h); y_pos-=105; } } void close_button_handler(void) { exit_chat = true; } void start() { allegro_init(); //Allegro needed function install_keyboard(); //Instaling keyboard //////////////// GRAPHICS INIT ///////////////////////////////// set_color_depth(16); if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) <= -1) { set_color_depth(15); if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) <= -1) { allegro_message("Error initializating graphics", NULL); exit(1); } } //////////////// WINDOW PROCEDURES //////////////////////////// // Window title, write everything you want here set_window_title("Chatic v0.01 SERWER"); // callback function for this litte [X] button in top right of // your window, we will write it soon, if you dont want it // just comment this line set_close_button_callback(close_button_handler); //For working in background, very important. //You will get strange results if you dont put this line set_display_switch_mode(SWITCH_BACKGROUND); //For double buffering buffer = create_bitmap(SCREEN_W,SCREEN_H); //Our chat will be composited with two windows: //talk_screen where messages are being displayed, //and write_screen where you type your message. //write_screen isnt really exist, we will write //directly on buffer // (-81) is size of talk screen and also size of write_screen talk_screen = create_bitmap(SCREEN_W, SCREEN_H-81); //temporary bitmap, we will need it to make text move //smothly talk_screen_temp = create_bitmap(SCREEN_W, SCREEN_H-81); //clearing bitmaps clear_to_color(buffer, makecol(0, 0, 0)); clear_to_color(talk_screen, makecol(128, 128, 128)); clear_to_color(talk_screen_temp, makecol(128, 128, 128)); //Configuration //I've maked simple config file to read your nickname //and other options, if you don't want to use it, comment //this but rememer to declare char* nickname = "nickname"; push_config_state(); set_config_file("config.ini"); nickname = ustrdup(get_config_string("user", "name", "noname")); pop_config_state(); //Messages on start textprintf_ex(talk_screen, font, 1, 1, makecol(255,255,255), -1, "Chatic version 0.01 SERVER started..."); textprintf_ex(talk_screen, font, 1, 15, makecol(255,255,255), -1, "All ok..."); zcom = new ZoidCom("netlog.txt"); //creating new object //netlog.txt is a file //where zoidcom will save his log if (!zcom || !zcom->Init()) exit(255); //if initialization fails -> quit //Important thing, timeout, after this time (in miliseconds), //if the other machine isn't respondig we break connection //and func ZCom_cbConnectionClosed() is executed. //10 seconds timeout zcom->setConnectionTimeout(10000); //Our server object based on class we defined eariler server = new Server(); server->ZCom_setDebugName("Chatic Server"); // port 10000 server->ZCom_initSockets(true, 10000, 0); } void DrawAll() { //Lines for divide talk_screen from write_screen line(buffer, 0, SCREEN_H-79, SCREEN_W, SCREEN_H-79, makecol(192,192,192)); line(buffer, 0, SCREEN_H-80, SCREEN_W, SCREEN_H-80, makecol(255,255,255)); line(buffer, 0, SCREEN_H-81, SCREEN_W, SCREEN_H-81, makecol(192,192,192)); //Your nickname should be always drawed to write_screen //I will explain why, later textprintf_ex(buffer, font, 1, SCREEN_H-76, makecol(255,255,255), -1, "%s>", nickname); //Drawing talk_screen to buffer and buffer to screen blit(talk_screen, buffer, 0, 0, 0, 0, talk_screen->w, talk_screen->h); blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); } void Process() { if(sts) { ZCom_BitStream *reply = new ZCom_BitStream(); //create stream reply->addString(nickname); //add your nick to stream reply->addString(message); //add your message to stream server->ZCom_sendData(other_id, reply); //send message to id //stored on start sts = false; //there is nothing more to send for(int u = 0; u < 256; u++) message[u] = 0; //clear message array } server->ZCom_processInput(); //zoidcom func processing all input server->ZCom_processOutput();//zoidcom func processing all output zcom->Sleep(1); //give rest of time to cpu } void GetInput() { if(key[KEY_ESC]) exit_chat = true; if(key[KEY_ENTER] || key[KEY_ENTER_PAD]) { put_string(message, nickname); //Drawing our message index = 0; //clearing index do{}while(key[KEY_ENTER] || key[KEY_ENTER_PAD]); //pausing clear_keybuf(); //clearing keyboard queue clear_to_color(buffer, makecol(0, 0, 0)); //clearing buffer sts = true; //telling our networking //engine (which doesn't exist //for now) that there is //something to send } if(key[KEY_BACKSPACE] && index > 0){ message[--index] = 0; do{}while(key[KEY_BACKSPACE]); clear_to_color(buffer, makecol(0, 0, 0)); textprintf_ex(buffer, font, text_length(font, nickname)+15 , SCREEN_H-76, makecol(255,255,255), makecol(0,0,0), "%s", message); clear_keybuf(); } if(keypressed()) { ch = readkey(); message[index++] = ch; textprintf_ex(buffer, font, text_length(font, nickname)+15, SCREEN_H-76, makecol(255,255,255), makecol(0,0,0), "%s", message); if(index>255) index = 255; //Brutal forcing index not to //writing over the array } } void shutdown() { destroy_bitmap(buffer); destroy_bitmap(talk_screen); destroy_bitmap(talk_screen_temp); delete server; delete zcom; allegro_exit(); } int main() { start(); do { DrawAll(); GetInput(); Process(); } while(!exit_chat); shutdown(); return 0; } END_OF_MAIN();