From 5817fea9ad2e4665df190a7ae295f82ad69d5591 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 3 Jul 2025 17:52:42 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 163 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 ++ flake.lock | 27 +++++++ flake.nix | 57 +++++++++++++++ src/bar.rs | 73 +++++++++++++++++++ src/command_queue.rs | 20 ++++++ src/game_state.rs | 39 +++++++++++ src/label.rs | 35 ++++++++++ src/main.rs | 43 ++++++++++++ 10 files changed, 465 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/bar.rs create mode 100644 src/command_queue.rs create mode 100644 src/game_state.rs create mode 100644 src/label.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b4916d5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,163 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "inherent" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rust-lzma" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d62915608f6cee1d7f2fc00f28b4f058ff79d6e4ec3c2fe0006b09b52437c84" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "servicepoint" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2800caad491cb44f67e5dd5b8c61ece368eecfe588155d03c7d9864acbad6919" +dependencies = [ + "bitvec", + "inherent", + "log", + "once_cell", + "rust-lzma", + "thiserror", +] + +[[package]] +name = "servicepoint-idle" +version = "0.1.0" +dependencies = [ + "servicepoint", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2d36a5f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "servicepoint-idle" +version = "0.1.0" +edition = "2024" + +[dependencies] +servicepoint = "0.15.1" \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4611621 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1750969886, + "narHash": "sha256-zW/OFnotiz/ndPFdebpo3X0CrbVNf22n4DjN2vxlb58=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a676066377a2fe7457369dd37c31fd2263b662f4", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..39d13ab --- /dev/null +++ b/flake.nix @@ -0,0 +1,57 @@ +{ + description = ""; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + }; + + outputs = + { self, nixpkgs }: + let + lib = nixpkgs.lib; + supported-systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ]; + forAllSystems = + f: + lib.genAttrs supported-systems ( + system: + f rec { + pkgs = nixpkgs.legacyPackages.${system}; + inherit system; + } + ); + in + { + devShells = forAllSystems ( + { pkgs, system }: + { + default = pkgs.mkShell rec { + packages = with pkgs; [ + (pkgs.symlinkJoin { + name = "rust-toolchain"; + paths = [ + rustc + cargo + rustPlatform.rustcSrc + rustfmt + clippy + cargo-expand + ]; + }) + + gdb + xz + pkg-config + ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }; + } + ); + + formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style); + }; +} diff --git a/src/bar.rs b/src/bar.rs new file mode 100644 index 0000000..4ac2a71 --- /dev/null +++ b/src/bar.rs @@ -0,0 +1,73 @@ +use crate::command_queue::PacketQueue; +use crate::{Currency, GenerateCommands, Progressable}; +use servicepoint::{Bitmap, BitmapCommand, Grid, Origin, TILE_SIZE, TILE_WIDTH, Tiles}; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct Bar { + progress: f64, + speed: f64, + factor: f64, + origin: Origin, + width_tiles: usize, +} + +impl Bar { + pub(crate) fn new(factor: f64, origin: Origin) -> Bar { + Self { + factor, + progress: 0f64, + speed: 0.01f64, + origin, + width_tiles: TILE_WIDTH / 2, // TODO: param + } + } +} + +impl Progressable for Bar { + fn progress(&self, delta: Duration) -> (Self, Currency) { + let extra_progress = delta.as_secs_f64() * self.speed; + let progress = self.progress + extra_progress; + let completions = progress.floor(); + let progress = progress - completions; + let currency = completions * self.factor; + + (Self { progress, ..*self }, currency) + } +} + +impl GenerateCommands for Bar { + fn generate_commands(&self, q: &mut impl PacketQueue) { + let mut bitmap = Bitmap::new(self.width_tiles * TILE_SIZE, TILE_SIZE).unwrap(); + + // border top + let last_row = bitmap.height() - 1; + for x in 0..bitmap.width() { + bitmap.set(x, 0, true); + bitmap.set(x, last_row, true); + } + + // border bottom + let last_col = bitmap.width() - 1; + for y in 0..bitmap.height() { + bitmap.set(0, y, true); + bitmap.set(last_col, y, true); + } + + // progress fill + let fill_to = (bitmap.width() as f64 * self.progress) as usize; + for y in 0..bitmap.height() { + for x in 0..fill_to { + bitmap.set(x, y, true); + } + } + + // to command + q.enqueue_command(BitmapCommand { + bitmap, + origin: Origin::from(&self.origin), + compression: Default::default(), + }) + .unwrap(); + } +} diff --git a/src/command_queue.rs b/src/command_queue.rs new file mode 100644 index 0000000..db56d2a --- /dev/null +++ b/src/command_queue.rs @@ -0,0 +1,20 @@ +use servicepoint::{Packet, UdpSocketExt}; +use std::net::UdpSocket; + +pub(crate) trait PacketQueue { + fn enqueue_command>(&mut self, packet: P) -> Result; +} + +impl PacketQueue for Vec { + fn enqueue_command>(&mut self, packet: P) -> Result { + self.push(packet.try_into()?); + Ok(true) + } +} + +impl PacketQueue for UdpSocket { + fn enqueue_command>(&mut self, packet: P) -> Result { + let packet = packet.try_into()?; + Ok(self.send_command(packet).is_some()) + } +} diff --git a/src/game_state.rs b/src/game_state.rs new file mode 100644 index 0000000..ce8e457 --- /dev/null +++ b/src/game_state.rs @@ -0,0 +1,39 @@ +use crate::bar::Bar; +use crate::command_queue::PacketQueue; +use crate::label::Label; +use crate::{Currency, GenerateCommands, Progressable}; +use servicepoint::{Origin, TILE_WIDTH}; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct GameState { + pub(crate) currency: Currency, + pub(crate) first_step: Bar, +} + +impl Progressable for GameState { + fn progress(&self, delta: Duration) -> (Self, Currency) { + let (first_step, first_currency) = self.first_step.progress(delta); + + let currency = self.currency + first_currency; + + ( + Self { + currency, + first_step, + }, + 0f64, + ) + } +} + +impl GenerateCommands for GameState { + fn generate_commands(&self, queue: &mut impl PacketQueue) { + Label::new(Origin::ZERO, TILE_WIDTH, "Discordia Boot Procedure").generate_commands(queue); + + self.first_step.generate_commands(queue); + Label::new(Origin::new(TILE_WIDTH / 2 + 1, 1), TILE_WIDTH / 2 - 1, "Power infrastructure") + .generate_commands(queue); + + } +} diff --git a/src/label.rs b/src/label.rs new file mode 100644 index 0000000..8e32f0f --- /dev/null +++ b/src/label.rs @@ -0,0 +1,35 @@ +use crate::GenerateCommands; +use crate::command_queue::PacketQueue; +use servicepoint::{CharGrid, CharGridCommand, Origin, Tiles}; + +pub(crate) struct Label> { + position: Origin, + text: S, + width_tiles: usize, +} + +impl Label { + pub(crate) fn new>(position: Origin, width_tiles: usize, text: S) -> Self { + // TODO: those should be grapheme clusters + let text = text.as_ref().chars().take(width_tiles).collect::(); + Self { + position, + text, + width_tiles, + } + } +} + +impl> GenerateCommands for Label { + fn generate_commands(&self, queue: &mut impl PacketQueue) { + let mut grid = CharGrid::new(self.width_tiles, 1); + grid.set_row_str(0, self.text.as_ref()).unwrap(); + + queue + .enqueue_command(CharGridCommand { + grid: CharGrid::from(self.text.as_ref()), + origin: self.position, + }) + .unwrap(); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..06740f9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,43 @@ +use crate::command_queue::PacketQueue; +use bar::Bar; +use game_state::GameState; +use servicepoint::{ClearCommand, Origin, UdpSocketExt}; +use std::net::UdpSocket; +use std::time::{Duration, Instant}; + +mod bar; +mod command_queue; +mod game_state; +mod label; + +type Currency = f64; + +trait Progressable: Sized { + #[must_use] + fn progress(&self, delta: Duration) -> (Self, Currency); +} + +trait GenerateCommands { + fn generate_commands(&self, queue: &mut impl PacketQueue); +} + +fn main() { + let mut state = GameState { + currency: 0f64, + first_step: Bar::new(1f64, Origin::new(0, 1)), + }; + + let mut connection = UdpSocket::bind_connect("127.0.0.1:2342").unwrap(); + + let mut last_refresh = Instant::now(); + loop { + let current_time = Instant::now(); + let delta = current_time - last_refresh; + last_refresh = current_time; + + (state, _) = state.progress(delta); + + connection.send_command(ClearCommand); + state.generate_commands(&mut connection); + } +}