Logo Search packages:      
Sourcecode: lbreakout2 version File versions  Download package

comm.c

/***************************************************************************
                          comm.c  -  description
                             -------------------
    begin                : Fri Aug 2 2002
    copyright            : (C) 2001 by Michael Speck
    email                : kulkanie@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "lbreakout.h"
#include "../gui/gui.h"
#include "../game/game.h"
#include "game.h"
#include "client_data.h"
#include "bricks.h"
#include "extras.h"
#include "comm.h"

extern char net_buffer[MAX_MSG_SIZE + PACKET_HEADER_SIZE];
extern int net_buffer_cur_size;
extern int msg_read_pos;
extern char msgbuf[MAX_MSG_SIZE];
extern int msglen;
extern NetSocket client;
extern StkFont *chat_font_error;
extern int client_is_connected;
extern int client_state;
extern Game *game, *local_game;
extern Player *cur_player;
extern Paddle *r_paddle;
#ifdef AUDIO_ENABLED
extern StkSound *wav_shot, *wav_reflect_paddle, *wav_reflect_brick, *wav_attach;
#endif
extern int game_stats[2][7];
extern int game_over;
extern int client_comm_delay;
extern int stats_received;
extern GuiWidget *dlg_pauseroom;
extern List *client_levelsets;
extern int client_recv_limit;

extern void set_state( int newstate );
extern void init_next_round( void );
extern void display_final_stats( void );
extern void open_pause_chat( char *text );
extern void close_pause_chat( void);

extern int last_ball_brick_reflect_x; /* HACK: used to play local sound */
extern int last_ball_paddle_reflect_x; /* HACK: used to play local sound */
extern int last_ball_attach_x; /* HACK: used to play local sound */
extern int last_shot_fire_x; /* HACK: used to play local sound */

/* handle modifications game::mod_* which were either copied
 * from local_game or received via network, apply the changes to
 * game and clear these entries. the current game entry must've
 * been set to 'game'. Ball and shot updates have already been
 * applied to the game context. */
#ifdef NETWORK_ENABLED
static void handle_mods()
{
      BrickHit *brickhits;
      int *extras, count;
      int i, j;

#ifdef AUDIO_ENABLED
    /* play sounds for attached, reflected balls or fired shots */
    /* HACK: in a local game use externals from game/balls.c
     * last_ball_*_x to play sound stereo. this cannot be used for
     * network games */
    if ( local_game == 0 )
    {
        if ( game_get_fired_shot_count() )
            stk_sound_play( wav_shot );
        if ( game_get_brick_reflected_ball_count() )
            stk_sound_play( wav_reflect_brick );
        if ( game_get_paddle_reflected_ball_count() )
            stk_sound_play( wav_reflect_paddle );
        if ( game_get_attached_ball_count() )
            stk_sound_play( wav_attach );
    }
    else
    {
        if ( last_ball_brick_reflect_x != -1 )
            stk_sound_play_x( last_ball_brick_reflect_x, wav_reflect_brick );
        if ( last_ball_paddle_reflect_x != -1 )
            stk_sound_play_x( last_ball_paddle_reflect_x, wav_reflect_paddle );
        if ( last_ball_attach_x != -1 )
            stk_sound_play_x( last_ball_attach_x, wav_attach );
        if ( last_shot_fire_x != -1 )
            stk_sound_play_x( last_shot_fire_x, wav_shot );
        last_ball_brick_reflect_x = -1;
        last_ball_paddle_reflect_x = -1;
        last_ball_attach_x = -1;
        last_shot_fire_x = -1;
    }
#endif
      /* handle brick hits and create new extras. the extras
       * are moved independently by the client and destroyed on
       * collection but DO NOT TAKE EFFECT */
      brickhits = game_get_brick_hits( &count );
      for ( i = 0; i < count; i++ )
            client_handle_brick_hit( &brickhits[i] );
      
      /* these collected extras take effect */
      for ( i = 0; i < game->paddle_count; i++ ) {
            extras = game_get_collected_extras( i, &count );
            for ( j = 0; j < count; j++ )
                  client_handle_collected_extra( game->paddles[i], extras[j] );
      }
      
      game_reset_mods();
}
#endif

