rust/hedgewars-server/src/server/handlers/common.rs
changeset 15074 c5a6e8566425
parent 15073 7732013ce64c
child 15075 e935b1ad23f3
equal deleted inserted replaced
15073:7732013ce64c 15074:c5a6e8566425
     1 use crate::{
       
     2     protocol::messages::server_chat,
       
     3     protocol::messages::{
       
     4         add_flags, remove_flags,
       
     5         HWProtocolMessage::{self, Rnd},
       
     6         HWServerMessage::{self, *},
       
     7         ProtocolFlags as Flags,
       
     8     },
       
     9     server::{
       
    10         client::HWClient,
       
    11         core::HWServer,
       
    12         coretypes::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType},
       
    13         room::HWRoom,
       
    14     },
       
    15     utils::to_engine_msg,
       
    16 };
       
    17 
       
    18 use super::Response;
       
    19 
       
    20 use crate::server::coretypes::RoomConfig;
       
    21 use rand::{self, seq::SliceRandom, thread_rng, Rng};
       
    22 use std::{iter::once, mem::replace};
       
    23 
       
    24 pub fn rnd_reply(options: &[String]) -> HWServerMessage {
       
    25     let mut rng = thread_rng();
       
    26 
       
    27     let reply = if options.is_empty() {
       
    28         (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string()
       
    29     } else {
       
    30         options.choose(&mut rng).unwrap().clone()
       
    31     };
       
    32 
       
    33     ChatMsg {
       
    34         nick: "[random]".to_string(),
       
    35         msg: reply,
       
    36     }
       
    37 }
       
    38 
       
    39 pub fn join_lobby(server: &mut HWServer, response: &mut Response) {
       
    40     let client_id = response.client_id();
       
    41 
       
    42     let client = &server.clients[client_id];
       
    43     let nick = vec![client.nick.clone()];
       
    44     let mut flags = vec![];
       
    45     if client.is_registered() {
       
    46         flags.push(Flags::Registered)
       
    47     }
       
    48     if client.is_admin() {
       
    49         flags.push(Flags::Admin)
       
    50     }
       
    51     if client.is_contributor() {
       
    52         flags.push(Flags::Contributor)
       
    53     }
       
    54 
       
    55     let all_nicks: Vec<_> = server.collect_nicks(|_| true);
       
    56 
       
    57     let mut flag_selectors = [
       
    58         (
       
    59             Flags::Registered,
       
    60             server.collect_nicks(|(_, c)| c.is_registered()),
       
    61         ),
       
    62         (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())),
       
    63         (
       
    64             Flags::Contributor,
       
    65             server.collect_nicks(|(_, c)| c.is_contributor()),
       
    66         ),
       
    67         (
       
    68             Flags::InRoom,
       
    69             server.collect_nicks(|(_, c)| c.room_id.is_some()),
       
    70         ),
       
    71     ];
       
    72 
       
    73     let server_msg = ServerMessage(server.get_greetings(client_id).to_string());
       
    74 
       
    75     let rooms_msg = Rooms(
       
    76         server
       
    77             .rooms
       
    78             .iter()
       
    79             .filter(|(_, r)| r.protocol_number == client.protocol_number)
       
    80             .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id])))
       
    81             .collect(),
       
    82     );
       
    83 
       
    84     response.add(LobbyJoined(nick).send_all().but_self());
       
    85     response.add(
       
    86         ClientFlags(add_flags(&flags), all_nicks.clone())
       
    87             .send_all()
       
    88             .but_self(),
       
    89     );
       
    90 
       
    91     response.add(LobbyJoined(all_nicks).send_self());
       
    92     for (flag, nicks) in &mut flag_selectors {
       
    93         if !nicks.is_empty() {
       
    94             response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
       
    95         }
       
    96     }
       
    97 
       
    98     response.add(server_msg.send_self());
       
    99     response.add(rooms_msg.send_self());
       
   100 }
       
   101 
       
   102 pub fn remove_teams(
       
   103     room: &mut HWRoom,
       
   104     team_names: Vec<String>,
       
   105     is_in_game: bool,
       
   106     response: &mut Response,
       
   107 ) {
       
   108     if let Some(ref mut info) = room.game_info {
       
   109         for team_name in &team_names {
       
   110             info.left_teams.push(team_name.clone());
       
   111 
       
   112             if is_in_game {
       
   113                 let msg = once(b'F').chain(team_name.bytes());
       
   114                 response.add(
       
   115                     ForwardEngineMessage(vec![to_engine_msg(msg)])
       
   116                         .send_all()
       
   117                         .in_room(room.id)
       
   118                         .but_self(),
       
   119                 );
       
   120 
       
   121                 info.teams_in_game -= 1;
       
   122 
       
   123                 let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
       
   124                 if let Some(m) = &info.sync_msg {
       
   125                     info.msg_log.push(m.clone());
       
   126                     info.sync_msg = None
       
   127                 }
       
   128                 info.msg_log.push(remove_msg.clone());
       
   129 
       
   130                 response.add(
       
   131                     ForwardEngineMessage(vec![remove_msg])
       
   132                         .send_all()
       
   133                         .in_room(room.id)
       
   134                         .but_self(),
       
   135                 );
       
   136             }
       
   137         }
       
   138     }
       
   139 
       
   140     for team_name in team_names {
       
   141         room.remove_team(&team_name);
       
   142         response.add(TeamRemove(team_name).send_all().in_room(room.id));
       
   143     }
       
   144 }
       
   145 
       
   146 fn remove_client_from_room(
       
   147     client: &mut HWClient,
       
   148     room: &mut HWRoom,
       
   149     response: &mut Response,
       
   150     msg: &str,
       
   151 ) {
       
   152     room.players_number -= 1;
       
   153     if room.players_number > 0 || room.is_fixed() {
       
   154         if client.is_ready() && room.ready_players_number > 0 {
       
   155             room.ready_players_number -= 1;
       
   156         }
       
   157 
       
   158         let team_names: Vec<_> = room
       
   159             .client_teams(client.id)
       
   160             .map(|t| t.name.clone())
       
   161             .collect();
       
   162         remove_teams(room, team_names, client.is_in_game(), response);
       
   163 
       
   164         if room.players_number > 0 {
       
   165             response.add(
       
   166                 RoomLeft(client.nick.clone(), msg.to_string())
       
   167                     .send_all()
       
   168                     .in_room(room.id)
       
   169                     .but_self(),
       
   170             );
       
   171         }
       
   172 
       
   173         if client.is_master() && !room.is_fixed() {
       
   174             client.set_is_master(false);
       
   175             response.add(
       
   176                 ClientFlags(
       
   177                     remove_flags(&[Flags::RoomMaster]),
       
   178                     vec![client.nick.clone()],
       
   179                 )
       
   180                 .send_all()
       
   181                 .in_room(room.id),
       
   182             );
       
   183             room.master_id = None;
       
   184         }
       
   185     }
       
   186 
       
   187     client.room_id = None;
       
   188 
       
   189     let update_msg = if room.players_number == 0 && !room.is_fixed() {
       
   190         RoomRemove(room.name.clone())
       
   191     } else {
       
   192         RoomUpdated(room.name.clone(), room.info(Some(&client)))
       
   193     };
       
   194     response.add(update_msg.send_all().with_protocol(room.protocol_number));
       
   195 
       
   196     response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all());
       
   197 }
       
   198 
       
   199 pub fn change_master(
       
   200     server: &mut HWServer,
       
   201     room_id: RoomId,
       
   202     new_master_id: ClientId,
       
   203     response: &mut Response,
       
   204 ) {
       
   205     let room = &mut server.rooms[room_id];
       
   206     if let Some(master_id) = room.master_id {
       
   207         server.clients[master_id].set_is_master(false);
       
   208         response.add(
       
   209             ClientFlags(
       
   210                 remove_flags(&[Flags::RoomMaster]),
       
   211                 vec![server.clients[master_id].nick.clone()],
       
   212             )
       
   213             .send_all()
       
   214             .in_room(room_id),
       
   215         )
       
   216     }
       
   217 
       
   218     room.master_id = Some(new_master_id);
       
   219     server.clients[new_master_id].set_is_master(true);
       
   220 
       
   221     response.add(
       
   222         ClientFlags(
       
   223             add_flags(&[Flags::RoomMaster]),
       
   224             vec![server.clients[new_master_id].nick.clone()],
       
   225         )
       
   226         .send_all()
       
   227         .in_room(room_id),
       
   228     );
       
   229 }
       
   230 
       
   231 pub fn enter_room(
       
   232     server: &mut HWServer,
       
   233     client_id: ClientId,
       
   234     room_id: RoomId,
       
   235     response: &mut Response,
       
   236 ) {
       
   237     let nick = server.clients[client_id].nick.clone();
       
   238     server.move_to_room(client_id, room_id);
       
   239 
       
   240     response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id));
       
   241     response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all());
       
   242     let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id));
       
   243     response.add(RoomJoined(nicks).send_self());
       
   244 
       
   245     get_room_teams(server, room_id, client_id, response);
       
   246 
       
   247     let room = &server.rooms[room_id];
       
   248     get_room_config(room, client_id, response);
       
   249 
       
   250     let mut flag_selectors = [
       
   251         (
       
   252             Flags::RoomMaster,
       
   253             server.collect_nicks(|(_, c)| c.is_master()),
       
   254         ),
       
   255         (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())),
       
   256         (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())),
       
   257     ];
       
   258 
       
   259     for (flag, nicks) in &mut flag_selectors {
       
   260         response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
       
   261     }
       
   262 
       
   263     if !room.greeting.is_empty() {
       
   264         response.add(
       
   265             ChatMsg {
       
   266                 nick: "[greeting]".to_string(),
       
   267                 msg: room.greeting.clone(),
       
   268             }
       
   269             .send_self(),
       
   270         );
       
   271     }
       
   272 }
       
   273 
       
   274 pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) {
       
   275     let client = &mut server.clients[client_id];
       
   276 
       
   277     if let Some(room_id) = client.room_id {
       
   278         let room = &mut server.rooms[room_id];
       
   279 
       
   280         remove_client_from_room(client, room, response, msg);
       
   281 
       
   282         if !room.is_fixed() {
       
   283             if room.players_number == 0 {
       
   284                 server.rooms.remove(room_id);
       
   285             } else if room.master_id == None {
       
   286                 let new_master_id = server.room_clients(room_id).next();
       
   287                 if let Some(new_master_id) = new_master_id {
       
   288                     let new_master_nick = server.clients[new_master_id].nick.clone();
       
   289                     let room = &mut server.rooms[room_id];
       
   290                     room.master_id = Some(new_master_id);
       
   291                     server.clients[new_master_id].set_is_master(true);
       
   292 
       
   293                     if room.protocol_number < 42 {
       
   294                         room.name = new_master_nick.clone();
       
   295                     }
       
   296 
       
   297                     room.set_join_restriction(false);
       
   298                     room.set_team_add_restriction(false);
       
   299                     room.set_unregistered_players_restriction(true);
       
   300 
       
   301                     response.add(
       
   302                         ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick])
       
   303                             .send_all()
       
   304                             .in_room(room.id),
       
   305                     );
       
   306                 }
       
   307             }
       
   308         }
       
   309     }
       
   310 }
       
   311 
       
   312 pub fn remove_client(server: &mut HWServer, response: &mut Response, msg: String) {
       
   313     let client_id = response.client_id();
       
   314     let client = &mut server.clients[client_id];
       
   315     let nick = client.nick.clone();
       
   316 
       
   317     exit_room(server, client_id, response, &msg);
       
   318 
       
   319     server.remove_client(client_id);
       
   320 
       
   321     response.add(LobbyLeft(nick, msg.to_string()).send_all());
       
   322     response.add(Bye("User quit: ".to_string() + &msg).send_self());
       
   323     response.remove_client(client_id);
       
   324 }
       
   325 
       
   326 pub fn get_room_update(
       
   327     room_name: Option<String>,
       
   328     room: &HWRoom,
       
   329     master: Option<&HWClient>,
       
   330     response: &mut Response,
       
   331 ) {
       
   332     let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master));
       
   333     response.add(update_msg.send_all().with_protocol(room.protocol_number));
       
   334 }
       
   335 
       
   336 pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) {
       
   337     response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client));
       
   338     for cfg in config.to_game_config() {
       
   339         response.add(cfg.to_server_msg().send(to_client));
       
   340     }
       
   341 }
       
   342 
       
   343 pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) {
       
   344     get_room_config_impl(room.active_config(), to_client, response);
       
   345 }
       
   346 
       
   347 pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response)
       
   348 where
       
   349     I: Iterator<Item = &'a TeamInfo>,
       
   350 {
       
   351     for team in teams {
       
   352         response.add(TeamAdd(team.to_protocol()).send(to_client));
       
   353         response.add(TeamColor(team.name.clone(), team.color).send(to_client));
       
   354         response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client));
       
   355     }
       
   356 }
       
   357 
       
   358 pub fn get_room_teams(
       
   359     server: &HWServer,
       
   360     room_id: RoomId,
       
   361     to_client: ClientId,
       
   362     response: &mut Response,
       
   363 ) {
       
   364     let room = &server.rooms[room_id];
       
   365     let current_teams = match room.game_info {
       
   366         Some(ref info) => &info.teams_at_start,
       
   367         None => &room.teams,
       
   368     };
       
   369 
       
   370     get_teams(current_teams.iter().map(|(_, t)| t), to_client, response);
       
   371 }
       
   372 
       
   373 pub fn get_room_flags(
       
   374     server: &HWServer,
       
   375     room_id: RoomId,
       
   376     to_client: ClientId,
       
   377     response: &mut Response,
       
   378 ) {
       
   379     let room = &server.rooms[room_id];
       
   380     if let Some(id) = room.master_id {
       
   381         response.add(
       
   382             ClientFlags(
       
   383                 add_flags(&[Flags::RoomMaster]),
       
   384                 vec![server.clients[id].nick.clone()],
       
   385             )
       
   386             .send(to_client),
       
   387         );
       
   388     }
       
   389     let nicks: Vec<_> = server
       
   390         .clients
       
   391         .iter()
       
   392         .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready())
       
   393         .map(|(_, c)| c.nick.clone())
       
   394         .collect();
       
   395     if !nicks.is_empty() {
       
   396         response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client));
       
   397     }
       
   398 }
       
   399 
       
   400 pub fn apply_voting_result(
       
   401     server: &mut HWServer,
       
   402     room_id: RoomId,
       
   403     response: &mut Response,
       
   404     kind: VoteType,
       
   405 ) {
       
   406     match kind {
       
   407         VoteType::Kick(nick) => {
       
   408             if let Some(client) = server.find_client(&nick) {
       
   409                 if client.room_id == Some(room_id) {
       
   410                     let id = client.id;
       
   411                     response.add(Kicked.send(id));
       
   412                     exit_room(server, id, response, "kicked");
       
   413                 }
       
   414             }
       
   415         }
       
   416         VoteType::Map(None) => (),
       
   417         VoteType::Map(Some(name)) => {
       
   418             if let Some(location) = server.rooms[room_id].load_config(&name) {
       
   419                 response.add(
       
   420                     server_chat(location.to_string())
       
   421                         .send_all()
       
   422                         .in_room(room_id),
       
   423                 );
       
   424                 let room = &server.rooms[room_id];
       
   425                 let room_master = if let Some(id) = room.master_id {
       
   426                     Some(&server.clients[id])
       
   427                 } else {
       
   428                     None
       
   429                 };
       
   430                 get_room_update(None, room, room_master, response);
       
   431 
       
   432                 for (_, client) in server.clients.iter() {
       
   433                     if client.room_id == Some(room_id) {
       
   434                         super::common::get_room_config(&server.rooms[room_id], client.id, response);
       
   435                     }
       
   436                 }
       
   437             }
       
   438         }
       
   439         VoteType::Pause => {
       
   440             if let Some(ref mut info) = server.rooms[room_id].game_info {
       
   441                 info.is_paused = !info.is_paused;
       
   442                 response.add(
       
   443                     server_chat("Pause toggled.".to_string())
       
   444                         .send_all()
       
   445                         .in_room(room_id),
       
   446                 );
       
   447                 response.add(
       
   448                     ForwardEngineMessage(vec![to_engine_msg(once(b'I'))])
       
   449                         .send_all()
       
   450                         .in_room(room_id),
       
   451                 );
       
   452             }
       
   453         }
       
   454         VoteType::NewSeed => {
       
   455             let seed = thread_rng().gen_range(0, 1_000_000_000).to_string();
       
   456             let cfg = GameCfg::Seed(seed);
       
   457             response.add(cfg.to_server_msg().send_all().in_room(room_id));
       
   458             server.rooms[room_id].set_config(cfg);
       
   459         }
       
   460         VoteType::HedgehogsPerTeam(number) => {
       
   461             let r = &mut server.rooms[room_id];
       
   462             let nicks = r.set_hedgehogs_number(number);
       
   463 
       
   464             response.extend(
       
   465                 nicks
       
   466                     .into_iter()
       
   467                     .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)),
       
   468             );
       
   469         }
       
   470     }
       
   471 }
       
   472 
       
   473 fn add_vote(room: &mut HWRoom, response: &mut Response, vote: Vote) -> Option<bool> {
       
   474     let client_id = response.client_id;
       
   475     let mut result = None;
       
   476 
       
   477     if let Some(ref mut voting) = room.voting {
       
   478         if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) {
       
   479             response.add(server_chat("Your vote has been counted.".to_string()).send_self());
       
   480             voting.votes.push((client_id, vote.is_pro));
       
   481             let i = voting.votes.iter();
       
   482             let pro = i.clone().filter(|(_, v)| *v).count();
       
   483             let contra = i.filter(|(_, v)| !*v).count();
       
   484             let success_quota = voting.voters.len() / 2 + 1;
       
   485             if vote.is_forced && vote.is_pro || pro >= success_quota {
       
   486                 result = Some(true);
       
   487             } else if vote.is_forced && !vote.is_pro || contra > voting.voters.len() - success_quota
       
   488             {
       
   489                 result = Some(false);
       
   490             }
       
   491         } else {
       
   492             response.add(server_chat("You already have voted.".to_string()).send_self());
       
   493         }
       
   494     } else {
       
   495         response.add(server_chat("There's no voting going on.".to_string()).send_self());
       
   496     }
       
   497 
       
   498     result
       
   499 }
       
   500 
       
   501 pub fn submit_vote(server: &mut HWServer, vote: Vote, response: &mut Response) {
       
   502     let client_id = response.client_id;
       
   503     let client = &server.clients[client_id];
       
   504 
       
   505     if let Some(room_id) = client.room_id {
       
   506         let room = &mut server.rooms[room_id];
       
   507 
       
   508         if let Some(res) = add_vote(room, response, vote) {
       
   509             response.add(
       
   510                 server_chat("Voting closed.".to_string())
       
   511                     .send_all()
       
   512                     .in_room(room.id),
       
   513             );
       
   514             let voting = replace(&mut room.voting, None).unwrap();
       
   515             if res {
       
   516                 apply_voting_result(server, room_id, response, voting.kind);
       
   517             }
       
   518         }
       
   519     }
       
   520 }
       
   521 
       
   522 pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
       
   523     let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server
       
   524         .clients
       
   525         .iter()
       
   526         .map(|(id, c)| (id, c.nick.clone()))
       
   527         .unzip();
       
   528     let room = &mut server.rooms[room_id];
       
   529 
       
   530     if !room.has_multiple_clans() {
       
   531         response.add(
       
   532             Warning("The game can't be started with less than two clans!".to_string()).send_self(),
       
   533         );
       
   534     } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number {
       
   535         response.add(Warning("Not all players are ready".to_string()).send_self());
       
   536     } else if room.game_info.is_some() {
       
   537         response.add(Warning("The game is already in progress".to_string()).send_self());
       
   538     } else {
       
   539         room.start_round();
       
   540         for id in room_clients {
       
   541             let c = &mut server.clients[id];
       
   542             c.set_is_in_game(true);
       
   543             c.team_indices = room.client_team_indices(c.id);
       
   544         }
       
   545         response.add(RunGame.send_all().in_room(room.id));
       
   546         response.add(
       
   547             ClientFlags(add_flags(&[Flags::InGame]), room_nicks)
       
   548                 .send_all()
       
   549                 .in_room(room.id),
       
   550         );
       
   551 
       
   552         let room_master = if let Some(id) = room.master_id {
       
   553             Some(&server.clients[id])
       
   554         } else {
       
   555             None
       
   556         };
       
   557         get_room_update(None, room, room_master, response);
       
   558     }
       
   559 }
       
   560 
       
   561 pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
       
   562     let room = &mut server.rooms[room_id];
       
   563     room.ready_players_number = 1;
       
   564     let room_master = if let Some(id) = room.master_id {
       
   565         Some(&server.clients[id])
       
   566     } else {
       
   567         None
       
   568     };
       
   569     get_room_update(None, room, room_master, response);
       
   570     response.add(RoundFinished.send_all().in_room(room_id));
       
   571 
       
   572     if let Some(info) = replace(&mut room.game_info, None) {
       
   573         for (_, client) in server.clients.iter() {
       
   574             if client.room_id == Some(room_id) && client.is_joined_mid_game() {
       
   575                 super::common::get_room_config(room, client.id, response);
       
   576                 response.extend(
       
   577                     info.left_teams
       
   578                         .iter()
       
   579                         .map(|name| TeamRemove(name.clone()).send(client.id)),
       
   580                 );
       
   581             }
       
   582         }
       
   583     }
       
   584 
       
   585     let nicks: Vec<_> = server
       
   586         .clients
       
   587         .iter_mut()
       
   588         .filter(|(_, c)| c.room_id == Some(room_id))
       
   589         .map(|(_, c)| {
       
   590             c.set_is_ready(c.is_master());
       
   591             c.set_is_joined_mid_game(false);
       
   592             c
       
   593         })
       
   594         .filter_map(|c| {
       
   595             if !c.is_master() {
       
   596                 Some(c.nick.clone())
       
   597             } else {
       
   598                 None
       
   599             }
       
   600         })
       
   601         .collect();
       
   602 
       
   603     if !nicks.is_empty() {
       
   604         let msg = if room.protocol_number < 38 {
       
   605             LegacyReady(false, nicks)
       
   606         } else {
       
   607             ClientFlags(remove_flags(&[Flags::Ready]), nicks)
       
   608         };
       
   609         response.add(msg.send_all().in_room(room_id));
       
   610     }
       
   611 }
       
   612 
       
   613 #[cfg(test)]
       
   614 mod tests {
       
   615     use super::*;
       
   616     use crate::protocol::messages::HWServerMessage::ChatMsg;
       
   617     use crate::server::actions::PendingMessage;
       
   618 
       
   619     fn reply2string(r: HWServerMessage) -> String {
       
   620         match r {
       
   621             ChatMsg { msg: p, .. } => String::from(p),
       
   622             _ => panic!("expected a ChatMsg"),
       
   623         }
       
   624     }
       
   625 
       
   626     fn run_handle_test(opts: Vec<String>) {
       
   627         let opts2 = opts.clone();
       
   628         for opt in opts {
       
   629             while reply2string(rnd_reply(&opts2)) != opt {}
       
   630         }
       
   631     }
       
   632 
       
   633     /// This test terminates almost surely.
       
   634     #[test]
       
   635     fn test_handle_rnd_empty() {
       
   636         run_handle_test(vec![])
       
   637     }
       
   638 
       
   639     /// This test terminates almost surely.
       
   640     #[test]
       
   641     fn test_handle_rnd_nonempty() {
       
   642         run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()])
       
   643     }
       
   644 
       
   645     /// This test terminates almost surely (strong law of large numbers)
       
   646     #[test]
       
   647     fn test_distribution() {
       
   648         let eps = 0.000001;
       
   649         let lim = 0.5;
       
   650         let opts = vec![0.to_string(), 1.to_string()];
       
   651         let mut ones = 0;
       
   652         let mut tries = 0;
       
   653 
       
   654         while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps {
       
   655             tries += 1;
       
   656             if reply2string(rnd_reply(&opts)) == 1.to_string() {
       
   657                 ones += 1;
       
   658             }
       
   659         }
       
   660     }
       
   661 }