diff --git a/.gitignore b/.gitignore index 0d88ed5..3ea2906 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .envrc .direnv -/nixos.qcow2 # Added by cargo diff --git a/Rocket.toml b/Rocket.toml new file mode 100644 index 0000000..6c67aa7 --- /dev/null +++ b/Rocket.toml @@ -0,0 +1,12 @@ +[debug] +blurred_move = [0.0005, 0.0005] +bonus_timeout = 5000 +event_timeout = 100 +admin_token = "root" +serve_static = true +base_teams = [ + ["team00", "Équipe 00", false], + ["team01", "Équipe 01", false], + ["npc0", "PNJ 0", true], + ["npc1", "PNJ 1", true], +] diff --git a/nix/vm.nix b/nix/vm.nix deleted file mode 100644 index 30237d5..0000000 --- a/nix/vm.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ pkgs, lib, ... }: -{ - nix = { - nixPath = [ - "nixpkgs=${builtins.storePath pkgs.path}" - "nixos=${builtins.storePath pkgs.path}" - ]; - package = pkgs.lix; - }; - environment.systemPackages = with pkgs; [ wget tmux ]; - services.nginx = { - enable = true; - virtualHosts."localhost" = { - default = true; - locations = { - "/" = { - root = "/traque/static"; - tryFiles = "$uri @backend"; - }; - "@backend" = { - recommendedProxySettings = true; - proxyPass = "http://localhost:8000"; - extraConfig = '' - proxy_set_header Connection '''; - proxy_http_version 1.1; - chunked_transfer_encoding off; - proxy_buffering off; - proxy_cache off; - ''; - }; - }; - #extraConfig = '' - # error_page 502 =503; - #''; - }; - }; - services.getty = { - autologinUser = "root"; - helpLine = lib.mkForce '' - On serial console: type Ctrl-a c to switch to the qemu console and `quit` to stop the VM. - traque source is on /traque (rw), `cd /traque && ./target/debug/traque` to run the test. - Compile from the host for performance (the VM is highly limited).''; - }; - nixos-shell.mounts = { - mountHome = false; - mountNixProfile = false; - cache = "none"; - extraMounts = { - "/traque" = { - target = toString ../.; - cache = "none"; - }; - }; - }; - virtualisation = { - forwardPorts = [ - { from = "host"; host.port = 8000; guest.port = 80; } - ]; - }; - - system.stateVersion = "24.11"; -} diff --git a/src/admin.rs b/src/admin.rs index 1cf53d3..e3c1fc2 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -3,7 +3,7 @@ use rocket::{ serde::json::Json, tokio::{ select, - time::{self}, + time::{self, Duration}, }, Route, Shutdown, State, }; @@ -54,6 +54,7 @@ fn admin_events<'a>( admin_key: &State, admin_queue: &'a State, tracking: &State, + config: &State, mut shutdown: Shutdown, ) -> Option { if tok == Some(admin_key.to_string()) { @@ -63,13 +64,14 @@ fn admin_events<'a>( .iter() .map(|(_, tracked)| admin_view(&tracked.read().unwrap())) .collect(); + let timeout = Duration::from_millis(config.event_timeout); Some(EventStream! { yield Event::json(&full_info).event("full_update"); - let mut interval = time::interval(EVENT_TIMEOUT); + let mut interval = time::interval(timeout); loop { select!{ _ = interval.tick() =>{ - for evt in evts_to_send(admin_queue){ + for evt in evts_to_send(admin_queue, timeout){ //println!("{:?}", evt); yield evt; } diff --git a/src/global.rs b/src/global.rs index aca80be..1c496c9 100644 --- a/src/global.rs +++ b/src/global.rs @@ -9,9 +9,16 @@ use std::{ sync::{Arc, RwLock}, }; -pub const BLURRED_MOVE: (f32, f32) = (0.0005, 0.0005); -pub const BONUS_TIMEOUT: Duration = Duration::from_millis(5000); -pub const EVENT_TIMEOUT: Duration = Duration::from_millis(100); +#[derive(Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct Config { + pub blurred_move : (f32, f32), + pub bonus_timeout : u64, + pub event_timeout : u64, + pub admin_token : String, + pub base_teams : Vec<(String, String, bool)>, + pub serve_static : bool, +} #[derive(Serialize, Deserialize, Clone)] #[serde(crate = "rocket::serde")] @@ -135,8 +142,8 @@ pub struct QueuedEvent { } impl QueuedEvent { - pub fn expired(&self) -> bool { - self.date.elapsed() >= EVENT_TIMEOUT + pub fn expired(&self, timeout: Duration) -> bool { + self.date.elapsed() >= timeout } } impl From for QueuedEvent { @@ -204,7 +211,7 @@ pub fn admin_view(team: &Tracked) -> AdminTrackedInfo { } } -pub fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option { +pub fn apparent_info(watcher: &Tracked, team: &Tracked, blurred_move : (f32, f32)) -> Option { if watcher.id == team.id { None } else if let Conscrit { @@ -219,8 +226,8 @@ pub fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option { let (lat, lon) = team.pos; Some(TrackedInfo { pos: ( - lat + BLURRED_MOVE.0 * (rng.gen::() * 2.0 - 1.0), - lon + BLURRED_MOVE.1 * (rng.gen::() * 2.0 - 1.0), + lat + blurred_move.0 * (rng.gen::() * 2.0 - 1.0), + lon + blurred_move.1 * (rng.gen::() * 2.0 - 1.0), ), ..base_view(team) }) @@ -239,12 +246,12 @@ pub fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option { } } -pub fn evts_to_send(evt_queue: &RwLock>) -> Vec { +pub fn evts_to_send(evt_queue: &RwLock>, timeout: Duration) -> Vec { evt_queue .read() .unwrap() .iter() - .filter(|qevt| !qevt.expired()) + .filter(|qevt| !qevt.expired(timeout)) .map(|qevt| qevt.evt.clone()) .collect() } diff --git a/src/main.rs b/src/main.rs index 65a28fd..a00d431 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ #[macro_use] extern crate rocket; use rocket::{ + fs::{relative, FileServer}, + fairing::AdHoc, response::stream::Event, tokio::{ self, select, @@ -23,13 +25,13 @@ fn index() -> &'static str { "Hello, world!" } -fn send_coords(tracking: &Tracking, evt_queue: &TrackingEventQueue) { +fn send_coords(tracking: &Tracking, evt_queue: &TrackingEventQueue, config: &Config) { let tracking_lock = tracking.read().unwrap(); for (id, queue) in evt_queue.read().unwrap().iter() { let watcher = tracking_lock.get(id).unwrap().read().unwrap(); let mut infos: Vec = Vec::new(); for (_, tracked) in tracking_lock.iter() { - if let Some(info) = apparent_info(&watcher, &tracked.read().unwrap()) { + if let Some(info) = apparent_info(&watcher, &tracked.read().unwrap(), config.blurred_move) { infos.push(info); } } @@ -40,11 +42,11 @@ fn send_coords(tracking: &Tracking, evt_queue: &TrackingEventQueue) { } } -fn clean_expired_evt(evt_queues: &TrackingEventQueue, admin_queue: &AdminEventQueue) { +fn clean_expired_evt(evt_queues: &TrackingEventQueue, admin_queue: &AdminEventQueue, config: &Config) { for (_, queue) in evt_queues.read().unwrap().iter() { let queue = &mut queue.write().unwrap(); while let Some(queued_evt) = queue.front() { - if queued_evt.expired() { + if queued_evt.expired(Duration::from_millis(config.event_timeout)) { queue.pop_front(); } else { break; @@ -53,7 +55,7 @@ fn clean_expired_evt(evt_queues: &TrackingEventQueue, admin_queue: &AdminEventQu } let queue = &mut admin_queue.write().unwrap(); while let Some(queued_evt) = queue.front() { - if queued_evt.expired() { + if queued_evt.expired(Duration::from_millis(config.event_timeout)) { queue.pop_front(); } else { break; @@ -63,6 +65,8 @@ fn clean_expired_evt(evt_queues: &TrackingEventQueue, admin_queue: &AdminEventQu #[launch] async fn rocket() -> _ { + let rocket = rocket::build(); + let config : Config = rocket.figment().extract().unwrap(); //TODO: read a config file on release let tracking: Tracking = Arc::new(RwLock::new(HashMap::from([ ( @@ -93,8 +97,9 @@ async fn rocket() -> _ { let admin_evt_queue: AdminEventQueue = Arc::new(RwLock::new(VecDeque::new())); let key: AdminKey = "root".to_string(); //TODO : random on release println!("Admin token: {}", key); - let rocket = rocket::build() + let mut rocket = rocket .attach(Template::fairing()) + .attach(AdHoc::config::()) .manage(tracking.clone()) .manage(evt_queue.clone()) .manage(admin_evt_queue.clone()) @@ -102,13 +107,16 @@ async fn rocket() -> _ { .mount("/", routes![index]) .mount("/track", track::routes()) .mount("/admin", admin::routes()); + if config.serve_static { + rocket = rocket.mount("/", FileServer::from(relative!("static"))); + } tokio::spawn(async move { - let mut clean_interval = time::interval(5 * EVENT_TIMEOUT); + let mut clean_interval = time::interval(5 * Duration::from_millis(config.event_timeout)); let mut coord_interval = time::interval(Duration::from_millis(3000)); loop { select! { - _ = coord_interval.tick() => send_coords(&tracking, &evt_queue), - _ = clean_interval.tick() => clean_expired_evt(&evt_queue, &admin_evt_queue), + _ = coord_interval.tick() => send_coords(&tracking, &evt_queue, &config), + _ = clean_interval.tick() => clean_expired_evt(&evt_queue, &admin_evt_queue, &config), } } }); diff --git a/src/track.rs b/src/track.rs index 6b8c962..e32fe07 100644 --- a/src/track.rs +++ b/src/track.rs @@ -2,7 +2,7 @@ use rocket::{ response::stream::{Event, EventStream}, tokio::{ self, select, - time::{self, sleep}, + time::{self, sleep, Duration}, }, Route, Shutdown, State, }; @@ -35,23 +35,25 @@ fn tracked_view( } } -fn evts_for(id: &str, evt_queues: &TrackingEventQueue) -> Vec { - evts_to_send(evt_queues.read().unwrap().get(&id.to_string()).unwrap()) +fn evts_for(id: &str, evt_queues: &TrackingEventQueue, timeout: Duration) -> Vec { + evts_to_send(evt_queues.read().unwrap().get(&id.to_string()).unwrap(), timeout) } #[get("//events")] fn tracked_events<'a>( id: &'a str, evt_queue: &'a State, + config: &State, mut shutdown: Shutdown, ) -> Option { if evt_queue.read().unwrap().contains_key(&id.to_string()) { + let timeout = Duration::from_millis(config.event_timeout); Some(EventStream! { - let mut interval = time::interval(EVENT_TIMEOUT); + let mut interval = time::interval(timeout); loop { select!{ _ = interval.tick() =>{ - for evt in evts_for(id, evt_queue){ + for evt in evts_for(id, evt_queue, timeout){ //println!("{:?}", evt); yield evt; } @@ -111,6 +113,7 @@ pub async fn activate_invisibility( tracking: &State, evt_queues: &State, admin_queue: &State, + config: &State, ) -> Option<()> { let tracking_lock = tracking.read().unwrap(); let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap(); @@ -128,8 +131,9 @@ pub async fn activate_invisibility( let queue_clone = (*evt_queues).clone(); let admin_clone = (*admin_queue).clone(); let id_str = id.to_string(); + let timeout = Duration::from_millis(config.bonus_timeout); tokio::spawn(async move { - sleep(BONUS_TIMEOUT).await; + sleep(timeout).await; if let Conscrit { ref mut invisible, .. } = track_clone @@ -170,6 +174,7 @@ pub async fn activate_blur( tracking: &State, evt_queues: &State, admin_queue: &State, + config: &State, ) -> Option<()> { let tracking_lock = tracking.read().unwrap(); let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap(); @@ -187,8 +192,9 @@ pub async fn activate_blur( let queue_clone = (*evt_queues).clone(); let admin_clone = (*admin_queue).clone(); let id_str = id.to_string(); + let timeout = Duration::from_millis(config.bonus_timeout); tokio::spawn(async move { - sleep(BONUS_TIMEOUT).await; + sleep(timeout).await; if let Conscrit { ref mut blurred, .. } = track_clone