Intro: understanding network
============================

Use network functions for multiplayer games with UDP or TCP.
Up to NW_MAXCLIENT (defined in vgagames2.h, currently 16) players can connect.

UDP is for not-synchronized connections (it's the default), that means
packets are transmitted without waiting for the clients to read them.
TCP is for synchronized connections, that means if one client delays reading
a packet, all other clients have to wait, too.

There is support for IPv4 and IPv6
and for searching the network-server via broadcast/multicast.


How VgaGames-network-games work

There are always
 - one master player, who starts (and connects to) the network-server
 - one or more client players, who connect to the network-server
 - perhaps one or more virtual players, who are managed by the master

The master does all the work, he
 - examinates keystrokes and mouse events of the local player and moves him
 - examinates keystrokes and mouse events of the client players,
   received by the network-server, and moves them
 - moves the virtual players, using a moving algorithm
 - checks for any interaction among all players and objects
 - sends all data of all objects and players to the network-server

The clients only
 - send their keystrokes and mouse events to the network-server
 - receive data of all objects from the network-server to draw them on screen

The virtual players
 - are fully controlled by the master
 - exist only to complete missing players


The network-packet

To have access to the data of all players and objects, which are sent and
received over network, it is important to define at first the network-packet,
which holds pointers to the network-data:
 - network variables which are needed for every player
   (called player-data)
 - network variables which are needed for objects not related to players
   (called common-block)
The size of the network-packet is 1152 bytes.

The best way to do this, is to define a struct with pointers, which point
to the network variables, e.g.

  struct {
    int * x_position;  // for x-position of player-number 1 to NW_MAXCLIENT
    int * y_position;  // for y-position of player-number 1 to NW_MAXCLIENT
  } player_data[NW_MAXCLIENT];

  struct {
    char * sillytext;  // for a silly text appearing at every players screen
  } common_block;



Overview over network-starting

The master has to
 - define the structs for player-data and common-block (see above)
 - define the network variables in the network-packet
   using vg_nw_setvar() and vg_nw_setcommon()
 - set the minimum and maximum number of players
   using vg_nw_setplayer()
 - start the network-server
   using vg_nw_startserver()
 - wait for the connects of the clients
   using vg_nw_waitforconnects()
 - set the pointers of the structs for player-data and common-block to the
   network variables, using vg_nw_getvar() and vg_nw_getcommon()
 - initialize the values of the network variables (using the pointers)
 - send an initial packet to the network-server
   using vg_nw_sendinit()
Now the master can begin the game loop.

The clients have to
 - define the structs for player-data and common-block as well as the master
 - define the network variables in the network-packet as well as the master
   using vg_nw_setvar() and vg_nw_setcommon()
 - connect to the network-server
   using vg_nw_connect()
 - wait for the connects of all other players
   using vg_nw_waitforconnects()
 - set the pointers of the structs for player-data and common-block to the
   network variables, using vg_nw_getvar() and vg_nw_getcommon()
   as well as the master
 - receive the initial master packet from the network-server
   using vg_nw_recvinit()
Now the clients can begin the game loop.


Overview over the game loop

When all connections have been established,
the players can go to the game loop.

The master has to:
 - go through all players (1 to vg_nw_maxplayer())
 - examinate keystrokes and mouse events for each player:
   + If the current player-number is equal to vg_nw_myplayer()
     it's the master's player: use vg_key_update(), vg_key_pressed() and so on
   + If the current player-number is greater-equal to vg_nw_virtualplayer()
     it's a virtual player: use don't know which algorithm
   + else it's a client player:
     use vg_recvkeys() to get the client's keystrokes and mouse events,
     use for examination: vg_nw_keypressed(), vg_nw_mousepressed()
                          vg_nw_mousex(), vg_nw_mousey()
 - update every player's network-data (player-data)
   and send it to the network-server using vg_nw_senddata()
 - set common variables of the common-block (non-player related objects)
   and send them to the network-server using vg_nw_sendcommon()
 - draw the players on the screen, if they are alive (vg_nw_isalive())
 - draw the common objects on the screen

The clients have to:
 - get keystrokes and mouse events, using vg_key_update()
   (where additional an automatic network-transfer is performed),
   check for the return value (network could have been closed)
 - draw the players (1 to vg_nw_maxplayer()) on the screen,
   if they are alive (vg_nw_isalive())
 - draw the common objects on the screen

If a player wants to quit,
 - the function vg_nw_setdead() has to be called
   to inform all other players of the death of this player
 - vg_nw_close() closes the network-connection

Be careful: As long as any player is still alive, the master must not close
            the connection, because this exits the network-server, too.
            Remember the master is responsible for moving the clients and cannot
            therefore simply exit, when the master's player died.


Example

A game, where 2 up to 4 players move a little circle around.
A silly text appears.

See ../tutorial/tut-network0.html

At first we have to consider, which network variables are needed:
 - for the players we need a x-position and a y-position,
   we want to use two short-integer values
 - for the silly text we need an array[40] of char

So our structs (for master and clients) are:


  struct {
    short * xpos;  // x-position
    short * ypos;  // y-position
  } player_data[NW_MAXCLIENT];  // NW_MAXCLIENT is always save

  struct {
    char * sillytext;  // array for silly text
    int textsize;      // set it later to 40
  } common_block;



Now both master and clients have to set network variables:


  // player-data
  if (vg_nw_setvar(NWVAR_SHORT,1,"xpos") < 1) {goto error;}
  if (vg_nw_setvar(NWVAR_SHORT,1,"ypos") < 1) {goto error;}

  // common-block
  common_block.textsize=40;
  if (vg_nw_setcommon(NWVAR_CHAR,common_block.textsize,"sillytext") < 1) {
    goto error;
  }



The master starts the server (and a broadcast-/multicast-server):


  // set number of players
  int pl_min=2, pl_max=4;
  if (vg_nw_setplayer(pl_min,pl_max) < pl_min) {goto error;}

  // start network-server, UDP, timeout connect=20, IPv4, timeout receiving=30
  char serverport[]="1234", mbcastport[]="1235";
  if (vg_nw_startserver(PROTO_UDP,serverport,20,4,mbcastport,30) < 0) {
    goto error;
  }



The clients connect to the server (using the broadcast-/multicast-server):


  char serverport[]="1234", mbcastport[]="1235";
  if (vg_nw_connect(PROTO_UDP,NULL,serverport,mbcastport) < 0) {goto error;}



Both master and clients have to wait for all other connects:


  // the master may end this waiting with space-key
  if (vg_nw_waitforconnects() < 0) {goto error;}



After all connects both master and clients set the struct pointers:


  // get pointers for player-data
  int nr;
  for (nr=1; nr <= vg_nw_maxplayer(); nr++) {  // for each player
    player_data[nr-1].xpos=(short *)vg_nw_getvar("xpos",nr);
    player_data[nr-1].ypos=(short *)vg_nw_getvar("ypos",nr);
  }

  // get pointers for common-block
  common_block.sillytext=(char *)vg_nw_getcommon("sillytext");



The master initializes the values of the network variables
and sends an initial packet to the network-server:


  // initialize values
  int nr;
  for (nr=1; nr <= vg_nw_maxplayer(); nr++) {  // for each player
    player_data[nr-1].xpos[0]=SC_WIDTH/2;   // or any other x-position value
    player_data[nr-1].ypos[0]=SC_HEIGHT/2;  // or any other y-position value
  }
  snprintf(common_block.sillytext,common_block.textsize,"%s","No way out");

  // send initial packet
  vg_nw_sendinit();



The clients receive the master's initial packet from the network-server:


  if (vg_nw_recvinit() < 0) {goto error;}



Now both are ready for the game-loop.


The master's game-loop:


  for (;;) {  // game-loop
    int alive,nr,x,y;
    vg_bitmap_clear(NULL,RGB_BLACK);  // clear window

    // move players

    alive=0;  // check for any living real player
    for (nr=1; nr <= vg_nw_maxplayer(); nr++) {  // for each player

      if (vg_nw_isalive(nr)) {  // player is alive

        // save moving request in x and y
        x=y=0;
        if (nr==vg_nw_myplayer()) {  // master's player
          if (vg_key_update() < 0) {goto quitgame;}    // network closed
          if (vg_key_pressed(KEY_DCURS,LONGKEY)) {y=1;}   // down
          if (vg_key_pressed(KEY_UCURS,LONGKEY)) {y=-1;}  // up
          if (vg_key_pressed(KEY_LCURS,LONGKEY)) {x=-1;}  // left
          if (vg_key_pressed(KEY_RCURS,LONGKEY)) {x=1;}   // right
          if (vg_key_pressed(KEY_SPACE,SHORTKEY)) {       // player tired
            vg_nw_setdead(nr);
            continue;  // next player
          }
          alive=1;  // at least one real player still alive

        } else if (nr >= vg_nw_virtualplayer()) {  // virtual players
          x=time(NULL)%3-1;  // very silly moving algorithm
          y=time(NULL)%3-1;  // very silly moving algorithm

        } else {  // client players
          if (vg_nw_recvkeys(nr)) {  // client has sent a request
            if (vg_nw_keypressed(KEY_DCURS,LONGKEY)) {y=1;}   // down
            if (vg_nw_keypressed(KEY_UCURS,LONGKEY)) {y=-1;}  // up
            if (vg_nw_keypressed(KEY_LCURS,LONGKEY)) {x=-1;}  // left
            if (vg_nw_keypressed(KEY_RCURS,LONGKEY)) {x=1;}   // right
          }
          alive=1;  // at least one real player still alive
        }

        // now move according to x and y
        player_data[nr-1].xpos[0]+=x;
        if (player_data[nr-1].xpos[0] < 0) {player_data[nr-1].xpos[0]=0;}
        if (player_data[nr-1].xpos[0] >= SC_WIDTH) {
          player_data[nr-1].xpos[0]=SC_WIDTH-1;
        }
        player_data[nr-1].ypos[0]+=y;
        if (player_data[nr-1].ypos[0] < 0) {player_data[nr-1].ypos[0]=0;}
        if (player_data[nr-1].ypos[0] >= SC_HEIGHT) {
          player_data[nr-1].ypos[0]=SC_HEIGHT-1;
        }

        // send player's data to network-server
        vg_nw_senddata(nr);
      }
    } // end for each player
    if (alive==0) {break;}  // no living player

    // set silly text (common-block) and send it to network-server
    switch(time(NULL)%12) {
      case 0:
        snprintf(common_block.sillytext,common_block.textsize,"%s","No way out");
        break;
      case 4:
        snprintf(common_block.sillytext,common_block.textsize,"%s","Check your defaults");
        break;
      case 8:
        snprintf(common_block.sillytext,common_block.textsize,"%s","Medio tutissime ibis");
        break;
    }
    vg_nw_sendcommon();

    // draw players
    for (nr=1; nr <= vg_nw_maxplayer(); nr++) {  // for each player
      if (vg_nw_isalive(nr)) {  // player is alive
        vg_draw_circle(NULL,player_data[nr-1].xpos[0],player_data[nr-1].ypos[0],5+nr*2,RGB_WHITE,0);
      }
    }

    // draw silly text
    vg_draw_text(NULL,RGB_WHITE,0,0,common_block.sillytext,NULL,RGB_TRANS);

    // do rest
    vg_window_flush();
    vg_wait_time(50);
  }  // end game-loop

quitgame:
  vg_nw_close();



The clients' game-loop:


  for (;;) {  // game-loop
    int nr;
    vg_bitmap_clear(NULL,RGB_BLACK);  // clear window

    // get keystrokes and do network-transfer
    if (vg_key_update() < 0) {break;}  // network closed
    if (vg_key_pressed(KEY_SPACE,SHORTKEY)) {  // player tired
      vg_nw_setdead(vg_nw_myplayer());
      break;
    }

    // draw players
    for (nr=1; nr <= vg_nw_maxplayer(); nr++) {  // for each player
      if (vg_nw_isalive(nr)) {  // player is alive
        vg_draw_circle(NULL,player_data[nr-1].xpos[0],player_data[nr-1].ypos[0],5+nr*2,RGB_WHITE,0);
      }
    }

    // draw silly text
    vg_draw_text(NULL,RGB_WHITE,0,0,common_block.sillytext,NULL,RGB_TRANS);

    // do rest
    vg_window_flush();
    vg_wait_time(50);
  }  // end game-loop
  vg_nw_close();



FUNCTIONS

vg_nw_setplayer()
vg_nw_startserver()
vg_nw_connect()
vg_nw_waitforconnects()
vg_nw_setvar()
vg_nw_getvar()
vg_nw_setcommon()
vg_nw_getcommon()
vg_nw_dumppacket()
vg_nw_sendinit()
vg_nw_recvinit()
vg_nw_myplayer()
vg_nw_maxplayer()
vg_nw_virtualplayer()
vg_nw_isalive()
vg_nw_recvkeys()
vg_nw_keypressed()
vg_nw_mousepressed()
vg_nw_mousex()
vg_nw_mousey()
vg_nw_senddata()
vg_nw_sendcommon()
vg_nw_dumpsprite()
vg_nw_undumpsprite()
vg_nw_setdead()
vg_nw_close()

Back to Index