⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 controller.c

📁 一个网络和单机的麻将游戏源码
💻 C
📖 第 1 页 / 共 5 页
字号:
	  despatch_line(i,line);	}      }    }  }}/* function to act on Player Messages. NB it takes a connection number,   not an id, since ids may be unassigned.*/static void handle_pmsg(PMsgMsg *pmp, int cnx);/* despatch_line: process the line of input received on cnx. */static void despatch_line(int cnx, char *line) {  PMsgMsg *pmp;  if ( logfile ) {    fprintf(logfile,"<cnx%d %s",cnx,line);  }  if ( line == NULL ) {    warn("receive error on cnx %d, player id %d\n",cnx, cnx_to_id(cnx));    handle_cnx_error(cnx);    return;  }  pmp = decode_pmsg(line);  /* increment the sequence number */  connections[cnx].seqno++;  if ( pmp == NULL ) {    CMsgErrorMsg em;    em.type = CMsgError;    em.error = "Protocol error";    em.seqno = connections[cnx].seqno;    send_packet(cnx,(CMsgMsg *)&em,1);    warn("Protocol error on cnx %d, player id %d; ignoring\n",cnx, cnx_to_id(cnx));    return;  }  handle_pmsg(pmp,cnx);}/* little function to check that the minimum time has passed    since the last tile moving activity.   The argument multiplies the usual delay time between the current   call and the next call. It's used to increase the delay after   claim implementations. bloody emacs ' */static void check_min_time(float factor) {  static struct timeval disctime, timenow;  static int min_time_this_time;  if ( min_time_this_time > 0 ) {    int n;    if ( disctime.tv_sec > 0 ) { /* not firsttime */      gettimeofday(&timenow,NULL);      n = (timenow.tv_sec * 1000 + timenow.tv_usec/1000)	- (disctime.tv_sec * 1000 + disctime.tv_usec/1000);      n = 100*min_time_this_time - n;      if ( n > 0 ) usleep(1000*n);    }    gettimeofday(&disctime,NULL);  }  min_time_this_time = (int)(min_time*factor);}static void handle_pmsg(PMsgMsg *pmp, int cnx) {  /* We mustn't act on most requests if the game is not active.     There's a race possible otherwise: we can suspend the game, and     meanwhile a player sends a request: we then act on this, but it     doesn't get into the history records of the suspended players. */  if ( ! the_game->active ) {    CMsgErrorMsg em;    em.type = CMsgError;    em.error = "Game not active";    em.seqno = connections[cnx].seqno;    switch ( pmp->type ) {    case PMsgSaveState:    case PMsgLoadState:    case PMsgConnect:    case PMsgSetPlayerOption:    case PMsgSendMessage:    case PMsgQueryGameOption:    case PMsgListGameOptions:      break; /* these are OK */    default:      send_packet(cnx,(CMsgMsg *)&em,0);      return;    }  }  /* check for debugging messages */  if ( ! debug && pmp->type >= DebugMsgsStart ) {    CMsgErrorMsg em;    em.type = CMsgError;    em.seqno = connections[cnx].seqno;    em.error = "Debugging not enabled";    send_packet(cnx,(CMsgMsg *)&em,0);    return;  }  switch ( pmp->type ) {  case PMsgSaveState:    {       PMsgSaveStateMsg *m = (PMsgSaveStateMsg *)pmp;      CMsgErrorMsg em;      PlayerP p; int id; seats seat;      char *res;            id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);            em.type = CMsgError;      em.seqno = connections[cnx].seqno;            if ( (res = save_state(the_game,m->filename)) ) {	if ( the_game->protversion >= 1025 ) {	  CMsgStateSavedMsg ssm;	  ssm.type = CMsgStateSaved;	  ssm.id = id;	  ssm.filename = res;	  send_all(the_game,&ssm);	}      } else {	em.error = "Unable to save state";	send_id(id,&em);      }      return;    }  case PMsgLoadState:    {      PMsgLoadStateMsg *m = (PMsgLoadStateMsg *) pmp;      CMsgErrorMsg em;      PlayerP p; int id; seats seat;            id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);            em.type = CMsgError;      em.seqno = connections[cnx].seqno;      em.error = NULL;       /* I think we can load state any time before the game starts,	 and only then */      if ( protocol_version < 1038 ) {	em.error = "Not all players support dynamic state loading";      } else if ( game_has_started(the_game) ) {	em.error = "Can't load game state when already playing";      } else if ( m->filename == NULL || m->filename[0] == 0 ) {	em.error = "No game file specified in LoadState";      } else {	int i;	CMsgPlayerMsg pm;	loadstate = 1;	strmcpy(loadfilename,m->filename,1023);	/* first delete all players from the clients */	pm.type = CMsgPlayer;	pm.name = NULL;	for ( i = 0 ; i < MAX_CONNECTIONS ; i++ ) {	  if ( connections[i].inuse && connections[i].player ) {	    pm.id = connections[i].player->id;	    send_all(the_game,&pm);	  }	}	/* now remove from maps ... */	for ( i = 0 ; i < MAX_CONNECTIONS ; i++ ) {	  if ( connections[i].inuse && connections[i].player ) {	    remove_from_maps(i);	    num_connected_players--;	  }	}	if ( ! load_state(the_game) ) {	  em.error = "Loading game state failed";	}	the_game->active = 0;	/* now reprocess connection messages */	for ( i = 0 ; i < MAX_CONNECTIONS ; i++ ) {	  if ( connections[i].inuse && connections[i].cm) {	    handle_pmsg(connections[i].cm,i);	  }	}      }      if ( em.error ) {	send_id(id,&em);      }      return;    }  case PMsgConnect:    {      PMsgConnectMsg *m = (PMsgConnectMsg *) pmp;      PlayerP p = NULL;      CMsgConnectReplyMsg cm;      CMsgPlayerMsg thisplayer;      CMsgGameMsg gamemsg;      char *refusal = NULL;      static int player_id = 0;      int i;      /* start filling in reply message */      cm.type = CMsgConnectReply;      cm.pvers = PROTOCOL_VERSION;      if ( m->pvers/1000 != PROTOCOL_VERSION/1000 ) refusal = "Protocol major version mismatch";      if ( num_connected_players == NUM_PLAYERS )	refusal = "Already have players";      /* it's reasonable to insist on a name being supplied */      if ( m->name == NULL ) 	refusal = "A non-empty name must be given";      /* If we have a previous game state loaded, we need to associate	 this player with one in the game. Match on ids */      if ( loadstate && !refusal ) {	/* if no id specified, match on names */	if ( m->last_id != 0 ) {	  for ( i = 0; i < NUM_SEATS		  && m->last_id != the_game->players[i]->id ; i++ ) ;	} else {	  for ( i = 0; i < NUM_SEATS		  && (strcmp(m->name,the_game->players[i]->name) != 0) ; i++ ) ;	}	/* if no id required, just match this to the first player	   not currently connected */	if ( i == NUM_SEATS && noidrequired ) {	  for ( i = 0; i < NUM_SEATS 		  && id_to_cnx(the_game->players[i]->id) >= 0 ; i++ );	}	if ( i == NUM_SEATS ) {	  refusal = "Can't find you in the resumed game";	} else {	  /* better check that this player isn't already connected */	  if ( id_to_cnx(m->last_id) >= 0 )	    refusal = "Your id is already connected";	  else {	    p = the_game->players[i];	    set_player_name(p,m->name); /* not preserved by saving */	    setup_maps(cnx,p); /* set up maps between id and cnx etc*/	    num_connected_players++;	  }	}      } else {	/* ignore the lastid field, and assign this player a new	   id */	p = game_id_to_player(the_game,0); /* get free player structure */	if ( p == NULL ) {	  warn("can't get player structure; exiting");	  exit(1);	}	initialize_player(p);	num_connected_players++;	set_player_id(p,++player_id);	set_player_name(p,m->name);	/* store the id of the first human player to connect.	   This assumes that computer players are called Robot.. */	if ( ! first_id 	     && strncmp(p->name,"Robot",5) != 0 ) first_id = num_connected_players;	setup_maps(cnx,p); /* set up maps between id and cnx etc*/      }      if (refusal) {	cm.id = 0;	cm.reason = refusal;	send_packet(cnx,(CMsgMsg *)&cm,1);	close_connection(cnx);	return;      }      /* send the reply */      cm.id = p->id;      cm.reason = NULL;      send_packet(cnx,(CMsgMsg *)&cm,1);      /* store the protocol version of this player */      pextras(p)->protversion = m->pvers;      /* Yes, this is right: the fact that this player is connecting	 means that it has been disconnected! */      pextras(p)->disconnected = 1;      /* keep a copy of the message if not already there */      if ( connections[cnx].cm == NULL ) {	connections[cnx].cm = pmsg_deepcopy(pmp);      }      /* Now we need to tell this player who's already here, and	 tell the others about this one */      thisplayer.type = CMsgPlayer;      thisplayer.id = p->id;      thisplayer.name = p->name;      for ( i = 0; i < NUM_SEATS; i++ ) {	CMsgPlayerMsg pm;	PlayerP q;	q = the_game->players[i];	if ( q->id == 0 ) continue;		if ( q->id == p->id ) continue;	pm.type = CMsgPlayer;	pm.id = q->id;	pm.name = q->name;	  	send_packet(cnx,(CMsgMsg *)&pm,1);	send_id(q->id,&thisplayer); /* fails harmlessly if not connected */      }      /* set the protocol version to the greatest supported version.	 We inspect all players again in case somebody disconnected	 before the game was set up */      protocol_version = PROTOCOL_VERSION;      for ( i = 0; i < NUM_SEATS ; i++ ) {	PlayerP q = the_game->players[i];	if ( q->id == 0 ) continue;	if ( protocol_version > pextras(q)->protversion )	  protocol_version = pextras(q)->protversion;      }      /* if not everybody is connected, just wait for the next */      if ( num_connected_players < NUM_SEATS ) return;      /* otherwise, set up the game state (if we aren't 	 already in the middle of an interrupted game */      if ( ! loadstate ) {	GameOptionEntry goe;	/* randomize the seats if desired */	if ( randomseats ) {	  int i,j,n;	  PlayerP temp[NUM_SEATS];	  for (i=0; i<NUM_SEATS; i++) temp[i] = the_game->players[i];	  for (i=0; i<NUM_SEATS; i++) {	    /* random number between 0 and seatsleft-1 */	    n = rand_index(NUM_SEATS-(i+1));	    /* skip already used */	    j = 0;	    while ( (!temp[j]) || j < n ) {	      if ( !temp[j] ) n++;	      j++;	    }	    the_game->players[i] = temp[j];	    temp[j] = NULL;	  }	}	the_game->state = HandComplete;	the_game->player = noseat;	the_game->round = EastWind;	the_game->hands_as_east = 0;	the_game->firsteast = the_game->players[east]->id;	/* protocol_version has been maintained during the	   connection process */	the_game->protversion = protocol_version;	the_game->manager = nomanager ? -1 : first_id;	game_set_options_from_defaults(the_game);	/* initialize the timeout time from the command line option */	goe = *game_get_option_entry(the_game,GOTimeout,NULL);	goe.value.optint = timeout_time;	game_set_option(the_game,&goe);	/* And I think we will have the normal dead wall as default,	   rather than millington style */	goe = *game_get_option_entry(the_game,GODeadWall16,NULL);	if ( goe.enabled ) {	  goe.value.optbool = 0;	  game_set_option(the_game,&goe);	}	/* Now load the game option file, if there is one */	if ( optfilename ) {	  FILE *optfile;	  char buf[1024];	  optfile = fopen(optfilename,"r");	  if ( optfile ) {	    CMsgMsg *m;	    while ( ! feof(optfile) ) {	      fgets(buf,1024,optfile);	      m = decode_cmsg(buf);	      if ( ! m ) {		warn("Error decoding game option file entry %s",buf);	      } else {		if ( handle_cmsg(the_game,m) < 0 ) {		  warn("Error applying game option (%s)",the_game->cmsg_err);		}		cmsg_deepfree(m);	      }	    }	  } else {	    warn("couldn't open game option file %s (%s)",		 optfilename,strerror(errno));	  }	}      }      the_game->cmsg_check = 1;      gamemsg.type = CMsgGame;      gamemsg.east = the_game->players[east]->id;      gamemsg.south = the_game->players[south]->id;      gamemsg.west = the_game->players[west]->id;      gamemsg.north = the_game->players[north]->id;      gamemsg.round = the_game->round;      gamemsg.hands_as_east = the_game->hands_as_east;      gamemsg.firsteast = the_game->firsteast;      gamemsg.east_score = the_game->players[east]->cumulative_score;      gamemsg.south_score = the_game->players[south]->cumulative_score;      gamemsg.west_score = the_game->players[west]->cumulative_score;      gamemsg.north_score = the_game->players[north]->cumulative_score;      gamemsg.protversion = the_game->protversion;      gamemsg.manager = the_game->manager;            /* we only send the game message to the players who	 have been disconnected. (In the usual case, this	 is all players...) */      for ( i=0 ; i < NUM_SEATS; i++ ) {	PlayerP p = the_game->players[i];	if ( ! pextras(p)->disconnected ) continue;	send_id(p->id,&gamemsg);	/* we don't actually clear the history records	   until the NewHand message is sent. But we don't	   want to send history for a complete hand.	   However, if the game is paused, we need to tell the player so. */	if ( the_game->state == HandComplete ) {	  if ( the_game->paused ) {	    CMsgPauseMsg pm;	    CMsgPlayerReadyMsg prm;	    int i;	    pm.type = CMsgPause;	    pm.exempt = 0;	    pm.requestor = 0;	    pm.reason = the_game->paused;	    send_id(p->id,&pm);	    prm.type = CMsgPlayerReady;	    for ( i = 0; i < NUM_SEATS; i++ ) {	      if ( the_game->ready[i] ) {		prm.id = the_game->players[i]->id;		send_id(p->id,&prm);	      }	    }	  }	}      }      /* now we need to send the history to disconnected players.*/      if ( the_game->state != HandComplete ) {	int j;	if ( ! usehist ) {	  CMsgErrorMsg em;	  em.type = CMsgError;	  em.seqno = connections[cnx].seqno;	  em.error = "No history kept: resumption not supported";	  send_all(the_game,&em);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -