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)
            }
        }
    }
}