http2byond/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#![crate_name = "http2byond"]

use std::io::prelude::*;
use std::net::{SocketAddr,TcpStream};
use bytes::{Bytes, BytesMut, Buf, BufMut};
use std::time::Duration;

/// This enum represents the possible return types of send_byond
/// It can be nothing, a String (containing String), or a Number (containing f32)
pub enum ByondTopicValue {
    None,
    String(String),
    Number(f32),
}

/// Main (and only) function of this library.
/// 
/// # Arguments
/// 
/// * `target` - A TCP SocketAddr of a Dream Daemon instance.
/// * `topic` - The string you want sent to Dream Daemon. Make sure to always start this with the character `?`.ByondTopicValue
/// 
/// # Examples
/// 
/// ```
/// use http2byond::{send_byond, ByondTopicValue};
/// match send_byond(&SocketAddr::from(([127, 0, 0, 1], 1337)), "?status") {
///     Err(_) => {}
///     Ok(btv_result) => {
///         match btv_result {
///             ByondTopicValue::None => println!("Byond returned nothing"),
///             ByondTopicValue::String(str) => println!("Byond returned string {}", str),
///             ByondTopicValue::Number(num) => println!("Byond returned number {}", num),
///         }
///     }
/// }
/// ```
pub fn send_byond(target: &SocketAddr, topic: &str) -> std::io::Result<ByondTopicValue> {
    let mut stream = TcpStream::connect(target)?;
    stream.set_read_timeout(Some(Duration::new(5, 0)))?;

    let topic_bytes = topic.as_bytes();

    let mut buf = BytesMut::with_capacity(1024);
    // Header of 00 83
    buf.put_u16(0x0083);

    // Unsigned short of data length
    buf.put_u16(topic_bytes.len() as u16 + 6);

    // 40 bytes of padding
    buf.put_u32(0x0);
    buf.put_u8(0x0);

    // Append our topic
    buf.put(topic_bytes);

    // End with a 00
    buf.put_u8(0x0);

    println!("{:02X?}", &buf[..]);

    stream.write(&buf)?;

    let mut recv_buf = [0; 1024];

    let bytes_read = stream.read(&mut recv_buf)?;

    if bytes_read == 0 {
        return Ok(ByondTopicValue::None);
    }

    let mut recv_buf = Bytes::from(Vec::from(recv_buf));

    if recv_buf.get_u16() == 0x0083 {
        let mut size = recv_buf.get_u16() - 1;
        let data_type = recv_buf.get_u8();

        let ret = match data_type {
            0x2a => ByondTopicValue::Number(recv_buf.get_f32_le()),
            0x06 => {
                let mut str = String::new();
                while size > 0 {
                    str.push(recv_buf.get_u8() as char);
                    size -= 1;
                }
                ByondTopicValue::String(str)
            },
            _ => ByondTopicValue::None,
        };

        return Ok(ret)
    }

    Ok(ByondTopicValue::None)
}

#[test]
fn it_works() {
    let res = send_byond(&SocketAddr::from(([127, 0, 0, 1], 1337)), "?status");
    match res {
        Err(x) => panic!("Error from send_byond {}", x),
        Ok(wrapper) => {
            match wrapper {
                ByondTopicValue::None => println!("Returned NONE"),
                ByondTopicValue::String(str) => println!("Returned string {}", str),
                ByondTopicValue::Number(num) => println!("Returned f32 {}", num)
            }
        }
    }
}