Implement title and subtitle

This commit is contained in:
Ryan 2022-07-06 00:16:07 -07:00
parent bfba7a1d11
commit 5fcde5f7ae
3 changed files with 91 additions and 6 deletions

View file

@ -19,16 +19,17 @@ use crate::player_textures::SignedPlayerTextures;
use crate::protocol::packets::play::c2s::{ use crate::protocol::packets::play::c2s::{
C2sPlayPacket, DiggingStatus, InteractKind, PlayerCommandId, C2sPlayPacket, DiggingStatus, InteractKind, PlayerCommandId,
}; };
pub use crate::protocol::packets::play::s2c::SetTitleAnimationTimes as TitleAnimationTimes;
use crate::protocol::packets::play::s2c::{ use crate::protocol::packets::play::s2c::{
Animate, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, Animate, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound,
BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, BlockChangeAck, BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, BlockChangeAck,
ChatType, ChatTypeChat, ChatTypeNarration, ChatTypeRegistry, ChatTypeRegistryEntry, ChatType, ChatTypeChat, ChatTypeNarration, ChatTypeRegistry, ChatTypeRegistryEntry,
DimensionType, DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityEvent, ClearTitles, DimensionType, DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect,
ForgetLevelChunk, GameEvent, GameEventReason, KeepAlive, Login, MoveEntityPosition, EntityEvent, ForgetLevelChunk, GameEvent, GameEventReason, KeepAlive, Login,
MoveEntityPositionAndRotation, MoveEntityRotation, PlayerPosition, PlayerPositionFlags, MoveEntityPosition, MoveEntityPositionAndRotation, MoveEntityRotation, PlayerPosition,
RegistryCodec, RemoveEntities, Respawn, RotateHead, S2cPlayPacket, SetChunkCacheCenter, PlayerPositionFlags, RegistryCodec, RemoveEntities, Respawn, RotateHead, S2cPlayPacket,
SetChunkCacheRadius, SetEntityMetadata, SetEntityMotion, SpawnPosition, SystemChat, SetChunkCacheCenter, SetChunkCacheRadius, SetEntityMetadata, SetEntityMotion, SetSubtitleText,
TeleportEntity, ENTITY_EVENT_MAX_BOUND, SetTitleText, SpawnPosition, SystemChat, TeleportEntity, ENTITY_EVENT_MAX_BOUND,
}; };
use crate::protocol::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt}; use crate::protocol::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt};
use crate::server::C2sPacketChannels; use crate::server::C2sPacketChannels;
@ -310,6 +311,32 @@ impl Client {
self.new_game_mode = new_game_mode; self.new_game_mode = new_game_mode;
} }
pub fn set_title(
&mut self,
title: impl Into<Text>,
subtitle: impl Into<Text>,
animation: impl Into<Option<TitleAnimationTimes>>,
) {
let title = title.into();
let subtitle = subtitle.into();
self.send_packet(SetTitleText { text: title });
if !subtitle.is_empty() {
self.send_packet(SetSubtitleText {
subtitle_text: subtitle,
});
}
if let Some(anim) = animation.into() {
self.send_packet(anim);
}
}
pub fn clear_title(&mut self) {
self.send_packet(ClearTitles { reset: true });
}
pub fn on_ground(&self) -> bool { pub fn on_ground(&self) -> bool {
self.flags.on_ground() self.flags.on_ground()
} }

View file

@ -724,6 +724,12 @@ pub mod play {
} }
} }
def_struct! {
ClearTitles 0x0d {
reset: bool,
}
}
def_struct! { def_struct! {
Disconnect 0x17 { Disconnect 0x17 {
reason: Text, reason: Text,
@ -1168,6 +1174,12 @@ pub mod play {
} }
} }
def_struct! {
SetSubtitleText 0x58 {
subtitle_text: Text,
}
}
def_struct! { def_struct! {
SetTime 0x59 { SetTime 0x59 {
/// The age of the world in 1/20ths of a second. /// The age of the world in 1/20ths of a second.
@ -1179,6 +1191,24 @@ pub mod play {
} }
} }
def_struct! {
SetTitleText 0x5a {
text: Text,
}
}
def_struct! {
#[derive(Copy, PartialEq, Eq)]
SetTitleAnimationTimes 0x5b {
/// Ticks to spend fading in.
fade_in: u32,
/// Ticks to keep the title displayed.
stay: u32,
/// Ticks to spend fading out.
fade_out: u32,
}
}
def_struct! { def_struct! {
SystemChat 0x5f { SystemChat 0x5f {
chat: Text, chat: Text,
@ -1216,6 +1246,7 @@ pub mod play {
BlockEvent, BlockEvent,
BlockUpdate, BlockUpdate,
BossEvent, BossEvent,
ClearTitles,
Disconnect, Disconnect,
EntityEvent, EntityEvent,
ForgetLevelChunk, ForgetLevelChunk,
@ -1239,7 +1270,10 @@ pub mod play {
SpawnPosition, SpawnPosition,
SetEntityMetadata, SetEntityMetadata,
SetEntityMotion, SetEntityMotion,
SetSubtitleText,
SetTime, SetTime,
SetTitleText,
SetTitleAnimationTimes,
SystemChat, SystemChat,
TabList, TabList,
TeleportEntity, TeleportEntity,

View file

@ -81,6 +81,21 @@ pub struct Text {
extra: Vec<Text>, extra: Vec<Text>,
} }
impl Text {
pub fn is_empty(&self) -> bool {
for extra in &self.extra {
if !extra.is_empty() {
return false;
}
}
match &self.content {
TextContent::Text { text } => text.is_empty(),
TextContent::Translate { translate } => translate.is_empty(),
}
}
}
/// Provides the methods necessary for working with [`Text`] objects. /// Provides the methods necessary for working with [`Text`] objects.
/// ///
/// This trait exists to allow using `Into<Text>` types without having to first /// This trait exists to allow using `Into<Text>` types without having to first
@ -539,4 +554,13 @@ mod tests {
assert_eq!(color_from_str("#000000"), Some(Color::BLACK)); assert_eq!(color_from_str("#000000"), Some(Color::BLACK));
assert_eq!(color_from_str("#"), None); assert_eq!(color_from_str("#"), None);
} }
#[test]
fn empty() {
assert!("".into_text().is_empty());
let txt = "".into_text() + Text::translate("") + ("".italic().color(Color::RED) + "");
assert!(txt.is_empty());
assert!(txt.to_plain().is_empty());
}
} }