mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Implement title and subtitle
This commit is contained in:
parent
bfba7a1d11
commit
5fcde5f7ae
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
24
src/text.rs
24
src/text.rs
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue