This commit is contained in:
Daniel S. 2020-06-18 12:31:25 +02:00
parent 717a389a22
commit 35a0c40d14
8 changed files with 3359 additions and 0 deletions

19
rust/cargo_check.py Normal file
View File

@ -0,0 +1,19 @@
import toml
import subprocess as SP
import os
def set_version(rev=None):
with open("Cargo.toml") as fh:
cargo = toml.loads(fh.read())
cargo["dependencies"]["pyo3"]["rev"] = rev
if rev is None:
del cargo["dependencies"]["pyo3"]["rev"]
with open("Cargo.toml", "w") as fh:
toml.dump(cargo, fh)
for commit in open("ch.txt").readlines():
set_version(commit.strip())
if os.system("cargo check") == 0:
break

2203
rust/ch.txt Normal file

File diff suppressed because it is too large Load Diff

19
rust/rustup_check.py Normal file
View File

@ -0,0 +1,19 @@
import subprocess as SP
import os
from datetime import timedelta
from datetime import datetime
dt = timedelta(days=1)
d = datetime.today().date()
while True:
toolchain="nightly-{}".format(d.isoformat())
ret = os.system(
f"rustup default {toolchain}"
)
if ret==0:
os.system("cargo +{} clean".format(toolchain))
if os.system("cargo +{} check".format(toolchain))==0:
print(d)
break
d = d - dt

289
rust/src/data.json Normal file
View File

@ -0,0 +1,289 @@
{
"timestamp": "2019-09-25T21:29:51Z",
"event": "Loadout",
"Ship": "asp",
"ShipID": 0,
"ShipName": "Nightmaregreen_N",
"ShipIdent": "NMGR_N",
"HullValue": 6144793,
"ModulesValue": 33042643,
"HullHealth": 1.000000,
"UnladenMass": 347.200012,
"CargoCapacity": 0,
"MaxJumpRange": 56.372398,
"FuelCapacity": {
"Main": 64.000000,
"Reserve": 0.630000
},
"Rebuy": 1959374,
"Modules": [
{
"Slot": "ShipCockpit",
"Item": "asp_cockpit",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "CargoHatch",
"Item": "modularcargobaydoor",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "TinyHardpoint1",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"AmmoInClip": 1,
"AmmoInHopper": 2,
"Health": 1.000000
},
{
"Slot": "TinyHardpoint2",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"AmmoInClip": 1,
"AmmoInHopper": 2,
"Health": 1.000000
},
{
"Slot": "TinyHardpoint3",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"AmmoInClip": 1,
"AmmoInHopper": 2,
"Health": 1.000000
},
{
"Slot": "TinyHardpoint4",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"AmmoInClip": 1,
"AmmoInHopper": 2,
"Health": 1.000000
},
{
"Slot": "PaintJob",
"Item": "paintjob_asp_operator_red",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Armour",
"Item": "asp_armour_grade1",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "PowerPlant",
"Item": "int_powerplant_size5_class2",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "MainEngines",
"Item": "int_engine_size4_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "FrameShiftDrive",
"Item": "int_hyperdrive_size5_class5",
"On": true,
"Priority": 0,
"Health": 1.000000,
"Engineering": {
"Engineer": "Felicity Farseer",
"EngineerID": 300100,
"BlueprintID": 128673694,
"BlueprintName": "FSD_LongRange",
"Level": 5,
"Quality": 1.000000,
"ExperimentalEffect": "special_fsd_heavy",
"ExperimentalEffect_Localised": "Mass Manager",
"Modifiers": [
{
"Label": "Mass",
"Value": 26.000000,
"OriginalValue": 20.000000,
"LessIsGood": 1
},
{
"Label": "Integrity",
"Value": 93.840004,
"OriginalValue": 120.000000,
"LessIsGood": 0
},
{
"Label": "PowerDraw",
"Value": 0.690000,
"OriginalValue": 0.600000,
"LessIsGood": 1
},
{
"Label": "FSDOptimalMass",
"Value": 1692.599976,
"OriginalValue": 1050.000000,
"LessIsGood": 0
}
]
}
},
{
"Slot": "LifeSupport",
"Item": "int_lifesupport_size4_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "PowerDistributor",
"Item": "int_powerdistributor_size4_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Radar",
"Item": "int_sensors_size5_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "FuelTank",
"Item": "int_fueltank_size5_class3",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Decal1",
"Item": "decal_explorer_starblazer",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Decal2",
"Item": "decal_explorer_starblazer",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Decal3",
"Item": "decal_explorer_starblazer",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "ShipName0",
"Item": "nameplate_shipname_white",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "ShipName1",
"Item": "nameplate_shipname_white",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "ShipID0",
"Item": "nameplate_shipid_white",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "ShipID1",
"Item": "nameplate_shipid_white",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Slot01_Size6",
"Item": "int_fuelscoop_size6_class5",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot02_Size5",
"Item": "int_fueltank_size5_class3",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "Slot03_Size3",
"Item": "int_repairer_size3_class5",
"On": false,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot04_Size3",
"Item": "int_shieldgenerator_size3_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot05_Size3",
"Item": "int_buggybay_size2_class2",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot06_Size2",
"Item": "int_detailedsurfacescanner_tiny",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot07_Size2",
"Item": "int_dockingcomputer_standard",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "Slot08_Size1",
"Item": "int_supercruiseassist",
"On": true,
"Priority": 0,
"Health": 1.000000
},
{
"Slot": "PlanetaryApproachSuite",
"Item": "int_planetapproachsuite",
"On": true,
"Priority": 1,
"Health": 1.000000
},
{
"Slot": "VesselVoice",
"Item": "voicepack_eden",
"On": true,
"Priority": 1,
"Health": 1.000000
}
]
}

