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

📄 controller.c

📁 支持网络和单机的麻将游戏
💻 C
📖 第 1 页 / 共 5 页
字号:
	  warn(em.error);	  exit(1);	}	for ( j=0; j < gextras(the_game)->histcount; j++ ) {	  int sleeptime;	  for ( i=0 ; i < NUM_SEATS; i++ ) {	    PlayerP p = the_game->players[i];	    if ( ! pextras(p)->disconnected ) continue;	    resend(p->id,gextras(the_game)->history[j]);	  }	  /* this is an undocumented feature to make things 	     a bit nicer for humans reconnecting: we'll 	     unilaterally slow down the feed, by adding a 	     0.15 second delay between items, or 1 second after	     a claim implementation. */	  switch ( gextras(the_game)->history[j]->type ) {	  case CMsgPlayerPairs:	  case CMsgPlayerChows:	  case CMsgPlayerPungs:	  case CMsgPlayerKongs:	  case CMsgPlayerSpecialSet:	    sleeptime = 1000; /* ms */	    break;	  default:	    sleeptime = 150;	  }	  usleep(sleeptime*1000);	}      }      /* and now mark those players connected again */      for ( i=0 ; i < NUM_SEATS; i++ ) {	PlayerP p = the_game->players[i];	if ( ! pextras(p)->disconnected ) continue;	pextras(p)->disconnected = 0;      }      /* Now we should tell somebody to do something. Namely:	 In state HandComplete: tell everybody, and start a hand	 if things aren't paused.	 In state Dealing: everybody.	 In Discarded, several players might want to do something,	 so we don't specify the id.	 Ditto in MahJonging.         Otherwise, it's the player.      */      {	CMsgStartPlayMsg sm;	CMsgPauseMsg pm;	sm.type = CMsgStartPlay;	switch (the_game->state) {	case HandComplete:	case Dealing:	case Discarded:	case MahJonging:	  sm.id = 0;	  break;	default:	  sm.id = the_game->players[the_game->player]->id;	  break;	}	/* now we should ask the players whether they are ready to	   continue. We send the pause request *before* making the	   game active */	pm.type = CMsgPause;	pm.reason = (the_game->state == HandComplete)	  ? "to start hand" : "to continue play";	pm.exempt = 0;	pm.requestor = 0;	handle_cmsg(the_game,&pm);	send_all(the_game,&pm);	send_all(the_game,&sm);	/* and set the game active */	the_game->active = 1;      }      /* now set loadstate */      loadstate = 1;      return;    } /* end of case PMsgConnect */  case PMsgRequestPause:    { PMsgRequestPauseMsg *m = (PMsgRequestPauseMsg *)pmp;      PlayerP p; int id; seats seat;      CMsgPauseMsg pm;      CMsgErrorMsg em;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      pm.type = CMsgPause;      pm.exempt = 0;      pm.requestor = id;      pm.reason = m->reason;            em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      if ( !(the_game->state == Discarding	     || the_game->state == HandComplete) ) {	em.error = "Not reasonable to pause at this point";	send_id(id,&em);	return;      }            send_all(the_game,&pm);      break;    } /* end of PMsgRequestPauseMsg */  case PMsgSetPlayerOption:    {      PMsgSetPlayerOptionMsg *m = (PMsgSetPlayerOptionMsg *)pmp;      PlayerP p; int id; seats seat;      CMsgPlayerOptionSetMsg posm;      CMsgErrorMsg em;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      posm.type = CMsgPlayerOptionSet;      posm.option = m->option;      posm.value = m->value;      posm.text = m->text;      posm.ack = 1;      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      if ( m->option == (unsigned)-1 ) m->option = POUnknown;      if ( m->option == POUnknown ) em.error = "Unknown option";      else switch ( m->option ) {	/* validity checking  */	/* boolean options */      case POInfoTiles:	if ( m->value < 0 || m->value > 1 ) em.error = "Bad value for InfoTiles";	break;      case POLocalTimeouts:	/* This is a bit messy. As a matter of policy, we only allow	   a client to do local timeouts if everybody else is too.	   So we simply remember the requests, unless all have requested,	   in which case we ack everybody. */	/* if this is an ack, ignore it. We should only get acks	   after we've forcibly set things to zero, and then	   we don't care about the ack. */	if ( m->ack ) break;	/* if this is a request to start local timeouts: */	if ( m->value ) {	  int i,lt;	  pextras(p)->localtimeouts = 1;	  for ( i=0, lt=1; i < NUM_SEATS; i++ ) {	    lt = (lt && pextras(the_game->players[i])->localtimeouts);	  }	  if ( ! lt ) return; /* just wait */	  localtimeouts = 1;	  timeout_time = get_timeout_time(the_game,localtimeouts);	  for ( i=0; i < NUM_SEATS; i++ ) {	    popts(the_game->players[i])[m->option] = 1;	  }	  send_all(the_game,&posm);	  /* and that's it -- we don't want to drop through */	  return;	} else {	  unsigned int i;	  /* request to disable local timeouts */	  localtimeouts = 0;	  timeout_time = get_timeout_time(the_game,localtimeouts);	  pextras(p)->localtimeouts = 0;	  /* instruct all the other players to drop local timeouts */	  posm.ack = 0;	  for ( i=0; i < NUM_SEATS; i++ ) {	    if ( i != seat ) { send_seat(the_game,i,&posm); }	  }	  posm.ack = 1;	  /* now drop through to ack and set this one. */	}	break;	/* non-boolean options */      case PODelayTime:	/* players *can* request any positive value */	if ( m->value < 0 ) em.error = "Bad value for DelayTime";	break;      default:	;      }      if ( em.error ) {	send_id(id,&em);	return;      }      if ( ! m->ack ) { send_id(id,&posm); }      popts(p)[m->option] = m->value;      /* action may now be required */      switch ( m->option ) {	int i;      case PODelayTime:	/* find the highest value requested by any player, or option */	min_time = min_time_opt;	for ( i = 0; i < NUM_SEATS; i++ ) {	  if ( popts(the_game->players[i])[PODelayTime] > min_time )	    min_time = popts(the_game->players[i])[PODelayTime];	}	/* highest reasonable value is 5 seconds */	if ( min_time > 50 ) min_time = 50;	break;      default:	;      }      return;    }  case PMsgReady:    {      PlayerP p; int id; seats seat;      CMsgPlayerReadyMsg prm;      CMsgErrorMsg em;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      prm.type = CMsgPlayerReady;      prm.id = id;      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      /* if the player is already ready, ignore this message */      if ( the_game->ready[seat] ) return;      if ( handle_cmsg(the_game,&prm) < 0 ) {	em.error = the_game->cmsg_err;	send_id(id,&em);	return;      }      send_all(the_game,&prm);      /* if state is handcomplete and no longer pausing, 	 start the next hand */      if ( the_game->state == HandComplete && ! the_game->paused ) start_hand(the_game);      /* and in the discarded or konging state, reinstate the timeout */      if ( the_game->state == Discarded 	   || (the_game->state == Discarding && the_game->konging ) )	timeout = 1000*timeout_time;      return;    } /* end of case PMsgReady */  case PMsgDeclareSpecial:    {      PMsgDeclareSpecialMsg *m = (PMsgDeclareSpecialMsg *) pmp;      PlayerP p; int id; seats seat;      CMsgErrorMsg em;      CMsgPlayerDeclaresSpecialMsg pdsm;      CMsgPlayerDrawsMsg  pdlm; /* FIXME */      int res;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      pdlm.id = pdsm.id = id;      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      pdsm.type = CMsgPlayerDeclaresSpecial;      pdlm.type = CMsgPlayerDraws;      em.error = NULL;      /* Legality checking is done by handle cmsg, so	 just set up the cmsg and apply it */      pdsm.tile = m->tile;      res = handle_cmsg(the_game,&pdsm);      if ( res < -1 ) {	warn("Consistency error: giving up");	exit(1);      }      if ( res < 0 ) {	em.error = the_game->cmsg_err;	send_id(id,&em);	return;      }      check_min_time(1);      send_all(the_game,&pdsm);      if ( pdsm.tile == HiddenTile ) {	/* if we've now started play (i.e. state == Discarding),	   ask everybody (except east) if they're ready */	if ( the_game->state == Discarding ) {	  CMsgPauseMsg pm;	  pm.type = CMsgPause;	  pm.reason = "to start play";	  pm.exempt = the_game->players[east]->id;	  pm.requestor = 0;	  if ( handle_cmsg(the_game,&pm) >= 0 ) {	    send_all(the_game,&pm);	  } else {	    warn("Failed to pause at start of play: %s",the_game->cmsg_err);	  }	}	return;      }      /* and now draw the replacement */      if ( game_get_option_value(the_game,GOFlowersLoose,NULL) ) {	pdlm.tile = game_peek_loose_tile(the_game);	pdlm.type = CMsgPlayerDrawsLoose;      } else {	pdlm.tile = game_peek_tile(the_game);	pdlm.type = CMsgPlayerDraws;      }      if ( pdlm.tile == ErrorTile ) {	washout(NULL);	return;      }      if ( the_game->state == Discarding ) {	/* don't do this if declaring specials */	/* stash a copy of the player, in case it goes mahjong */	copy_player(gextras(the_game)->caller,p);      }      if ( handle_cmsg(the_game,&pdlm) < 0 ) {	/* error should be impossible */	warn("Consistency error: giving up");	exit(1);      }      send_id(id,&pdlm);      pdlm.tile = HiddenTile;      check_min_time(1);      send_others(the_game,id,&pdlm);      send_infotiles(p);      return;      assert(0);    } /* end of case PMsgDeclareSpecial */  case PMsgDiscard:    {      PMsgDiscardMsg *m = (PMsgDiscardMsg *) pmp;      PlayerP p; int id; seats seat;      CMsgErrorMsg em;      CMsgPlayerDiscardsMsg pdm;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      pdm.type = CMsgPlayerDiscards;      pdm.id = id;      pdm.tile = m->tile;      pdm.discard = the_game->serial+1;      pdm.calling = m->calling;      if ( handle_cmsg(the_game,&pdm) < 0 ) {	em.error = the_game->cmsg_err;	send_id(id,&em);	return;      }      check_min_time(1);      send_all(the_game,&pdm);      send_infotiles(p);      /* set the timeout */      timeout = 1000*timeout_time;      return;    } /* end of case PMsgDiscard */  case PMsgNoClaim:    {      PMsgNoClaimMsg *m = (PMsgNoClaimMsg *) pmp;      PlayerP p; int id; seats seat;      CMsgErrorMsg em;      CMsgPlayerDoesntClaimMsg pdcm;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      pdcm.type = CMsgPlayerDoesntClaim;      pdcm.id = id;      pdcm.discard = m->discard;      pdcm.timeout = 0;      if ( handle_cmsg(the_game,&pdcm) < 0 ) {	em.error = the_game->cmsg_err;	send_id(id,&em);	return;      }      /* handle_cmsg ignores noclaims in wrong state, so	 we need to check it */      if ( ! (the_game->state == Discarded 	   || ( (the_game->state == Discarding 		 || the_game->state == DeclaringSpecials)		&& the_game->konging ) ) ) return;      /* acknowledge to the player only */      send_id(id,&pdcm);      /* if all claims received, process */      check_claims(the_game);      return;    } /* end of case PMsgNoClaim */  case PMsgPung:    {      PMsgPungMsg *m = (PMsgPungMsg *) pmp;      PlayerP p; int id; seats seat;      CMsgErrorMsg em;      id = cnx_to_id(cnx);      p = id_to_player(id);      seat = id_to_seat(id);      /* fill in basic fields of possible replies */      em.type = CMsgError ;      em.seqno = connections[cnx].seqno;      em.error = NULL;      if ( the_game->state != MahJonging ) {	CMsgPlayerClaimsPungMsg pcpm;	pcpm.type = CMsgPlayerClaimsPung;	pcpm.id = id;	pcpm.discard = m->discard;	if ( handle_cmsg(the_game,&pcpm) < 0 ) {	  em.error = the_game->cmsg_err;	  send_id(id,&em);	  return;	}	send_all(the_game,&pcpm);	/* if all claims received, process */	check_claims(the_game);	return;      } else { /* MahJonging */	CMsgPlayerPungsMsg ppm;	ppm.type = CMsgPlayerPungs;	ppm.id = id;	ppm.tile = the_game->tile;		if ( handle_cmsg(the_game,&ppm) < 0 ) {	  em.error = the_game->cmsg_err;	  send_id(id,&em);	  return;	}	check_min_time(1);	send_all(the_game,&ppm);	send_infotiles(p);

⌨️ 快捷键说明

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