Update
This commit is contained in:
parent
717a389a22
commit
35a0c40d14
8 changed files with 3359 additions and 0 deletions
19
rust/cargo_check.py
Normal file
19
rust/cargo_check.py
Normal 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
2203
rust/ch.txt
Normal file
File diff suppressed because it is too large
Load diff
19
rust/rustup_check.py
Normal file
19
rust/rustup_check.py
Normal 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
289
rust/src/data.json
Normal 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
193
rust/src/data_guardian.json
Normal 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
196
rust/src/edsm.rs
Normal 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
169
rust/src/journal.rs
Normal 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
271
rust/src/ship.rs
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue