1use rosc::{encoder, OscMessage, OscPacket, OscType};
7use std::{
8 io,
9 net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
10};
11
12pub const VRCHAT_OSC_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9000);
14
15#[derive(Copy, Clone, Debug)]
17pub struct ClientBuilder {
18 client: SocketAddr,
19 server: SocketAddr,
20}
21
22#[derive(Debug)]
26pub struct Client {
27 client_socket: UdpSocket,
28 server_address: SocketAddr,
29}
30
31impl Client {
32 pub fn new(server_address: SocketAddr, client_socket: UdpSocket) -> Self {
35 Client {
36 client_socket,
37 server_address,
38 }
39 }
40}
41
42impl ClientBuilder {
43 pub fn new() -> Self {
46 ClientBuilder {
47 client: SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0),
48 server: VRCHAT_OSC_ADDR,
49 }
50 }
51
52 pub fn with_server_ip(mut self, ip: IpAddr) -> ClientBuilder {
53 self.server.set_ip(ip);
54 self
55 }
56
57 pub fn with_server_port(mut self, port: u16) -> ClientBuilder {
58 self.server.set_port(port);
59 self
60 }
61
62 pub fn with_client_port(mut self, port: u16) -> ClientBuilder {
64 self.client.set_port(port);
65 self
66 }
67
68 pub fn build(&self) -> io::Result<Client> {
71 let socket = UdpSocket::bind(self.client)?;
72 socket.connect(self.server)?;
73 Ok(Client {
74 client_socket: socket,
75 server_address: self.server,
76 })
77 }
78}
79
80impl Client {
81 pub fn change_server_address(&mut self, new_address: SocketAddr) -> io::Result<()> {
85 self.change_server_ip(new_address.ip())?;
86 self.change_server_port(new_address.port())?;
87 Ok(())
88 }
89
90 pub fn change_server_ip(&mut self, new_ip: IpAddr) -> io::Result<()> {
94 self.server_address.set_ip(new_ip);
95 self.client_socket.connect(self.server_address)?;
96 Ok(())
97 }
98
99 pub fn change_server_port(&mut self, new_port: u16) -> io::Result<()> {
103 self.server_address.set_port(new_port);
104 self.client_socket.connect(self.server_address)?;
105 Ok(())
106 }
107
108 pub fn typing_indicator(&self, toggle: bool) -> io::Result<()> {
114 match encoder::encode(&OscPacket::Message(OscMessage {
115 addr: String::from("/chatbox/typing"),
116 args: vec![OscType::Bool(toggle)],
117 })) {
118 Err(e) => {
119 panic!("vrchat-box error when encoding osc message: {e}")
120 }
121 Ok(msg_buf) => {
122 self.client_socket.send(&msg_buf)?;
123 }
124 }
125 Ok(())
126 }
127
128 pub fn send_message(&self, msg: &str, keyboard: bool, sfx: bool) -> io::Result<()> {
134 match encoder::encode(&OscPacket::Message(OscMessage {
135 addr: String::from("/chatbox/input"),
136 args: vec![
137 OscType::String(msg.to_string()),
138 OscType::Bool(keyboard),
139 OscType::Bool(sfx),
140 ],
141 })) {
142 Err(e) => {
143 panic!("vrchat-box error when encoding osc message: {e}")
144 }
145 Ok(msg_buf) => {
146 self.client_socket.send(&msg_buf)?;
147 }
148 }
149 Ok(())
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn does_it_work() {
159 let vrc_client = ClientBuilder::new()
160 .with_server_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))
161 .with_server_port(9000)
162 .build();
163 assert!(vrc_client.is_ok());
164 }
165
166 #[test]
167 fn does_it_send() {
168 let server = UdpSocket::bind(SocketAddr::new(VRCHAT_OSC_ADDR.ip(), 0)).unwrap();
169 let server_addr = server.local_addr().unwrap();
170 server.set_nonblocking(true).unwrap();
171
172 let client = ClientBuilder::new()
173 .with_server_ip(server_addr.ip())
174 .with_server_port(server_addr.port())
175 .build()
176 .unwrap();
177
178 assert!(client
179 .send_message("helloooooOO!!!!!", false, false)
180 .is_ok());
181
182 let mut buf = [0; 8];
183 server.recv(&mut buf).unwrap();
184 }
185}