rewrite backend in rust #32
5 changed files with 222 additions and 82 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
165
src/main.rs
165
src/main.rs
|
@ -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()
|
||||||
|
|
114
static/utils.js
114
static/utils.js
|
@ -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: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://www.stamen.com/" target="_blank">Stamen Design</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue