From fa2241c038c9edcd9121d7b288866a450353a7dc Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 29 Jun 2022 21:33:42 -0700 Subject: [PATCH] Basic chat message support --- examples/terrain.rs | 4 +++- src/client.rs | 42 ++++++++++++++++++++++++++++-------------- src/client/event.rs | 6 ++++++ src/packets.rs | 22 ++++++++++++++++++++-- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/examples/terrain.rs b/examples/terrain.rs index 89c87ea..37738a1 100644 --- a/examples/terrain.rs +++ b/examples/terrain.rs @@ -6,7 +6,7 @@ use log::LevelFilter; use noise::{NoiseFn, Seedable, SuperSimplex}; use rayon::iter::ParallelIterator; use valence::block::{BlockState, PropName, PropValue}; -use valence::client::GameMode; +use valence::client::{ChatMessageType, GameMode}; use valence::config::{Config, ServerListPing}; use valence::text::Color; use valence::util::chunks_in_view_distance; @@ -113,6 +113,8 @@ impl Config for Game { 0, None, ); + + client.send_message("welcome!", ChatMessageType::Chat); } let dist = client.view_distance(); diff --git a/src/client.rs b/src/client.rs index 5ed9fb7..5697e17 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,6 +2,7 @@ mod event; use std::collections::HashSet; use std::iter::FusedIterator; +use std::time::Duration; pub use event::*; use flume::{Receiver, Sender, TrySendError}; @@ -16,6 +17,7 @@ use crate::dimension::{Dimension, DimensionEffects}; use crate::entity::types::Player; use crate::entity::{velocity_to_packet_units, EntityType}; use crate::packets::play::c2s::{C2sPlayPacket, DiggingStatus}; +pub use crate::packets::play::s2c::ChatMessageType; use crate::packets::play::s2c::{ AcknowledgeBlockChanges, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, @@ -23,7 +25,8 @@ use crate::packets::play::s2c::{ DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityMetadata, EntityPosition, EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame, KeepAlive, PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec, - S2cPlayPacket, SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition, + S2cPlayPacket, SpawnPosition, SystemChatMessage, UnloadChunk, UpdateViewDistance, + UpdateViewPosition, }; use crate::player_textures::SignedPlayerTextures; use crate::protocol::{BoundedInt, Nbt, RawBytes}; @@ -136,6 +139,7 @@ pub struct Client { old_game_mode: GameMode, settings: Option, dug_blocks: Vec, + msgs_to_send: Vec<(Text, ChatMessageType)>, /// The metadata for the client's own player entity. player_meta: Player, } @@ -178,6 +182,7 @@ impl Client { old_game_mode: GameMode::Survival, settings: None, dug_blocks: Vec::new(), + msgs_to_send: Vec::new(), player_meta: Player::new(), } } @@ -198,6 +203,10 @@ impl Client { self.textures.as_ref() } + pub fn send_message(&mut self, msg: impl Into, typ: ChatMessageType) { + self.msgs_to_send.push((msg.into(), typ)); + } + pub fn position(&self) -> Vec3 { self.new_position } @@ -402,7 +411,10 @@ impl Client { C2sPlayPacket::QueryBlockNbt(_) => {} C2sPlayPacket::SetDifficulty(_) => {} C2sPlayPacket::ChatCommand(_) => {} - C2sPlayPacket::ChatMessage(_) => {} + C2sPlayPacket::ChatMessage(p) => self.events.push(Event::ChatMessage { + message: p.message.0, + timestamp: Duration::from_millis(p.timestamp), + }), C2sPlayPacket::ChatPreview(_) => {} C2sPlayPacket::ClientStatus(_) => {} C2sPlayPacket::ClientSettings(p) => { @@ -719,6 +731,11 @@ impl Client { }); } + for (msg, typ) in self.msgs_to_send.drain(..) { + // TODO: wont work without proper chat registry. + send_packet(&mut self.send, SystemChatMessage { chat: msg, typ }); + } + let mut entities_to_unload = Vec::new(); // Update all entities that are visible and unload entities that are no @@ -824,21 +841,18 @@ impl Client { } // Update the client's own player metadata. - { - let mut data = Vec::new(); - self.player_meta.updated_metadata(&mut data); + let mut data = Vec::new(); + self.player_meta.updated_metadata(&mut data); - if !data.is_empty() { - data.push(0xff); + if !data.is_empty() { + data.push(0xff); - self.send_packet(EntityMetadata { - entity_id: VarInt(0), - metadata: RawBytes(data), - }); - } - - self.player_meta.clear_modifications(); + self.send_packet(EntityMetadata { + entity_id: VarInt(0), + metadata: RawBytes(data), + }); } + self.player_meta.clear_modifications(); // Spawn new entities within the view distance. let pos = self.position(); diff --git a/src/client/event.rs b/src/client/event.rs index c7cb2f7..3f713a0 100644 --- a/src/client/event.rs +++ b/src/client/event.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use vek::Vec3; use crate::packets::play::c2s::BlockFace; @@ -7,6 +9,10 @@ use crate::{BlockPos, EntityId}; #[derive(Debug)] pub enum Event { + ChatMessage { + message: String, + timestamp: Duration, + }, /// Settings were changed. The value in this variant is the previous client /// settings. SettingsChanged(Option), diff --git a/src/packets.rs b/src/packets.rs index b34fe70..e036ccb 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -1005,6 +1005,7 @@ pub mod play { } def_enum! { + #[derive(Copy, PartialEq, Eq)] ChatMessageType: VarInt { Chat = 0, SystemMessage = 1, @@ -1017,6 +1018,12 @@ pub mod play { } } + impl Default for ChatMessageType { + fn default() -> Self { + ChatMessageType::Chat + } + } + def_enum! { PlayerInfo 0x34: VarInt { AddPlayer: Vec = 0, @@ -1132,6 +1139,13 @@ pub mod play { } } + def_struct! { + SystemChatMessage 0x5f { + chat: Text, + typ: ChatMessageType, + } + } + def_struct! { PlayerListHeaderFooter 0x60 { header: Text, @@ -1240,6 +1254,7 @@ pub mod play { EntityMetadata, EntityVelocity, TimeUpdate, + SystemChatMessage, PlayerListHeaderFooter, EntityTeleport, } @@ -1280,8 +1295,11 @@ pub mod play { def_struct! { ChatMessage 0x04 { - message: BoundedString<0, 256> - // TODO: + message: BoundedString<0, 256>, + timestamp: u64, + salt: u64, + signature: Vec, + signed_preview: bool, } }