Pages

Thursday, December 17, 2015

Arduino Console

Everybody needs some time to do nothing,
I mean not do things because you need to do,
but instead to do things just to free your mind

I like to code, and I like electronics so I started wondering which options were in the market to be able perform my own ´experiments´ so after some research I end up with two finalists:

  • RaspberryPI
  • Arduino

Raspberry is awesome in many senses but it adds an additional layer of complexity which discoraged me to take it so I finally end buying an Arduino Nano ´Chinese clone´ for 2$, yes you are reading right, 2$!, I also bought several components to perform experiments: electric resistances, leds lights, a pair of stepper motors, ultrasound sensor, buttons, a joystick, and a pair of TFT screens , each one for about 6-8$ (1.8inches and 2.4 inches).. I felt really like stealing...

 I decided to play with the idea of actualy building my own ´Hardware´ gaming box, so I performed all these steps:


  1. Attach Arduino Nano board to a breadboard
  2. Attach the 1.8 Inches TFT screen to the breadboard
  3. Connect Arduino power-out pins to power lines in the breadboard
  4. Connect TFT pins with Digital data pins D8,D9,D10,D11 and D13
  5. Connect the joystick to Analog pins A0 and A1

And the final hardware setup:



Details for the Nano pin connections

And for the joystick Pins:

Connecting the TFT screen involved a little research as all components came without documentation, but hey Google is always your friend! I end up in a web page from a guy who made the device work and kindly shared how to setup the cables:

http://blog.simtronyx.de/en/a-1-8-inch-tft-color-display-hy-1-8-spi-and-an-arduino/

Arduino (Mini, Nano, Uno)HY-1.8 SPI
D9Pin 07 (A0)
D10 (SS)Pin 10 (CS)
D11 (MOSI)Pin 08 (SDA)
D13 (SCK)Pin 09 (SCK)
D8Pin 06 (RESET)
5V (VCC)Pin 02 (VCC)
GNDPin 01 (GND)
3.3V or 5V (VCC)Pin 15 (LED+)
GNDPin 16 (LED-)
After setting up the hardware I was ready for the coding part, it involved the following steps:

  1. Download and install Arduino IDE from https://www.arduino.cc/en/Main/Software
  2. Download and decompres libraries Adafruit_ST7735 and Adafruit_GFX from https://learn.adafruit.com/adafruit-gfx-graphics-library/overview
  3. Restart Arduino IDE and you are ready!
The only diference regarding code posted by Adafruit is the pin initialization:

#define TFT_PIN_CS   10 // Arduino-Pin an Display CS   
#define TFT_PIN_DC   9  // Arduino-Pin an 
#define TFT_PIN_RST  8  // Arduino Reset-Pin

#include <SPI.h>
#include <Adafruit_GFX.h>    
#include <Adafruit_ST7735.h> 

After this small change you are ready to use the TFT, as a proof of concept I made a simple game to just travel around a mace gathering some kind of diamonds:
 

Red point is the player, and green point is exit of the level, I am actually happy as it was very simple to set up everything and Arduino code is basically C/C++ (my old friend), so I just need to define some variables:

Adafruit_ST7735 screen = Adafruit_ST7735(screen_PIN_CS, screen_PIN_DC, screen_PIN_RST);  

int sensorY = A0; //Analog pin Y ->A0
int sensorX = A1; //Analog pin X ->A1
int sensorValueY = 0;//Value for Y analog pin
int sensorValueX = 0;//Value for X analog pin
int myx =1;//X location of the player
int myy =1;//Y location of the player

int tx=0;//Used to convert joystick X analog input into +-1
int ty=0;//Used to convert joystick Y analog input into +-1

int x=0;//temp variables for loops
int y=0;//temp variables for loops

int tilesize = 10;//Size of the tiles(in pixels)
int mymap[20][20];//Game map

int points =0;//Points..
int level =1;//Level..

Then some functions to draw the map and load levels map:
void DrawMap(){//Draw array on the screen
screen.fillScreen(ST7735_BLACK);
for (x=0;x<14;x++){
  for (y=0;y<14;y++){
    if( mymap[x][y]==1){screen.fillRoundRect(x*tilesize+1,y*tilesize+1,9,9,3,ST7735_CYAN);}//Wall
    if( mymap[x][y]==2){screen.fillRoundRect(x*tilesize+3,y*tilesize+3,5,5,3,ST7735_WHITE);}//Diamond
    if( mymap[x][y]==9){screen.fillRoundRect(x*tilesize+1,y*tilesize+1,9,9,3,ST7735_GREEN);}//End of level
    }
  }  
}

