rust/hedgewars-server/src/core/server.rs
changeset 15482 4cc9ec732392
parent 15441 61a0bd0bb021
child 15487 91f0c5ec37b5
--- a/rust/hedgewars-server/src/core/server.rs	Thu Oct 24 09:41:10 2019 -0400
+++ b/rust/hedgewars-server/src/core/server.rs	Sat Oct 26 02:36:08 2019 +0300
@@ -28,6 +28,22 @@
     Restricted,
 }
 
+pub enum LeaveRoomResult {
+    RoomRemoved,
+    RoomRemains {
+        is_empty: bool,
+        was_master: bool,
+        was_in_game: bool,
+        new_master: Option<ClientId>,
+        removed_teams: Vec<String>,
+    },
+}
+
+#[derive(Debug)]
+pub enum LeaveRoomError {
+    NoRoom,
+}
+
 #[derive(Debug)]
 pub struct UninitializedError();
 #[derive(Debug)]
@@ -138,6 +154,20 @@
     }
 
     #[inline]
+    pub fn client_and_room(&self, client_id: ClientId, room_id: RoomId) -> (&HwClient, &HwRoom) {
+        (&self.clients[client_id], &self.rooms[room_id])
+    }
+
+    #[inline]
+    pub fn client_and_room_mut(
+        &mut self,
+        client_id: ClientId,
+        room_id: RoomId,
+    ) -> (&mut HwClient, &mut HwRoom) {
+        (&mut self.clients[client_id], &mut self.rooms[room_id])
+    }
+
+    #[inline]
     pub fn is_admin(&self, client_id: ClientId) -> bool {
         self.clients
             .get(client_id)
@@ -244,6 +274,81 @@
         }
     }
 
+    pub fn leave_room(&mut self, client_id: ClientId) -> Result<LeaveRoomResult, LeaveRoomError> {
+        let client = &mut self.clients[client_id];
+        if let Some(room_id) = client.room_id {
+            let room = &mut self.rooms[room_id];
+
+            room.players_number -= 1;
+            client.room_id = None;
+
+            let is_empty = room.players_number == 0;
+            let is_fixed = room.is_fixed();
+            let was_master = room.master_id == Some(client_id);
+            let was_in_game = client.is_in_game();
+            let mut removed_teams = vec![];
+
+            if is_empty && !is_fixed {
+                if client.is_ready() && room.ready_players_number > 0 {
+                    room.ready_players_number -= 1;
+                }
+
+                removed_teams = room
+                    .client_teams(client.id)
+                    .map(|t| t.name.clone())
+                    .collect();
+
+                for team_name in &removed_teams {
+                    room.remove_team(team_name);
+                }
+
+                if client.is_master() && !is_fixed {
+                    client.set_is_master(false);
+                    room.master_id = None;
+                }
+            }
+
+            client.set_is_ready(false);
+            client.set_is_in_game(false);
+
+            if !is_fixed {
+                if room.players_number == 0 {
+                    self.rooms.remove(room_id);
+                } else if room.master_id == None {
+                    let new_master_id = self.room_clients(room_id).next();
+                    if let Some(new_master_id) = new_master_id {
+                        let room = &mut self.rooms[room_id];
+                        room.master_id = Some(new_master_id);
+                        let new_master = &mut self.clients[new_master_id];
+                        new_master.set_is_master(true);
+
+                        if room.protocol_number < 42 {
+                            room.name = new_master.nick.clone();
+                        }
+
+                        room.set_join_restriction(false);
+                        room.set_team_add_restriction(false);
+                        room.set_unregistered_players_restriction(true);
+                    }
+                }
+            }
+
+            if is_empty && !is_fixed {
+                Ok(LeaveRoomResult::RoomRemoved)
+            } else {
+                Ok(LeaveRoomResult::RoomRemains {
+                    is_empty,
+                    was_master,
+                    was_in_game,
+                    new_master: self.rooms[room_id].master_id,
+                    removed_teams,
+                })
+            }
+        } else {
+            Err(LeaveRoomError::NoRoom)
+        }
+    }
+
     #[inline]
     pub fn set_var(&mut self, client_id: ClientId, var: ServerVar) -> Result<(), AccessError> {
         if self.clients[client_id].is_admin() {