diff options
| author | Fuwn <[email protected]> | 2024-06-12 01:28:50 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-06-12 01:28:50 -0700 |
| commit | a47eee894e431f039afe76cc39ecc67966d94183 (patch) | |
| tree | 5954a0d262547450d7fedd5aed69d9e983d93cee | |
| parent | refactor(crates): update idioms (diff) | |
| download | whirl-a47eee894e431f039afe76cc39ecc67966d94183.tar.xz whirl-a47eee894e431f039afe76cc39ecc67966d94183.zip | |
feat(whirl_server): short object id sending
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/appear_actor.rs | 25 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/long_location.rs | 53 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/mod.rs | 1 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/register_object_id.rs | 30 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/subscribe_distance.rs | 7 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/commands/teleport.rs | 34 | ||||
| -rw-r--r-- | crates/whirl_server/src/cmd/extendable.rs | 5 | ||||
| -rw-r--r-- | crates/whirl_server/src/distributor.rs | 6 | ||||
| -rw-r--r-- | crates/whirl_server/src/hub.rs | 102 | ||||
| -rw-r--r-- | crates/whirl_server/src/interaction/shared.rs | 10 | ||||
| -rw-r--r-- | crates/whirl_server/src/lib.rs | 18 | ||||
| -rw-r--r-- | crates/whirl_server/src/packet_parser.rs | 5 |
12 files changed, 233 insertions, 63 deletions
diff --git a/crates/whirl_server/src/cmd/commands/appear_actor.rs b/crates/whirl_server/src/cmd/commands/appear_actor.rs index 6100fcc..1eb9ad0 100644 --- a/crates/whirl_server/src/cmd/commands/appear_actor.rs +++ b/crates/whirl_server/src/cmd/commands/appear_actor.rs @@ -8,29 +8,28 @@ use { #[derive(Debug)] pub struct AppearActor { - pub short_object_id: i8, - pub room_id: u16, - pub x: i16, - pub y: i16, - pub z: i16, - pub direction: i16, + pub room_id: u16, + pub x: i16, + pub y: i16, + pub z: i16, + pub direction: i16, } + impl Creatable for AppearActor { - fn create(&self) -> Vec<u8> { + fn create_with_short_object_id(&self, short_object_id: u8) -> Vec<u8> { let mut command = BytesMut::new(); // Header - command.put_u8(0xFE); // ObjId + command.put_u8(short_object_id); // ObjId #[allow(clippy::cast_possible_truncation)] command.put_i8(Command::ApprActr as i32 as i8); // Type // Content - command.put_i8(self.short_object_id); // ObjId, why is it here? Worlds... command.put_u16(self.room_id); // Room ID - command.put_u16(self.x as u16); // X - command.put_u16(self.y as u16); // Y - command.put_u16(self.z as u16); // Z - command.put_u16(self.direction as u16); // Direction + command.put_i16(self.x); // X + command.put_i16(self.y); // Y + command.put_i16(self.z); // Z + command.put_i16(self.direction); // Direction // Length let mut command_as_vec = command.to_vec(); diff --git a/crates/whirl_server/src/cmd/commands/long_location.rs b/crates/whirl_server/src/cmd/commands/long_location.rs new file mode 100644 index 0000000..1cb054f --- /dev/null +++ b/crates/whirl_server/src/cmd/commands/long_location.rs @@ -0,0 +1,53 @@ +use { + crate::cmd::{ + constants::Command, + extendable::{Creatable, Parsable}, + }, + byteorder::{BigEndian, ReadBytesExt}, + bytes::{Buf, BufMut, BytesMut}, +}; + +#[derive(Debug)] +pub struct LongLocation { + pub x: i16, + pub y: i16, + pub z: i16, + pub direction: i16, +} + +impl Parsable for LongLocation { + fn parse(data: Vec<u8>) -> Self { + let mut data = BytesMut::from(data.as_slice()).reader(); + + Self { + x: data.read_i16::<BigEndian>().unwrap(), + y: data.read_i16::<BigEndian>().unwrap(), + z: data.read_i16::<BigEndian>().unwrap(), + direction: data.read_i16::<BigEndian>().unwrap(), + } + } +} + +impl Creatable for LongLocation { + fn create_with_short_object_id(&self, short_object_id: u8) -> Vec<u8> { + let mut command = BytesMut::new(); + + // Header + command.put_u8(short_object_id); // ObjId + #[allow(clippy::cast_possible_truncation)] + command.put_i8(Command::LongLoc as i32 as i8); // Type + + // Content + command.put_i16(self.x); + command.put_i16(self.y); + command.put_i16(self.z); + command.put_i16(self.direction); + + // Length + let mut command_as_vec = command.to_vec(); + command_as_vec.insert(0, command.len() as u8 + 1); + + // Return bytes + command_as_vec + } +} diff --git a/crates/whirl_server/src/cmd/commands/mod.rs b/crates/whirl_server/src/cmd/commands/mod.rs index 0129d47..0da031f 100644 --- a/crates/whirl_server/src/cmd/commands/mod.rs +++ b/crates/whirl_server/src/cmd/commands/mod.rs @@ -4,6 +4,7 @@ pub mod action; pub mod appear_actor; pub mod buddy_list; +pub mod long_location; pub mod property; pub mod redirect_id; pub mod register_object_id; diff --git a/crates/whirl_server/src/cmd/commands/register_object_id.rs b/crates/whirl_server/src/cmd/commands/register_object_id.rs index bdf4bca..8c73d8c 100644 --- a/crates/whirl_server/src/cmd/commands/register_object_id.rs +++ b/crates/whirl_server/src/cmd/commands/register_object_id.rs @@ -2,15 +2,21 @@ // SPDX-License-Identifier: GPL-3.0-only use { - crate::cmd::{constants::Command, extendable::Creatable}, - bytes::{BufMut, BytesMut}, + crate::cmd::{ + constants::Command, + extendable::{Creatable, Parsable}, + }, + byteorder::ReadBytesExt, + bytes::{Buf, BufMut, BytesMut}, + std::io::Read, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RegisterObjectId { pub long_object_id: String, pub short_object_id: i8, } + impl Creatable for RegisterObjectId { fn create(&self) -> Vec<u8> { let mut command = BytesMut::new(); @@ -33,3 +39,21 @@ impl Creatable for RegisterObjectId { command_as_vec } } + +impl Parsable for RegisterObjectId { + fn parse(data: Vec<u8>) -> Self { + // https://stackoverflow.com/questions/41034635/how-do-i-convert-between-string-str-vecu8-and-u8 + let mut data = BytesMut::from(data.as_slice()).reader(); + + // Content + let long_object_id_length = data.read_u8().unwrap() as usize; + let mut long_object_id = vec![0; long_object_id_length]; + data.read_exact(&mut long_object_id).unwrap(); + let short_object_id = data.read_i8().unwrap(); + + Self { + long_object_id: String::from_utf8(long_object_id).unwrap(), + short_object_id, + } + } +} diff --git a/crates/whirl_server/src/cmd/commands/subscribe_distance.rs b/crates/whirl_server/src/cmd/commands/subscribe_distance.rs index 9877a5d..61d1975 100644 --- a/crates/whirl_server/src/cmd/commands/subscribe_distance.rs +++ b/crates/whirl_server/src/cmd/commands/subscribe_distance.rs @@ -16,10 +16,9 @@ impl Parsable for SubscribeDistance { fn parse(data: Vec<u8>) -> Self { // https://stackoverflow.com/questions/41034635/how-do-i-convert-between-string-str-vecu8-and-u8 let mut data = BytesMut::from(data.as_slice()).reader(); + let room_number = data.read_i16::<BigEndian>().unwrap(); + let distance = data.read_i16::<BigEndian>().unwrap(); - Self { - distance: data.read_i16::<BigEndian>().unwrap(), - room_number: data.read_i16::<BigEndian>().unwrap(), - } + Self { distance, room_number } } } diff --git a/crates/whirl_server/src/cmd/commands/teleport.rs b/crates/whirl_server/src/cmd/commands/teleport.rs index aa4b7dd..f3a5290 100644 --- a/crates/whirl_server/src/cmd/commands/teleport.rs +++ b/crates/whirl_server/src/cmd/commands/teleport.rs @@ -2,9 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only use { - crate::cmd::extendable::Parsable, + crate::cmd::{ + constants::Command, + extendable::{Creatable, Parsable}, + }, byteorder::{BigEndian, ReadBytesExt}, - bytes::{Buf, BytesMut}, + bytes::{Buf, BufMut, BytesMut}, }; #[derive(Debug)] @@ -17,6 +20,7 @@ pub struct Teleport { pub z: f32, pub direction: f32, } + impl Parsable for Teleport { fn parse(data: Vec<u8>) -> Self { // https://stackoverflow.com/questions/41034635/how-do-i-convert-between-string-str-vecu8-and-u8 @@ -33,3 +37,29 @@ impl Parsable for Teleport { } } } + +impl Creatable for Teleport { + fn create_with_short_object_id(&self, short_object_id: u8) -> Vec<u8> { + let mut command = BytesMut::new(); + + // Header + command.put_u8(short_object_id); // ObjId + command.put_u8(Command::Teleport as u8); // Type + + // Content + command.put_i16(i16::from(self.room_id)); + command.put_u8(self.exit_type); + command.put_u8(self.entry_type); + command.put_i16(self.x as i16); + command.put_i16(self.y as i16); + command.put_i16(self.z as i16); + command.put_i16(self.direction as i16); + + // Length + let mut command_as_vec = command.to_vec(); + command_as_vec.insert(0, command.len() as u8 + 1); + + // Return bytes + command_as_vec + } +} diff --git a/crates/whirl_server/src/cmd/extendable.rs b/crates/whirl_server/src/cmd/extendable.rs index 03d7ce9..e7e63af 100644 --- a/crates/whirl_server/src/cmd/extendable.rs +++ b/crates/whirl_server/src/cmd/extendable.rs @@ -6,7 +6,10 @@ pub trait Parsable { } pub trait Creatable { - fn create(&self) -> Vec<u8>; + fn create(&self) -> Vec<u8> { vec![] } + fn create_with_short_object_id(&self, _short_object_id: u8) -> Vec<u8> { + vec![] + } } /// Having to do this makes me with there was operator overloading in Rust. diff --git a/crates/whirl_server/src/distributor.rs b/crates/whirl_server/src/distributor.rs index e89b8d8..55efc8e 100644 --- a/crates/whirl_server/src/distributor.rs +++ b/crates/whirl_server/src/distributor.rs @@ -15,7 +15,7 @@ use { crate::{ cmd::{ commands::{ - action::create, + // action::create, buddy_list::BuddyList, property::create::{ property_request_as_distributor, property_update_as_distributor, @@ -94,8 +94,8 @@ impl Server for Distributor { sender: Config::get().whirlsplash.worldsmaster_username, content: Config::get().distributor.worldsmaster_greeting, }.create()).await?; - peer.bytes.get_mut() - .write_all(&create()).await?; + // peer.bytes.get_mut() + // .write_all(&create()).await?; trace!("sent text to {}", username); } Some(Command::BuddyListUpdate) => { diff --git a/crates/whirl_server/src/hub.rs b/crates/whirl_server/src/hub.rs index f1a8120..0279453 100644 --- a/crates/whirl_server/src/hub.rs +++ b/crates/whirl_server/src/hub.rs @@ -8,6 +8,8 @@ //! client after they have been redirected to a room (Hub) and finished their //! business with the Distributor (`AutoServer`). +#![allow(clippy::significant_drop_in_scrutinee)] + use { crate::{ cmd::{ @@ -15,6 +17,7 @@ use { action::create, appear_actor::AppearActor, buddy_list::BuddyList, + long_location::LongLocation, property::create::{property_request_as_hub, property_update_as_hub}, register_object_id::RegisterObjectId, session_exit::SessionExit, @@ -57,7 +60,6 @@ impl Server for Hub { let mut peer = Peer::new(state.clone(), bytes, count.to_string()).await?; // let mut room_ids = vec![]; let mut username = String::from("unknown"); - let mut show_avatar = false; loop { @@ -68,6 +70,8 @@ impl Server for Hub { } result = peer.bytes.next() => match result { Some(Ok(msg)) => { + let short_object_id = msg.get(1).unwrap().to_owned(); + // trace!("got some bytes: {:?}", &msg); for msg in parse_commands_from_packet(msg) { match num_traits::FromPrimitive::from_i32(i32::from(msg.get(2).unwrap().to_owned())) { @@ -101,6 +105,19 @@ impl Server for Hub { .write_all(&create()).await?; trace!("sent text to {}", username); } + Some(Command::RegObjId) => {} + Some(Command::LongLoc) => { + let long_location = LongLocation::parse(msg[3..].to_vec()); + + debug!("received long location from {}: {:?}", username, long_location); + + state.lock().await.broadcast(&LongLocation { + x: long_location.x, + y: long_location.y, + z: long_location.z, + direction: long_location.direction, + }.create_with_short_object_id(short_object_id)).await; + } Some(Command::BuddyListUpdate) => { let buddy = BuddyList::parse(msg.to_vec()); debug!("received buddy list update from {}: {}", username, buddy.buddy); @@ -138,17 +155,26 @@ impl Server for Hub { } Some(Command::Text) => { let text = Text::parse(msg.to_vec(), &[&username]); + debug!("received text from {}: {}", username, text.content); - { - state.lock().await.broadcast(&Text { - sender: (*username).to_string(), - content: text.content.clone(), - }.create()).await; - } - debug!("broadcasted text to hub"); + if !text.content.starts_with('/') { + state.lock().await.broadcast(&Text { + sender: (*username).to_string(), + content: text.content.clone(), + }.create()).await; + debug!("broadcasted text to hub"); + } match text.content.as_str() { + "/objects" => { + for object_id in &state.lock().await.object_ids { + peer.bytes.get_mut().write_all(&Text { + sender: Config::get().whirlsplash.worldsmaster_username, + content: format!("{object_id:?}"), + }.create()).await?; + } + } // Makes the friend "fuwn" come online "/friend online fuwn" => { peer.bytes.get_mut().write_all(&[ @@ -163,11 +189,17 @@ impl Server for Hub { 0x00, ]).await?; } + "/me" => { + peer.bytes.get_mut().write_all(&Text { + sender: Config::get().whirlsplash.worldsmaster_username, + content: format!("{short_object_id}"), + }.create()).await?; + } // Spawns a test avatar with the name "fuwn" "/spawn fuwn" => { - show_avatar = true; + // show_avatar = true; - peer.bytes.get_mut().write_all(&[ + state.lock().await.broadcast(&[ // REGOBJID 0x09, 0xff, 0x0d, 0x04, 0x66, 0x75, 0x77, 0x6e, 0x02, @@ -193,7 +225,7 @@ impl Server for Hub { 0x16, 0x02, 0x10, 0x05, 0x40, 0x01, 0x0f, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x3a, 0x56, 0x61, 0x6d, 0x70, 0x2e, 0x6d, 0x6f, 0x76, - ]).await?; + ]).await; } // Puts the test avatar "fuwn" into the asleep action "/sleep fuwn" => { @@ -211,28 +243,52 @@ impl Server for Hub { debug!("received subscribe room from {}: {:?}", username, subscribe_room); - peer.bytes.get_mut().write_all(&RegisterObjectId { - long_object_id: "fuwn".to_string(), - short_object_id: 2, - }.create()).await?; - peer.bytes.get_mut().write_all(&AppearActor { - short_object_id: 2, - room_id: 1, - x: 191, - y: 173, - z: 0, - direction: 45, - }.create()).await?; + // peer.bytes.get_mut().write_all(&AppearActor { + // short_object_id: 2, + // room_id: 1, + // x: 191, + // y: 173, + // z: 0, + // direction: 45, + // }.create()).await?; + + for object_id in &state.lock().await.object_ids { + peer.bytes.get_mut().write_all(&object_id.create()).await?; + } } Some(Command::SubDist) => { let subscribe_distance = SubscribeDistance::parse(msg[3..].to_vec()); debug!("received subscribe distance from {}: {:?}", username, subscribe_distance); + + for object_id in &state.lock().await.object_ids { + let _actor = AppearActor { + room_id: 1, + x: 1200, + y: 600, + z: 0, + direction: 208, + }.create_with_short_object_id(object_id.short_object_id as u8); + // peer.bytes.get_mut().write_all(&actor).await?; + } } Some(Command::Teleport) => { let teleport = Teleport::parse(msg[3..].to_vec()); + let objects_length = state.lock().await.object_ids.len(); + let object_id = RegisterObjectId { + long_object_id: format!("{username} ({objects_length})"), + short_object_id: objects_length as i8, + }; + debug!("received teleport from {}: {:?}", username, teleport); + debug!("registered object ID: {:?}", object_id); + + state.lock().await.object_ids.push(object_id.clone()); + state.lock().await.broadcast(&object_id.create()).await; + } + Some(Command::AppInit) => { + debug!("received app initialization from {}", username); } Some(Command::ShortLoc) => { // This is all just test stuff. Once the drone system has been diff --git a/crates/whirl_server/src/interaction/shared.rs b/crates/whirl_server/src/interaction/shared.rs index 4e1d158..d59fa2f 100644 --- a/crates/whirl_server/src/interaction/shared.rs +++ b/crates/whirl_server/src/interaction/shared.rs @@ -1,13 +1,17 @@ // Copyright (C) 2021-2021 The Whirlsplash Collective // SPDX-License-Identifier: GPL-3.0-only -use {bytes::BytesMut, std::collections::HashMap}; +use { + crate::cmd::commands::register_object_id::RegisterObjectId, bytes::BytesMut, + std::collections::HashMap, +}; pub struct Shared { - pub peers: HashMap<String, tokio::sync::mpsc::UnboundedSender<BytesMut>>, + pub peers: HashMap<String, tokio::sync::mpsc::UnboundedSender<BytesMut>>, + pub object_ids: Vec<RegisterObjectId>, } impl Shared { - pub fn new() -> Self { Self { peers: HashMap::new() } } + pub fn new() -> Self { Self { peers: HashMap::new(), object_ids: vec![] } } #[allow(clippy::unused_async)] pub async fn broadcast(&mut self, message: &[u8]) { diff --git a/crates/whirl_server/src/lib.rs b/crates/whirl_server/src/lib.rs index 410522b..baf31c5 100644 --- a/crates/whirl_server/src/lib.rs +++ b/crates/whirl_server/src/lib.rs @@ -26,16 +26,8 @@ clippy::cast_possible_wrap )] -#[macro_use] extern crate log; #[macro_use] extern crate async_trait; - -mod cmd; -mod interaction; -mod net; - -mod distributor; -mod hub; -mod packet_parser; +#[macro_use] extern crate log; use { crate::interaction::shared::Shared, @@ -46,6 +38,14 @@ use { }, }; +mod cmd; +mod interaction; +mod net; + +mod distributor; +mod hub; +mod packet_parser; + /// The type of server the `listen` method of the `Server` trait will /// implemented for. #[derive(Debug)] diff --git a/crates/whirl_server/src/packet_parser.rs b/crates/whirl_server/src/packet_parser.rs index e0da32d..923f18f 100644 --- a/crates/whirl_server/src/packet_parser.rs +++ b/crates/whirl_server/src/packet_parser.rs @@ -12,12 +12,11 @@ use bytes::BytesMut; /// 4. Iterate and do this for the remaining commands within `buffer`. pub fn parse_commands_from_packet(mut buffer: BytesMut) -> Vec<BytesMut> { let mut commands: Vec<BytesMut> = Vec::new(); - trace!("initial buffer: {:?}, length: {}", buffer, buffer.len()); let data_length = buffer.first().unwrap().to_owned() as usize; if buffer.len() > data_length { loop { - trace!("loop: {:?}, length: {}", buffer, buffer.len()); + trace!("grouped command: {:?}, length: {}", buffer, buffer.len()); let command_length = buffer.first().unwrap().to_owned() as usize; commands.push(BytesMut::from(buffer.get(0..command_length).unwrap())); @@ -30,6 +29,8 @@ pub fn parse_commands_from_packet(mut buffer: BytesMut) -> Vec<BytesMut> { } } } else { + trace!("single command: {:?}, length: {}", buffer, buffer.len()); + // There will always be at least one command, push it. commands.push(BytesMut::from(buffer.get(0..data_length).unwrap())); } |