rewrite backend in rust #32

Merged
lbailly merged 14 commits from rewrite into master 2024-06-13 13:20:05 +02:00
5 changed files with 222 additions and 82 deletions
Showing only changes of commit e3aea1494d - Show all commits

1
Cargo.lock generated
View file

@ -1566,6 +1566,7 @@ dependencies = [
name = "traque" name = "traque"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rand",
"rocket", "rocket",
"rocket_dyn_templates", "rocket_dyn_templates",
] ]

View file

@ -5,6 +5,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"
[dependencies.rocket] [dependencies.rocket]
version = "0.5.0" version = "0.5.0"
features = ["json"] features = ["json"]

View file

@ -1,5 +1,6 @@
#[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, EventStream},
@ -18,7 +19,7 @@ enum TrackedState {
invisible: bool, invisible: bool,
blurred: bool, blurred: bool,
captured: bool, captured: bool,
malette: bool, mallette: bool,
invisibility_codes: u32, invisibility_codes: u32,
blur_codes: u32, blur_codes: u32,
}, },
@ -28,29 +29,98 @@ enum TrackedState {
}, },
} }
use TrackedState::{Conscrit, Vieux};
const BLURRED_MOVE: (f32, f32) = (0.0005, 0.0005);
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 { struct Tracked {
id: String,
name: String, name: String,
pos: (f32, f32), pos: (f32, f32),
state: TrackedState, state: TrackedState,
} }
fn build_conscrit(name: String) -> Tracked { fn build_conscrit(id: String, name: String) -> Tracked {
Tracked { Tracked {
id: id,
name: name, name: name,
pos: (0.0, 0.0), pos: (0.0, 0.0),
state: TrackedState::Conscrit { state: TrackedState::Conscrit {
invisible: false, invisible: false,
blurred: false, blurred: false,
captured: false, captured: false,
malette: false, mallette: false,
invisibility_codes: 0, invisibility_codes: 0,
blur_codes: 0, blur_codes: 0,
}, },
} }
} }
fn build_vieux(name: String) -> Tracked { fn build_vieux(id: String, name: String) -> Tracked {
Tracked { Tracked {
id: id,
name: name, name: name,
pos: (0.0, 0.0), pos: (0.0, 0.0),
state: TrackedState::Vieux { state: TrackedState::Vieux {
@ -67,6 +137,64 @@ type Tracking = RwLock<HashMap<String, RwLock<Tracked>>>;
struct TrackedInfo { struct TrackedInfo {
name: String, name: String,
pos: (f32, f32), pos: (f32, f32),
me: bool,
color: u8,
}
fn base_view(team: &Tracked) -> TrackedInfo {
TrackedInfo {
name: team.name.clone(),
pos: team.pos,
me: false,
color: team.state.color(),
}
}
fn admin_view(team: &Tracked) -> TrackedInfo {
TrackedInfo {
name: team.name.clone(),
pos: team.pos,
me: false,
color: team.state.admin_color(),
}
}
fn apparent_info(watcher: &Tracked, team: &Tracked) -> Option<TrackedInfo> {
if watcher.id == team.id {
Some(TrackedInfo {
me: true,
..admin_view(team)
})
} 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))
}
} }
#[get("/track/<id>?<gpslog>")] #[get("/track/<id>?<gpslog>")]
@ -88,6 +216,18 @@ fn tracked_view(id: &str, gpslog: Option<bool>, tracking: &State<Tracking>) -> O
} }
} }
fn info_to_send(id: &String, tracking: &Tracking) -> Vec<TrackedInfo> {
let tracking_lock = tracking.read().unwrap();
let watcher = tracking_lock.get(id).unwrap().read().unwrap();
let mut infos: Vec<TrackedInfo> = Vec::new();
for (_, tracked) in tracking_lock.iter() {
if let Some(info) = apparent_info(&watcher, &tracked.read().unwrap()) {
infos.push(info);
}
}
infos
}
#[get("/track/<id>/events")] #[get("/track/<id>/events")]
fn tracked_events<'a>( fn tracked_events<'a>(
id: &'a str, id: &'a str,
@ -99,13 +239,8 @@ fn tracked_events<'a>(
let mut interval = time::interval(Duration::from_secs(5)); let mut interval = time::interval(Duration::from_secs(5));
loop { loop {
select!{ select!{
_ = interval.tick() => { _ = interval.tick() =>
let info = TrackedInfo { yield Event::json(&info_to_send(&id.to_string(), &tracking)).event("coords"),
name: id.to_string(),
pos: tracking.read().unwrap().get(&id.to_string()).unwrap().read().unwrap().pos
};
yield Event::json(&info).event("coords")
},
_ = &mut shutdown => break _ = &mut shutdown => break
} }
} }
@ -132,19 +267,19 @@ fn rocket() -> _ {
let tracking = HashMap::<String, RwLock<Tracked>>::from([ let tracking = HashMap::<String, RwLock<Tracked>>::from([
( (
"team00".to_string(), "team00".to_string(),
RwLock::new(build_conscrit("Équipe 0".to_string())), RwLock::new(build_conscrit("team00".to_string(), "Équipe 0".to_string())),
), ),
( (
"team01".to_string(), "team01".to_string(),
RwLock::new(build_conscrit("Équipe 1".to_string())), RwLock::new(build_conscrit("team01".to_string(), "Équipe 1".to_string())),
), ),
( (
"npc0".to_string(), "npc0".to_string(),
RwLock::new(build_vieux("PNJ 0".to_string())), RwLock::new(build_vieux("npc0".to_string(), "PNJ 0".to_string())),
), ),
( (
"npc1".to_string(), "npc1".to_string(),
RwLock::new(build_vieux("PNJ 1".to_string())), RwLock::new(build_vieux("npc1".to_string(), "PNJ 1".to_string())),
), ),
]); ]);
rocket::build() rocket::build()

View file

@ -1,9 +1,5 @@
var protocol = location.protocol; var evtsource;
var server = location.hostname; var markers = [];
var port = location.port;
var socket;
var id;
var markers = {};
var CircleIcon = L.Icon.extend({ var CircleIcon = L.Icon.extend({
options: { options: {
@ -67,70 +63,80 @@ var map_border = [
var map; var map;
function setup_map(){ function setup_map(){
map = L.map('map').setView([48.8448, 2.3550], 13); map = L.map('map').setView([48.8448, 2.3550], 13);
L.tileLayer('https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', { L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}{r}.{ext}', {
maxZoom: 19, maxZoom: 20,
attribution: '© OpenStreetMap' minZoom: 0,
}).addTo(map); attribution: '&copy; <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> &copy; <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
ext: 'png'
}).addTo(map);
L.polyline(map_border, {color: 'red'}).addTo(map); L.polyline(map_border, {color: 'red'}).addTo(map);
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// SOCKET // EVENT LISTENNING
function setup_socket_common(){ function setup_evtlisten_common(){
socket.on("moving", function(data){ evtSource.addEventListener("coords", (event) => {
console.log("moving", data); const data = JSON.parse(event.data);
if(!(data.id in markers)){ console.log(data);
if(data.id == id){ var i = 0;
markers[data.id] = L.marker(data.position, {"icon": self_icons[data.color]}).addTo(map); for (tracked of data) {
markers[data.id].setZIndexOffset(10000); if (i == markers.length) {
} markers.push(L.marker([0,0], {"icon": icons[0] }).addTo(map));
else markers[i].bindPopup("");
markers[data.id] = L.marker(data.position, {"icon": icons[data.color]}).addTo(map); }
markers[data.id].bindPopup(data.id); markers[i].setLatLng(tracked.pos);
} else{ markers[i].setPopupContent(tracked.name);
markers[data.id].setLatLng(data.position); if (tracked.me) {
if(data.id == id) markers[i].setIcon(self_icons[tracked.color]);
markers[data.id].setIcon(self_icons[data.color]); markers[i].setZIndexOffset(1000);
else } else {
markers[data.id].setIcon(icons[data.color]); markers[i].setIcon(icons[tracked.color]);
} markers[i].setZIndexOffset(0);
}); }
++i;
}
for (; i < markers.length; ++i) {
markers[i].setLatLng([0,0]);
}
});
socket.on("popup", function(data){ //socket.on("popup", function(data){
alert(data.content); // alert(data.content);
}); //});
socket.on("remove", function(data){ //socket.on("remove", function(data){
if(data.id in markers) // if(data.id in markers)
markers[data.id].remove(); // markers[data.id].remove();
}); //});
socket.on("newTracker", function(data){ //socket.on("newTracker", function(data){
L.marker(data.position, {"icon": icons[1]}).addTo(map); // L.marker(data.position, {"icon": icons[1]}).addTo(map);
}); //});
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// GEOLOCALISATION // GEOLOCALISATION
function setup_geoLoc(){ function setup_geoLoc(){
function geoLoc_success(pos) { const requestOptions = { method: 'PUT' };
fetch("/log?id="+id+"&lat="+pos.coords.latitude+"&lon="+pos.coords.longitude);
}
function geoLoc_error(err) { function geoLoc_success(pos) {
console.error(`ERROR(${err.code}): ${err.message}`); fetch("/log/"+id+"?lat="+pos.coords.latitude+"&long="+pos.coords.longitude, requestOptions);
} }
var options = { function geoLoc_error(err) {
enableHighAccuracy: false, console.error(`ERROR(${err.code}): ${err.message}`);
timeout: 5000, }
maximumAge: 0
};
navigator.geolocation.watchPosition(geoLoc_success, geoLoc_error, options); var options = {
enableHighAccuracy: false,
timeout: 5000,
maximumAge: 0
};
navigator.geolocation.watchPosition(geoLoc_success, geoLoc_error, options);
} }

View file

@ -24,7 +24,7 @@
} }
</style> </style>
<!-- <script type="text/javascript" src="/utils.js"></script> --> <script type="text/javascript" src="/utils.js"></script>
</head> </head>
<body> <body>
<div id="map"></div><br/> <div id="map"></div><br/>
@ -36,21 +36,16 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
const evtSource = new EventSource("/track/{{id}}/events"); //////////////////////////////////////////////////////////////////////////////
evtSource.addEventListener("coords", (event) => { // SETUP MAP
console.log(event);
});
// //////////////////////////////////////////////////////////////////////////////
// // SETUP MAP
// setup_map(); setup_map();
// ////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// // SOCKET // EVENT LISTENNING
// id = "{{id}}"; evtSource = new EventSource("/track/{{id}}/events");
// socket = io({rejectUnauthorized: false, auth: {id: id, type:"conscrit"}}); setup_evtlisten_common();
// setup_socket_common();
// ////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////
// // SETTINGS -- CODE // // SETTINGS -- CODE