193
rust/src/data_guardian.json Normal file
View File

@ -0,0 +1,193 @@
{
"timestamp": "2019-09-25T21:29:51Z",
"event": "Loadout",
"Ship": "asp",
"ShipName": "Nightmaregreen_G",
"ShipIdent": "NMGR_G",
"HullValue": 6144793,
"ModulesValue": 33181682,
"UnladenMass": 348.500061,
"CargoCapacity": 0,
"MaxJumpRange": 60.164637,
"FuelCapacity": {
"Main": 64,
"Reserve": 0.63
},
"Rebuy": 1966323,
"Modules": [
{
"Slot": "CargoHatch",
"Item": "modularcargobaydoor",
"On": true,
"Priority": 0
},
{
"Slot": "TinyHardpoint1",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"Value": 3071
},
{
"Slot": "TinyHardpoint2",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"Value": 3071
},
{
"Slot": "TinyHardpoint3",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"Value": 3071
},
{
"Slot": "TinyHardpoint4",
"Item": "hpt_heatsinklauncher_turret_tiny",
"On": true,
"Priority": 0,
"Value": 3071
},
{
"Slot": "Armour",
"Item": "asp_armour_grade1",
"On": true,
"Priority": 1,
"Value": 0
},
{
"Slot": "PowerPlant",
"Item": "int_powerplant_size5_class2",
"On": true,
"Priority": 1,
"Value": 140523
},
{
"Slot": "MainEngines",
"Item": "int_engine_size4_class2",
"On": true,
"Priority": 0,
"Value": 52325
},
{
"Slot": "FrameShiftDrive",
"Item": "int_hyperdrive_size5_class5",
"On": true,
"Priority": 0,
"Value": 4478716,
"Engineering": {
"BlueprintName": "FSD_LongRange",
"Level": 5,
"Quality": 1,
"ExperimentalEffect": "special_fsd_heavy",
"Modifiers": [
{
"Label": "Mass",
"Value": 26.000061,
"OriginalValue": 20
},
{
"Label": "Integrity",
"Value": 93.839832,
"OriginalValue": 120
},
{
"Label": "PowerDraw",
"Value": 0.690001,
"OriginalValue": 0.6
},
{
"Label": "FSDOptimalMass",
"Value": 1692.58667,
"OriginalValue": 1050
}
]
}
},
{
"Slot": "LifeSupport",
"Item": "int_lifesupport_size4_class2",
"On": true,
"Priority": 0,
"Value": 24895
},
{
"Slot": "PowerDistributor",
"Item": "int_powerdistributor_size4_class2",
"On": true,
"Priority": 0,
"Value": 24895
},
{
"Slot": "Radar",
"Item": "int_sensors_size5_class2",
"On": true,
"Priority": 0,
"Value": 69709
},
{
"Slot": "FuelTank",
"Item": "int_fueltank_size5_class3",
"On": true,
"Priority": 1,
"Value": 85776
},
{
"Slot": "Slot01_Size6",
"Item": "int_fuelscoop_size6_class5",
"On": true,
"Priority": 0,
"Value": 25240068
},
{
"Slot": "Slot02_Size5",
"Item": "int_fueltank_size5_class3",
"On": true,
"Priority": 1,
"Value": 85776
},
{
"Slot": "Slot03_Size3",
"Item": "int_repairer_size3_class5",
"On": false,
"Priority": 0,
"Value": 2302911
},
{
"Slot": "Slot04_Size3",
"Item": "int_shieldgenerator_size3_class2",
"On": true,
"Priority": 0,
"Value": 16506
},
{
"Slot": "Slot05_Size3",
"Item": "int_buggybay_size2_class2",
"On": true,
"Priority": 0,
"Value": 18954
},
{
"Slot": "Slot06_Size2",
"Item": "int_detailedsurfacescanner_tiny",
"On": true,
"Priority": 0,
"Value": 219375
},
{
"Slot": "Slot07_Size2",
"Item": "int_dockingcomputer_standard",
"On": true,
"Priority": 0,
"Value": 3949
},
{
"Slot": "Slot08_Size1",
"Item": "int_guardianfsdbooster_size1",
"On": true,
"Priority": 0,
"Value": 405020
}
]
}