void LoadLevel1(){//Init level array
for (x=0;x<20;x++){
  for (y=0;y<20;y++){
  mymap[x][y]=2;
  }
}

for (x=0;x<13;x++){
   mymap[x][0]=1;
   mymap[x][13]=1;
}
for (x=0;x<14;x++){
   mymap[0][x]=1;
   mymap[12][x]=1;
}

I think it is easy to imagine LoadLevel2 and LoadLevel3 how do they work ;), next step is to perform initialization function from Arduino:

void setup(void) {//Arduino init function
screen.initR(INITR_BLACKTAB);   //Init TFT
LoadLevel1();//Load level 1
DrawMap();//Draw Map
screen.setTextSize(1);
screen.setTextWrap(true); 
screen.setTextColor(ST7735_WHITE);
screen.setCursor(1,140);
screen.print("Points:");//Draw starting points
screen.setCursor(50,140);
screen.print(points);
screen.setCursor(1,150);
screen.print("Level:");//Draw starting level
screen.setCursor(50,150);
screen.print(level);
}//End of setup

Not much to say here, finally there is the game loop:
void loop() {//Game loop (Arduino loop)
tx=0;
ty=0;
sensorValueY = analogRead(sensorY);//Read joystick analog Y
sensorValueX = analogRead(sensorX);//Read joystick analog D
screen.fillRoundRect(myx*tilesize+1,myy*tilesize+1,9,9,3,ST7735_BLACK);//Replace current player location with a black box

//Convert analog values to +1 .. -1
if (sensorValueY>600){ ty=1;}
if (sensorValueY<400){ ty=-1;}
if (sensorValueX>600){ tx=1;}
if (sensorValueX<400){ tx=-1;}

if( mymap[myx+tx][myy+ty]!=1){//is it a wall?
  //No: update player location
  myx+=tx;
  myy+=ty;
}

if( mymap[myx][myy]==2){//is it a coin?
  points++;
  mymap[myx][myy]=0;//Update map 
  screen.fillRect(1,140,140,10,ST7735_BLACK);
  screen.setCursor(1,140);
  screen.print("Points:");//Update points counter
  screen.setCursor(50,140);
  screen.print(points);
 }

if( mymap[myx][myy]==9){//is it end of level?
  level++;
  //If we change level we load new level
  if (level == 1)LoadLevel1();
  if (level == 2)LoadLevel2();
  if (level == 3)LoadLevel3();
  if (level == 4)level=1;//Back to first level
  DrawMap();//Draw map again
  screen.setCursor(1,140);
  screen.print("Points:");
  screen.setCursor(50,140);
  screen.print(points);
  screen.setCursor(1,150);
  screen.print("Level:");
  screen.setCursor(50,150);
  screen.print(level); 
}

screen.fillRoundRect(myx*tilesize+1,myy*tilesize+1,9,9,3,ST7735_RED);//Draw player
delay(100); //Delay to adjust game speed
}//End of loop

Compared with other coding projects I have made I feel shame of this crap coding, but it actually works, it is not very complicated to think in replacing the colored squares with a real tileset, and make some real game clones like Tetris , Asteroids, Mario bross, etc.

More content to come!



Wednesday, May 15, 2013

OpenGL 2.1 +.OBJ files

                                      <My land ahead>


I have been out of business for a long time, too long in fact, but I gathered all my strength and decided I still wont surrender , so I decided to take a few steps to continue with my project.

First thing I thought was I needed to improve my Opengl Skills, that involved:
  • Drooping my Opengl1.x skills ( forgetting all the immediate drawing stuff)
  • Start with at least Opengl 2.1 (vertex and fragment shaders, + working with VAOS)


I could have chosen Opengl 3.3 or even 4.3, but that would mean dropping support for a wide amount of persons including all the "vga integrated in the motherboard" + mac users, so Opengl 2.1 + shader model #120 was my target.

I started learning on "modern opengl tutorial sites" like:
  • http://www.opengl-tutorial.org/
  • http://ogldev.atspace.co.uk/index.html
  • http://arcsynthesis.org/gltut/
First dilemma I had  was which API could I use with Opengl, I could have used SDL, but that would mean switching to SDL2, then I had Glut, not for me!! finally I chose "GLFW": http://www.glfw.org/ which is basically a framework to use opengl + window management + keyboard + mouse input, everything simple and working on Linux /Windows /Mac OS/X isn't it nice?

So I went my way working on it until I had an idea about how to use basic functions, and made a simple class to load exported objects on "Wavefront .obj" format, the class is quite simple to use:

  • cobj myasset; //instance an object..
  • myasset.loadmodel("land.obj","map.tga"); //load the obj file + the texture
  • myasset.draw(MVP); //draws the object!
