rewrite backend in rust #32
4 changed files with 602 additions and 560 deletions
88
src/admin.rs
Normal file
88
src/admin.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use rocket::{
|
||||||
|
response::stream::{Event, EventStream},
|
||||||
|
serde::json::Json,
|
||||||
|
tokio::{
|
||||||
|
select,
|
||||||
|
time::{self},
|
||||||
|
},
|
||||||
|
Route, Shutdown, State,
|
||||||
|
};
|
||||||
|
use rocket_dyn_templates::{context, Template};
|
||||||
|
|
||||||
|
use crate::global::*;
|
||||||
|
|
||||||
|
#[get("/?<tok>&<dbg>")]
|
||||||
|
fn admin_page(
|
||||||
|
tok: Option<AdminKey>,
|
||||||
|
dbg: Option<bool>,
|
||||||
|
admin_key: &State<AdminKey>,
|
||||||
|
) -> Option<Template> {
|
||||||
|
if tok == Some(admin_key.to_string()) {
|
||||||
|
Some(Template::render(
|
||||||
|
"admin",
|
||||||
|
context! { tok: tok.unwrap(), dbg: dbg.unwrap_or(false) },
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/<id>?<tok>", data = "<nstate>")]
|
||||||
|
fn admin_set_state(
|
||||||
|
tok: Option<AdminKey>,
|
||||||
|
id: &str,
|
||||||
|
admin_key: &State<AdminKey>,
|
||||||
|
nstate: Json<TrackedState>,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
evt_queue: &State<TrackingEventQueue>,
|
||||||
|
admin_queue: &State<AdminEventQueue>,
|
||||||
|
) -> Option<()> {
|
||||||
|
if tok == Some(admin_key.to_string()) {
|
||||||
|
let tracking_lock = tracking.read().unwrap();
|
||||||
|
let tracked = tracking_lock.get(&id.to_string()).unwrap();
|
||||||
|
tracked.write().unwrap().state = nstate.into_inner();
|
||||||
|
state_update(&tracked.read().unwrap(), &evt_queue, &admin_queue);
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/events?<tok>")]
|
||||||
|
fn admin_events<'a>(
|
||||||
|
tok: Option<AdminKey>,
|
||||||
|
admin_key: &State<AdminKey>,
|
||||||
|
admin_queue: &'a State<AdminEventQueue>,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
mut shutdown: Shutdown,
|
||||||
|
) -> Option<EventStream![Event + 'a]> {
|
||||||
|
if tok == Some(admin_key.to_string()) {
|
||||||
|
let full_info: Vec<AdminTrackedInfo> = tracking
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|(_, tracked)| admin_view(&tracked.read().unwrap()))
|
||||||
|
.collect();
|
||||||
|
Some(EventStream! {
|
||||||
|
yield Event::json(&full_info).event("full_update");
|
||||||
|
let mut interval = time::interval(EVENT_TIMEOUT);
|
||||||
|
loop {
|
||||||
|
select!{
|
||||||
|
_ = interval.tick() =>{
|
||||||
|
for evt in evts_to_send(admin_queue){
|
||||||
|
//println!("{:?}", evt);
|
||||||
|
yield evt;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = &mut shutdown => break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![admin_page, admin_set_state, admin_events]
|
||||||
|
}
|
269
src/global.rs
Normal file
269
src/global.rs
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
use rand::Rng;
|
||||||
|
use rocket::{
|
||||||
|
response::stream::Event,
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
tokio::time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
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(Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum TrackedState {
|
||||||
|
Conscrit {
|
||||||
|
invisible: bool,
|
||||||
|
blurred: bool,
|
||||||
|
captured: bool,
|
||||||
|
mallette: bool,
|
||||||
|
invisibility_codes: u32,
|
||||||
|
blur_codes: u32,
|
||||||
|
},
|
||||||
|
Vieux {
|
||||||
|
color: u8,
|
||||||
|
invisible: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
use TrackedState::{Conscrit, Vieux};
|
||||||
|
|
||||||
|
impl TrackedState {
|
||||||
|
pub fn invisible(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Conscrit { invisible, .. } => *invisible,
|
||||||
|
Vieux { invisible, .. } => *invisible,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn blurred(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Conscrit { blurred, .. } => *blurred,
|
||||||
|
Vieux { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn global_viewed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Conscrit {
|
||||||
|
captured,
|
||||||
|
mallette,
|
||||||
|
invisible,
|
||||||
|
..
|
||||||
|
} => (*captured || *mallette) && !*invisible,
|
||||||
|
Vieux { invisible, .. } => !*invisible,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn color(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Vieux { color, .. } => *color,
|
||||||
|
Conscrit { captured, .. } => {
|
||||||
|
if *captured {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn admin_color(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Vieux { color, invisible } => {
|
||||||
|
if *invisible {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
*color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Conscrit {
|
||||||
|
invisible,
|
||||||
|
captured,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if *invisible {
|
||||||
|
2
|
||||||
|
} else if *captured {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tracked {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub pos: (f32, f32),
|
||||||
|
pub state: TrackedState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_conscrit(id: String, name: String) -> Tracked {
|
||||||
|
Tracked {
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
pos: (0.0, 0.0),
|
||||||
|
state: TrackedState::Conscrit {
|
||||||
|
invisible: false,
|
||||||
|
blurred: false,
|
||||||
|
captured: false,
|
||||||
|
mallette: false,
|
||||||
|
invisibility_codes: 0,
|
||||||
|
blur_codes: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_vieux(id: String, name: String) -> Tracked {
|
||||||
|
Tracked {
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
pos: (0.0, 0.0),
|
||||||
|
state: TrackedState::Vieux {
|
||||||
|
invisible: true,
|
||||||
|
color: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QueuedEvent {
|
||||||
|
pub date: Instant,
|
||||||
|
pub evt: Event,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueuedEvent {
|
||||||
|
pub fn expired(&self) -> bool {
|
||||||
|
self.date.elapsed() >= EVENT_TIMEOUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Event> for QueuedEvent {
|
||||||
|
fn from(evt: Event) -> QueuedEvent {
|
||||||
|
QueuedEvent {
|
||||||
|
date: Instant::now(),
|
||||||
|
evt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<QueuedEvent> for Event {
|
||||||
|
fn from(queued_evt: QueuedEvent) -> Event {
|
||||||
|
queued_evt.evt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Tracking = Arc<RwLock<HashMap<String, RwLock<Tracked>>>>;
|
||||||
|
pub type TrackingEventQueue = Arc<RwLock<HashMap<String, RwLock<VecDeque<QueuedEvent>>>>>;
|
||||||
|
pub type AdminEventQueue = Arc<RwLock<VecDeque<QueuedEvent>>>;
|
||||||
|
pub type AdminKey = String;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TrackedInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub pos: (f32, f32),
|
||||||
|
pub color: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct AdminTrackedInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub id: String,
|
||||||
|
pub pos: (f32, f32),
|
||||||
|
pub color: u8,
|
||||||
|
pub state: TrackedState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AdminTrackedInfo> for TrackedInfo {
|
||||||
|
fn from(admin_info: AdminTrackedInfo) -> TrackedInfo {
|
||||||
|
TrackedInfo {
|
||||||
|
name: admin_info.name,
|
||||||
|
pos: admin_info.pos,
|
||||||
|
color: admin_info.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_view(team: &Tracked) -> TrackedInfo {
|
||||||
|
TrackedInfo {
|
||||||
|
name: team.name.clone(),
|
||||||
|
pos: team.pos,
|
||||||
|
color: team.state.color(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn admin_view(team: &Tracked) -> AdminTrackedInfo {
|
||||||
|
AdminTrackedInfo {
|
||||||
|
name: team.name.clone(),
|
||||||
|
id: team.id.clone(),
|
||||||
|
pos: team.pos,
|
||||||
|
color: team.state.admin_color(),
|
||||||
|
state: team.state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option<TrackedInfo> {
|
||||||
|
if watcher.id == team.id {
|
||||||
|
None
|
||||||
|
} else if let Conscrit {
|
||||||
|
captured, mallette, ..
|
||||||
|
} = watcher.state
|
||||||
|
{
|
||||||
|
if captured {
|
||||||
|
if team.state.invisible() {
|
||||||
|
None
|
||||||
|
} else if team.state.blurred() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let (lat, lon) = team.pos;
|
||||||
|
Some(TrackedInfo {
|
||||||
|
pos: (
|
||||||
|
lat + BLURRED_MOVE.0 * (rng.gen::<f32>() * 2.0 - 1.0),
|
||||||
|
lon + BLURRED_MOVE.1 * (rng.gen::<f32>() * 2.0 - 1.0),
|
||||||
|
),
|
||||||
|
..base_view(team)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Some(base_view(team))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if mallette || team.state.global_viewed() {
|
||||||
|
Some(base_view(team))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(admin_view(team).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evts_to_send(evt_queue: &RwLock<VecDeque<QueuedEvent>>) -> Vec<Event> {
|
||||||
|
evt_queue
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.filter(|qevt| !qevt.expired())
|
||||||
|
.map(|qevt| qevt.evt.clone())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state_update(
|
||||||
|
tracked: &Tracked,
|
||||||
|
evt_queues: &TrackingEventQueue,
|
||||||
|
admin_queue: &AdminEventQueue,
|
||||||
|
) {
|
||||||
|
evt_queues
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&tracked.id)
|
||||||
|
.unwrap()
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.push_back(Event::json(&admin_view(tracked)).event("self_info").into());
|
||||||
|
admin_queue
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.push_back(Event::json(&admin_view(tracked)).event("update").into());
|
||||||
|
}
|
570
src/main.rs
570
src/main.rs
|
@ -1,561 +1,23 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
use rand::Rng;
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
fs::{relative, FileServer},
|
fs::{relative, FileServer},
|
||||||
response::stream::{Event, EventStream},
|
response::stream::Event,
|
||||||
serde::{json::Json, Deserialize, Serialize},
|
|
||||||
tokio::{
|
tokio::{
|
||||||
self, select,
|
self, select,
|
||||||
time::{self, sleep, Duration, Instant},
|
time::{self, Duration},
|
||||||
},
|
},
|
||||||
Shutdown, State,
|
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::{context, Template};
|
use rocket_dyn_templates::Template;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
mod admin;
|
||||||
#[serde(crate = "rocket::serde")]
|
mod global;
|
||||||
enum TrackedState {
|
mod track;
|
||||||
Conscrit {
|
use global::*;
|
||||||
invisible: bool,
|
|
||||||
blurred: bool,
|
|
||||||
captured: bool,
|
|
||||||
mallette: bool,
|
|
||||||
invisibility_codes: u32,
|
|
||||||
blur_codes: u32,
|
|
||||||
},
|
|
||||||
Vieux {
|
|
||||||
color: u8,
|
|
||||||
invisible: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
use TrackedState::{Conscrit, Vieux};
|
|
||||||
|
|
||||||
const BLURRED_MOVE: (f32, f32) = (0.0005, 0.0005);
|
|
||||||
const BONUS_TIMEOUT: Duration = Duration::from_millis(5000);
|
|
||||||
const EVENT_TIMEOUT: Duration = Duration::from_millis(100);
|
|
||||||
|
|
||||||
impl TrackedState {
|
|
||||||
fn invisible(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Conscrit { invisible, .. } => *invisible,
|
|
||||||
Vieux { invisible, .. } => *invisible,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn blurred(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Conscrit { blurred, .. } => *blurred,
|
|
||||||
Vieux { .. } => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn global_viewed(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Conscrit {
|
|
||||||
captured,
|
|
||||||
mallette,
|
|
||||||
invisible,
|
|
||||||
..
|
|
||||||
} => (*captured || *mallette) && !*invisible,
|
|
||||||
Vieux { invisible, .. } => !*invisible,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn color(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Vieux { color, .. } => *color,
|
|
||||||
Conscrit { captured, .. } => {
|
|
||||||
if *captured {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn admin_color(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Vieux { color, invisible } => {
|
|
||||||
if *invisible {
|
|
||||||
2
|
|
||||||
} else {
|
|
||||||
*color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Conscrit {
|
|
||||||
invisible,
|
|
||||||
captured,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if *invisible {
|
|
||||||
2
|
|
||||||
} else if *captured {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Tracked {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
pos: (f32, f32),
|
|
||||||
state: TrackedState,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_conscrit(id: String, name: String) -> Tracked {
|
|
||||||
Tracked {
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
pos: (0.0, 0.0),
|
|
||||||
state: TrackedState::Conscrit {
|
|
||||||
invisible: false,
|
|
||||||
blurred: false,
|
|
||||||
captured: false,
|
|
||||||
mallette: false,
|
|
||||||
invisibility_codes: 0,
|
|
||||||
blur_codes: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_vieux(id: String, name: String) -> Tracked {
|
|
||||||
Tracked {
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
pos: (0.0, 0.0),
|
|
||||||
state: TrackedState::Vieux {
|
|
||||||
invisible: true,
|
|
||||||
color: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct QueuedEvent {
|
|
||||||
date: Instant,
|
|
||||||
evt: Event,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QueuedEvent {
|
|
||||||
fn expired(&self) -> bool {
|
|
||||||
self.date.elapsed() >= EVENT_TIMEOUT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Event> for QueuedEvent {
|
|
||||||
fn from(evt: Event) -> QueuedEvent {
|
|
||||||
QueuedEvent {
|
|
||||||
date: Instant::now(),
|
|
||||||
evt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<QueuedEvent> for Event {
|
|
||||||
fn from(queued_evt: QueuedEvent) -> Event {
|
|
||||||
queued_evt.evt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tracking = Arc<RwLock<HashMap<String, RwLock<Tracked>>>>;
|
|
||||||
type TrackingEventQueue = Arc<RwLock<HashMap<String, RwLock<VecDeque<QueuedEvent>>>>>;
|
|
||||||
type AdminEventQueue = Arc<RwLock<VecDeque<QueuedEvent>>>;
|
|
||||||
type AdminKey = String;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct TrackedInfo {
|
|
||||||
name: String,
|
|
||||||
pos: (f32, f32),
|
|
||||||
color: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct AdminTrackedInfo {
|
|
||||||
name: String,
|
|
||||||
id: String,
|
|
||||||
pos: (f32, f32),
|
|
||||||
color: u8,
|
|
||||||
state: TrackedState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AdminTrackedInfo> for TrackedInfo {
|
|
||||||
fn from(admin_info: AdminTrackedInfo) -> TrackedInfo {
|
|
||||||
TrackedInfo {
|
|
||||||
name: admin_info.name,
|
|
||||||
pos: admin_info.pos,
|
|
||||||
color: admin_info.color,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn base_view(team: &Tracked) -> TrackedInfo {
|
|
||||||
TrackedInfo {
|
|
||||||
name: team.name.clone(),
|
|
||||||
pos: team.pos,
|
|
||||||
color: team.state.color(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn admin_view(team: &Tracked) -> AdminTrackedInfo {
|
|
||||||
AdminTrackedInfo {
|
|
||||||
name: team.name.clone(),
|
|
||||||
id: team.id.clone(),
|
|
||||||
pos: team.pos,
|
|
||||||
color: team.state.admin_color(),
|
|
||||||
state: team.state.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option<TrackedInfo> {
|
|
||||||
if watcher.id == team.id {
|
|
||||||
None
|
|
||||||
} else if let Conscrit {
|
|
||||||
captured, mallette, ..
|
|
||||||
} = watcher.state
|
|
||||||
{
|
|
||||||
if captured {
|
|
||||||
if team.state.invisible() {
|
|
||||||
None
|
|
||||||
} else if team.state.blurred() {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let (lat, lon) = team.pos;
|
|
||||||
Some(TrackedInfo {
|
|
||||||
pos: (
|
|
||||||
lat + BLURRED_MOVE.0 * (rng.gen::<f32>() * 2.0 - 1.0),
|
|
||||||
lon + BLURRED_MOVE.1 * (rng.gen::<f32>() * 2.0 - 1.0),
|
|
||||||
),
|
|
||||||
..base_view(team)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Some(base_view(team))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if mallette || team.state.global_viewed() {
|
|
||||||
Some(base_view(team))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(admin_view(team).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/track/<id>?<gpslog>&<dbg>")]
|
|
||||||
fn tracked_view(
|
|
||||||
id: &str,
|
|
||||||
gpslog: Option<bool>,
|
|
||||||
dbg: Option<bool>,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
) -> Option<Template> {
|
|
||||||
if let Some(tracked) = tracking.read().unwrap().get(&id.to_string()) {
|
|
||||||
Some(Template::render(
|
|
||||||
match tracked.read().unwrap().state {
|
|
||||||
Vieux { .. } => "vieux",
|
|
||||||
Conscrit { .. } => "conscrit",
|
|
||||||
},
|
|
||||||
context! {
|
|
||||||
name: &tracked.read().unwrap().name,
|
|
||||||
id: &id,
|
|
||||||
gpslog: gpslog.unwrap_or(true),
|
|
||||||
dbg: dbg.unwrap_or(false),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evts_to_send(evt_queue: &RwLock<VecDeque<QueuedEvent>>) -> Vec<Event> {
|
|
||||||
evt_queue
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.filter(|qevt| !qevt.expired())
|
|
||||||
.map(|qevt| qevt.evt.clone())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evts_for(id: &str, evt_queues: &TrackingEventQueue) -> Vec<Event> {
|
|
||||||
evts_to_send(evt_queues.read().unwrap().get(&id.to_string()).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/track/<id>/events")]
|
|
||||||
fn tracked_events<'a>(
|
|
||||||
id: &'a str,
|
|
||||||
evt_queue: &'a State<TrackingEventQueue>,
|
|
||||||
mut shutdown: Shutdown,
|
|
||||||
) -> Option<EventStream![Event + 'a]> {
|
|
||||||
if evt_queue.read().unwrap().contains_key(&id.to_string()) {
|
|
||||||
Some(EventStream! {
|
|
||||||
let mut interval = time::interval(EVENT_TIMEOUT);
|
|
||||||
loop {
|
|
||||||
select!{
|
|
||||||
_ = interval.tick() =>{
|
|
||||||
for evt in evts_for(id, evt_queue){
|
|
||||||
//println!("{:?}", evt);
|
|
||||||
yield evt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ = &mut shutdown => break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/admin?<tok>&<dbg>")]
|
|
||||||
fn admin_page(
|
|
||||||
tok: Option<AdminKey>,
|
|
||||||
dbg: Option<bool>,
|
|
||||||
admin_key: &State<AdminKey>,
|
|
||||||
) -> Option<Template> {
|
|
||||||
if tok == Some(admin_key.to_string()) {
|
|
||||||
Some(Template::render(
|
|
||||||
"admin",
|
|
||||||
context! { tok: tok.unwrap(), dbg: dbg.unwrap_or(false) },
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("/admin/<id>?<tok>", data = "<nstate>")]
|
|
||||||
fn admin_set_state(
|
|
||||||
tok: Option<AdminKey>,
|
|
||||||
id: &str,
|
|
||||||
admin_key: &State<AdminKey>,
|
|
||||||
nstate: Json<TrackedState>,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
evt_queue: &State<TrackingEventQueue>,
|
|
||||||
admin_queue: &State<AdminEventQueue>,
|
|
||||||
) -> Option<()> {
|
|
||||||
if tok == Some(admin_key.to_string()) {
|
|
||||||
let tracking_lock = tracking.read().unwrap();
|
|
||||||
let tracked = tracking_lock.get(&id.to_string()).unwrap();
|
|
||||||
tracked.write().unwrap().state = nstate.into_inner();
|
|
||||||
state_update(&tracked.read().unwrap(), &evt_queue, &admin_queue);
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/admin/events?<tok>")]
|
|
||||||
fn admin_events<'a>(
|
|
||||||
tok: Option<AdminKey>,
|
|
||||||
admin_key: &State<AdminKey>,
|
|
||||||
admin_queue: &'a State<AdminEventQueue>,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
mut shutdown: Shutdown,
|
|
||||||
) -> Option<EventStream![Event + 'a]> {
|
|
||||||
if tok == Some(admin_key.to_string()) {
|
|
||||||
let full_info: Vec<AdminTrackedInfo> = tracking
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|(_, tracked)| admin_view(&tracked.read().unwrap()))
|
|
||||||
.collect();
|
|
||||||
Some(EventStream! {
|
|
||||||
yield Event::json(&full_info).event("full_update");
|
|
||||||
let mut interval = time::interval(EVENT_TIMEOUT);
|
|
||||||
loop {
|
|
||||||
select!{
|
|
||||||
_ = interval.tick() =>{
|
|
||||||
for evt in evts_to_send(admin_queue){
|
|
||||||
//println!("{:?}", evt);
|
|
||||||
yield evt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ = &mut shutdown => break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state_update(tracked: &Tracked, evt_queues: &TrackingEventQueue, admin_queue: &AdminEventQueue) {
|
|
||||||
evt_queues
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&tracked.id)
|
|
||||||
.unwrap()
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(Event::json(&admin_view(tracked)).event("self_info").into());
|
|
||||||
admin_queue
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(Event::json(&admin_view(tracked)).event("update").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/track/<id>/pos?<lat>&<long>")]
|
|
||||||
fn store_pos(
|
|
||||||
id: &str,
|
|
||||||
lat: f32,
|
|
||||||
long: f32,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
evt_queues: &State<TrackingEventQueue>,
|
|
||||||
admin_queue: &State<AdminEventQueue>,
|
|
||||||
) {
|
|
||||||
if let Some(tracked) = tracking.read().unwrap().get(&id.to_string()) {
|
|
||||||
tracked.write().unwrap().pos = (lat, long);
|
|
||||||
state_update(&tracked.read().unwrap(), &evt_queues, &admin_queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/track/<id>/state?<inv>&<col>")]
|
|
||||||
fn set_state(
|
|
||||||
id: &str,
|
|
||||||
inv: bool,
|
|
||||||
col: u8,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
evt_queues: &State<TrackingEventQueue>,
|
|
||||||
admin_queue: &State<AdminEventQueue>,
|
|
||||||
) -> Option<()> {
|
|
||||||
let tracking_lock = tracking.read().unwrap();
|
|
||||||
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
|
||||||
if let Vieux {
|
|
||||||
ref mut invisible,
|
|
||||||
ref mut color,
|
|
||||||
} = tracked.state
|
|
||||||
{
|
|
||||||
*invisible = inv;
|
|
||||||
*color = col;
|
|
||||||
state_update(&tracked, &evt_queues, &admin_queue);
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/track/<id>/vanish")]
|
|
||||||
async fn activate_invisibility(
|
|
||||||
id: &str,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
evt_queues: &State<TrackingEventQueue>,
|
|
||||||
admin_queue: &State<AdminEventQueue>,
|
|
||||||
) -> Option<()> {
|
|
||||||
let tracking_lock = tracking.read().unwrap();
|
|
||||||
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
|
||||||
if let Conscrit {
|
|
||||||
ref mut invisible,
|
|
||||||
ref mut invisibility_codes,
|
|
||||||
..
|
|
||||||
} = tracked.state
|
|
||||||
{
|
|
||||||
if *invisibility_codes > 0 {
|
|
||||||
*invisibility_codes -= 1;
|
|
||||||
*invisible = true;
|
|
||||||
state_update(&tracked, &evt_queues, &admin_queue);
|
|
||||||
let track_clone = (*tracking).clone();
|
|
||||||
let queue_clone = (*evt_queues).clone();
|
|
||||||
let admin_clone = (*admin_queue).clone();
|
|
||||||
let id_str = id.to_string();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
sleep(BONUS_TIMEOUT).await;
|
|
||||||
if let Conscrit {
|
|
||||||
ref mut invisible, ..
|
|
||||||
} = track_clone
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&id_str)
|
|
||||||
.unwrap()
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.state
|
|
||||||
{
|
|
||||||
*invisible = false;
|
|
||||||
}
|
|
||||||
state_update(
|
|
||||||
&track_clone
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&id_str)
|
|
||||||
.unwrap()
|
|
||||||
.read()
|
|
||||||
.unwrap(),
|
|
||||||
&queue_clone,
|
|
||||||
&admin_clone,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[put("/track/<id>/blur")]
|
|
||||||
async fn activate_blur(
|
|
||||||
id: &str,
|
|
||||||
tracking: &State<Tracking>,
|
|
||||||
evt_queues: &State<TrackingEventQueue>,
|
|
||||||
admin_queue: &State<AdminEventQueue>,
|
|
||||||
) -> Option<()> {
|
|
||||||
let tracking_lock = tracking.read().unwrap();
|
|
||||||
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
|
||||||
if let Conscrit {
|
|
||||||
ref mut blurred,
|
|
||||||
ref mut blur_codes,
|
|
||||||
..
|
|
||||||
} = tracked.state
|
|
||||||
{
|
|
||||||
if *blur_codes > 0 {
|
|
||||||
*blur_codes -= 1;
|
|
||||||
*blurred = true;
|
|
||||||
state_update(&tracked, &evt_queues, &admin_queue);
|
|
||||||
let track_clone = (*tracking).clone();
|
|
||||||
let queue_clone = (*evt_queues).clone();
|
|
||||||
let admin_clone = (*admin_queue).clone();
|
|
||||||
let id_str = id.to_string();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
sleep(BONUS_TIMEOUT).await;
|
|
||||||
if let Conscrit {
|
|
||||||
ref mut blurred, ..
|
|
||||||
} = track_clone
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&id_str)
|
|
||||||
.unwrap()
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.state
|
|
||||||
{
|
|
||||||
*blurred = false;
|
|
||||||
}
|
|
||||||
state_update(
|
|
||||||
&track_clone
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&id_str)
|
|
||||||
.unwrap()
|
|
||||||
.read()
|
|
||||||
.unwrap(),
|
|
||||||
&queue_clone,
|
|
||||||
&admin_clone,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Some(())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index() -> &'static str {
|
fn index() -> &'static str {
|
||||||
|
@ -638,21 +100,9 @@ async fn rocket() -> _ {
|
||||||
.manage(evt_queue.clone())
|
.manage(evt_queue.clone())
|
||||||
.manage(admin_evt_queue.clone())
|
.manage(admin_evt_queue.clone())
|
||||||
.manage(key)
|
.manage(key)
|
||||||
.mount(
|
.mount("/", routes![index])
|
||||||
"/",
|
.mount("/track", track::routes())
|
||||||
routes![
|
.mount("/admin", admin::routes())
|
||||||
index,
|
|
||||||
store_pos,
|
|
||||||
tracked_view,
|
|
||||||
tracked_events,
|
|
||||||
set_state,
|
|
||||||
admin_page,
|
|
||||||
admin_events,
|
|
||||||
admin_set_state,
|
|
||||||
activate_invisibility,
|
|
||||||
activate_blur,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.mount("/", FileServer::from(relative!("static")));
|
.mount("/", FileServer::from(relative!("static")));
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut clean_interval = time::interval(5 * EVENT_TIMEOUT);
|
let mut clean_interval = time::interval(5 * EVENT_TIMEOUT);
|
||||||
|
|
235
src/track.rs
Normal file
235
src/track.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
use rocket::{
|
||||||
|
response::stream::{Event, EventStream},
|
||||||
|
tokio::{
|
||||||
|
self, select,
|
||||||
|
time::{self, sleep},
|
||||||
|
},
|
||||||
|
Route, Shutdown, State,
|
||||||
|
};
|
||||||
|
use rocket_dyn_templates::{context, Template};
|
||||||
|
|
||||||
|
use crate::global::{TrackedState::*, *};
|
||||||
|
|
||||||
|
#[get("/<id>?<gpslog>&<dbg>")]
|
||||||
|
fn tracked_view(
|
||||||
|
id: &str,
|
||||||
|
gpslog: Option<bool>,
|
||||||
|
dbg: Option<bool>,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
) -> Option<Template> {
|
||||||
|
if let Some(tracked) = tracking.read().unwrap().get(&id.to_string()) {
|
||||||
|
Some(Template::render(
|
||||||
|
match tracked.read().unwrap().state {
|
||||||
|
Vieux { .. } => "vieux",
|
||||||
|
Conscrit { .. } => "conscrit",
|
||||||
|
},
|
||||||
|
context! {
|
||||||
|
name: &tracked.read().unwrap().name,
|
||||||
|
id: &id,
|
||||||
|
gpslog: gpslog.unwrap_or(true),
|
||||||
|
dbg: dbg.unwrap_or(false),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evts_for(id: &str, evt_queues: &TrackingEventQueue) -> Vec<Event> {
|
||||||
|
evts_to_send(evt_queues.read().unwrap().get(&id.to_string()).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/<id>/events")]
|
||||||
|
fn tracked_events<'a>(
|
||||||
|
id: &'a str,
|
||||||
|
evt_queue: &'a State<TrackingEventQueue>,
|
||||||
|
mut shutdown: Shutdown,
|
||||||
|
) -> Option<EventStream![Event + 'a]> {
|
||||||
|
if evt_queue.read().unwrap().contains_key(&id.to_string()) {
|
||||||
|
Some(EventStream! {
|
||||||
|
let mut interval = time::interval(EVENT_TIMEOUT);
|
||||||
|
loop {
|
||||||
|
select!{
|
||||||
|
_ = interval.tick() =>{
|
||||||
|
for evt in evts_for(id, evt_queue){
|
||||||
|
//println!("{:?}", evt);
|
||||||
|
yield evt;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = &mut shutdown => break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/<id>/pos?<lat>&<long>")]
|
||||||
|
fn store_pos(
|
||||||
|
id: &str,
|
||||||
|
lat: f32,
|
||||||
|
long: f32,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
evt_queues: &State<TrackingEventQueue>,
|
||||||
|
admin_queue: &State<AdminEventQueue>,
|
||||||
|
) {
|
||||||
|
if let Some(tracked) = tracking.read().unwrap().get(&id.to_string()) {
|
||||||
|
tracked.write().unwrap().pos = (lat, long);
|
||||||
|
state_update(&tracked.read().unwrap(), &evt_queues, &admin_queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/<id>/state?<inv>&<col>")]
|
||||||
|
fn set_state(
|
||||||
|
id: &str,
|
||||||
|
inv: bool,
|
||||||
|
col: u8,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
evt_queues: &State<TrackingEventQueue>,
|
||||||
|
admin_queue: &State<AdminEventQueue>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let tracking_lock = tracking.read().unwrap();
|
||||||
|
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
||||||
|
if let Vieux {
|
||||||
|
ref mut invisible,
|
||||||
|
ref mut color,
|
||||||
|
} = tracked.state
|
||||||
|
{
|
||||||
|
*invisible = inv;
|
||||||
|
*color = col;
|
||||||
|
state_update(&tracked, &evt_queues, &admin_queue);
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/<id>/vanish")]
|
||||||
|
pub async fn activate_invisibility(
|
||||||
|
id: &str,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
evt_queues: &State<TrackingEventQueue>,
|
||||||
|
admin_queue: &State<AdminEventQueue>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let tracking_lock = tracking.read().unwrap();
|
||||||
|
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
||||||
|
if let Conscrit {
|
||||||
|
ref mut invisible,
|
||||||
|
ref mut invisibility_codes,
|
||||||
|
..
|
||||||
|
} = tracked.state
|
||||||
|
{
|
||||||
|
if *invisibility_codes > 0 {
|
||||||
|
*invisibility_codes -= 1;
|
||||||
|
*invisible = true;
|
||||||
|
state_update(&tracked, &evt_queues, &admin_queue);
|
||||||
|
let track_clone = (*tracking).clone();
|
||||||
|
let queue_clone = (*evt_queues).clone();
|
||||||
|
let admin_clone = (*admin_queue).clone();
|
||||||
|
let id_str = id.to_string();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sleep(BONUS_TIMEOUT).await;
|
||||||
|
if let Conscrit {
|
||||||
|
ref mut invisible, ..
|
||||||
|
} = track_clone
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&id_str)
|
||||||
|
.unwrap()
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.state
|
||||||
|
{
|
||||||
|
*invisible = false;
|
||||||
|
}
|
||||||
|
state_update(
|
||||||
|
&track_clone
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&id_str)
|
||||||
|
.unwrap()
|
||||||
|
.read()
|
||||||
|
.unwrap(),
|
||||||
|
&queue_clone,
|
||||||
|
&admin_clone,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/<id>/blur")]
|
||||||
|
pub async fn activate_blur(
|
||||||
|
id: &str,
|
||||||
|
tracking: &State<Tracking>,
|
||||||
|
evt_queues: &State<TrackingEventQueue>,
|
||||||
|
admin_queue: &State<AdminEventQueue>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let tracking_lock = tracking.read().unwrap();
|
||||||
|
let tracked = &mut tracking_lock.get(&id.to_string()).unwrap().write().unwrap();
|
||||||
|
if let Conscrit {
|
||||||
|
ref mut blurred,
|
||||||
|
ref mut blur_codes,
|
||||||
|
..
|
||||||
|
} = tracked.state
|
||||||
|
{
|
||||||
|
if *blur_codes > 0 {
|
||||||
|
*blur_codes -= 1;
|
||||||
|
*blurred = true;
|
||||||
|
state_update(&tracked, &evt_queues, &admin_queue);
|
||||||
|
let track_clone = (*tracking).clone();
|
||||||
|
let queue_clone = (*evt_queues).clone();
|
||||||
|
let admin_clone = (*admin_queue).clone();
|
||||||
|
let id_str = id.to_string();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sleep(BONUS_TIMEOUT).await;
|
||||||
|
if let Conscrit {
|
||||||
|
ref mut blurred, ..
|
||||||
|
} = track_clone
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&id_str)
|
||||||
|
.unwrap()
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.state
|
||||||
|
{
|
||||||
|
*blurred = false;
|
||||||
|
}
|
||||||
|
state_update(
|
||||||
|
&track_clone
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&id_str)
|
||||||
|
.unwrap()
|
||||||
|
.read()
|
||||||
|
.unwrap(),
|
||||||
|
&queue_clone,
|
||||||
|
&admin_clone,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![
|
||||||
|
store_pos,
|
||||||
|
tracked_view,
|
||||||
|
tracked_events,
|
||||||
|
set_state,
|
||||||
|
activate_invisibility,
|
||||||
|
activate_blur,
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue