1use bytes::{Buf, BufMut, Bytes, BytesMut};
2use std::io::prelude::*;
3use std::net::{SocketAddr, TcpStream};
4use std::time::Duration;
5
6pub enum ByondTopicValue {
9 None,
10 String(String),
11 Number(f32),
12}
13
14pub fn send_byond(target: &SocketAddr, topic: &str) -> std::io::Result<ByondTopicValue> {
38 let mut stream = TcpStream::connect(target)?;
39 stream.set_read_timeout(Some(Duration::new(5, 0)))?;
40
41 let topic_bytes = topic.as_bytes();
42
43 let mut buf = BytesMut::with_capacity(1024);
44 buf.put_u16(0x0083);
46
47 buf.put_u16(topic_bytes.len() as u16 + 6);
49
50 buf.put_u32(0x0);
52 buf.put_u8(0x0);
53
54 buf.put(topic_bytes);
56
57 buf.put_u8(0x0);
59
60 stream.write_all(&buf)?;
61
62 let mut recv_buf = [0; 4096];
64 let bytes_read = stream.read(&mut recv_buf)?;
65
66 if bytes_read == 0 {
67 return Ok(ByondTopicValue::None);
68 }
69
70 let mut recv_buf = Bytes::from(Vec::from(recv_buf));
71
72 if recv_buf.try_get_u16()? == 0x0083 {
73 let mut size = recv_buf.try_get_u16()? - 1;
74 let data_type = recv_buf.try_get_u8()?;
75
76 let ret = match data_type {
77 0x2a => ByondTopicValue::Number(recv_buf.try_get_f32_le()?),
78 0x06 => {
79 let mut str = String::new();
80 while size > 0 {
81 str.push(recv_buf.try_get_u8()? as char);
82 size -= 1;
83 }
84 ByondTopicValue::String(str)
85 }
86 _ => ByondTopicValue::None,
87 };
88
89 return Ok(ret);
90 }
91
92 Ok(ByondTopicValue::None)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn it_works() {
101 let res = send_byond(&SocketAddr::from(([127, 0, 0, 1], 27012)), "?status");
102 match res {
103 Err(x) => panic!("Error from send_byond {x}"),
104 Ok(wrapper) => match wrapper {
105 ByondTopicValue::None => println!("Returned NONE"),
106 ByondTopicValue::String(s) => println!("Returned string {s}"),
107 ByondTopicValue::Number(num) => println!("Returned f32 {num}"),
108 },
109 }
110 }
111}