aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-06-12 01:28:50 -0700
committerFuwn <[email protected]>2024-06-12 01:28:50 -0700
commita47eee894e431f039afe76cc39ecc67966d94183 (patch)
tree5954a0d262547450d7fedd5aed69d9e983d93cee
parentrefactor(crates): update idioms (diff)
downloadwhirl-a47eee894e431f039afe76cc39ecc67966d94183.tar.xz
whirl-a47eee894e431f039afe76cc39ecc67966d94183.zip
feat(whirl_server): short object id sending
-rw-r--r--crates/whirl_server/src/cmd/commands/appear_actor.rs25
-rw-r--r--crates/whirl_server/src/cmd/commands/long_location.rs53
-rw-r--r--crates/whirl_server/src/cmd/commands/mod.rs1
-rw-r--r--crates/whirl_server/src/cmd/commands/register_object_id.rs30
-rw-r--r--crates/whirl_server/src/cmd/commands/subscribe_distance.rs7
-rw-r--r--crates/whirl_server/src/cmd/commands/teleport.rs34
-rw-r--r--crates/whirl_server/src/cmd/extendable.rs5
-rw-r--r--crates/whirl_server/src/distributor.rs6
-rw-r--r--crates/whirl_server/src/hub.rs102
-rw-r--r--crates/whirl_server/src/interaction/shared.rs10
-rw-r--r--crates/whirl_server/src/lib.rs18
-rw-r--r--crates/whirl_server/src/packet_parser.rs5
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()));
}