I think it's hard to make it easier to use ;) the result of my work:

                                   <Forgive me for the silly models!>

So I had an object loader, what now? I needed somewhere to place the models, that was my second dilemma: I have no artistic skill so how to make a map?...

-libnoise (http://libnoise.sourceforge.net/) came to rescue me.. again, I just recovered an old project I used to generate noise maps and made an exporter so I could write obj files with the noise map, that is basically an height map + a color map, after a few attempts I got it working, and I was able to load it into blender!!


 And, adding a model + a transparent sea texture I got the final result:

<back into the business again!>

I'm exhausted, but at the same time very happy, good things coming soon!

 
 






Thursday, November 15, 2012

New Tilesets

I'm back, again!

I have retaken my project after the MySql + OpenGL experiments

My first step after deciding to improve the graphics of my game was to update my horrible tileset, so I searched for GPL'ed tilesets,... I was about to surrender but finally I found a very nice place:  RL Tiles , a nice collection of GPL tilesets that gave me basically all what I needed:

Monsters / characters (From Dungeon Crawl):



Items (From Dungeon Crawl):



But at this point I needed some aid with the terrains + some magic effects, so I remembered a very fun, addictive rogue like made in Java: Tyrant - The Java Roguelike I asked the creator, Mike Anderson if I could include the game images and he accepted if I redistribute it under GPL license, Thanks Mike!!

Mixed terrain from Dungeon crawl / Tyrant:

Magic Effects (Courtesy from Tyrant - the Java Roguelike):




All tilesets are 1024*1024 size, with the transparency set to Red:255/Green:0/Blue:255 (Pink), each one is divided into 32* 32 tiles with a size of 32*32 pixels (1024 tiles / tileset, space to grow!), finally all them are free to redistribute / use under GPL license.

In the next post I will speak about map storage with MySql and loading to ram..

Bye

Monday, November 12, 2012

Banned from 4Shared

<Today I got a crazy e-mail from 4shared:>

Dear Sir/Madam,

We have received a notification that certain materials you posted on the 4Shared website as described below infringe upon someone else’s copyright:


http://www.4shared.com/zip/HeeXFWb7/rpg.htm


<I got really shocked... me infringing copyrights? what??? Then I went to my blog and I got into my download section : >




<So a tutorial I have made coding with Open Source tools is infringing copyrights? libSDL? maybe Code::Blocks?>

<The e-mail continues:>

Access to the material complained of has been temporarily disabled or such material has been removed. In addition, the public sharing function for your account has been disabled. For instructions on how to dispute the copyright infringement claim, please read the section titled Counter Notification Requirements below. For instructions on how to re-activate your sharing function, read the section titled Re-activation of the sharing function of your account at the end of this letter.

In accordance with 4shared’s Copyright Policy, to which you are bound by virtue of agreeing to our Terms of Use, your account will be terminated and your access to 4shared services permanently banned following three instances of copyright infringement. Unless you respond by counter notification and successfully dispute the copyright owner’s claim, this will be considered your FIRST instance of infringement under the three-strike policy.

<So I have been accused by someone I don't know from something I have not done, and unless I demonstrate It's not right I may get permanently banned... strike one!!>

Counter Notification Requirements
If you do not believe that such materials are infringing and wish to have access restored, please provide a counter notification containing the following information to 4Shared Designated Agent for Notice of Claims of Copyright Infringement (“Designated Agent”) as set forth below:
1) Identify the material that was removed or disabled, and the location where it appeared before it was removed or disabled;
2) Declare under penalty of perjury that you have a good faith belief that the material at issue was removed or disabled as a result of either mistake or misidentification of the material to be removed or disabled;
3) Provide your name, address, and telephone number;
4) Provide a statement that you consent to the jurisdiction of the federal district court for the judicial district in which your address is located, or if your address is outside of the United States, for any judicial district in which 4Shared may be found, and that you will accept service of process from the person who provided the notification or an agent of such person.
5) Provide your physical or electronic signature.

Such counter notification must be sent via e-mail to our Designated Agent at dmca_agent@4Shared.com. 


 <So if I want to avoid being banned I have to send my PERSONAL ADDRESS, PHONE and I HAVE TO SIGN??? is that a judgement? should I pay a lawyer???, the email continues...>

Alternatively, you may send the counter notification to our Designated Agent using the contact information below:

Name of Designated Agent: Ganka Hadjipetrova

