Tutorial: moving sunnyboys
==========================

Multiplayer program, where every player moves his own sunnyboy with arrow keys.
Up to 4 players are can connect, virtual players replace missing players.


step 0
The global declaration of the program in tut-network1.h,
used by master player and client players:

We declare the network ports:

==>
static char serverport[]="2222";  // port of network-server
static char mbcastport[]="2223";  // broadcast/multicast port to get server-ip
<== We want to send the sunnyboy's position over network, so we declare following struct. To save network memory, we don't use an integer for x-position and an integer for y-position, but an unsigned short for x- and y-position, using the formula: y-position * SC_WIDTH + x-position (pl_data[].n_xypos[0] = pl_data[].ypos * SC_WIDTH + pl_data[].xpos) ==>
struct {
  /* player's values */
  bitmap * sunboy;     /* players sunnyboy */
  int xpos,ypos;       /* position of sunnyboy */
  int keyno,keycount;  /* only for virtual player's moving algorithmus */

  /* network pointers */
  unsigned short * n_xypos;  /* pointer to network-x/y-position */
} pl_data[NW_MAXCLIENT];
<== Setting the network variables before connecting to network will be done by: ==>
static int set_variables() {
  memset(pl_data,0,sizeof(pl_data));
  /* define network variables: pl_data[].n_xypos[0] */
  if (vg_nw_setvar(NWVAR_USHORT,1,"xypos") < 1) {return(-1);}

  return(0);
} /* end set_variables */
<== Setting the struct pointer "n_xypos" to the network variable after connecting and creating the sunnyboys will be done by: ==>
static int get_pointers(int number_of_pl) {
/* 1.arg: total number of players */
  int i1;
  /* selection of colors for sunnyboys */
  int sunny_color[8]={CL_RED, CL_YELLOW, CL_GREEN, CL_BLUE, CL_VIOLET, CL_PINK, CL_ORANGE, CL_TURQUOISE};

  memset(pl_data,0,sizeof(pl_data));

  /* get network pointers */
  for (i1=1; i1 <= number_of_pl; i1++) {  // for each player
    pl_data[i1-1].n_xypos=(unsigned short *)vg_nw_getvar("xypos",i1);

    /* create sunnyboy of player */
    pl_data[i1-1].sunboy=create_sunnyboy(vg_color_index(sunny_color[(i1-1)%8],100));
    if (pl_data[i1-1].sunboy==NULL) {return(-1);}
  }
  return(0);
} /* end get_pointers */
<== For the rest, see tut-network1.h. In the following we will concentrate to the essential code for networking. step 1a The master player (tut-network1.c) sets the network variables, starts the network-server, connects to it, sets the pointers, initializes the pointer values and sends the initial packet to the clients. ==>
  /* +++ start networking +++ */

  /* set network variables */
  if (set_variables() < 0) {vg_window_close(); exit(1);}

  /* start network-server and connect to it,
  ** pass the number of players we want to have
  */
  if (start_server(nop_from,nop_to) < 0) {vg_window_close(); exit(1);}

  /* get network pointers and create sunnyboys */
  if (get_pointers(vg_nw_maxplayer()) < 0) {vg_nw_close(); vg_window_close(); exit(1);}

  /* initialize values for network pointers */
  init_values(vg_nw_maxplayer());

  /* send initial packet to clients */
  vg_nw_sendinit();
