diff --git a/src/@types/journalLines.d.ts b/src/@types/journalLines.d.ts deleted file mode 100755 index a585234..0000000 --- a/src/@types/journalLines.d.ts +++ /dev/null @@ -1,188 +0,0 @@ -interface journalEntry { - 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 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 extends journalEntry<'Scan'> { - ScanType: scanType, - BodyName: string, - BodyID: number, - Parents: bodyParent[], - StarSystem: string, - SystemAddress: number, - DistanceFromArrivalLS: number, - WasDiscovered: boolean, - WasMapped: boolean, -} - -export interface planetScan 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[], -} diff --git a/src/models/Journal.ts b/src/models/Journal.ts deleted file mode 100755 index 5c95587..0000000 --- a/src/models/Journal.ts +++ /dev/null @@ -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 { - 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(); - } -}