Address of Designated Agent: 745 Distel Dr., Ste. 110, Los Altos, CA 94022
Facsimile Number of Designated Agent: (650) 433-5471
If we receive a proper counter notice, then we may replace or cease disabling access to the allegedly infringing materials in ten business days in accordance with the provisions of the DMCA, unless our Designated Agent first receives notice that an action has been filed by the complainant seeking a court order to restrain the account holder from engaging in infringing activity relating to the material on our system or network.




You should also note that if you make any material misrepresentation in your counter notification that the material or activity was removed or disabled by mistake or misidentification, you will be liable for all damages, including costs and attorneys’ fees, incurred by 4Shared or any copyright owner or copyright owner’s authorized licensee as the result of our relying upon such misrepresentation in replacing the removed material or ceasing to disable access to it.
In the event you provide us with counter notification compliant with all requirements described above and the rights owner does not pursue the infringement claim further, 4shared will remove this instance of copyright infringement from your record and will not consider the take-down notice for purposes of the three-strike repeat infringer termination policy. If we receive a compliant counter notification, we will also re-activate your account’s sharing capability. Note that regardless of whether or not you challenge the infringement claim via a counter notification, 4shared will keep the take-down notification for its records.


 <So resuming FIRST: Take down my content with no real facts that it's something illegal , THEN if you dare give all your personal data to someone you don't know you may get your content restored... It stinks!! >

Re-Activation of the Sharing Function of Your Account
Note that if you send us a counter notification fulfilling all requirements described above, your account’s sharing functionality will be re-activated.
In case you do not challenge the take-down notification, you may still be able to re-activate the sharing function of your account. To do that, you must remove all copyrighted content for which you do not hold author’s rights and do not otherwise have authorization to use. Once you have removed all unauthorized materials from your account, you must declare under penalty of perjury that your account does not contain infringing files here. Take note that you will still have access to your files if you do not verify your account is infringement free, but you will not be able to share them publicly.


 Thank you.
Sincerely,
4shared Support Team
support@4shared.com


 <At this moment I was thinking.... OK I'm pretty sure that is just an scammer, but when I got into my 4shared account and found my file has been removed I got really angry!!!!>

< Guess what? I wont reply, I wont stay in a place I can get blamed for anybody with no , I will let the account die and will never return here>

< +1 to Digital Millennium Copyright Act, they have stopped an indie developer from sharing his Open Source - projects at 4shared!! Must feel real pride..>

<Sweet Internet Freedom>








Thursday, October 4, 2012

libNoise, OpenGL and George R.R. Martin

Hello

       I've been quite busy in the last weeks, but still had time to investigate a pair of libs:

When I was thinking in how to make a procedural generated map I saw simple-map-generation from Amit Pattel, so I knew I had to use a noise function generator, that lead me to discovered libnoise: it is a simple library that allows to generate noise maps, apply colors , generate height maps, and many other things!!:

<2d noise map loaded (left), and a zoomed window (right) >

After playing a while with it I knew my maps would be generated by 2D noise functions , first I loaded the map with standard SDL, and made a map explorer, with the zoomed map..

Then I tought.. what if those colored quads were drawn by OpenGL???

I was kinda afraid and excited about the idea at the same time...

Excited about all the things I could do with OpenGL...

Afraid because OpenGL meant start from 0 again .... So I got all the courage I had and told me myself... "If you are into computers you will be learning all your life"

So I started to read the "OpenGL RedBook" and after like 250 pages and failing a few times I finally got my color-map to load on screen with Opengl!



Well, it was just a bunch of quads, but you could just Imagine it is a map...

After that I loaded the height map too and made the "z" from the map depend in the height, so I got the first improvements:



But I didn't wanted to make a minecraft-clone... so I decided to make the quads based in the corners, not just floating quads...






I failed in my first attempt, but had a very fun land strips...



But finally made it! I was starting to get very satisfied with the results, but I wanted to make it smooth so I divided the quads into triangles:


It looked much better, but after I applied normals to all vertexes I got the look I was looking for:






Got it, finally!! , so resuming:

<A Map>

+


<An height map>

=

<A 3D map!>

A final Note: had a few hard weeks at work so many days I just didn't had energy to code, I have been reading instead George R.R.Martin book "A song of Ice and fire", and got pretty hooked to it, in fact I read the 3rd book (+-900 pages) in a week...

I'm still were do I continue now, any suggestions?

See you

Monday, August 20, 2012

Handle user accounts with MySql

Hello

    Now we have a solid way to store and request data the next step is going to be the creation of user accounts.

First we are going to prepare the database, to do so we will connect to the mysql server:
mysql -h localhost -u root -p

Create a database
crete database rpgdata;

Use the newly created database
use rpgdata;