#ifdef NETWORK_ENABLED
/* copy stuff from local_game to game */
static void comm_recv_local( void )
{
      Ball *ball, *ballcopy;
      Shot *shot, *shotcopy;

      /* copy balls */
      list_clear( game->balls );
      list_reset( local_game->balls );
      while ( (ball = list_next(local_game->balls)) ) {
            ballcopy = salloc( 1, sizeof(Ball) );
            ballcopy->x = ball->x;
            ballcopy->y = ball->y;
            ballcopy->attached = ball->attached;
            if ( ball->attached ) {
                  if ( ball->paddle == local_game->paddles[0] )
                        ballcopy->paddle = game->paddles[0];
                  else
                        ballcopy->paddle = game->paddles[1];
            }
            list_add( game->balls, ballcopy );
      }
      
      /* copy shots */
      list_clear( game->shots );
      list_reset( local_game->shots );
      while ( (shot = list_next(local_game->shots)) ) {
            shotcopy = salloc( 1, sizeof(Shot) );
            shotcopy->x = shot->x;
            shotcopy->y = shot->y;
            list_add( game->shots, shotcopy ); 
      }
      
      /* copy score */
      game->paddles[0]->score = local_game->paddles[0]->score;

      /* copy level_over */
      game->level_over = local_game->level_over;
      game->winner = local_game->winner;

      /* modifications are transferred to game::mod
       * and handled by the same function that does this
       * for net updates below. while handle_mods()
       * resets the mods in the game struct, the
       * local_game mods must be cleared as well */
      game->mod = local_game->mod;
      handle_mods();
      local_game->mod = game->mod;

      return;
}

/* parse incoming packets and handle them according to the client's
 * state */
