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
//! The TCP connection used for communication between the external API and Tabletop Simulator
use crate::messages::{Answer, Message};
use std::fmt::Debug;
use std::io::{self, Read, Write};
use std::net::{TcpListener, TcpStream};
/// A struct representing Tabletop Simulators [External Editor API](https://api.tabletopsimulator.com/externaleditorapi/).
#[derive(Debug)]
pub struct ExternalEditorApi {
listener: TcpListener,
}
impl ExternalEditorApi {
/// Creates a new ExternalEditorApi struct and binds the TcpListener to its socket adress.
pub fn new() -> Self {
let listener = TcpListener::bind("127.0.0.1:39998").unwrap();
Self { listener }
}
/// Sends a [`Message`] in a TcpStream. If no connection to the game can be established, an [`io::Error`] gets returned.
pub fn send(&self, message: Message) -> io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:39999")?;
let json_message = serde_json::to_string(&message).unwrap();
stream.write_all(json_message.as_bytes()).unwrap();
stream.flush().unwrap();
Ok(())
}
/// Accepts the next incoming [`Answer`] from the listener.
/// This function will block the calling thread until a new TCP connection is established and an answer gets recieved.
pub fn read(&self) -> Answer {
let (mut stream, _addr) = self.listener.accept().unwrap();
let mut buffer = String::new();
stream.read_to_string(&mut buffer).unwrap();
serde_json::from_str(&buffer).unwrap()
}
/// Reads incoming [`Answer`] messages until an answer matches the generic.
/// This function will block the calling thread until a new TCP connection is established and an answer gets recieved.
pub fn wait<T: TryFrom<Answer>>(&self) -> T {
loop {
if let Ok(answer) = T::try_from(self.read()) {
return answer;
}
}
}
}
/// Creates a new ExternalEditorApi struct and binds the TcpListener to its socket adress.
/// This is functionally the same as using `ExternalEditorApi::new()`.
impl Default for ExternalEditorApi {
fn default() -> Self {
Self::new()
}
}