diff --git a/Cargo.lock b/Cargo.lock index 198c921..7026a3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -89,6 +89,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + [[package]] name = "arrayvec" version = "0.7.4" @@ -515,7 +521,19 @@ checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.4", ] [[package]] @@ -628,9 +646,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -658,34 +676,37 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "lighthouse-client" -version = "1.0.3" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de7463d5750cb0280a773e3c0f26fad0b6ecbb466b78e584ab9446b4d848fe9f" +checksum = "18691375aad352822d0a1839a167378db47a59fb2c65aef29b2c1e567bf25203" dependencies = [ "async-tungstenite", "futures", "lighthouse-protocol", - "rand", + "rand 0.8.5", "rmp-serde", "serde", "serde_with", + "stream-guard", + "thiserror", "tokio", "tracing", ] [[package]] name = "lighthouse-protocol" -version = "1.0.3" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02acf8c42c7e00af78c9a762c12925e13bf5e5a48977f778afa20827a0c4b2d5" +checksum = "d1f5b333204f358e5a1c72edd5c0b92d06a886982abe0e229cb2a3d2f1ecd9ca" dependencies = [ - "rand", + "rand 0.8.5", + "rmpv", "serde", "serde_with", ] @@ -700,13 +721,14 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" name = "litris" version = "0.1.1" dependencies = [ + "anyhow", "arrayvec", "clap", "dotenvy", "futures", "itertools", "lighthouse-client", - "rand", + "rand 0.9.0", "tokio", "tracing", "tracing-subscriber", @@ -749,7 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -873,6 +895,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -928,8 +970,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy", ] [[package]] @@ -939,7 +992,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -948,7 +1011,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.12", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", ] [[package]] @@ -997,9 +1069,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rmp" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ "byteorder", "num-traits", @@ -1017,6 +1089,18 @@ dependencies = [ "serde", ] +[[package]] +name = "rmpv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58450723cd9ee93273ce44a20b6ec4efe17f8ed2e3631474387bfdecf18bb2a9" +dependencies = [ + "num-traits", + "rmp", + "serde", + "serde_bytes", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1083,6 +1167,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -1180,6 +1273,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "stream-guard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d822c252e766a20acaa3e39f9001cadbd91c05b913e716bf184879204bdcb4" +dependencies = [ + "futures", + "pin-project", +] + [[package]] name = "strsim" version = "0.10.0" @@ -1401,7 +1504,7 @@ dependencies = [ "httparse", "log", "native-tls", - "rand", + "rand 0.8.5", "sha1", "thiserror", "url", @@ -1482,6 +1585,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -1698,3 +1810,32 @@ name = "windows_x86_64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.4.2", +] + +[[package]] +name = "zerocopy" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09612fda0b63f7cb9e0af7e5916fe5a1f8cdcb066829f10f36883207628a4872" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79f81d38d7a2ed52d8f034e62c568e111df9bf8aba2f7cf19ddc5bf7bd89d520" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 3735f35..1431bb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,14 @@ repository = "https://github.com/fwcd/litris" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lighthouse-client = { version = "1.0", features = ["tokio"] } +lighthouse-client = { version = "5.0.2", features = ["tokio"] } tokio = { version = "1.21", features = ["rt", "macros", "time"] } futures = "0.3" tracing = "0.1" -rand = "0.8" -itertools = "0.12" +rand = "0.9" +itertools = "0.14.0" arrayvec = "0.7" tracing-subscriber = { version = "0.3", features = ["env-filter", "std"] } clap = { version = "4.5", features = ["derive", "env"] } dotenvy = "0.15.7" +anyhow = "1.0.97" diff --git a/src/controller.rs b/src/controller.rs index 2d7dec7..4853a1a 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,27 +1,35 @@ use std::sync::Arc; +use anyhow::Result; use futures::{Stream, lock::Mutex, StreamExt}; -use lighthouse_client::protocol::{ServerMessage, Payload}; +use lighthouse_client::protocol::{Direction, GamepadButtonEvent, GamepadControlEvent, GamepadEvent, InputEvent, KeyEvent, ServerMessage}; use crate::model::{State, Key}; pub async fn run( - mut stream: impl Stream + Unpin, + mut stream: impl Stream>> + Unpin, shared_state: Arc>> -) { +) -> Result<()> { while let Some(msg) = stream.next().await { - if let Payload::InputEvent(event) = msg.payload { - let opt_key = match event.key { - Some(37) => Some(Key::Left), - Some(38) => Some(Key::Up), - Some(39) => Some(Key::Right), - Some(40) => Some(Key::Down), - _ => None, - }; + let input_event = msg?.payload; - if let Some(key) = opt_key { + let opt_key = match input_event.direction() { + Some(Direction::Left) => Some(Key::Left), + Some(Direction::Up) => Some(Key::Up), + Some(Direction::Right) => Some(Key::Right), + Some(Direction::Down) => Some(Key::Down), + _ => None, + }; + let opt_down = match input_event { + InputEvent::Key(KeyEvent { down, .. }) => Some(down), + InputEvent::Gamepad(GamepadEvent { control: GamepadControlEvent::Button(GamepadButtonEvent { down, .. }), .. }) => Some(down), + _ => None, + }; + + if let Some(key) = opt_key { + if let Some(down) = opt_down { let mut state = shared_state.lock().await; - if event.is_down { + if down { if key == Key::Up { state.click(key); } else { @@ -33,4 +41,5 @@ pub async fn run( } } } + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 3a8cd65..a7ff5db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod ticker; use std::sync::Arc; +use anyhow::Result; use clap::Parser; use futures::lock::Mutex; use lighthouse_client::{Lighthouse, LIGHTHOUSE_URL, protocol::{Authentication, LIGHTHOUSE_COLS, LIGHTHOUSE_ROWS}}; @@ -28,7 +29,7 @@ struct Args { } #[tokio::main(flavor = "current_thread")] -async fn main() { +async fn main() -> Result<()> { tracing_subscriber::fmt() .compact() .with_env_filter(EnvFilter::builder() @@ -42,16 +43,17 @@ async fn main() { let auth = Authentication::new(&args.username, &args.token); let state = Arc::new(Mutex::new(State::::new())); - let mut lh = Lighthouse::connect_with_tokio_to(&args.url, auth).await.unwrap(); + let lh = Lighthouse::connect_with_tokio_to(&args.url, auth).await?; info!("Connected to the lighthouse."); - let stream = lh.stream_model().await.unwrap(); + let input = lh.stream_input().await?; let renderer_handle = task::spawn(renderer::run(lh, state.clone())); let ticker_handle = task::spawn(ticker::run(state.clone())); - let controller_handle = task::spawn(controller::run(stream, state)); + let controller_handle = task::spawn(controller::run(input, state)); - renderer_handle.await.unwrap().unwrap(); - ticker_handle.await.unwrap(); - controller_handle.await.unwrap(); + renderer_handle.await??; + ticker_handle.await?; + controller_handle.await??; + Ok(()) } diff --git a/src/model/board.rs b/src/model/board.rs index af1707d..93d5906 100644 --- a/src/model/board.rs +++ b/src/model/board.rs @@ -2,8 +2,8 @@ use std::{collections::HashSet, iter::repeat}; use arrayvec::ArrayVec; use itertools::Itertools; -use lighthouse_client::protocol::{Color, Pos, Rotation, Rect, Delta}; -use rand::{thread_rng, seq::SliceRandom}; +use lighthouse_client::protocol::{Color, Delta, Pos, Rect, Rotation, Zero}; +use rand::{self, seq::IndexedRandom}; use tracing::info; use super::{FallingTetromino, Tetromino}; @@ -34,7 +34,7 @@ impl Board { /// Creates a new falling tetromino. fn new_falling_tetromino() -> FallingTetromino { - let mut rng = thread_rng(); + let mut rng = rand::rng(); let tetromino = Tetromino::random_with(&mut rng); let pos = Pos::new(W as i32 / 2, 1); let rotation = Rotation::IDENTITY; @@ -50,7 +50,7 @@ impl Board { } /// Moves the falling tetromino. - pub fn move_falling(&mut self, delta: Delta) { + pub fn move_falling(&mut self, delta: Delta) { let next = self.falling.moved_by(delta); if !self.collides_with(next) { self.falling = next; @@ -58,7 +58,7 @@ impl Board { } /// Rotates the falling tetromino. - pub fn rotate_falling(&mut self, rotation: Rotation) { + pub fn rotate_falling(&mut self, rotation: Rotation) { let next = self.falling.rotated_by(rotation); if !self.collides_with(next) { self.falling = next; @@ -73,12 +73,12 @@ impl Board { } /// The bounding rectangle. - pub fn bounds(&self) -> Rect { + pub fn bounds(&self) -> Rect { Rect::new(Pos::ZERO, Delta::new(W as i32, H as i32)) } /// The occupied pixels. - pub fn occupied_pixels(&self) -> HashSet { + pub fn occupied_pixels(&self) -> HashSet> { (0..H as i32) .flat_map(|y| (0..W as i32).map(move |x| Pos::new(x, y))) .filter(|&p| self.get_field(p).is_some()) @@ -133,12 +133,12 @@ impl Board { } /// Fetches the field at the given position. - fn get_field(&self, pos: Pos) -> Option { + fn get_field(&self, pos: Pos) -> Option { self.fields[pos.y as usize][pos.x as usize] } /// Fetches the color at the given position. - pub fn get(&self, pos: Pos) -> Option { + pub fn get(&self, pos: Pos) -> Option { if self.falling.contains(pos) { Some(self.falling.color()) } else { diff --git a/src/model/falling_tetromino.rs b/src/model/falling_tetromino.rs index 63f6903..8aa4427 100644 --- a/src/model/falling_tetromino.rs +++ b/src/model/falling_tetromino.rs @@ -8,16 +8,16 @@ pub struct FallingTetromino { /// The shape of the tetromino. tetromino: Tetromino, /// The position on the board. - pos: Pos, + pos: Pos, /// The rotation. - rotation: Rotation, + rotation: Rotation, /// The color of the tetromino. color: Color, } impl FallingTetromino { /// Creates a new falling tetromino with the given configuration. - pub const fn new(tetromino: Tetromino, pos: Pos, rotation: Rotation, color: Color) -> Self { + pub const fn new(tetromino: Tetromino, pos: Pos, rotation: Rotation, color: Color) -> Self { Self { tetromino, pos, rotation, color } } @@ -28,13 +28,13 @@ impl FallingTetromino { } /// The tetromino moved by the given delta. - pub fn moved_by(mut self, delta: Delta) -> Self { + pub fn moved_by(mut self, delta: Delta) -> Self { self.pos += delta; self } /// Rotates by the given delta. - pub fn rotated_by(mut self, rotation: Rotation) -> Self { + pub fn rotated_by(mut self, rotation: Rotation) -> Self { self.rotation = rotation * self.rotation; self } @@ -45,12 +45,12 @@ impl FallingTetromino { } /// The positions occupied by this falling tetromino. - pub fn pixels(&self) -> [Pos; 4] { + pub fn pixels(&self) -> [Pos; 4] { self.tetromino.pixels.map(|d| self.pos + self.rotation * d) } /// Whether this falling tetromino contains the given position. - pub fn contains(&self, pos: Pos) -> bool { + pub fn contains(&self, pos: Pos) -> bool { self.pixels().contains(&pos) } } diff --git a/src/model/tetromino.rs b/src/model/tetromino.rs index a1b5519..12575ab 100644 --- a/src/model/tetromino.rs +++ b/src/model/tetromino.rs @@ -1,11 +1,11 @@ use lighthouse_client::protocol::Delta; -use rand::{Rng, seq::SliceRandom}; +use rand::{seq::IndexedRandom, Rng}; /// A game piece composed of four pixels connected orthogonally. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Tetromino { /// The relative positions of the pixels. - pub pixels: [Delta; 4], + pub pixels: [Delta; 4], } impl Tetromino { @@ -67,7 +67,7 @@ impl Tetromino { ]; /// Creates a new tetromino from the given configuration. - pub const fn new(pixels: [Delta; 4]) -> Self { + pub const fn new(pixels: [Delta; 4]) -> Self { Self { pixels } } diff --git a/src/renderer.rs b/src/renderer.rs index 2b5c8ba..b6cd2c1 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -8,7 +8,7 @@ use tracing::debug; use crate::model::State; pub async fn run( - mut lh: Lighthouse, + lh: Lighthouse, shared_state: Arc>> ) -> Result<()> { loop {