Del Journal & the last of Journal types.
This commit is contained in:
parent
86af719b97
commit
9f8e890bdc
2 changed files with 0 additions and 530 deletions
188
src/@types/journalLines.d.ts
vendored
188
src/@types/journalLines.d.ts
vendored
|
@ -1,188 +0,0 @@
|
|||
interface journalEntry<eventType = string> {
|
||||
timestamp: string,
|
||||
event: eventType,
|
||||
}
|
||||
|
||||
interface bodyParent {
|
||||
[index: string]: number,
|
||||
}
|
||||
|
||||
interface bodyAtmosphere {
|
||||
Name: string,
|
||||
Percent: number,
|
||||
}
|
||||
|
||||
interface bodyMaterials {
|
||||
Name: string,
|
||||
Percent: number,
|
||||
}
|
||||
|
||||
interface bodyRings {
|
||||
Name: string,
|
||||
RingClass: string,
|
||||
MassMT: number,
|
||||
InnerRad: number,
|
||||
OuterRad: number,
|
||||
}
|
||||
|
||||
export interface starScan<scanType> extends journalEntry<'Scan'> {
|
||||
ScanType: scanType,
|
||||
BodyName: string,
|
||||
BodyID: number,
|
||||
Parents: bodyParent[],
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
DistanceFromArrivalLS: number,
|
||||
StarType: string,
|
||||
Subclass: number,
|
||||
StellarMass: number,
|
||||
Radius: number,
|
||||
AbsoluteMagnitude: number,
|
||||
Age_MY: number,
|
||||
SurfaceTemperature: number,
|
||||
Luminosity: string,
|
||||
SemiMajorAxis: number,
|
||||
Eccentricity: number,
|
||||
OrbitalInclination: number,
|
||||
Periapsis: number,
|
||||
OrbitalPeriod: number,
|
||||
AscendingNode: number,
|
||||
MeanAnomaly: number,
|
||||
RotationPeriod: number,
|
||||
AxialTilt: number,
|
||||
WasDiscovered: boolean,
|
||||
WasMapped: boolean,
|
||||
}
|
||||
|
||||
export interface asteroidScan<scanType> extends journalEntry<'Scan'> {
|
||||
ScanType: scanType,
|
||||
BodyName: string,
|
||||
BodyID: number,
|
||||
Parents: bodyParent[],
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
DistanceFromArrivalLS: number,
|
||||
WasDiscovered: boolean,
|
||||
WasMapped: boolean,
|
||||
}
|
||||
|
||||
export interface planetScan<scanType> extends journalEntry<'Scan'> {
|
||||
ScanType: scanType,
|
||||
BodyName: string,
|
||||
BodyID: number,
|
||||
Parents: bodyParent[],
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
DistanceFromArrivalLS: number,
|
||||
TidalLock: boolean,
|
||||
TerraformState: string,
|
||||
PlanetClass: string,
|
||||
Atmosphere: string,
|
||||
AtmosphereType: string,
|
||||
AtmosphereComposition?: bodyAtmosphere[]
|
||||
Volcanism: string,
|
||||
MassEM: number,
|
||||
Radius: number,
|
||||
SurfaceGravity: number,
|
||||
SurfaceTemperature: number,
|
||||
SurfacePressure: number,
|
||||
Landable: boolean,
|
||||
Materials: bodyMaterials[],
|
||||
Composition: {
|
||||
Ice: number,
|
||||
Rock: number,
|
||||
Metal: number,
|
||||
},
|
||||
SemiMajorAxis: number,
|
||||
Eccentricity: number,
|
||||
OrbitalInclination: number,
|
||||
Periapsis: number,
|
||||
OrbitalPeriod: number,
|
||||
AscendingNode: number,
|
||||
MeanAnomaly: number,
|
||||
RotationPeriod: number,
|
||||
AxialTilt: number,
|
||||
Rings?: bodyRings[],
|
||||
ReserveLevel?: string,
|
||||
WasDiscovered: boolean,
|
||||
WasMapped: boolean,
|
||||
}
|
||||
|
||||
export type autoScan = starScan<'AutoScan'>|asteroidScan<'AutoScan'>|planetScan<'AutoScan'>
|
||||
export type detailedScan = starScan<'Detailed'>&asteroidScan<'Detailed'>&planetScan<'Detailed'>
|
||||
|
||||
interface faction {
|
||||
Name: string,
|
||||
FactionState: string,
|
||||
Government: string,
|
||||
Influence: number,
|
||||
Allegiance: string,
|
||||
Happiness: string,
|
||||
Happiness_Localised: string,
|
||||
MyReputation: number,
|
||||
RecoveringStates?: { State: string, Trend: number }[],
|
||||
ActiveStates?: { State: string }[],
|
||||
}
|
||||
|
||||
export interface completeFsdJump extends journalEntry<'FSDJump'> {
|
||||
Taxi: boolean,
|
||||
Multicrew: boolean,
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
StarPos: [number, number, number],
|
||||
SystemAllegiance: string,
|
||||
SystemEconomy: string,
|
||||
SystemEconomy_Localised: string,
|
||||
SystemSecondEconomy: string,
|
||||
SystemSecondEconomy_Localised: string,
|
||||
SystemGovernment: string,
|
||||
SystemGovernment_Localised: string,
|
||||
SystemSecurity: string,
|
||||
SystemSecurity_Localised: string,
|
||||
Population: number,
|
||||
Body: string,
|
||||
BodyID: number,
|
||||
BodyType: string,
|
||||
JumpDist: number,
|
||||
FuelUsed: number,
|
||||
FuelLevel: number,
|
||||
Factions?: faction[],
|
||||
SystemFaction?: { Name: string },
|
||||
}
|
||||
|
||||
export interface location extends journalEntry<'Location'> {
|
||||
Docked: boolean,
|
||||
Taxi: boolean,
|
||||
Multicrew: boolean,
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
StarPos: [number, number, number],
|
||||
SystemAllegiance: string,
|
||||
SystemEconomy: string,
|
||||
SystemEconomy_Localised: string,
|
||||
SystemSecondEconomy: string,
|
||||
SystemSecondEconomy_Localised: string,
|
||||
SystemGovernment: string,
|
||||
SystemGovernment_Localised: string,
|
||||
SystemSecurity: string,
|
||||
SystemSecurity_Localised: string,
|
||||
Population: number,
|
||||
Body: string,
|
||||
BodyID: number,
|
||||
BodyType: string,
|
||||
Factions: faction[],
|
||||
SystemFaction: { Name: string },
|
||||
}
|
||||
|
||||
export interface navRouteSystem {
|
||||
StarSystem: string,
|
||||
SystemAddress: number,
|
||||
StarPos: [number, number, number],
|
||||
StarClass: string,
|
||||
}
|
||||
|
||||
export interface navRoute {
|
||||
timestamp: string,
|
||||
event: 'NavRoute',
|
||||
Route: navRouteSystem[],
|
||||
}
|
|
@ -1,342 +0,0 @@
|
|||
import type { Tail as TailType } from 'tail';
|
||||
import type {
|
||||
autoScan,
|
||||
completeFsdJump,
|
||||
detailedScan,
|
||||
journalEntry,
|
||||
navRoute,
|
||||
planetScan,
|
||||
} from '../@types/journalLines';
|
||||
|
||||
const EventEmitter = require('node:events');
|
||||
import * as _ from 'lodash-es';
|
||||
|
||||
const path = require('node:path');
|
||||
const { readFile } = require('node:fs/promises');
|
||||
const reverseLineReader = require('reverse-line-reader');
|
||||
const Tail = require('tail').Tail;
|
||||
|
||||
import { System } from './System';
|
||||
import { Log } from './Log';
|
||||
import { Body } from './Body';
|
||||
|
||||
|
||||
export class Journal extends EventEmitter {
|
||||
readonly #path: string;
|
||||
location: System;
|
||||
navRoute: System[];
|
||||
#tail?: TailType;
|
||||
|
||||
constructor(journalPath: string) {
|
||||
super();
|
||||
|
||||
this.#path = journalPath;
|
||||
this.location = new System();
|
||||
this.navRoute = [];
|
||||
|
||||
// Start ReverseLineReader chain here.
|
||||
Log.write(`Journal initialized. Attempting to find current location.`);
|
||||
this.#getLastFsdJump();
|
||||
// TODO: return the reverseLineReader and then call .then after ^?
|
||||
// -> IF no FSD Jump: this.#getLastLocation()
|
||||
// --> this.#getScannedBodies()
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- #getLastFsdJump ---- */
|
||||
|
||||
// Get current location on setup, so if app is restarted, user can pick up where they left off
|
||||
// Rather than waiting til they jump to the next system to use the program again.
|
||||
#getLastFsdJump(): void {
|
||||
reverseLineReader.eachLine(this.#path, (raw: string) => {
|
||||
if (raw) { //skip blank line at end of file
|
||||
const line: journalEntry = JSON.parse(raw);
|
||||
|
||||
if (line.event === 'FSDJump') {
|
||||
this.location = new System((line as completeFsdJump));
|
||||
Log.write(`Current location set to ${this.location.name}.`);
|
||||
this.emit('ENTERED_NEW_SYSTEM');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
if (this.location.name === 'Unknown') {
|
||||
Log.write('Unable to find last hyperspace jump. Searching for last known location.');
|
||||
this.#getLastLocation();
|
||||
} else {
|
||||
Log.write('Attempting to find scanned bodies in current system.');
|
||||
this.#getScannedBodies();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- #getLastLocation ---- */
|
||||
|
||||
// If no FSDJump found, search for a location entry as this is populated when journal is created.
|
||||
#getLastLocation(): void {
|
||||
reverseLineReader.eachLine(this.#path, (raw: string, last: boolean) => {
|
||||
// Extra check just to be sure.
|
||||
if (this.location.name !== 'Unknown') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
const line: journalEntry = JSON.parse(raw);
|
||||
|
||||
if (line.event === 'Location') {
|
||||
this.location = new System((line as completeFsdJump));
|
||||
Log.write(`Current location set to ${this.location.name}.`);
|
||||
this.emit('ENTERED_NEW_SYSTEM');
|
||||
return false;
|
||||
|
||||
} else if (last) {
|
||||
Log.write('WARNING: Unable to find last known location.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
if (this.location.name !== 'Unknown') {
|
||||
Log.write('Attempting to find scanned bodies in current system.');
|
||||
this.#getScannedBodies();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------- #getScannedBodies ---- */
|
||||
|
||||
// Look for all scanned bodies before last FSDJump, for same reasons as getting location.
|
||||
#getScannedBodies(): void {
|
||||
let dssLine: detailedScan|null = null;
|
||||
|
||||
reverseLineReader.eachLine(this.#path, (raw: string) => {
|
||||
if (raw) {
|
||||
const line: journalEntry = JSON.parse(raw);
|
||||
|
||||
// Check if previous line was ScanType = Detailed, and handle that.
|
||||
if (dssLine) {
|
||||
if (line.event === 'SAAScanComplete') {
|
||||
// This was a DSS, so add to list with DSS flag set to true.
|
||||
this.location.bodies.push(new Body(dssLine, true));
|
||||
} else {
|
||||
// Else, check that the body hasn't already been added (by a DSS scan line).
|
||||
const dupChecker = { 'BodyName': dssLine.BodyName, 'BodyID': dssLine.BodyID };
|
||||
const r = _.find(this.location.bodies, dupChecker);
|
||||
|
||||
if (r === undefined) {
|
||||
// Body was not already logged, so add to list.
|
||||
this.location.bodies.push(new Body(dssLine));
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, clear the variable.
|
||||
dssLine = null;
|
||||
}
|
||||
|
||||
// Now move on to evaluating the current line.
|
||||
if (line.event === 'Scan' && 'ScanType' in line) {
|
||||
// If ScanType = Detailed and body is not a star, save the line, so we can check
|
||||
// the one immediately above for event = SAAScanComplete, which indicates this
|
||||
// was a DSS.
|
||||
if (line.ScanType === 'Detailed' && !('StarType' in line)) {
|
||||
dssLine = (line as detailedScan);
|
||||
|
||||
} else if ('StarType' in line) { // Save stars to bodies list.
|
||||
this.location.bodies.push(new Body((line as autoScan|detailedScan)));
|
||||
|
||||
} else if (line.ScanType === 'AutoScan') { // Save auto/discovery scan bodies.
|
||||
// Check if planet, and then do the duplicate check (otherwise it's an
|
||||
// asteroid, as we've already accounted for stars).
|
||||
if ('PlanetClass' in line) {
|
||||
const dupChecker = {
|
||||
'BodyName': (line as planetScan<'AutoScan'>).BodyName,
|
||||
'BodyID': (line as planetScan<'AutoScan'>).BodyID,
|
||||
};
|
||||
const r = _.find(this.location.bodies, dupChecker);
|
||||
|
||||
if (r === undefined) {
|
||||
this.location.bodies.push(new Body((line as autoScan)));
|
||||
}
|
||||
|
||||
} else { // Asteroids.
|
||||
this.location.bodies.push(new Body((line as autoScan)));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (line.event === 'FSDJump') {
|
||||
// Stop evaluating once we reach the beginning of current system entries.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
if (this.location.bodies.length > 0) {
|
||||
Log.write('Scanned bodies found.');
|
||||
this.emit('BUILD_BODY_LIST');
|
||||
} else {
|
||||
Log.write('No scanned bodies found in current system.');
|
||||
}
|
||||
|
||||
Log.write('Checking for nav route.');
|
||||
this.#getNavRoute();
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ #getNavRoute ---- */
|
||||
|
||||
async #getNavRoute(): Promise<void> {
|
||||
this.navRoute = []; // Clear previous route, to catch overwritten routes.
|
||||
let routeFile: string|null = null;
|
||||
|
||||
try {
|
||||
const filePath: string = path.dirname(this.#path) + '/NavRoute.json';
|
||||
routeFile = await readFile(filePath, { encoding: 'utf8' });
|
||||
} catch (err) {
|
||||
Log.write(`Error reading nav route file: ${err.message}.`);
|
||||
}
|
||||
|
||||
if (routeFile) {
|
||||
const route: navRoute = JSON.parse(routeFile);
|
||||
|
||||
// system -> skip
|
||||
// CURRENT -> push = true; skip
|
||||
// system -> push
|
||||
let push: boolean = false;
|
||||
route.Route.forEach((system) => {
|
||||
if (!push && system.SystemAddress === this.location.SystemAddress) {
|
||||
push = true;
|
||||
}
|
||||
|
||||
if (push && system.SystemAddress !== this.location.SystemAddress) {
|
||||
this.navRoute.push(new System(system));
|
||||
}
|
||||
});
|
||||
|
||||
if (this.navRoute.length > 0) {
|
||||
Log.write('Nav route set.');
|
||||
} else {
|
||||
Log.write('No nav route found.');
|
||||
}
|
||||
|
||||
// Call this no matter what, so that cleared routes are properly dealt with.
|
||||
this.emit('SET_NAV_ROUTE');
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------- watch() ---- */
|
||||
|
||||
// Watch the journal for changes.
|
||||
watch(): void {
|
||||
this.#tail = new Tail(this.#path, { useWatchFile: true });
|
||||
|
||||
Log.write(`Watching ${path.basename(this.#path)}...`);
|
||||
|
||||
this.#tail?.on('line', (data) => data ? this.#parseLine(data) : undefined);
|
||||
this.#tail?.on('error', (err) => Log.write(`Tail error in Journal.watch(): ${err}`));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ #parseLine() ---- */
|
||||
|
||||
// Parse and handle journal lines.
|
||||
#parseLine(raw: string) {
|
||||
const line: journalEntry = JSON.parse(raw);
|
||||
let dssFlag: boolean = false;
|
||||
|
||||
switch (line.event) {
|
||||
// Hyperspace jump started (3.. 2.. 1..)
|
||||
case 'StartJump': {
|
||||
if ('JumpType' in line && line.JumpType === 'Hyperspace') {
|
||||
this.emit('ENTERING_WITCH_SPACE');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// CMDR jumped to new system, so update current location.
|
||||
case 'FSDJump': {
|
||||
this.#handleFsdJump((line as completeFsdJump));
|
||||
break;
|
||||
}
|
||||
|
||||
// CMDR completed DSS scan, so set flag for when next line processes, and we want to
|
||||
// figure out what kind of scan occurred.
|
||||
case 'SAAScanComplete': {
|
||||
dssFlag = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// A scan occurred, so let's hand that info off to the appropriate function and then
|
||||
// reset the DSS flag.
|
||||
case 'Scan': {
|
||||
this.#handleScanLine((line as autoScan|detailedScan), dssFlag);
|
||||
dssFlag = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// CMDR set a new nav route.
|
||||
case 'NavRoute': {
|
||||
this.#getNavRoute();
|
||||
break;
|
||||
}
|
||||
|
||||
// CMDR cleared the nav route.
|
||||
case 'NavRouteClear': {
|
||||
this.navRoute = [];
|
||||
Log.write('Nav route cleared.');
|
||||
this.emit('SET_NAV_ROUTE');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- #handleFsdJump ---- */
|
||||
|
||||
#handleFsdJump(line: completeFsdJump): void {
|
||||
this.location = new System(line);
|
||||
Log.write(`FSD Jump detected, current location updated to ${this.location.name}.`);
|
||||
|
||||
if (this.navRoute.length > 0) {
|
||||
_.remove(this.navRoute, (system) => {
|
||||
return system.SystemAddress === this.location.SystemAddress;
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('ENTERED_NEW_SYSTEM');
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- #handleScanLine ---- */
|
||||
|
||||
#handleScanLine(line: autoScan|detailedScan, DSS: boolean = false): void {
|
||||
const dupChecker = { 'BodyName': line.BodyName, 'BodyID': line.BodyID };
|
||||
let body: Body|null = null;
|
||||
|
||||
// If it's a DSS scan, then we should have already added the body to the list. But we'll
|
||||
// check to make sure.
|
||||
if (DSS) {
|
||||
// Using findIndex() rather than find() so we can edit the body if found.
|
||||
const bodyIndex: number = _.findIndex(this.location.bodies, dupChecker);
|
||||
|
||||
if (bodyIndex > -1) { // Body was found in list, so simply toggle the DSS flag.
|
||||
this.location.bodies[bodyIndex].DSSDone = true;
|
||||
|
||||
} else { // Body was missed on initial journal scan, so add it to the list.
|
||||
body = new Body(line, true);
|
||||
this.location.bodies.push(body);
|
||||
}
|
||||
|
||||
} else { // Otherwise it's an FSS or auto scan, and needs to be added to the list.
|
||||
// Probably overkill, but do a duplicate check just in case.
|
||||
const r = _.find(this.location.bodies, dupChecker);
|
||||
|
||||
if (r === undefined) {
|
||||
body = new Body(line);
|
||||
this.location.bodies.push(body);
|
||||
}
|
||||
}
|
||||
|
||||
Log.write(`Scan detected. Body: ${line.BodyName}.`);
|
||||
this.emit('BODY_SCANNED', body, DSS);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------- shutdown ---- */
|
||||
|
||||
shutdown(): void {
|
||||
this.#tail?.unwatch();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue