Initial commit
This commit is contained in:
parent
623f78355d
commit
bf3042cbf0
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "shockosc-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
rand = "0.9.0"
|
||||||
|
rosc = "0.10.1"
|
||||||
|
rzap = "0.2.1"
|
||||||
|
slint = "1.9.2"
|
||||||
|
tokio = {version = "1.43.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
slint-build = "1.8.0"
|
||||||
3
build.rs
Normal file
3
build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
slint_build::compile("ui/main.slint").expect("Slint build failed");
|
||||||
|
}
|
||||||
132
src/main.rs
Normal file
132
src/main.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use std::{net::SocketAddrV4, str::FromStr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use rand::{rng, Rng};
|
||||||
|
use rosc::{decoder, encoder, OscMessage, OscPacket, OscType};
|
||||||
|
use rzap::api_builder::OpenShockAPIBuilder;
|
||||||
|
use tokio::{net::UdpSocket, sync::Mutex};
|
||||||
|
|
||||||
|
slint::include_modules!();
|
||||||
|
|
||||||
|
struct Values {
|
||||||
|
pub min_shocking: i32,
|
||||||
|
pub max_shocking: i32,
|
||||||
|
pub min_time: i32,
|
||||||
|
pub max_time: i32,
|
||||||
|
pub cooldown: i32,
|
||||||
|
}
|
||||||
|
impl Values {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Values{ min_shocking: 1, max_shocking: 20, min_time: 300, max_time: 3000, cooldown: 5000 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let openshock_token = dotenv::var("OPENSHOCK_TOKEN").expect("missing OPENSHOCK_TOKEN");
|
||||||
|
let app_name = env!("CARGO_PKG_NAME");
|
||||||
|
let app_version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
assert_ne!(openshock_token, "");
|
||||||
|
|
||||||
|
let openshock_api = OpenShockAPIBuilder::new()
|
||||||
|
.with_app(app_name.to_string(), Some(app_version.to_string()))
|
||||||
|
.with_default_api_token(openshock_token)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let state = Arc::new(Mutex::new(false));
|
||||||
|
let state_cloned = state.clone();
|
||||||
|
let sock = UdpSocket::bind(SocketAddrV4::from_str("127.0.0.1:9001").unwrap()).await.unwrap();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
println!("starting lisening");
|
||||||
|
loop {
|
||||||
|
let mut buf = vec![0; 1024];
|
||||||
|
let msg = sock.recv_from(&mut buf).await;
|
||||||
|
if let Err(_) = msg {
|
||||||
|
println!("Couldnt recv packet");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let osc_msg = decoder::decode_udp(&buf);
|
||||||
|
if let Err(_) = osc_msg {
|
||||||
|
println!("Couldnt decode packet");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match osc_msg.unwrap().1 {
|
||||||
|
OscPacket::Message(msg) => {
|
||||||
|
if !msg.addr.eq("/avatar/parameters/ShockOsc/leftleg") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let binding = msg.args[0].clone().bool();
|
||||||
|
if let Some(state) = binding {
|
||||||
|
*state_cloned.lock().await = state;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OscPacket::Bundle(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let send_addr: SocketAddrV4 = SocketAddrV4::from_str("127.0.0.1:9000").unwrap();
|
||||||
|
let sock = UdpSocket::bind(SocketAddrV4::from_str("0.0.0.0:64000").unwrap()).await.unwrap();
|
||||||
|
let values = Arc::new(Mutex::new(Values::new()));
|
||||||
|
let vals = values.clone();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
let mut enter: i32 = 0;
|
||||||
|
let mut cooldown: i32 = 0;
|
||||||
|
let mut task_interval = tokio::time::interval(Duration::from_millis(10));
|
||||||
|
loop {
|
||||||
|
let state = *state.lock().await;
|
||||||
|
if state {
|
||||||
|
if enter > 200 {
|
||||||
|
enter = 0;
|
||||||
|
// Shock
|
||||||
|
let vals = vals.lock().await;
|
||||||
|
cooldown = vals.cooldown;
|
||||||
|
let intensity = rng().random_range(vals.min_shocking..vals.max_shocking);
|
||||||
|
let dura = (rng().random_range(vals.min_time..vals.max_time) as f32 / 100.0).round() as i32 *100;
|
||||||
|
|
||||||
|
println!("Shocking {intensity}% for {dura}ms");
|
||||||
|
let msg;
|
||||||
|
match openshock_api.post_control("ef258419-fb00-422e-9609-70cc6dfd64b7".to_string(), rzap::data_type::ControlType::Shock, intensity.try_into().unwrap(), dura.try_into().unwrap(), None).await {
|
||||||
|
Ok(_) => msg = format!("[ShockOSC-rs]\nIntensity: {intensity}\nDuration: {}s", (dura as f32)/1000.0),
|
||||||
|
Err(_) => msg = "[ShockOSC-rs]\nShocker is paused".to_string(),
|
||||||
|
}
|
||||||
|
let msg_buf = encoder::encode(&OscPacket::Message(OscMessage {
|
||||||
|
addr: "/chatbox/input".to_string(),
|
||||||
|
args: vec![OscType::String(msg), rosc::OscType::Bool(true),rosc::OscType::Bool(false)],
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
sock.send_to(&msg_buf, send_addr).await.unwrap();
|
||||||
|
}else if cooldown <= 0 {
|
||||||
|
enter += 10;
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
enter = 0;
|
||||||
|
}
|
||||||
|
if cooldown > 0 {
|
||||||
|
cooldown -= 10;
|
||||||
|
}
|
||||||
|
task_interval.tick().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let ui = ShockerConfig::new().unwrap();
|
||||||
|
let vals = values.clone();
|
||||||
|
let ui_handle = ui.as_weak();
|
||||||
|
ui.on_update(move || {
|
||||||
|
let ui = ui_handle.unwrap();
|
||||||
|
let min_shocking = ui.get_min_shock();
|
||||||
|
let max_shocking = ui.get_max_shock();
|
||||||
|
let min_time = ui.get_min_time();
|
||||||
|
let max_time = ui.get_max_time();
|
||||||
|
let cooldown = ui.get_cooldown();
|
||||||
|
let new_vals = Values { min_shocking, max_shocking, min_time, max_time, cooldown };
|
||||||
|
let vals = vals.clone();
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
*vals.lock().await = new_vals;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.run().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
99
ui/main.slint
Normal file
99
ui/main.slint
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Button, VerticalBox, Slider, HorizontalBox, GroupBox } from "std-widgets.slint";
|
||||||
|
|
||||||
|
export component ShockerConfig inherits Window {
|
||||||
|
in-out property <int> min_shock: 10;
|
||||||
|
in-out property <int> max_shock: 50;
|
||||||
|
in-out property <int> min_time: 300;
|
||||||
|
in-out property <int> max_time: 3000;
|
||||||
|
in-out property <int> cooldown: 5000;
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
title: "ShockOSC-rs";
|
||||||
|
callback update();
|
||||||
|
VerticalBox {
|
||||||
|
GroupBox {
|
||||||
|
title: "Shock Time";
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: min_time + " ms";
|
||||||
|
}
|
||||||
|
Slider {
|
||||||
|
minimum: 300;
|
||||||
|
value: min_time;
|
||||||
|
maximum: max_time;
|
||||||
|
step: 100;
|
||||||
|
changed => {
|
||||||
|
min_time = round(self.value / 100) * 100;
|
||||||
|
root.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: max_time + " ms";
|
||||||
|
}
|
||||||
|
Slider {
|
||||||
|
minimum: min_time;
|
||||||
|
value: max_time;
|
||||||
|
maximum: 30000;
|
||||||
|
step: 100;
|
||||||
|
changed => {
|
||||||
|
max_time = round(self.value / 100) * 100;
|
||||||
|
root.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupBox {
|
||||||
|
title: "Shock Intensity";
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: min_shock + " %";
|
||||||
|
}
|
||||||
|
Slider {
|
||||||
|
minimum: 1;
|
||||||
|
value: min_shock;
|
||||||
|
maximum: max_shock;
|
||||||
|
step: 1;
|
||||||
|
changed => {
|
||||||
|
min_shock = round(self.value);
|
||||||
|
root.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: max_shock + " %";
|
||||||
|
}
|
||||||
|
Slider {
|
||||||
|
minimum: min_shock;
|
||||||
|
value: max_shock;
|
||||||
|
maximum: 100;
|
||||||
|
step: 1;
|
||||||
|
changed => {
|
||||||
|
max_shock = round(self.value);
|
||||||
|
root.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupBox {
|
||||||
|
title: "Cooldown";
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: cooldown + " ms";
|
||||||
|
}
|
||||||
|
Slider {
|
||||||
|
minimum: 1;
|
||||||
|
value: cooldown;
|
||||||
|
maximum: 30000;
|
||||||
|
step: 1;
|
||||||
|
changed => {
|
||||||
|
cooldown = round(self.value / 100)*100;
|
||||||
|
root.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user