2020-05-08 23:17:41 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate bitflags;
|
|
|
|
|
|
|
|
pub(crate) mod controller;
|
2020-05-09 00:47:36 +00:00
|
|
|
pub(crate) mod twitch;
|
2020-05-08 23:17:41 +00:00
|
|
|
|
2020-05-08 22:32:29 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
use log::{debug, error, info, warn};
|
2020-05-08 21:54:57 +00:00
|
|
|
use std::{
|
|
|
|
fs::{File, OpenOptions},
|
|
|
|
io::{Read, Write},
|
|
|
|
str::from_utf8,
|
2020-05-08 23:17:41 +00:00
|
|
|
sync::{Arc, RwLock},
|
|
|
|
thread::spawn,
|
|
|
|
time::Instant,
|
2020-05-08 21:54:57 +00:00
|
|
|
};
|
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct State {
|
|
|
|
controller: [u8; 4],
|
|
|
|
last_got: Box<Instant>,
|
|
|
|
ok: bool,
|
2020-05-09 03:44:03 +00:00
|
|
|
frame: u64,
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) type MTState = Arc<RwLock<State>>;
|
|
|
|
|
2020-05-08 22:32:29 +00:00
|
|
|
fn main() -> Result<()> {
|
2020-05-08 21:54:57 +00:00
|
|
|
pretty_env_logger::try_init()?;
|
2020-05-09 00:47:36 +00:00
|
|
|
kankyo::init()?;
|
2020-05-08 21:54:57 +00:00
|
|
|
|
|
|
|
let mut vblank = File::open("vblank")?;
|
|
|
|
let mut input = OpenOptions::new().write(true).open("input")?;
|
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
let st = {
|
|
|
|
let st = State {
|
|
|
|
controller: [0; 4],
|
|
|
|
last_got: Box::new(Instant::now()),
|
|
|
|
ok: true,
|
2020-05-09 03:44:03 +00:00
|
|
|
frame: 0,
|
2020-05-08 23:17:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Arc::new(RwLock::new(st))
|
|
|
|
};
|
|
|
|
|
2020-05-08 22:00:20 +00:00
|
|
|
info!("ready");
|
2020-05-08 21:54:57 +00:00
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
{
|
|
|
|
let st = st.clone();
|
2020-05-09 00:47:36 +00:00
|
|
|
spawn(move || twitch::run(st));
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-09 03:44:03 +00:00
|
|
|
const LERP_TIME: f64 = 1.0; // 15 frames to lerp stick positions down to 0
|
|
|
|
let mut xmax_frame: u64 = 0;
|
|
|
|
let mut ymax_frame: u64 = 0;
|
|
|
|
|
2020-05-08 21:54:57 +00:00
|
|
|
loop {
|
|
|
|
let mut data = [0; 3];
|
2020-05-08 22:18:45 +00:00
|
|
|
debug!("waiting for vblank");
|
2020-05-08 21:54:57 +00:00
|
|
|
vblank.read(&mut data)?;
|
|
|
|
let str = from_utf8(&data)?;
|
|
|
|
debug!("got data: {}", str);
|
2020-05-08 22:32:29 +00:00
|
|
|
|
|
|
|
match str {
|
2020-05-08 23:17:41 +00:00
|
|
|
"OK\n" => {
|
2020-05-08 23:23:56 +00:00
|
|
|
let mut data = st.write().unwrap();
|
2020-05-09 03:44:03 +00:00
|
|
|
data.frame += 1;
|
|
|
|
|
|
|
|
let mut stickx = data.controller[2] as i8;
|
|
|
|
let mut sticky = data.controller[3] as i8;
|
|
|
|
|
|
|
|
let dist = stick_distance(stickx, sticky);
|
|
|
|
if dist <= 10 {
|
|
|
|
stickx = 0;
|
|
|
|
sticky = 0;
|
|
|
|
xmax_frame = 0;
|
|
|
|
ymax_frame = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
stickx = match stickx {
|
|
|
|
0 => stickx,
|
|
|
|
127 => {
|
|
|
|
xmax_frame = data.frame;
|
|
|
|
stickx - 10
|
|
|
|
},
|
|
|
|
-128 => {
|
|
|
|
xmax_frame = data.frame;
|
|
|
|
stickx + 10
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
let t = (data.frame - xmax_frame) as f64 / (LERP_TIME as f64);
|
|
|
|
lerp(stickx, 0, t)
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
sticky = match stickx {
|
|
|
|
0 => sticky,
|
|
|
|
127 => {
|
|
|
|
ymax_frame = data.frame;
|
|
|
|
sticky - 10
|
|
|
|
},
|
|
|
|
-128 => {
|
|
|
|
ymax_frame = data.frame;
|
|
|
|
sticky + 10
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
let t = (data.frame - ymax_frame) as f64 / (LERP_TIME as f64);
|
|
|
|
lerp(sticky, 0, t)
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-05-08 23:23:56 +00:00
|
|
|
input.write(&data.controller)?;
|
2020-05-09 00:47:36 +00:00
|
|
|
data.controller[0] = 0;
|
|
|
|
data.controller[1] = 0;
|
2020-05-09 03:44:03 +00:00
|
|
|
data.controller[2] = stickx as u8;
|
|
|
|
data.controller[3] = sticky as u8;
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
2020-05-08 22:32:29 +00:00
|
|
|
"BYE" => {
|
|
|
|
warn!("asked to exit by the game");
|
|
|
|
return Ok(());
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
2020-05-08 22:32:29 +00:00
|
|
|
_ => {
|
|
|
|
error!("got unknown FIFO data {}", str);
|
|
|
|
return Err(anyhow!("unknown FIFO data received"));
|
|
|
|
}
|
|
|
|
};
|
2020-05-08 21:54:57 +00:00
|
|
|
}
|
2020-05-08 20:30:43 +00:00
|
|
|
}
|
2020-05-09 03:44:03 +00:00
|
|
|
|
|
|
|
fn lerp(start: i8, end: i8, t: f64) -> i8 {
|
|
|
|
(start as f64 * (1.0 - t) + (end as f64) * t) as i8
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stick_distance(x: i8, y: i8) -> i8 {
|
|
|
|
let x = (x as f64).powi(2);
|
|
|
|
let y = (y as f64).powi(2);
|
|
|
|
(x + y).sqrt() as i8
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
#[test]
|
|
|
|
fn lerp_scale() {
|
|
|
|
for case in [(0.1, 10), (0.5, 31)].iter() {
|
|
|
|
let t = case.0;
|
|
|
|
let start = 127.0 * t;
|
|
|
|
assert_eq!(crate::lerp(start as i8, 0, t), case.1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn stick_distance() {
|
|
|
|
for case in [
|
|
|
|
(0, 0, 0),
|
|
|
|
(127, 0, 127),
|
|
|
|
(64, 64, 90),
|
|
|
|
(-64, 64, 90),
|
|
|
|
(-64, -64, 90),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
{
|
|
|
|
let x = case.0;
|
|
|
|
let y = case.1;
|
|
|
|
assert_eq!(crate::stick_distance(x, y), case.2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|