veloren_server/events/
group_manip.rs

1use crate::client::Client;
2use common::{
3    comp::{
4        self, ChatType, Content, GroupManip,
5        group::{ChangeNotification, Group, GroupManager},
6        invite::{InviteKind, PendingInvites},
7    },
8    event::GroupManipEvent,
9    uid::{IdMaps, Uid},
10};
11use common_net::msg::ServerGeneral;
12use specs::{DispatcherBuilder, Entities, Read, ReadStorage, Write, WriteStorage, world::Entity};
13
14use super::{ServerEvent, event_dispatch};
15
16pub(super) fn register_event_systems(builder: &mut DispatcherBuilder) {
17    event_dispatch::<GroupManipEvent>(builder, &[]);
18}
19
20pub fn can_invite(
21    clients: &ReadStorage<'_, Client>,
22    groups: &ReadStorage<'_, Group>,
23    group_manager: &GroupManager,
24    pending_invites: &mut WriteStorage<'_, PendingInvites>,
25    max_group_size: u32,
26    inviter: Entity,
27    invitee: Entity,
28) -> bool {
29    // Disallow inviting entity that is already in your group
30    let already_in_same_group = groups.get(inviter).is_some_and(|group| {
31        group_manager
32            .group_info(*group)
33            .is_some_and(|g| g.leader == inviter)
34            && groups.get(invitee) == Some(group)
35    });
36    if already_in_same_group {
37        // Inform of failure
38        if let Some(client) = clients.get(inviter) {
39            client.send_fallible(ServerGeneral::server_msg(
40                ChatType::Meta,
41                Content::Plain(
42                    "Invite failed, can't invite someone already in your group".to_string(),
43                ),
44            ));
45        }
46        return false;
47    }
48
49    // Check if group max size is already reached
50    // Adding the current number of pending invites
51    let group_size_limit_reached = groups
52        .get(inviter)
53        .copied()
54        .and_then(|group| {
55            // If entity is currently the leader of a full group then they can't invite
56            // anyone else
57            group_manager
58                .group_info(group)
59                .filter(|i| i.leader == inviter)
60                .map(|i| i.num_members)
61        })
62        .unwrap_or(1) as usize
63        + pending_invites.get(inviter).map_or(0, |p| {
64            p.0.iter()
65                .filter(|(_, k, _)| *k == InviteKind::Group)
66                .count()
67        })
68        >= max_group_size as usize;
69    if group_size_limit_reached {
70        // Inform inviter that they have reached the group size limit
71        if let Some(client) = clients.get(inviter) {
72            client.send_fallible(ServerGeneral::server_msg(
73                ChatType::Meta,
74                Content::Plain(
75                    "Invite failed, pending invites plus current group size have reached the \
76                     group size limit"
77                        .to_owned(),
78                ),
79            ));
80        }
81        return false;
82    }
83
84    true
85}
86
87pub fn update_map_markers<'a>(
88    map_markers: &ReadStorage<'a, comp::MapMarker>,
89    uids: &ReadStorage<'a, Uid>,
90    client: &Client,
91    change: &ChangeNotification<Entity>,
92) {
93    use comp::group::ChangeNotification::*;
94    let send_update = |entity| {
95        if let (Some(map_marker), Some(uid)) = (map_markers.get(entity), uids.get(entity)) {
96            client.send_fallible(ServerGeneral::MapMarker(
97                comp::MapMarkerUpdate::GroupMember(
98                    *uid,
99                    comp::MapMarkerChange::Update(map_marker.0),
100                ),
101            ));
102        }
103    };
104    match change {
105        &Added(entity, _) => {
106            send_update(entity);
107        },
108        NewGroup { leader: _, members } => {
109            for (entity, _) in members {
110                send_update(*entity);
111            }
112        },
113        // Removed and NoGroup can be inferred by the client, NewLeader does not affect map markers
114        Removed(_) | NoGroup | NewLeader(_) => {},
115    }
116}
117
118impl ServerEvent for GroupManipEvent {
119    type SystemData<'a> = (
120        Entities<'a>,
121        Write<'a, GroupManager>,
122        Read<'a, IdMaps>,
123        WriteStorage<'a, Group>,
124        ReadStorage<'a, Client>,
125        ReadStorage<'a, Uid>,
126        ReadStorage<'a, comp::Alignment>,
127        ReadStorage<'a, comp::MapMarker>,
128    );
129
130    fn handle(
131        events: impl ExactSizeIterator<Item = Self>,
132        (entities, mut group_manager, id_maps, mut groups, clients, uids, alignments, map_markers): Self::SystemData<'_>,
133    ) {
134        for GroupManipEvent(entity, manip) in events {
135            match manip {
136                GroupManip::Leave => {
137                    group_manager.leave_group(
138                        entity,
139                        &mut groups,
140                        &alignments,
141                        &uids,
142                        &entities,
143                        &mut |entity, group_change| {
144                            clients
145                                .get(entity)
146                                .and_then(|c| {
147                                    group_change
148                                        .try_map_ref(|e| uids.get(*e).copied())
149                                        .map(|g| (g, c))
150                                })
151                                .map(|(g, c)| {
152                                    update_map_markers(&map_markers, &uids, c, &group_change);
153                                    c.send_fallible(ServerGeneral::GroupUpdate(g));
154                                });
155                        },
156                    );
157                },
158                GroupManip::Kick(uid) => {
159                    let target = match id_maps.uid_entity(uid) {
160                        Some(t) => t,
161                        None => {
162                            // Inform of failure
163                            if let Some(client) = clients.get(entity) {
164                                client.send_fallible(ServerGeneral::server_msg(
165                                    ChatType::Meta,
166                                    Content::Plain(
167                                        "Kick failed, target does not exist.".to_string(),
168                                    ),
169                                ));
170                            }
171                            continue;
172                        },
173                    };
174
175                    // Can't kick pet
176                    if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target) != Some(owner))
177                    {
178                        if let Some(general_stream) = clients.get(entity) {
179                            general_stream.send_fallible(ServerGeneral::server_msg(
180                                ChatType::Meta,
181                                Content::Plain("Kick failed, you can't kick pets.".to_string()),
182                            ));
183                        }
184                        continue;
185                    }
186                    // Can't kick yourself
187                    if uids.get(entity).is_some_and(|u| *u == uid) {
188                        if let Some(client) = clients.get(entity) {
189                            client.send_fallible(ServerGeneral::server_msg(
190                                ChatType::Meta,
191                                Content::Plain("Kick failed, you can't kick yourself.".to_string()),
192                            ));
193                        }
194                        continue;
195                    }
196
197                    // Make sure kicker is the group leader
198                    match groups
199                        .get(target)
200                        .and_then(|group| group_manager.group_info(*group))
201                    {
202                        Some(info) if info.leader == entity => {
203                            // Remove target from group
204                            group_manager.leave_group(
205                                target,
206                                &mut groups,
207                                &alignments,
208                                &uids,
209                                &entities,
210                                &mut |entity, group_change| {
211                                    clients
212                                        .get(entity)
213                                        .and_then(|c| {
214                                            group_change
215                                                .try_map_ref(|e| uids.get(*e).copied())
216                                                .map(|g| (g, c))
217                                        })
218                                        .map(|(g, c)| {
219                                            update_map_markers(
220                                                &map_markers,
221                                                &uids,
222                                                c,
223                                                &group_change,
224                                            );
225                                            c.send_fallible(ServerGeneral::GroupUpdate(g));
226                                        });
227                                },
228                            );
229
230                            // Tell them the have been kicked
231                            if let Some(client) = clients.get(target) {
232                                client.send_fallible(ServerGeneral::server_msg(
233                                    ChatType::Meta,
234                                    Content::Plain("You were removed from the group.".to_string()),
235                                ));
236                            }
237                            // Tell kicker that they were successful
238                            if let Some(client) = clients.get(entity) {
239                                client.send_fallible(ServerGeneral::server_msg(
240                                    ChatType::Meta,
241                                    Content::Plain("Player kicked.".to_string()),
242                                ));
243                            }
244                        },
245                        Some(_) => {
246                            // Inform kicker that they are not the leader
247                            if let Some(client) = clients.get(entity) {
248                                client.send_fallible(ServerGeneral::server_msg(
249                                    ChatType::Meta,
250                                    Content::Plain(
251                                        "Kick failed: You are not the leader of the target's \
252                                         group."
253                                            .to_string(),
254                                    ),
255                                ));
256                            }
257                        },
258                        None => {
259                            // Inform kicker that the target is not in a group
260                            if let Some(client) = clients.get(entity) {
261                                client.send_fallible(ServerGeneral::server_msg(
262                                    ChatType::Meta,
263                                    Content::Plain(
264                                        "Kick failed: Your target is not in a group.".to_string(),
265                                    ),
266                                ));
267                            }
268                        },
269                    }
270                },
271                GroupManip::AssignLeader(uid) => {
272                    let target = match id_maps.uid_entity(uid) {
273                        Some(t) => t,
274                        None => {
275                            // Inform of failure
276                            if let Some(client) = clients.get(entity) {
277                                client.send_fallible(ServerGeneral::server_msg(
278                                    ChatType::Meta,
279                                    Content::Plain(
280                                        "Leadership transfer failed, target does not exist"
281                                            .to_string(),
282                                    ),
283                                ));
284                            }
285                            continue;
286                        },
287                    };
288                    // Make sure assigner is the group leader
289                    match groups
290                        .get(target)
291                        .and_then(|group| group_manager.group_info(*group))
292                    {
293                        Some(info) if info.leader == entity => {
294                            // Assign target as group leader
295                            group_manager.assign_leader(
296                                target,
297                                &groups,
298                                &entities,
299                                &alignments,
300                                &uids,
301                                |entity, group_change| {
302                                    clients
303                                        .get(entity)
304                                        .and_then(|c| {
305                                            group_change
306                                                .try_map_ref(|e| uids.get(*e).copied())
307                                                .map(|g| (g, c))
308                                        })
309                                        .map(|(g, c)| {
310                                            update_map_markers(
311                                                &map_markers,
312                                                &uids,
313                                                c,
314                                                &group_change,
315                                            );
316                                            c.send_fallible(ServerGeneral::GroupUpdate(g));
317                                        });
318                                },
319                            );
320                            // Tell them they are the leader
321                            if let Some(client) = clients.get(target) {
322                                client.send_fallible(ServerGeneral::server_msg(
323                                    ChatType::Meta,
324                                    Content::Plain("You are the group leader now.".to_string()),
325                                ));
326                            }
327                            // Tell the old leader that the transfer was succesful
328                            if let Some(client) = clients.get(entity) {
329                                client.send_fallible(ServerGeneral::server_msg(
330                                    ChatType::Meta,
331                                    Content::Plain(
332                                        "You are no longer the group leader.".to_string(),
333                                    ),
334                                ));
335                            }
336                        },
337                        Some(_) => {
338                            // Inform transferer that they are not the leader
339                            if let Some(client) = clients.get(entity) {
340                                client.send_fallible(ServerGeneral::server_msg(
341                                    ChatType::Meta,
342                                    Content::Plain(
343                                        "Transfer failed: You are not the leader of the target's \
344                                         group."
345                                            .to_string(),
346                                    ),
347                                ));
348                            }
349                        },
350                        None => {
351                            // Inform transferer that the target is not in a group
352                            if let Some(client) = clients.get(entity) {
353                                client.send_fallible(ServerGeneral::server_msg(
354                                    ChatType::Meta,
355                                    Content::Plain(
356                                        "Transfer failed: Your target is not in a group."
357                                            .to_string(),
358                                    ),
359                                ));
360                            }
361                        },
362                    }
363                },
364            }
365        }
366    }
367}