Then create a table, that will store the user accounts
 CREATE TABLE accounts (     id INT AUTO_INCREMENT,     fullname VARCHAR(60),     email VARCHAR(120),     password VARCHAR(30), 
    lastlogin TIMESTAMP,
 PRIMARY KEY (id));


We want to login into the game with an existent account or to create a new one, after that step we will get into game character creation (the next step), to simplify things I'm gonna make a text-only client , and a simplified server too.

The logic will be:
  1. Client connect to the server socket
  2. Client displays two options:2a-create a new character, 2b-login with an existing character
  3. If we choose to create a new character the client will show list of text fields we need to fill to create the account
  4. If we choose to login with an existing account we will fill our username (email) and password. 
In the first case, it asks for:
    • Full user name
    • email(we will use it like our user account)
    • password
    • password (another time), to prevent mistakes when typing
            cout << "NEW ACCOUNT CREATION" << endl;
            cout << "Full name?: ";
            cin  >> fullname;
            cout << "e-mail?: ";
            cin  >> username;
            cout << "Password?: ";
            cin  >> password;
            cout << "Repeat Password?: ";
            cin  >> password2;
    
            if(password.compare(password2)==0)
                {
                    done=true;
                }else{
                    cout << "Passwords don't match" << endl;
                }

    After filling all the data, it will pack it and sent it to the server
    std::stringstream Msg;
    Msg << fullname << "#"<< username << "#" << password << "#";
    NET.SockSend(CREATE_NEW_ACCOUNT,Msg.str().c_str());

    After we receive the message in the server we are going to unpack the data and get the email.
    switch (command){
                case CREATE_NEW_ACCOUNT:
                    cout << "New account request" << endl;
                    data2.clear();
                    split(data[3],'#',data2);//I split it into strings
    
                    fullname = data2[0];
                    username = data2[1];
                    password = data2[2];
    
                    cout << "Fullname " << fullname << endl;
                    cout << "Username " << username << endl;
                    cout << "Password " << password << endl;

     We will make a query to the MySql server with that email account
                    aquery.clear();
                    aquery = "select * from accounts where email = '";
                    aquery+= username;
                    aquery += "'";
                    cout << aquery << endl;
                    aSQL.executequery(aquery.c_str());//executing select query


    If there is already an account with the same email we will reject the data and send a "duplicate account" message to the client, if we don't find it, then we save a new account to the server and send a "account created properly" message to the client
     if (aSQL.getrow())
                        {
                            //Username duplicate..
                            cout << "Duplicate username.." << endl;
                            aNET.Clients[atoi(data[0].c_str())]->SockSend(DUPLICATE_ACCOUNT,"");
                        }else{
                            //We have not found username, ok to insert..
                            cout << "We have not found username, ok to insert.." << endl;
                            aquery.clear();
                            aquery = "insert into accounts values (NULL,'";
                            aquery+= fullname;
                            aquery+="' , '";
                            aquery+= username;
                            aquery+="' , '";
                            aquery+= password;
                            aquery+="', NULL)";
                            cout << aquery << endl;
                            aSQL.executequery(aquery.c_str());//executing select query
                            cout << "New user inserted" << endl;
                            aNET.Clients[atoi(data[0].c_str())]->SockSend(NEW_ACCOUNT_ACCEPTED,"");
                            }
                        break;

    After we create a new user, it will return to the main menu.
    In the second case, it asks for:
    • email
    • password

    After filling the data, it will pack and sent it to the server
        cout << "ACCOUNT LOGIN" << endl;
        cout << "e-mail?: ";
        cin >> username;
        cout << "Password?: ";
        cin >> password;
        std::stringstream Msg;
        Msg << username << "#" << password << "#";
        NET.SockSend(LOGIN_WITH_ACCOUNT,Msg.str().c_str());

    After we receive the message in the server we are going to unpack the data and get the email.
                case LOGIN_WITH_ACCOUNT:
                    cout << "Login request" << endl;
                    data2.clear();
                    split(data[3],'#',data2);//I split it into strings
    
                    username = data2[0];
                    password = data2[1];
                    cout << "Username " << username << endl;
                    cout << "Password " << password << endl;

    With the email we are going to make a query to the server asking for accounts with that email.
                    aquery.clear();
                    aquery = "select * from accounts where email = '";
                    aquery+= username;
                    aquery += "'";
                    cout << aquery << endl;
                    aSQL.executequery(aquery.c_str());//executing select query

    If we receive no accounts, obviously, there is no such account.
     if (aSQL.getrow())
                        {
            /... 
                        }else{
                        //We have not found username..
                        cout << "Username not found.." << endl;
                        aNET.Clients[atoi(data[0].c_str())]->SockSend(UNKNOW_USERNAME,"");
                        }

    If we have found that account we check the password match the one the user has typed.
    //Username found
                        cout << aSQL.row[0] << " " << aSQL.row[1] << " " << aSQL.row[2] << " " << aSQL.row[3] << " " << aSQL.row[4] << endl;
                        std::string pass;
                        pass = aSQL.row[3];
                        if (pass.compare(password)==0)
                            {
                            cout << "Username/password match, login ok" << endl;
                            aNET.Clients[atoi(data[0].c_str())]->SockSend(ACCOUNT_ACCEPTED,"");
                            aNET.Clients[atoi(data[0].c_str())]->Login  = true;
                            }else{
                            //wrong password
                            cout << "Password mismatch.. " << endl;
                            aNET.Clients[atoi(data[0].c_str())]->SockSend(BAD_PASSWORD,"");
                            }

    If the password is right, it will send an ACCOUNT_ACCEPTED message.

    In the client side, after sending data to creating a new account or login with an existing account we will wait to the server to send us a reply
    switch(command)
                {
                case ACCOUNT_ACCEPTED:
                cout << "ACCOUNT_ACCEPTED" << endl;
                reply = true;
                logedin = true;
                break;
    
                case NEW_ACCOUNT_ACCEPTED:
                cout << "NEW_ACCOUNT_ACCEPTED" << endl;
                reply = true;
                break;
    
                case DUPLICATE_ACCOUNT :
                cout << "DUPLICATE_ACCOUNT " << endl;
                reply = true;
                break;
    
                case UNKNOW_USERNAME:
                cout << "UNKNOW_USERNAME" << endl;
                reply=true;
                break;
    
                case BAD_PASSWORD:
                cout << "BAD_PASSWORD" << endl;
                reply=true;
                break;
                }//switch

    And basically that's all, We have now authenticated users, ready for the next step: Player creation process!

    Notes:
    1. Don't look for encryption: it is not yet implemented so the server / client are not secure, the reason for that is 1-Simplicity 2-Easier to understand incoming / outgoing packets and 3-It is very easy to add encryption later.
    2. Messages are not encoded: no binary numbers, just plain text, the reason the same in point 1.1 and 1.2 : making things simpler to understand.
    The full code can be downloaded at https://code.google.com/p/jbfprj/downloads/list or at the svn

    See you!

    Tuesday, August 7, 2012

    A server socket class

    Hello

    I'm going to describe a new class, (last one before I start with the server)

    We already have a class to store persistent data, now we need a way to communicate with the world.

    I need a class to start a TCP socket server then accept incoming TCP connections , I need to be able to send data to any of the clients too, and finally enqueue the incoming data so I can process it when I need to do.

    What the class is going to do is:
    1. Open a server socket in a port X
    2. Start a thread that will be listening in port X
    3. When we receive connections it will save the client info (ip/port) to a vector so we can communicate with the client, every client starts a new thread to get the incoming data from each client (it is a blocking-socket server)
    4. The data is stored into two different queues depending if the client has logged in  or not, I made that because I will apply different priority for the queues , the main (client updates) must be processed with maximum priority other way clients will be lagging.. in the other hand client login request can wait a pair of seconds without big problems.
    To achieve that I use two classes, first one is cNetwork, it is the main server, second one os cClient, used to store information from every connected client, the header from both classes are stored in the same file:


    <network.h>
    #ifndef NETWORK_H
    #define NETWORK_H
    
    #define MAXLEN 1024
    #include "SDL/SDL.h"
    #include "SDL/SDL_net.h"
    #include "SDL/SDL_thread.h"
    
    #include <vector>
    #include <queue>
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    class cNetwork;
    
    class cClient //the connected clients
    {
    public:
    //functions
    cClient();//creator
    virtual ~cClient();//destructor
    void StartThreads();//Start listener thread
    static int listener( void *data );//Listener Thread
    void Send(const char* Msg);//Send data to client
    
    //variables
    cNetwork *_server;//To comunicate with main server instance
    TCPsocket socket;//client socket
    SDL_Thread *ListenerThread;//The network client thread
    int         threadReturnValueclient;
    Uint16 UID;//Unique identifier
    bool Connected;//socket connected?
    bool Login; //loged in?
    
    };
    
    class cNetwork//main network server class
    {
          friend class cClient;
    
        public:
            //functions
            cNetwork();
            virtual ~cNetwork();
            void Start(Uint16 MAXUsers, Uint16 Port);
            void CleanClient(unsigned int Client);//clean a client so we can use it
            static int Master( void *data );//Master listener function/thread
            void ClearClients();//Clear disconnected clients
            void Finish();//close all client connections and finish server
    
            //variables
            queue<std::string> InputQueue;//Here we store received data
            queue<std::string> LoginQueue;//Here we store login requests
            SDL_sem *ClientsLock; //Semaphore to prevent server from accesing a variable from different threads
            cNetwork *myserver;//server pointer
            vector<cClient*> Clients;//Here I store clients
            SDL_Thread *MasterThread;//The Master thread >> accepts new clients
            int         threadReturnMaster;//return value for master thread
            bool ShutDown;//closing the server?
    
        private:
            //private variables
            IPaddress ip;//Server ip address
            TCPsocket ServerSock; //Server socket
    };
    
    #endif // NETWORK_H
    
    

    Then I have the client implementation:

    <client.cpp>
    #include "network.h"
    
    cClient::cClient()
    {
        //ctor
        ListenerThread = NULL;
        Connected = false;
    }
    
    cClient::~cClient()
    {
        //dtor
        //Stop the thread
        SDL_KillThread( ListenerThread );
        //close the socket
        SDLNet_TCP_Close(socket);
    }
    
    void cClient::StartThreads()
    {
        //Create and run the thread +
        ListenerThread = SDL_CreateThread( &listener, this );
    }
    
    int cClient::listener( void *data )
    { //While the program is not over
        cout << "Starting client listener Thread..." << endl;
        int result;
        char in_msg[MAXLEN];
        bool quit=false;
    
        while( quit == false )
            {
            memset(in_msg, 0, MAXLEN); // Clear in buffer
            if ( ((cClient*)data)->Connected)//If I'm connected...
                {
                    result=SDLNet_TCP_Recv(((cClient*)data)->socket,in_msg,MAXLEN);//Check for incoming data
                        if(result<=0)
                            {
                                //NO DATA
                                // TCP Connection is broken. (because of error or closure)
                                SDLNet_TCP_Close(((cClient*)data)->socket);
                                cout << "Socket closed..." << endl;
                                ((cClient*)data)->Connected = false;
                                quit=true;
                            }
    
                    stringstream idmsg;
                    idmsg << ((cClient*)data)->UID << "#" << in_msg;// I will add the UID from the client to the incoming message
                    try{
                        SDL_SemWait(((cClient*)data)->_server->ClientsLock);//lock smaphore to prevent client to freeze
                        cout << idmsg.str();//only for debuging
    
                        if  (((cClient*)data)-> Login==true)//Am I loged in?
                            {
                                ((cClient*)data)->_server->InputQueue.push(idmsg.str());  //I'm logged in
                            }else{
                                ((cClient*)data)->_server->LoginQueue.push(idmsg.str());  //New player requests
                            }
                        SDL_SemPost(((cClient*)data)->_server->ClientsLock);//unlock smaphore
                        }
                    catch(exception& e)
                        {
                            cout << "QUEUE IN ERROR: " << e.what() << endl;
                        }
                }//if connected
            }//while
        cout << "Closing client listener thread..." <<endl;
        return 0;
    }
    
    void cClient::Send(const char* Msg)
    {
        if (Connected==true)
            {
                int len;
                int result;
                len=strlen(Msg+1); // add one for the terminating NULL
                result=SDLNet_TCP_Send(socket,Msg,len);
                if(result<len) //If I can't send data probably I've been disconnected so....
                    {
                        cout << "SDLNet_TCP_Send: " << endl << SDLNet_GetError();
                        Connected = false;
                    }
            }else{
                cout << "Not connected!!" << endl;
                }
    }
    
    

    Notice the client stores the data in two different queues depending if the client has loged in or not, finally we have the main socket server class:

    <network.cpp>
    #include "network.h"
    
    cNetwork::cNetwork()
    {
        //ctor
    if(SDLNet_Init()==-1) {//Start SDL_NET
    cout << "SDLNet_TCP_INIT: \n " << SDLNet_GetError();
    exit(2);
    }
    
    //Clean thread variables
    MasterThread = NULL;
    ClientsLock = NULL;
    ClientsLock = SDL_CreateSemaphore( 1 );//semafor protector, previene que mas de un thread vuelque informaciĆ³n a la vez a la cola
    }
    
    cNetwork::~cNetwork()
    {
        //dtor
    SDLNet_TCP_Close(ServerSock);//Close socket
    SDLNet_Quit();//Close SDL_NET
    }
    
    
    void cNetwork::CleanClient(unsigned int Client)//clean a client so we can use it
    {
    //cout << "cleaning client: " << Client << endl;
    Clients[Client]->UID =Client;
    Clients[Client]->socket = NULL;
    Clients[Client]->_server = myserver;
    Clients[Client]->Connected = false;
    Clients[Client]->Login = false;
    }
    
    void cNetwork::Start(Uint16 MAXUsers, Uint16 Port)
    {
    
    //1-initialize MAXUsers clients
    //2-Start sock server
    
    unsigned int x;
    for (x=1;x<=MAXUsers;x++)
    {
    Clients.push_back(new cClient());//insert a new client into the clients vector
    CleanClient(Clients.size() -1);
    }
    
    cout << "OPENING SERVER SOCKET... " << endl;
    if(SDLNet_ResolveHost(&ip,NULL,Port)==-1) {
    cout << "SDLNet_TCP_ResolveHost:" << endl  << SDLNet_GetError();
    exit(1);
    }
    
    ServerSock=SDLNet_TCP_Open(&ip);
    if(!ServerSock) {
    cout << "SDLNet_TCP_open: " << endl << SDLNet_GetError();
    exit(2);
    }
    
    ShutDown = false;
    //Create and run the threads
    MasterThread = SDL_CreateThread( &Master, this );
    
    }
    
    
    
    int cNetwork::Master( void *data )//Master listener function/thread
    {
    TCPsocket new_tcpsock; //Temporary socket to store incoming connections
    new_tcpsock=NULL;
    
    cout << "Waiting for incoming connections... " << endl;
    
    bool doneMain=false;
    while(!doneMain)//MAIN LOOP
        {
        if(!new_tcpsock)//We have a new client incoming
        {
            new_tcpsock=SDLNet_TCP_Accept(((cNetwork*)data)->ServerSock); // accept a connection coming in on server_tcpsock
            SDL_Delay(5);//No new clients, wait a little
        }else{
            cout << "New client incoming..." << endl;
            unsigned int x;
            for(x=0;x<((cNetwork*)data)->Clients.size();x++)
            {
            if (((cNetwork*)data)->Clients[x]->Connected==false)
                {
                ((cNetwork*)data)->CleanClient(x);
                ((cNetwork*)data)->Clients[x]->socket=new_tcpsock;//asign the socket
                ((cNetwork*)data)->Clients[x]->Connected = true;
                ((cNetwork*)data)->Clients[x]->Login = false;
                ((cNetwork*)data)->Clients[x]->StartThreads();//start client listener thread
                break;
                }
        }//for
            new_tcpsock=NULL;//release temporary socket var
    
            }//if new data
    
    if (((cNetwork*)data)->ShutDown==true)doneMain =true;
    
    }//while
    cout << "Exiting Main thread..." << endl;
    return 0;
    }
    
    void cNetwork::ClearClients()//Clear disconnected clients
    {
    unsigned int x;
    bool done = false;
    //SDL_SemWait(ClientsLock);
    if (Clients.size()>0)
    {
    x=0;
    while (!done)
    {
     if (!Clients[x]->Connected)
                {
                Clients.erase(Clients.begin()+x);
               // cout << "Number of clients:" << Clients.size() << endl;
                if (x>0)x--;
                }//if !connected
    x++;
    if(x>=Clients.size())done=true;
    }
    }//clients size
    
    }
    
    void cNetwork::Finish()
    {
        ShutDown =true;
        SDL_WaitThread(MasterThread,&threadReturnMaster);
        unsigned int x;
        for(x=0;x<Clients.size();x++)
            {
                Clients[x]->Connected=false;//force disconnection
            }//for
    
        while (Clients.size()>0)
            {
                ClearClients();
            }
    }
    
    

    There are many things here, but I will resume it to the maximum, to do that, below there is a simple example to use the class:

    #include "network.h"//socket server class 
     
    cNetwork NET;
    NET.myserver = &NET;
    NET.Start(100,55555); //start networking, up to 100 clients on port 55555
     
    std::sting text;
    SDL_SemWait( NET.ClientsLock );
    text = NET.InputQueue.front();//take a message out from the queue
    NET.InputQueue.pop();
    SDL_SemPost( NET.ClientsLock ); 
     
    NET.Clients[X]->Send("data");//will send "data" to client X
     
    NET.Finish();//finish socket server 

    • We include the class
    • Then instantiate it
    • Start the listener thread (at this moment it is fully functional)
    • Then I extract a message from the input queue (let's suppose we have a connected client that send it..)
    • I send a message to the server ("data")
    • Finally, I close the server
    Want to test the class? basically start an empty project, add the three files I have described above and then add the example, after you compile the project you will be able to connect just using telnet (telnet localhost 55555)

    That's all for now, in the next post I will mix everything to make the client login a reality, I will make a text based client too and hope everything is clear that way.