2022-07-15 16:18:20 +10:00
|
|
|
//! The player list (tab list).
|
|
|
|
|
2022-06-28 10:52:23 +10:00
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
|
|
use bitfield_struct::bitfield;
|
|
|
|
use uuid::Uuid;
|
2022-11-14 01:10:42 +11:00
|
|
|
use valence_protocol::packets::s2c::play::{PlayerInfo, SetTabListHeaderAndFooter};
|
|
|
|
use valence_protocol::types::{GameMode, PlayerInfoAddPlayer, SignedProperty};
|
2022-11-17 13:22:44 +11:00
|
|
|
use valence_protocol::{Text, VarInt};
|
2022-06-28 10:52:23 +10:00
|
|
|
|
2022-08-10 07:44:04 +10:00
|
|
|
use crate::config::Config;
|
2022-07-01 04:53:57 +10:00
|
|
|
use crate::player_textures::SignedPlayerTextures;
|
2022-11-01 21:11:51 +11:00
|
|
|
use crate::server::PlayPacketController;
|
2022-08-10 07:44:04 +10:00
|
|
|
use crate::slab_rc::{Key, SlabRc};
|
2022-06-28 10:52:23 +10:00
|
|
|
|
2022-09-02 17:06:45 +10:00
|
|
|
/// A container for all [`PlayerList`]s on a server.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub struct PlayerLists<C: Config> {
|
|
|
|
slab: SlabRc<PlayerList<C>>,
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:06:45 +10:00
|
|
|
/// An identifier for a [`PlayerList`] on the server.
|
|
|
|
///
|
|
|
|
/// Player list IDs are refcounted. Once all IDs referring to the same player
|
|
|
|
/// list are dropped, the player list is automatically deleted.
|
|
|
|
///
|
|
|
|
/// The [`Ord`] instance on this type is correct but otherwise unspecified. This
|
|
|
|
/// is useful for storing IDs in containers such as
|
|
|
|
/// [`BTreeMap`](std::collections::BTreeMap).
|
2022-08-10 07:44:04 +10:00
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
pub struct PlayerListId(Key);
|
|
|
|
|
|
|
|
impl<C: Config> PlayerLists<C> {
|
|
|
|
pub(crate) fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
slab: SlabRc::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:37:02 +10:00
|
|
|
/// Creates a new player list and returns an exclusive reference to it along
|
|
|
|
/// with its ID.
|
2022-09-02 17:06:45 +10:00
|
|
|
///
|
2022-09-02 17:37:02 +10:00
|
|
|
/// The player list is automatically removed at the end of the tick once all
|
|
|
|
/// IDs to it have been dropped.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub fn insert(&mut self, state: C::PlayerListState) -> (PlayerListId, &mut PlayerList<C>) {
|
|
|
|
let (key, pl) = self.slab.insert(PlayerList {
|
|
|
|
state,
|
|
|
|
entries: HashMap::new(),
|
|
|
|
removed: HashSet::new(),
|
|
|
|
header: Text::default(),
|
|
|
|
footer: Text::default(),
|
|
|
|
modified_header_or_footer: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
(PlayerListId(key), pl)
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:37:02 +10:00
|
|
|
/// Returns the number of player lists.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.slab.len()
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:37:02 +10:00
|
|
|
/// Returns `true` if there are no player lists.
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.slab.len() == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets a shared reference to the player list with the given player list
|
|
|
|
/// ID.
|
|
|
|
///
|
|
|
|
/// This operation is infallible because [`PlayerListId`] is refcounted.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub fn get(&self, id: &PlayerListId) -> &PlayerList<C> {
|
|
|
|
self.slab.get(&id.0)
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:37:02 +10:00
|
|
|
/// Gets an exclusive reference to the player list with the given player
|
|
|
|
/// list ID.
|
|
|
|
///
|
|
|
|
/// This operation is infallible because [`PlayerListId`] is refcounted.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub fn get_mut(&mut self, id: &PlayerListId) -> &mut PlayerList<C> {
|
|
|
|
self.slab.get_mut(&id.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn update(&mut self) {
|
|
|
|
self.slab.collect_garbage();
|
|
|
|
for (_, pl) in self.slab.iter_mut() {
|
|
|
|
for entry in pl.entries.values_mut() {
|
|
|
|
entry.bits = EntryBits::new();
|
|
|
|
}
|
|
|
|
pl.removed.clear();
|
|
|
|
pl.modified_header_or_footer = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// The list of players on a server visible by pressing the tab key by default.
|
|
|
|
///
|
|
|
|
/// Each entry in the player list is intended to represent a connected client to
|
|
|
|
/// the server.
|
|
|
|
///
|
|
|
|
/// In addition to a list of players, the player list has a header and a footer
|
|
|
|
/// which can contain arbitrary text.
|
2022-08-10 07:44:04 +10:00
|
|
|
pub struct PlayerList<C: Config> {
|
|
|
|
/// Custom state
|
|
|
|
pub state: C::PlayerListState,
|
2022-06-28 10:52:23 +10:00
|
|
|
entries: HashMap<Uuid, PlayerListEntry>,
|
|
|
|
removed: HashSet<Uuid>,
|
|
|
|
header: Text,
|
|
|
|
footer: Text,
|
|
|
|
modified_header_or_footer: bool,
|
|
|
|
}
|
|
|
|
|
2022-08-10 07:44:04 +10:00
|
|
|
impl<C: Config> PlayerList<C> {
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Inserts a player into the player list.
|
|
|
|
///
|
|
|
|
/// If the given UUID conflicts with an existing entry, the entry is
|
|
|
|
/// overwritten and `false` is returned. Otherwise, `true` is returned.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn insert(
|
|
|
|
&mut self,
|
|
|
|
uuid: Uuid,
|
|
|
|
username: impl Into<String>,
|
|
|
|
textures: Option<SignedPlayerTextures>,
|
|
|
|
game_mode: GameMode,
|
|
|
|
ping: i32,
|
2022-11-14 01:10:42 +11:00
|
|
|
display_name: Option<Text>,
|
2022-07-11 22:08:02 +10:00
|
|
|
) -> bool {
|
2022-06-30 04:09:00 +10:00
|
|
|
match self.entries.entry(uuid) {
|
|
|
|
Entry::Occupied(mut oe) => {
|
|
|
|
let e = oe.get_mut();
|
|
|
|
let username = username.into();
|
|
|
|
|
|
|
|
if e.username() != username || e.textures != textures {
|
|
|
|
self.removed.insert(*oe.key());
|
|
|
|
|
|
|
|
oe.insert(PlayerListEntry {
|
|
|
|
username,
|
|
|
|
textures,
|
|
|
|
game_mode,
|
|
|
|
ping,
|
2022-11-14 01:10:42 +11:00
|
|
|
display_name,
|
2022-08-06 05:36:34 +10:00
|
|
|
bits: EntryBits::new().with_created_this_tick(true),
|
2022-06-30 04:09:00 +10:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
e.set_game_mode(game_mode);
|
|
|
|
e.set_ping(ping);
|
|
|
|
e.set_display_name(display_name);
|
|
|
|
}
|
2022-07-11 22:08:02 +10:00
|
|
|
false
|
2022-06-30 04:09:00 +10:00
|
|
|
}
|
|
|
|
Entry::Vacant(ve) => {
|
|
|
|
ve.insert(PlayerListEntry {
|
|
|
|
username: username.into(),
|
|
|
|
textures,
|
|
|
|
game_mode,
|
|
|
|
ping,
|
2022-11-14 01:10:42 +11:00
|
|
|
display_name,
|
2022-08-06 05:36:34 +10:00
|
|
|
bits: EntryBits::new().with_created_this_tick(true),
|
2022-06-30 04:09:00 +10:00
|
|
|
});
|
2022-07-11 22:08:02 +10:00
|
|
|
true
|
2022-06-30 04:09:00 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Removes an entry from the player list with the given UUID. Returns
|
|
|
|
/// whether the entry was present in the list.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn remove(&mut self, uuid: Uuid) -> bool {
|
|
|
|
if self.entries.remove(&uuid).is_some() {
|
|
|
|
self.removed.insert(uuid);
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-19 11:35:46 +10:00
|
|
|
/// Removes all entries from the player list for which `f` returns `false`.
|
2022-07-11 22:08:02 +10:00
|
|
|
///
|
|
|
|
/// All entries are visited in an unspecified order.
|
|
|
|
pub fn retain(&mut self, mut f: impl FnMut(Uuid, &mut PlayerListEntry) -> bool) {
|
|
|
|
self.entries.retain(|&uuid, entry| {
|
|
|
|
if !f(uuid, entry) {
|
|
|
|
self.removed.insert(uuid);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Removes all entries from the player list.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.removed.extend(self.entries.drain().map(|p| p.0))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the header part of the player list.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn header(&self) -> &Text {
|
|
|
|
&self.header
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Sets the header part of the player list.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn set_header(&mut self, header: impl Into<Text>) {
|
|
|
|
let header = header.into();
|
|
|
|
if self.header != header {
|
|
|
|
self.header = header;
|
|
|
|
self.modified_header_or_footer = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Gets the footer part of the player list.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn footer(&self) -> &Text {
|
|
|
|
&self.footer
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Sets the footer part of the player list.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn set_footer(&mut self, footer: impl Into<Text>) {
|
|
|
|
let footer = footer.into();
|
|
|
|
if self.footer != footer {
|
|
|
|
self.footer = footer;
|
|
|
|
self.modified_header_or_footer = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-09 09:12:30 +11:00
|
|
|
/// Returns a reference to the entry with the given UUID.
|
|
|
|
///
|
|
|
|
/// If the entry does not exist, `None` is returned.
|
|
|
|
pub fn entry(&self, uuid: Uuid) -> Option<&PlayerListEntry> {
|
|
|
|
self.entries.get(&uuid)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a mutable reference to the entry with the given UUID.
|
|
|
|
///
|
|
|
|
/// If the entry does not exist, `None` is returned.
|
|
|
|
pub fn entry_mut(&mut self, uuid: Uuid) -> Option<&mut PlayerListEntry> {
|
|
|
|
self.entries.get_mut(&uuid)
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Returns an iterator over all entries in an unspecified order.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn entries(&self) -> impl Iterator<Item = (Uuid, &PlayerListEntry)> + '_ {
|
|
|
|
self.entries.iter().map(|(k, v)| (*k, v))
|
|
|
|
}
|
|
|
|
|
2022-09-02 17:06:45 +10:00
|
|
|
/// Returns a mutable iterator over all entries in an unspecified order.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Uuid, &mut PlayerListEntry)> + '_ {
|
|
|
|
self.entries.iter_mut().map(|(k, v)| (*k, v))
|
|
|
|
}
|
|
|
|
|
2022-11-14 01:10:42 +11:00
|
|
|
pub(crate) fn send_initial_packets(
|
|
|
|
&self,
|
|
|
|
ctrl: &mut PlayPacketController,
|
|
|
|
) -> anyhow::Result<()> {
|
2022-06-28 10:52:23 +10:00
|
|
|
let add_player: Vec<_> = self
|
|
|
|
.entries
|
|
|
|
.iter()
|
2022-11-14 01:10:42 +11:00
|
|
|
.map(|(&uuid, e)| PlayerInfoAddPlayer {
|
2022-06-28 10:52:23 +10:00
|
|
|
uuid,
|
2022-11-14 01:10:42 +11:00
|
|
|
username: &e.username,
|
2022-06-28 10:52:23 +10:00
|
|
|
properties: {
|
|
|
|
let mut properties = Vec::new();
|
2022-06-29 11:29:29 +10:00
|
|
|
if let Some(textures) = &e.textures {
|
2022-11-14 01:10:42 +11:00
|
|
|
properties.push(SignedProperty {
|
|
|
|
name: "textures",
|
|
|
|
value: textures.payload(),
|
|
|
|
signature: Some(textures.signature()),
|
2022-06-28 10:52:23 +10:00
|
|
|
});
|
|
|
|
}
|
|
|
|
properties
|
|
|
|
},
|
|
|
|
game_mode: e.game_mode,
|
|
|
|
ping: VarInt(e.ping),
|
|
|
|
display_name: e.display_name.clone(),
|
|
|
|
sig_data: None,
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
if !add_player.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::AddPlayer(add_player))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.header != Text::default() || self.footer != Text::default() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&SetTabListHeaderAndFooter {
|
|
|
|
header: self.header.clone(),
|
|
|
|
footer: self.footer.clone(),
|
|
|
|
})?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
2022-11-01 21:11:51 +11:00
|
|
|
|
|
|
|
Ok(())
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-11-14 01:10:42 +11:00
|
|
|
pub(crate) fn send_update_packets(
|
|
|
|
&self,
|
|
|
|
ctrl: &mut PlayPacketController,
|
|
|
|
) -> anyhow::Result<()> {
|
2022-06-28 10:52:23 +10:00
|
|
|
if !self.removed.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::RemovePlayer(
|
|
|
|
self.removed.iter().cloned().collect(),
|
|
|
|
))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut add_player = Vec::new();
|
|
|
|
let mut game_mode = Vec::new();
|
|
|
|
let mut ping = Vec::new();
|
|
|
|
let mut display_name = Vec::new();
|
|
|
|
|
|
|
|
for (&uuid, e) in self.entries.iter() {
|
2022-08-06 05:36:34 +10:00
|
|
|
if e.bits.created_this_tick() {
|
2022-06-28 10:52:23 +10:00
|
|
|
let mut properties = Vec::new();
|
2022-06-29 11:29:29 +10:00
|
|
|
if let Some(textures) = &e.textures {
|
2022-11-14 01:10:42 +11:00
|
|
|
properties.push(SignedProperty {
|
|
|
|
name: "textures",
|
|
|
|
value: textures.payload(),
|
|
|
|
signature: Some(textures.signature()),
|
2022-06-28 10:52:23 +10:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-11-14 01:10:42 +11:00
|
|
|
add_player.push(PlayerInfoAddPlayer {
|
2022-06-28 10:52:23 +10:00
|
|
|
uuid,
|
2022-11-14 01:10:42 +11:00
|
|
|
username: e.username(),
|
2022-06-28 10:52:23 +10:00
|
|
|
properties,
|
|
|
|
game_mode: e.game_mode,
|
|
|
|
ping: VarInt(e.ping),
|
|
|
|
display_name: e.display_name.clone(),
|
|
|
|
sig_data: None,
|
|
|
|
});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-08-06 05:36:34 +10:00
|
|
|
if e.bits.modified_game_mode() {
|
2022-06-28 10:52:23 +10:00
|
|
|
game_mode.push((uuid, e.game_mode));
|
|
|
|
}
|
|
|
|
|
2022-08-06 05:36:34 +10:00
|
|
|
if e.bits.modified_ping() {
|
2022-06-28 10:52:23 +10:00
|
|
|
ping.push((uuid, VarInt(e.ping)));
|
|
|
|
}
|
|
|
|
|
2022-08-06 05:36:34 +10:00
|
|
|
if e.bits.modified_display_name() {
|
2022-06-28 10:52:23 +10:00
|
|
|
display_name.push((uuid, e.display_name.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !add_player.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::AddPlayer(add_player))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if !game_mode.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::UpdateGameMode(game_mode))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if !ping.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::UpdateLatency(ping))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if !display_name.is_empty() {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::UpdateDisplayName(display_name))?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.modified_header_or_footer {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&SetTabListHeaderAndFooter {
|
|
|
|
header: self.header.clone(),
|
|
|
|
footer: self.footer.clone(),
|
|
|
|
})?;
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
2022-11-01 21:11:51 +11:00
|
|
|
|
|
|
|
Ok(())
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-11-14 01:10:42 +11:00
|
|
|
pub(crate) fn queue_clear_packets(
|
|
|
|
&self,
|
|
|
|
ctrl: &mut PlayPacketController,
|
|
|
|
) -> anyhow::Result<()> {
|
2022-11-01 21:11:51 +11:00
|
|
|
ctrl.append_packet(&PlayerInfo::RemovePlayer(
|
|
|
|
self.entries.keys().cloned().collect(),
|
|
|
|
))
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Represents a player entry in the [`PlayerList`].
|
2022-06-28 10:52:23 +10:00
|
|
|
pub struct PlayerListEntry {
|
|
|
|
username: String,
|
2022-06-29 11:29:29 +10:00
|
|
|
textures: Option<SignedPlayerTextures>,
|
2022-06-28 10:52:23 +10:00
|
|
|
game_mode: GameMode,
|
|
|
|
ping: i32,
|
|
|
|
display_name: Option<Text>,
|
2022-08-06 05:36:34 +10:00
|
|
|
bits: EntryBits,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bitfield(u8)]
|
|
|
|
struct EntryBits {
|
|
|
|
created_this_tick: bool,
|
|
|
|
modified_game_mode: bool,
|
|
|
|
modified_ping: bool,
|
|
|
|
modified_display_name: bool,
|
|
|
|
#[bits(4)]
|
|
|
|
_pad: u8,
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PlayerListEntry {
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Gets the username of this entry.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn username(&self) -> &str {
|
|
|
|
&self.username
|
|
|
|
}
|
|
|
|
|
2022-07-15 16:18:20 +10:00
|
|
|
/// Gets the player textures for this entry.
|
2022-06-29 11:29:29 +10:00
|
|
|
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
|
|
|
self.textures.as_ref()
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Gets the game mode of this entry.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn game_mode(&self) -> GameMode {
|
|
|
|
self.game_mode
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Sets the game mode of this entry.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
|
|
|
if self.game_mode != game_mode {
|
|
|
|
self.game_mode = game_mode;
|
2022-08-06 05:36:34 +10:00
|
|
|
self.bits.set_modified_game_mode(true);
|
2022-06-30 04:09:00 +10:00
|
|
|
}
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Gets the ping (latency) of this entry measured in milliseconds.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn ping(&self) -> i32 {
|
|
|
|
self.ping
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Sets the ping (latency) of this entry measured in milliseconds.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn set_ping(&mut self, ping: i32) {
|
|
|
|
if self.ping != ping {
|
|
|
|
self.ping = ping;
|
2022-08-06 05:36:34 +10:00
|
|
|
self.bits.set_modified_ping(true);
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Gets the display name of this entry.
|
2022-06-30 04:09:00 +10:00
|
|
|
pub fn display_name(&self) -> Option<&Text> {
|
|
|
|
self.display_name.as_ref()
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
|
2022-07-11 22:08:02 +10:00
|
|
|
/// Sets the display name of this entry.
|
2022-06-28 10:52:23 +10:00
|
|
|
pub fn set_display_name(&mut self, display_name: impl Into<Option<Text>>) {
|
|
|
|
let display_name = display_name.into();
|
2022-06-30 04:09:00 +10:00
|
|
|
if self.display_name != display_name {
|
|
|
|
self.display_name = display_name;
|
2022-08-06 05:36:34 +10:00
|
|
|
self.bits.set_modified_display_name(true);
|
2022-06-28 10:52:23 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|