diff --git a/examples/conway.rs b/examples/conway.rs index e92e603..dd0c99c 100644 --- a/examples/conway.rs +++ b/examples/conway.rs @@ -13,9 +13,11 @@ use valence::dimension::{Dimension, DimensionId}; use valence::entity::types::Pose; use valence::entity::{EntityId, EntityKind, TrackedData}; use valence::player_list::PlayerListId; +use valence::protocol::packets::s2c::play::SoundCategory; use valence::server::{Server, SharedServer, ShutdownResult}; use valence::text::{Color, TextFormat}; use valence::{async_trait, ident}; +use vek::Vec3; pub fn main() -> ShutdownResult { env_logger::Builder::new() @@ -121,8 +123,6 @@ impl Config for Game { SIZE_Z as f64 / 2.0, ]; - server.state.paused = false; - server.clients.retain(|_, client| { if client.created_this_tick() { if self @@ -192,8 +192,21 @@ impl Config for Game { && (0..SIZE_Z as i32).contains(&position.z) && position.y == BOARD_Y { - server.state.board - [position.x as usize + position.z as usize * SIZE_X] = true; + let index = position.x as usize + position.z as usize * SIZE_X; + + let normal_position = Into::>::into(position); + + if !server.state.board[index] { + client.play_sound( + ident!("minecraft:block.note_block.banjo"), + SoundCategory::Block, + Vec3::::new(normal_position.x.into(), normal_position.y.into(), normal_position.z.into()), + 0.5f32, + 1f32, + ); + } + + server.state.board[index] = true; } } _ => {} @@ -201,7 +214,17 @@ impl Config for Game { } if let TrackedData::Player(data) = player.data() { - server.state.paused = data.get_pose() == Pose::Sneaking; + let sneaking = data.get_pose() == Pose::Sneaking; + if sneaking != server.state.paused { + server.state.paused = sneaking; + client.play_sound( + ident!("minecraft:block.note_block.pling"), + SoundCategory::Block, + client.position().into(), + 0.5f32, + if sneaking { 0.5f32 } else { 1f32 }, + ); + } } true diff --git a/src/client.rs b/src/client.rs index 16636a3..91bcb06 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,6 +21,7 @@ use crate::entity::data::Player; use crate::entity::{ velocity_to_packet_units, Entities, EntityEvent, EntityId, EntityKind, StatusOrAnimation, }; +use crate::ident::Ident; use crate::player_list::{PlayerListId, PlayerLists}; use crate::player_textures::SignedPlayerTextures; use crate::protocol::packets::c2s::play::{ @@ -32,9 +33,10 @@ use crate::protocol::packets::s2c::play::{ DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntitiesDestroy, EntityAnimation, EntityAttributes, EntityAttributesProperty, EntityPosition, EntitySetHeadYaw, EntityStatus, EntityTrackerUpdate, EntityVelocityUpdate, GameJoin, GameMessage, - GameStateChange, GameStateChangeReason, KeepAlive, MoveRelative, PlayerActionResponse, - PlayerPositionLook, PlayerPositionLookFlags, PlayerRespawn, PlayerSpawnPosition, RegistryCodec, - Rotate, RotateAndMoveRelative, S2cPlayPacket, UnloadChunk, UpdateSubtitle, UpdateTitle, + GameStateChange, GameStateChangeReason, KeepAlive, MoveRelative, PlaySoundId, + PlayerActionResponse, PlayerPositionLook, PlayerPositionLookFlags, PlayerRespawn, + PlayerSpawnPosition, RegistryCodec, Rotate, RotateAndMoveRelative, S2cPlayPacket, + SoundCategory, UnloadChunk, UpdateSubtitle, UpdateTitle, }; use crate::protocol::{BoundedInt, ByteAngle, NbtBridge, RawBytes, VarInt}; use crate::server::{C2sPacketChannels, NewClientData, S2cPlayMessage, SharedServer}; @@ -457,6 +459,25 @@ impl Client { self.new_game_mode = game_mode; } + /// Plays a sound to the client at a given position. + pub fn play_sound( + &mut self, + name: Ident, + category: SoundCategory, + pos: Vec3, + volume: f32, + pitch: f32, + ) { + self.send_packet(PlaySoundId { + name, + category, + position: pos.iter().map(|x| *x as i32 * 8).collect(), + volume, + pitch, + seed: 0, + }); + } + /// Sets the title this client sees. /// /// A title is a large piece of text displayed in the center of the screen diff --git a/src/protocol/packets/s2c.rs b/src/protocol/packets/s2c.rs index cc759ee..4a4f2e0 100644 --- a/src/protocol/packets/s2c.rs +++ b/src/protocol/packets/s2c.rs @@ -227,6 +227,32 @@ pub mod play { } } + def_enum! { + SoundCategory: VarInt { + Master = 0, + Music = 1, + Record = 2, + Weather = 3, + Block = 4, + Hostile = 5, + Neutral = 6, + Player = 7, + Ambient = 8, + Voice = 9, + } + } + + def_struct! { + PlaySoundId { + name: Ident, + category: SoundCategory, + position: Vec3, + volume: f32, + pitch: f32, + seed: i64, + } + } + def_struct! { Disconnect { reason: Text, @@ -704,6 +730,27 @@ pub mod play { } } + def_struct! { + PlaySoundFromEntity { + id: VarInt, + category: SoundCategory, + entity_id: VarInt, + volume: f32, + pitch: f32 + } + } + + def_struct! { + PlaySound { + id: VarInt, + category: SoundCategory, + position: Vec3, + volume: f32, + pitch: f32, + seed: i64 + } + } + def_struct! { GameMessage { chat: Text, @@ -765,6 +812,7 @@ pub mod play { BlockUpdate = 9, BossBar = 10, ClearTitles = 13, + PlaySoundId = 23, Disconnect = 25, EntityStatus = 26, UnloadChunk = 28, @@ -792,6 +840,8 @@ pub mod play { WorldTimeUpdate = 92, UpdateTitle = 93, TitleFade = 94, + PlaySoundFromEntity = 95, + PlaySound = 96, GameMessage = 98, PlayerListHeaderFooter = 99, EntityPosition = 102,