<== The relevant code from start_server(): ==>
static int start_server(int nop_from,int nop_to) {
/* start and make connection to server
** 1.+2.arg: number of players: at least and at most
*/
  if (vg_nw_setplayer(nop_from,nop_to) < nop_from) {return(-1);}

  /* start network-server
  ** using UDP,
  ** timeout for connects 20 sec.,
  ** IPv4,
  ** timeout for not receiving any packet 30 sec.
  */
  if (vg_nw_startserver(PROTO_UDP,serverport,20,4,mbcastport,30) < 0) {return(-1);}

  /* wait for all other connects */
  if (vg_nw_waitforconnects() < 0) {vg_nw_close(); return(-1);}

  return(0);
} /* end start_server */
<== The relevant code from init_values(): ==>
static void init_values(int number_of_pl) {
/* master: set initial values for all players
** 1.arg: total number of players
*/
  int i1;
  /* set values for players */
  for (i1=1; i1 <= number_of_pl; i1++) {
    /* set initial x- and y-position */
    pl_data[i1-1].xpos=SC_WIDTH/2;
    pl_data[i1-1].ypos=SC_HEIGHT/2;

    /* set network variable: n_xypos[0]=ypos*SC_WIDTH+xpos */
    pl_data[i1-1].n_xypos[0]=(unsigned short)(pl_data[i1-1].ypos*SC_WIDTH+pl_data[i1-1].xpos);
  }
} /* end init_values */
<== step 1b The client players (tut-network1_client.c) set the network variables, connect to the network-server, set the pointers and receive the initial packet from the master. ==>
  /* +++ start networking +++ */

  /* set network variables */
  if (set_variables() < 0) {vg_window_close(); exit(1);}

  /* connect to network-server */
  if (connect_server() < 0) {vg_window_close(); exit(1);}

  /* get network pointers and create sunnyboys */
  if (get_pointers(vg_nw_maxplayer()) < 0) {vg_nw_close(); vg_window_close(); exit(1);}

  /* receive initial packet from master */
  if (vg_nw_recvinit() < 0) {vg_nw_close(); vg_window_close(); exit(1);}
<== The relevant code from connect_server(): ==>
static int connect_server() {
  /* connect to network-server via broadcast/multicast */
  if (vg_nw_connect(PROTO_UDP,NULL,serverport,mbcastport) < 0) {return(-1);}

  /* wait for all other connects */
  if (vg_nw_waitforconnects() < 0) {vg_nw_close(); return(-1);}

  return(0);
} /* end connect_server */
<== step 2a The master's game loop now examinates keystrokes - from the master himself - from the client (or virtual player, if no client present) - moves the sunnyboy according to keystrokes - sends the (new) values back to the network-server - draws the sunnyboys of all players on screen ==>
    /* go through all players */
    for (plno=1; plno <= vg_nw_maxplayer(); plno++) {

      /* get keystrokes and set pl_data[] */
      rval=getkeys(plno);
      if (rval < 0) {break;}  /* network closed or end of game requested */

      /* draw sunnyboy, if it is alive */
      if (vg_nw_isalive(plno)) {  /* player is alive */
        vg_bitmap_copyto(NULL,pl_data[plno-1].xpos,pl_data[plno-1].ypos,pl_data[plno-1].sunboy,0,0,0,0,RGB_TRANS);
      } else if (plno==vg_nw_myplayer()) {  /* master is dead, draw it rotated */
        vg_bitmap_copyto(NULL,pl_data[plno-1].xpos,pl_data[plno-1].ypos,vg_bitmap_rotate(pl_data[plno-1].sunboy,180),0,0,0,0,RGB_TRANS);
      }
    }  // end for each player