196
rust/src/edsm.rs Normal file
View File

@ -0,0 +1,196 @@
use crate::common::get_mult;
use crate::common::SystemSerde;
use fnv::FnvHashMap;
use pyo3::prelude::*;
use serde::Deserialize;
use serde_json::Result;
use std::fs::File;
use std::io::Seek;
use std::io::{BufRead, BufReader, BufWriter, SeekFrom};
use std::path::PathBuf;
use std::str;
use std::time::Instant;
#[derive(Debug, Deserialize)]
#[allow(non_snake_case)]
struct Body {
name: String,
subType: String,
#[serde(rename = "type")]
body_type: String,
systemId: i32,
systemId64: i64,
#[serde(rename = "distanceToArrival")]
distance: f32,
}
#[derive(Debug, Deserialize)]
struct Coords {
x: f32,
y: f32,
z: f32,
}
#[derive(Debug, Deserialize)]
struct System {
id: i32,
id64: i64,
name: String,
coords: Coords,
}
#[derive(Debug)]
pub struct PreprocessState {
pub file: String,
pub message: String,
pub total: u64,
pub done: u64,
pub count: usize,
}
fn process(
path: &PathBuf,
func: &mut dyn for<'r> FnMut(&'r str) -> (),
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
) -> std::io::Result<()> {
let mut buffer = String::new();
let fh = File::open(path)?;
let total_size = fh.metadata()?.len();
let mut t_last = Instant::now();
let mut reader = BufReader::new(fh);
let mut state = PreprocessState {
file: path.to_str().unwrap().to_owned(),
total: total_size,
done: 0,
count: 0,
message: format!("Processing {} ...", path.to_str().unwrap()),
};
println!("Loading {} ...", path.to_str().unwrap());
while let Ok(n) = reader.read_line(&mut buffer) {
if n == 0 {
break;
}
buffer = buffer.trim_end().trim_end_matches(|c| c == ',').to_string();
if !buffer.is_empty() {
func(&buffer);
}
let pos = reader.seek(SeekFrom::Current(0)).unwrap();
state.done = pos;
state.count += 1;
if t_last.elapsed().as_millis() > 100 {
callback(&state)?;
t_last = Instant::now();
}
buffer.clear();
}
Ok(())
}
fn process_systems(
path: &PathBuf,
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
) -> FnvHashMap<i32, System> {
let mut ret = FnvHashMap::default();
process(
path,
&mut |line| {
let sys_res: Result<System> = serde_json::from_str(&line);
if let Ok(sys) = sys_res {
ret.insert(sys.id, sys);
} else {
eprintln!("\nError parsing: {}\n\t{:?}\n", line, sys_res.unwrap_err());
}
},
callback,
)
.unwrap();
ret
}
pub fn build_index(path: &PathBuf) -> std::io::Result<()> {
let mut wtr = BufWriter::new(File::create(path.with_extension("idx"))?);
let mut idx: Vec<u64> = Vec::new();
let mut records = (csv::Reader::from_path(path)?).into_deserialize::<SystemSerde>();
loop {
idx.push(records.reader().position().byte());
if records.next().is_none() {
break;
}
}
bincode::serialize_into(&mut wtr, &idx).unwrap();
Ok(())
}
fn process_bodies(
path: &PathBuf,
out_path: &PathBuf,
systems: &mut FnvHashMap<i32, System>,
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
) -> std::io::Result<()> {
println!(
"Processing {} into {} ...",
path.to_str().unwrap(),
out_path.to_str().unwrap(),
);
let mut n: u32 = 0;
let mut wtr = csv::Writer::from_path(out_path)?;
process(
path,
&mut |line| {
if !line.contains("Star") {
return;
}
let body_res: Result<Body> = serde_json::from_str(&line);
if let Ok(body) = body_res {
if !body.body_type.contains("Star") {
return;
}
if let Some(sys) = systems.get(&body.systemId) {
let sub_type = body.subType;
let mult = get_mult(&sub_type);
let sys_name = sys.name.clone();
let rec = SystemSerde {
id: n,
star_type: sub_type,
system: sys_name,
body: body.name,
mult,
distance: body.distance,
x: sys.coords.x,
y: sys.coords.y,
z: sys.coords.z,
};
wtr.serialize(rec).unwrap();
n += 1;
};
} else {
eprintln!("\nError parsing: {}\n\t{:?}\n", line, body_res.unwrap_err());
}
},
callback,
)
.unwrap();
println!("Total Systems: {}", n);
systems.clear();
Ok(())
}
pub fn preprocess_files(
bodies: &PathBuf,
systems: &PathBuf,
out_path: &PathBuf,
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
) -> std::io::Result<()> {
if !out_path.exists() {
let mut systems = process_systems(systems, &callback);
process_bodies(bodies, out_path, &mut systems, &callback)?;
} else {
println!(
"File '{}' exists, not overwriting it",
out_path.to_str().unwrap()
);
}
println!("Building index...");
println!("Index result: {:?}", build_index(&out_path));
Ok(())
}

169
rust/src/journal.rs Normal file
View File

@ -0,0 +1,169 @@
use crate::common::get_fsd_info;
use crate::ship::Ship;
use regex::Regex;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Event {
#[serde(flatten)]
pub event: EventData,
}
#[serde(tag = "event")]
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub enum EventData {
Loadout(Loadout),
#[serde(other)]
Unknown,
}
#[serde(rename_all = "PascalCase")]
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Modifier {
label: String,
value: f32,
}
#[serde(rename_all = "PascalCase")]
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Engineering {
modifiers: Vec<Modifier>,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Module {
engineering: Option<Engineering>,
item: String,
slot: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FuelCapacity {
main: f32,
reserve: f32,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Loadout {
#[serde(rename = "timestamp")]
timestamp: String,
ship: String,
ship_name: String,
ship_ident: String,
fuel_capacity: FuelCapacity,
unladen_mass: f32,
modules: Vec<Module>,
}
impl Engineering {
fn to_hashmap(&self) -> HashMap<String, f32> {
let mut h = HashMap::new();
for v in &self.modifiers {
h.insert(v.label.clone(), v.value);
}
return h;
}
}
impl Loadout {
fn get_booster(&self) -> Option<usize> {
self.modules
.iter()
.cloned()
.filter_map(|m| {
let Module { item, .. } = m;
if item.starts_with("int_guardianfsdbooster") {
return item
.chars()
.last()
.unwrap()
.to_digit(10)
.map(|v| v as usize);
}
return None;
})
.next()
}
fn get_fsd(&self) -> Option<(String, Option<Engineering>)> {
self.modules
.iter()
.cloned()
.filter_map(|m| {
let Module {
slot,
engineering,
item,
} = m;
if slot == "FrameShiftDrive" {
return Some((item, engineering));
}
return None;
})
.next()
}
pub fn try_into_ship(self) -> Result<Ship, String> {
let fsd = self.get_fsd().ok_or("No FSD found!")?;
let booster = self.get_booster().unwrap_or(0);
let fsd_type = Regex::new(r"^int_hyperdrive_size(\d+)_class(\d+)$")
.unwrap()
.captures(&fsd.0);
let fsd_type: (usize, usize) = fsd_type
.map(|m| {
let s = m.get(1)?.as_str().to_owned().parse().ok()?;
let c = m.get(2)?.as_str().to_owned().parse().ok()?;
return Some((c, s));
})
.flatten()
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?;
let eng = fsd
.1
.map(|eng| eng.to_hashmap())
.unwrap_or_else(HashMap::new);
let mut fsd_info = get_fsd_info(fsd_type.0, fsd_type.1)?;
let fsd_type = (
"_EDCBA"
.chars()
.nth(fsd_type.0)
.ok_or(format!("Invalid FSD found: {}", &fsd.0))?,
fsd_type.1 as u8,
);
fsd_info.extend(eng);
let max_fuel = fsd_info
.get("MaxFuel")
.ok_or(format!("Unknwon MaxFuelPerJump for FSD: {}", &fsd.0))?;
let opt_mass = fsd_info
.get("FSDOptimalMass")
.ok_or(format!("Unknwon FSDOptimalMass for FSD: {}", &fsd.0))?;
return Ship::new(
self.ship_name,
self.ship_ident,
self.ship,
self.unladen_mass,
self.fuel_capacity.main,
self.fuel_capacity.main,
fsd_type,
*max_fuel,
*opt_mass,
booster,
);
}
}
impl Event {
pub fn get_loadout(self) -> Option<Loadout> {
if let Event {
event: EventData::Loadout(loadout),
} = self
{
return Some(loadout);
}
None
}
}

271
rust/src/ship.rs Normal file
View File

@ -0,0 +1,271 @@
use crate::common::get_fsd_booster_info;
use crate::journal::*;
use pyo3::conversion::ToPyObject;
use pyo3::prelude::*;
use pyo3::types::PyDict;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FSD {
pub rating_val: f32,
pub class_val: f32,
pub opt_mass: f32,
pub max_fuel: f32,
pub boost: f32,
pub guardian_booster: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ship {
pub name: String,
pub ident: String,
pub ship_type: String,
pub base_mass: f32,
pub fuel_mass: f32,
pub fuel_capacity: f32,
pub fsd: FSD,
}
impl Ship {
pub fn new(
name: String,
ident: String,
ship_type: String,
base_mass: f32,
fuel_mass: f32,
fuel_capacity: f32,
fsd_type: (char, u8),
max_fuel: f32,
opt_mass: f32,
guardian_booster: usize,
) -> Result<Self, String> {
let rating_val: f32 = match fsd_type.0 {
'A' => 12.0,
'B' => 10.0,
'C' => 8.0,
'D' => 10.0,
'E' => 11.0,
err => {
return Err(format!("Invalid rating: {}", err));
}
};
if fsd_type.1 < 2 || fsd_type.1 > 8 {
return Err(format!("Invalid class: {}", fsd_type.1));
};
if guardian_booster!=0 {
return Err("Guardian booster not yet implemented!".to_owned())
}
let ret = Self {
name,
ident,
ship_type,
fuel_capacity,
fuel_mass,
base_mass,
fsd: FSD {
rating_val,
class_val: 2.0 + (0.15 * ((fsd_type.1 - 2) as f32)),
opt_mass,
max_fuel,
boost: 1.0,
guardian_booster: get_fsd_booster_info(guardian_booster)?,
},
};
Ok(ret)
}
pub fn new_from_json(data: &str) -> Result<Self, String> {
match serde_json::from_str::<Event>(&data) {
Ok(Event {
event: EventData::Unknown,
}) => {
return Err(format!("Invalid Loadout event: {}", data));
}
Ok(ev) => {
if let Some(loadout) = ev.get_loadout() {
return loadout.try_into_ship();
} else {
return Err(format!("Invalid Loadout event: {}", data));
}
}
Err(msg) => {
return Err(format!("{}", msg));
}
};
}
pub fn new_from_journal() -> Result<HashMap<String, Self>, String> {
let mut ret = HashMap::new();
let re = Regex::new(r"^Journal\.\d{12}\.\d{2}\.log$").unwrap();
let mut journals: Vec<PathBuf> = Vec::new();
let mut userprofile = PathBuf::from(std::env::var("Userprofile").unwrap());
userprofile.push("Saved Games");
userprofile.push("Frontier Developments");
userprofile.push("Elite Dangerous");
if let Ok(iter) = userprofile.read_dir() {
for entry in iter {
if let Ok(entry) = entry {
if re.is_match(&entry.file_name().to_string_lossy()) {
journals.push(entry.path());
};
}
}
}
journals.sort();
for journal in &journals {
let mut fh = BufReader::new(File::open(journal).unwrap());
let mut line = String::new();
while let Ok(n) = fh.read_line(&mut line) {
if n == 0 {
break;
}
match serde_json::from_str::<Event>(&line) {
Ok(Event {
event: EventData::Unknown,
}) => {}
Ok(ev) => {
if let Some(loadout) = ev.get_loadout() {
let mut ship = loadout.try_into_ship()?;
if ship.name == "" {
ship.name = "<NO NAME>".to_owned();
}
let key = format!(
"[{}] {} ({})",
ship.ident,
ship.name,
ship.ship_type.to_ascii_lowercase()
);
ret.insert(key, ship);
}
}
Err(_) => {}
};
line.clear();
}
}
if ret.is_empty() {
return Err("No ships loaded!".to_owned());
}
return Ok(ret);
}
pub fn can_jump(&self, d: f32) -> bool {
self.fuel_cost(d) <= self.fsd.max_fuel.min(self.fuel_mass)
}
pub fn boost(&mut self, boost: f32) {
self.fsd.boost = boost;
}
pub fn refuel(&mut self) {
self.fuel_mass = self.fuel_capacity;
}
pub fn make_jump(&mut self, d: f32) -> Option<f32> {
let cost = self.fuel_cost(d);
if cost > self.fsd.max_fuel.min(self.fuel_mass) {
return None;
}
self.fuel_mass -= cost;
self.fsd.boost = 1.0;
Some(cost)
}
fn jump_range(&self, fuel: f32, booster: bool) -> f32 {
let mass = self.base_mass + fuel;
let mut fuel = self.fsd.max_fuel.min(fuel);
if booster {
fuel *= self.boost_fuel_mult();
}
let opt_mass = self.fsd.opt_mass * self.fsd.boost;
return opt_mass * ((1000.0 * fuel) / self.fsd.rating_val).powf(self.fsd.class_val.recip())
/ mass;
}
pub fn max_range(&self) -> f32 {
return self.jump_range(self.fsd.max_fuel, true);
}
pub fn range(&self) -> f32 {
return self.jump_range(self.fuel_mass, true);
}
fn boost_fuel_mult(&self) -> f32 {
if self.fsd.guardian_booster == 0.0 {
return 1.0;
}
let base_range = self.jump_range(self.fuel_mass, false); // current range without booster
return ((base_range + self.fsd.guardian_booster) / base_range).powf(self.fsd.class_val);
}
pub fn fuel_cost(&self, d: f32) -> f32 {
if d == 0.0 {
return 0.0;
}
let mass = self.base_mass + self.fuel_mass;
let opt_mass = self.fsd.opt_mass * self.fsd.boost;
let base_cost = (d * mass) / opt_mass;
return (self.fsd.rating_val * 0.001 * base_cost.powf(self.fsd.class_val))
/ self.boost_fuel_mult();
}
}
/*
#[derive(Debug,Clone, Serialize, Deserialize, ToPyObject)]
pub struct FSD {
pub rating_val: f32,
pub class_val: f32,
pub opt_mass: f32,
pub max_fuel: f32,
pub boost: f32,
pub guardian_booster: f32,
}
#[derive(Debug,Clone, Serialize, Deserialize, ToPyObject)]
pub struct Ship {
pub name: String,
pub ident: String,
pub ship_type: String,
pub base_mass: f32,
pub fuel_mass: f32,
pub fuel_capacity: f32,
pub fsd: FSD,
}
*/
impl FSD {
pub fn to_object(&self, py: Python) -> PyResult<PyObject> {
let elem = PyDict::new(py);
elem.set_item("rating_val", self.rating_val)?;
elem.set_item("class_val", self.class_val)?;
elem.set_item("opt_mass", self.opt_mass)?;
elem.set_item("max_fuel", self.max_fuel)?;
elem.set_item("boost", self.boost)?;
elem.set_item("guardian_booster", self.guardian_booster)?;
Ok(elem.to_object(py))
}
}
impl Ship {
pub fn to_object(&self, py: Python) -> PyResult<PyObject> {
let elem = PyDict::new(py);
elem.set_item("name", self.name.clone())?;
elem.set_item("ident", self.ident.clone())?;
elem.set_item("ship_type", self.ship_type.clone())?;
elem.set_item("base_mass", self.base_mass)?;
elem.set_item("fuel_mass", self.fuel_mass)?;
elem.set_item("fuel_capacity", self.fuel_capacity)?;
elem.set_item("fsd", self.fsd.to_object(py)?)?;
Ok(elem.to_object(py))
}
}