static void comm_parse_packet()
{
      BrickHit *brickhits;
      unsigned char type;
      int *extras, count;
      int i, j;
      char name[16];
      int handled;

      /* the list of shots is reset before as shot_positions
       * will not be send when no shots are available */
      list_clear( game->shots );
      
      while ( 1 ) {
            type = (unsigned)msg_read_int8(); handled = 0;
            
            if ( msg_read_failed() ) break; /* no more messages */

            msglen = 0; /* game unpack functions require a position pointer which
                         is always 0 at the moment */
            
            switch ( type ) {
                  case MSG_SET_COMM_DELAY:
                        client_comm_delay = msg_read_int16();
                        printf( "comm_delay set to %i\n", client_comm_delay );
                        handled = 1;
                        break;
                  case MSG_DISCONNECT:
                        /* close the pause chatroom window if open */
                        if ( client_state == CS_PAUSE )
                              close_pause_chat();

                        set_state( CS_FATAL_ERROR );
                        display_text( chat_font_error, "You've been disconnected." );
                        client_data_clear();
                        client_is_connected = 0;
                        handled = 1;
                        break;
                  case MSG_ERROR:
                        /* close the pause chatroom window if open */
                        if ( client_state == CS_PAUSE )
                              close_pause_chat();
                        /* break up game but wait for the stats */
                        set_state( CS_FATAL_ERROR );
                        display_text( chat_font_error, msg_read_string() );
                        handled = 1;
                        break;
                  case MSG_LEVEL_DATA:
                        cur_player->next_paddle_id = msg_read_int8(); /* top or bottom? */
                        msg_read( 536, msgbuf );
                        if ( !msg_read_failed() ) {
                              comm_unpack_level( &cur_player->snapshot, msgbuf, &msglen );
                              cur_player->next_level_received = 1;
                              handled = 1;
                        }
                        else {
                              set_state( CS_FATAL_ERROR );
                              display_text( chat_font_error, "corrupted level data" );
                        }
                        break;
                  case MSG_ADD_USER:
                        i = msg_read_int32();
                        snprintf( name, 16, msg_read_string() ); name[15] = 0;
                        if ( msg_read_failed() ) break;
                        client_add_user( i, name );
                        handled = 1;
                        break;
                  case MSG_REMOVE_USER:
                        i = msg_read_int32();
                        if ( msg_read_failed() ) break;
                        client_remove_user( i );
                        handled = 1;
                        break;
                  case MSG_ADD_LEVELSET:
                        list_add( client_levelsets, strdup(msg_read_string()) );
                        handled = 1;
                        break;
                  case MSG_PAUSE:
                        open_pause_chat( "Remote player has paused the game." );
                        handled = 1;
                        break;
                  case MSG_UNPAUSE:
                        close_pause_chat();
                        handled = 1;
                        break;
                  case MSG_CHATTER:
                        client_add_pausechatter( msg_read_string(), 0 );
                        handled = 1;
                        break;
            }

            /* game_only packets but received any time */
            //if ( client_state == CS_PLAY ) 
            switch ( type ) {
                  case MSG_PADDLE_STATE:
                        comm_unpack_paddle( r_paddle, net_buffer, &msg_read_pos );
                        handled = 1;
                        break;
                  case MSG_SHOT_POSITIONS:
                        comm_unpack_shots( net_buffer, &msg_read_pos );
                        handled = 1;
                        break;
                  case MSG_BALL_POSITIONS:
                        comm_unpack_balls( net_buffer, &msg_read_pos );

#ifdef AUDIO_ENABLED
                        /* play sounds for attached, reflected balls or fired shots */
                        if ( game_get_fired_shot_count() )
                              stk_sound_play( wav_shot );
                                if ( game_get_brick_reflected_ball_count() )
                                        stk_sound_play( wav_reflect_brick );
                                if ( game_get_paddle_reflected_ball_count() )
                                        stk_sound_play( wav_reflect_paddle );
                        if ( game_get_attached_ball_count() )
                              stk_sound_play( wav_attach );
                        game->mod.fired_shot_count = 0;
                        game->mod.attached_ball_count = 0;
                        game->mod.brick_reflected_ball_count = 0;
                        game->mod.paddle_reflected_ball_count = 0;
#endif
                        handled = 1;
                        break;
                  case MSG_SCORES:
                        comm_unpack_scores( net_buffer, &msg_read_pos );
                        handled = 1;
                        break;
                  case MSG_BRICK_HITS:
                        comm_unpack_brick_hits( net_buffer, &msg_read_pos );

                        /* handle brick hits and create new extras. the extras
                         * are moved independently by the client and destroyed on
                         * collection but DO NOT TAKE EFFECT */
                        brickhits = game_get_brick_hits( &count );
                        for ( i = 0; i < count; i++ )
                              client_handle_brick_hit( &brickhits[i] );
                        game->mod.brick_hit_count = 0;
                        handled = 1;
                        break;
                  case MSG_NEW_EXTRAS:
                        comm_unpack_collected_extras( net_buffer, &msg_read_pos );
                        
                        /* these collected extras take effect */
                        for ( i = 0; i < game->paddle_count; i++ ) {
                              extras = game_get_collected_extras( i, &count );
                              for ( j = 0; j < count; j++ )
                                    client_handle_collected_extra( 
                                          game->paddles[i], extras[j] );
                        }
                        game->mod.collected_extra_count[0] = 0;
                        game->mod.collected_extra_count[1] = 0;
                        handled = 1;
                        break;
                  case MSG_ROUND_OVER:
                        game->level_over = 1;
                        game->winner = msg_read_int8();
                        handled = 1;
                        break;
                  case MSG_LAST_ROUND_OVER:
                        game->level_over = 1;
                        game->winner = msg_read_int8();
                        game_over = 1;
                        handled = 1;
                        break;
            }

            /* stats may arrive anywhere */
            switch ( type ) {
                  case MSG_GAME_STATS:
                        memset( game_stats, 0, sizeof( game_stats ) );
                        game_stats[0][0] = msg_read_int8();
                        game_stats[1][0] = msg_read_int8();
                        game_stats[0][1] = msg_read_int8();
                        game_stats[1][1] = msg_read_int8();
                        game_stats[0][2] = msg_read_int8();
                        game_stats[1][2] = msg_read_int8();
                        game_stats[0][3] = msg_read_int32();
                        game_stats[1][3] = msg_read_int32();
                        game_stats[0][4] = msg_read_int8();
                        game_stats[1][4] = msg_read_int8();
                        game_stats[0][5] = msg_read_int8();
                        game_stats[1][5] = msg_read_int8();
                        game_stats[0][6] = msg_read_int8();
                        game_stats[1][6] = msg_read_int8();
                        stats_received = 1;
                        handled = 1;
                        break;
            }
      
            if ( !handled ) {
                  printf( "game: state %i: invalid message %x: skipping %i bytes\n",
                        client_state, type, net_buffer_cur_size - msg_read_pos );
                  msg_read_pos = net_buffer_cur_size;
            }
      }
}
#endif

/* send state of local paddle */
void comm_send_paddle( Paddle *paddle )
{
      if ( game->game_type == GT_LOCAL ) {
            /* simply copy the data */
            if ( local_game->paddles[PADDLE_BOTTOM]->x != paddle->x )
                  local_game->paddles[PADDLE_BOTTOM]->invis_delay = PADDLE_INVIS_DELAY; 
            local_game->paddles[PADDLE_BOTTOM]->x = paddle->x;
            local_game->paddles[PADDLE_BOTTOM]->fire_left = paddle->fire_left;
            local_game->paddles[PADDLE_BOTTOM]->fire_right = paddle->fire_right;
            local_game->paddles[PADDLE_BOTTOM]->ball_return_key_pressed = 
                  paddle->ball_return_key_pressed;
                local_game->paddles[PADDLE_BOTTOM]->maxballspeed_request = 
                        paddle->maxballspeed_request;
                local_game->paddles[PADDLE_BOTTOM]->maxballspeed_request_old = 
                        paddle->maxballspeed_request_old;
            return;
      }

      msgbuf[0] = MSG_PADDLE_STATE; msglen = 1;
      comm_pack_paddle( paddle, msgbuf, &msglen );
      client_transmit( CODE_BLUE, msglen, msgbuf );
}

void comm_send_short( int msg )
{
      if ( game->game_type == GT_LOCAL ) return;

      /* send these messages as code blue. if they are dropped
       * the player and server will notice as nothing happens */
      msgbuf[0] = msg;
      client_transmit( CODE_BLUE, 1, msgbuf );
}

/* receive any data but only handle such messages valid
 * in the current state. */
void comm_recv( void )
{
#ifdef NETWORK_ENABLED
      int recv_limit;
      
      /* fake communication in local game */
      if ( game->game_type == GT_LOCAL ) {
            comm_recv_local();
            return;
      }
      
      if ( !client_is_connected ) return;

      recv_limit = client_recv_limit; /* limited number of packets if not -1 */
      while ( net_recv_packet() && ( recv_limit==-1 || recv_limit > 0) ) {
            /* check if this is a valid packet and update the socket */
            if ( msg_is_connectionless() )
                  msg_begin_connectionless_reading();
            else
                  if ( !socket_process_header( &client ) ) 
                        continue;
            comm_parse_packet();
            
            if ( recv_limit != -1 ) recv_limit--;
      }
#endif
}

/* send a heartbeat every 2 seconds except in the actual game as we
 * send paddle updates there */
void comm_send_heartbeat( void )
{
      static int last_heartbeat = 0;
      
      if ( client_state != CS_PLAY ) 
      if ( time(0) >= last_heartbeat + 3 ) {
            last_heartbeat = time(0);
            msgbuf[0] = MSG_HEARTBEAT;
            client_transmit( CODE_BLUE, 1, msgbuf );
      }

}


Generated by  Doxygen 1.6.0   Back to index