<== The relevant code from getkeys(): ==>
static int getkeys(int plno) {
/* get keystrokes (from keyboard or network or via an algorithm)
** and set pl_data[] (moving sunnyboy)
** 1.arg: number of a player
** return:  0=OK
**         -1=end of game requested or network closed
*/
  if (vg_nw_isalive(plno)) {  /* player is alive */
    int x,y;

    /* get sunnyboys moving request */
    x=y=0;
    if (plno==vg_nw_myplayer()) {
      /* your sunnyboy, move with keystrokes */
      if (vg_key_update() < 0) {return(-1);}  /* network closed */

      if (vg_key_pressed(KEY_SPACE,SHORTKEY)) {  // quit with space-key
        vg_nw_setdead(plno);  // set dead to inform network-server
        return(0);  // do not end the game (wait for the second keystroke)
      }
      /* save request values in x,y */
      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 */

    } else if (plno >= vg_nw_virtualplayer()) {
      /* virtual players, move with a simple algorithmus */
      if (pl_data[plno-1].keycount==0) {
        pl_data[plno-1].keyno=ZUFALL(4);  /* which arrow key */
        pl_data[plno-1].keycount=ZUFALL(25)+5;  /* how long pressed */
      }
      pl_data[plno-1].keycount--;
      /* save request values in x,y */
      switch (pl_data[plno-1].keyno) {
        case 0: y=1; break;
        case 1: y=-1; break;
        case 2: x=-1; break;
        case 3: x=1; break;
      }

    } else {
      /* sunnyboys of other clients,
         save request values in x,y
      */
      if (vg_nw_recvkeys(plno)) {  /* 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 */
      }
    }

    /* now really move sunnyboy, checking boundaries before */
    if (y==1) {  /* down request */
      int bound=SC_HEIGHT-vg_bitmap_height(pl_data[plno-1].sunboy)/2;
      if (++pl_data[plno-1].ypos > bound) {
        pl_data[plno-1].ypos=bound;
        pl_data[plno-1].keycount=0;
      }
    } else if (y==-1) {  /* up request */
      int bound=vg_bitmap_height(pl_data[plno-1].sunboy)/2;
      if (--pl_data[plno-1].ypos < bound) {
        pl_data[plno-1].ypos=bound;
        pl_data[plno-1].keycount=0;
      }
    }
    if (x==-1) {  /* left request */
      int bound=vg_bitmap_width(pl_data[plno-1].sunboy)/2;
      if (--pl_data[plno-1].xpos < bound) {
        pl_data[plno-1].xpos=bound;
        pl_data[plno-1].keycount=0;
      }
    } else if (x==1) {  /* right request */
      int bound=SC_WIDTH-vg_bitmap_width(pl_data[plno-1].sunboy)/2;
      if (++pl_data[plno-1].xpos > bound) {
        pl_data[plno-1].xpos=bound;
        pl_data[plno-1].keycount=0;
      }
    }

    /* update values of network pointers and send data */
    pl_data[plno-1].n_xypos[0]=(unsigned short)(pl_data[plno-1].ypos*SC_WIDTH+pl_data[plno-1].xpos);
    vg_nw_senddata(plno);

  } else {  /* player is not alive */
    if (plno==vg_nw_myplayer()) {  /* your sunnyboy */
      if (vg_key_update() < 0) {return(-1);}  /* network closed */
      if (vg_key_pressed(KEY_SPACE,SHORTKEY)) {  // quit with space-key
        return(-1);
      }
    }
  }
  return(0);
} /* end getkeys */
<== step 2b The client's game loop - calls vg_key_update() to get keystrokes and send them and receive master's network data - checks for space key, if the player wants to exit - draws the sunnyboys of all players on screen ==>
    // get keystrokes and do network-transfer
    if (vg_key_update() < 0) {break;}  // network closed
    if (vg_key_pressed(KEY_SPACE,SHORTKEY)) {  // player wants to quit
      vg_nw_setdead(vg_nw_myplayer());
      break;
    }

    /* get sunnyboys position, decompressing value of network-variable n_xypos */
    for (plno=1; plno <= vg_nw_maxplayer(); plno++) {
      pl_data[plno-1].xpos=pl_data[plno-1].n_xypos[0]%SC_WIDTH;
      pl_data[plno-1].ypos=pl_data[plno-1].n_xypos[0]/SC_WIDTH;
    }

    /* go through all players */
    for (plno=1; plno <= vg_nw_maxplayer(); plno++) {

      /* draw sunnyboy, if it is alive */
      if (vg_nw_isalive(plno)) {  /* player is alive */
        vg_bitmap_copyto(NULL,pl_data[plno-1].xpos,pl_data[plno-1].ypos,pl_data[plno-1].sunboy,0,0,0,0,RGB_TRANS);
      }
    }  // end for each player
<== step 3 To exit, just call vg_nw_close(). If the master exits with (second) space keystroke, the client also exits, because the network-server has exited, too. The whole program tut-network1.c tut-network1.h tut-network1_client.c
[Previous] - [Index] - [Next]