From 1bf5c5e83de6f8fa013e87b535e48167283d2845 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 10:14:16 +0200 Subject: [PATCH 1/8] fix: use home directory for user data --- apps/server/src/config/printer.config.ts | 4 +- apps/server/src/config/server.config.ts | 4 +- apps/server/src/config/winston.service.ts | 4 +- apps/server/src/main.ts | 36 ++++++++++++-- apps/server/src/terrain.ts | 49 +++++++++++++++++++ .../src/terrain/processing/maphandler.ts | 9 +++- apps/server/src/utilities/file.service.ts | 22 ++++----- apps/server/src/utilities/pathUtil.ts | 4 +- apps/server/src/utilities/printer.service.ts | 4 +- apps/server/src/utilities/systray.service.ts | 6 +-- 10 files changed, 111 insertions(+), 31 deletions(-) create mode 100644 apps/server/src/terrain.ts diff --git a/apps/server/src/config/printer.config.ts b/apps/server/src/config/printer.config.ts index bbbe0cea..45e606c2 100644 --- a/apps/server/src/config/printer.config.ts +++ b/apps/server/src/config/printer.config.ts @@ -1,12 +1,12 @@ import { registerAs } from '@nestjs/config'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { readFileSync } from 'fs'; import { join } from 'path'; const CONFIG_FILENAME = 'resources/properties.json'; export default registerAs('printer', () => { - const configPath = join(getExecutablePath(), CONFIG_FILENAME); + const configPath = join(getSimbridgeDir(), CONFIG_FILENAME); const properties = JSON.parse(readFileSync(configPath, 'utf8')); return { diff --git a/apps/server/src/config/server.config.ts b/apps/server/src/config/server.config.ts index d1296bff..c4eebf5f 100644 --- a/apps/server/src/config/server.config.ts +++ b/apps/server/src/config/server.config.ts @@ -1,12 +1,12 @@ import { registerAs } from '@nestjs/config'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { readFileSync } from 'fs'; import { join } from 'path'; const CONFIG_FILENAME = 'resources/properties.json'; export default registerAs('server', () => { - const configPath = join(getExecutablePath(), CONFIG_FILENAME); + const configPath = join(getSimbridgeDir(), CONFIG_FILENAME); const properties = JSON.parse(readFileSync(configPath, 'utf8')); return { diff --git a/apps/server/src/config/winston.service.ts b/apps/server/src/config/winston.service.ts index 603c8e72..7f26b36c 100644 --- a/apps/server/src/config/winston.service.ts +++ b/apps/server/src/config/winston.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { WinstonModuleOptions, WinstonModuleOptionsFactory, @@ -23,7 +23,7 @@ const consoleTransport = new winston.transports.Console({ const fileTransport = new winston.transports.DailyRotateFile({ frequency: '24h', filename: 'fbw-simbridge-%DATE%.log', - dirname: `${join(getExecutablePath(), 'resources/logs')}`, + dirname: `${join(getSimbridgeDir(), 'resources/logs')}`, datePattern: 'YYYY-MM-DD-HH', zippedArchive: true, maxSize: '20m', diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index e71a9cc8..7aa22316 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -9,10 +9,11 @@ import { NestFactory } from '@nestjs/core'; import { platform } from 'os'; import { hideConsole } from 'node-hide-console-window'; import * as path from 'path'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { ShutDownService } from './utilities/shutdown.service'; import { AppModule } from './app.module'; import { NetworkService } from './utilities/network.service'; +import { existsSync, writeFileSync } from 'fs'; declare const module: any; @@ -40,9 +41,6 @@ async function bootstrap() { // Validation app.useGlobalPipes(new ValidationPipe({ whitelist: true })); - // Folder creation - generateResourceFolders(); - // Swagger const swaggerConfig = new DocumentBuilder() .setTitle('FlyByWire SimBridge') @@ -70,12 +68,40 @@ async function bootstrap() { } } +generateResourceFolders(); +generateDefaultProperties(); + bootstrap(); function generateResourceFolders() { dirs.forEach((dir) => { access(dir, (error) => { - if (error) mkdirSync(path.join(getExecutablePath(), dir), { recursive: true }); + if (error) mkdirSync(path.join(getSimbridgeDir(), dir), { recursive: true }); }); }); } + +function generateDefaultProperties() { + + const propertiesFilePath = path.join(getSimbridgeDir(), '/resources', '/properties.json'); + + const defaultProperties = { + server: { + port: 8380, + hidden: true, + closeWithMSFS: false + }, + printer: { + enabled: false, + printerName: null, + fontSize: 19, + paperSize: "A4", + margin: 30 + } + }; + + + if (!existsSync(propertiesFilePath)) { + writeFileSync(propertiesFilePath, JSON.stringify(defaultProperties)); + } +} diff --git a/apps/server/src/terrain.ts b/apps/server/src/terrain.ts new file mode 100644 index 00000000..eb2120b1 --- /dev/null +++ b/apps/server/src/terrain.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs'; + +import axios from 'axios'; + +import * as path from 'path'; +import * as os from 'os'; + + +const SIMBRIDGE_FOLDER = path.join(os.homedir() + '/flybywire-externaltools-simbridge'); +const TERRAIN_MAP_FOLDER = path.join(SIMBRIDGE_FOLDER, '/terrain'); +const TERRAIN_MAP_PATH = path.join(TERRAIN_MAP_FOLDER, '/terrain.map'); + + +const TERRAIN_MAP_CDN = 'https://cdn.flybywiresim.com/addons/simbridge/terrain-db-binaries/terrain.map'; + +const execute = async () => { + try { + // Create the folders if they don't exist + if (!fs.existsSync(TERRAIN_MAP_FOLDER)) fs.mkdirSync(TERRAIN_MAP_FOLDER); + + // Make sure to unlink the old terrain map so we can update it if needed + // if (fs.existsSync(TERRAIN_MAP_PATH)) fs.unlinkSync(TERRAIN_MAP_PATH); + + if (!fs.existsSync(TERRAIN_MAP_PATH)) { + // Terrain map is not cached, download it + console.log('Downloading and caching terrain map'); + + const terrainResponse = await axios.get(TERRAIN_MAP_CDN, { responseType: 'stream' }); + + return new Promise((resolve, reject) => { + const writer = fs.createWriteStream(TERRAIN_MAP_PATH); + terrainResponse.data.pipe(writer); + + /* writer.on('error', err => { + writer.close(); + reject(err); + }); */ + + writer.on('close', resolve); + }); + + + } + } catch (error) { + console.error(error); + } +}; + +export default execute; diff --git a/apps/server/src/terrain/processing/maphandler.ts b/apps/server/src/terrain/processing/maphandler.ts index 70e373db..a814a311 100644 --- a/apps/server/src/terrain/processing/maphandler.ts +++ b/apps/server/src/terrain/processing/maphandler.ts @@ -1,7 +1,7 @@ import { GPU, IKernelRunShortcut, Texture } from 'gpu.js'; import { readFile } from 'fs/promises'; import { join } from 'path'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { AircraftStatus, ElevationProfile, NavigationDisplay, PositionData, TerrainRenderingMode } from '../types'; import { TerrainMap } from '../fileformat/terrainmap'; import { Worldmap } from '../mapdata/worldmap'; @@ -22,6 +22,8 @@ import { bearingWgs84, normalizeHeading, projectWgs84, wgs84toPixelCoordinate } import { ElevationProfileConstants, LocalElevationMapConstants } from './gpu/interfaces'; import { uploadTextureData } from './gpu/upload'; import { Logger } from './logging/logger'; +import { existsSync } from 'fs'; +import execute from '../../terrain'; // defines the maximum dimension length of the world map const GpuMaxPixelSize = 16384; @@ -146,7 +148,10 @@ export class MapHandler { private async readTerrainMap(): Promise { try { - const buffer = await readFile(join(getExecutablePath(), './terrain/terrain.map')); + if(!existsSync(join(getSimbridgeDir(), './terrain/terrain.map'))){ + await execute(); + } + const buffer = await readFile(join(getSimbridgeDir(), './terrain/terrain.map')); this.logging.info(`Read MB of terrainmap: ${(Buffer.byteLength(buffer) / (1024 * 1024)).toFixed(2)}`); return new TerrainMap(buffer); } catch (err) { diff --git a/apps/server/src/utilities/file.service.ts b/apps/server/src/utilities/file.service.ts index 69d4704c..36c62f64 100644 --- a/apps/server/src/utilities/file.service.ts +++ b/apps/server/src/utilities/file.service.ts @@ -4,14 +4,14 @@ import { readFileSync, lstatSync } from 'fs'; import * as xml2js from 'xml2js'; import { getDocument, PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf'; import { join } from 'path'; -import { getExecutablePath } from './pathUtil'; +import { getSimbridgeDir } from './pathUtil'; import { pdfToPng } from './pdfConversion'; // eslint-disable-next-line @typescript-eslint/no-var-requires const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js'); pdfjsLib.GlobalWorkerOptions.workerSrc = join( - getExecutablePath(), + getSimbridgeDir(), 'node_modules', 'pdfjs-dist', 'build', @@ -29,7 +29,7 @@ export class FileService { async getFileCount(directory: string): Promise { try { this.logger.debug(`Retrieving number of files in folder: ${directory}`); - const dir = join(getExecutablePath(), directory); + const dir = join(getSimbridgeDir(), directory); this.checkFilePathSafety(dir); const retrievedDir = await readdir(dir, { withFileTypes: true }); const fileNames = retrievedDir.filter((dir) => dir.isFile()).map((dir) => dir.name); @@ -45,7 +45,7 @@ export class FileService { try { this.logger.debug(`Reading all files in directory: ${directory}`); - const dir = join(getExecutablePath(), directory); + const dir = join(getSimbridgeDir(), directory); this.checkFilePathSafety(dir); const fileNames = (await readdir(dir, { withFileTypes: true })) .filter((dir) => dir.isFile()) @@ -66,7 +66,7 @@ export class FileService { async getFilenames(directory: string): Promise { try { this.logger.debug(`Reading all files in directory: ${directory}`); - const dir = join(getExecutablePath(), directory); + const dir = join(getSimbridgeDir(), directory); this.checkFilePathSafety(dir); return (await readdir(dir, { withFileTypes: true })).filter((dir) => dir.isFile()).map((dir) => dir.name); } catch (err) { @@ -79,7 +79,7 @@ export class FileService { async getFoldernames(directory: string): Promise { try { this.logger.debug(`Reading all Dirs in directory: ${directory}`); - const dir = join(getExecutablePath(), directory); + const dir = join(getSimbridgeDir(), directory); this.checkFilePathSafety(dir); return (await readdir(dir, { withFileTypes: true })).filter((dir) => dir.isDirectory()).map((dir) => dir.name); } catch (err) { @@ -93,7 +93,7 @@ export class FileService { try { this.logger.debug(`Retrieving file: ${fileName} in folder: ${directory}`); - const path = join(getExecutablePath(), directory, fileName); + const path = join(getSimbridgeDir(), directory, fileName); this.checkFilePathSafety(path); if (!lstatSync(path).isFile()) { @@ -122,7 +122,7 @@ export class FileService { throw new HttpException('Unexpected null byte encountered', HttpStatus.UNPROCESSABLE_ENTITY); } - if (filePath.indexOf(getExecutablePath()) !== 0) { + if (filePath.indexOf(getSimbridgeDir()) !== 0) { throw new HttpException('Unacceptable file path', HttpStatus.UNPROCESSABLE_ENTITY); } } @@ -134,14 +134,14 @@ export class FileService { scale: number = 4, ): Promise { // Some PDFs need external cmaps. - const CMAP_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`; + const CMAP_URL = `${join(getSimbridgeDir(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`; const CMAP_PACKED = true; // Where the standard fonts are located. - const STANDARD_FONT_DATA_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`; + const STANDARD_FONT_DATA_URL = `${join(getSimbridgeDir(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`; try { - const conversionFilePath = join(getExecutablePath(), directory, fileName); + const conversionFilePath = join(getSimbridgeDir(), directory, fileName); this.checkFilePathSafety(conversionFilePath); diff --git a/apps/server/src/utilities/pathUtil.ts b/apps/server/src/utilities/pathUtil.ts index 684a8727..9018269c 100644 --- a/apps/server/src/utilities/pathUtil.ts +++ b/apps/server/src/utilities/pathUtil.ts @@ -1,4 +1,4 @@ +import { homedir } from 'os' import * as path from 'path'; -// @ts-expect-error I don't know why -export const getExecutablePath = () => (process.pkg ? path.dirname(process.argv[0]) : process.cwd()); +export const getSimbridgeDir = () => (path.join(homedir() + '/flybywire-externaltools-simbridge')); diff --git a/apps/server/src/utilities/printer.service.ts b/apps/server/src/utilities/printer.service.ts index 46ec8071..480c53f9 100644 --- a/apps/server/src/utilities/printer.service.ts +++ b/apps/server/src/utilities/printer.service.ts @@ -5,7 +5,7 @@ import { tmpdir, platform } from 'os'; import * as print from 'pdf-to-printer'; import * as PDFDocument from 'pdfkit'; import { createWriteStream, readFileSync } from 'fs'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import printerConfig from '../config/printer.config'; @Injectable() @@ -66,7 +66,7 @@ export class PrinterService { doc.end(); print.print(pdfPath, { printer: foundPrinter.name, - sumatraPdfPath: `${getExecutablePath()}/resources/SumatraPDF.exe`, + sumatraPdfPath: `${getSimbridgeDir()}/resources/SumatraPDF.exe`, }); } } catch (error) { diff --git a/apps/server/src/utilities/systray.service.ts b/apps/server/src/utilities/systray.service.ts index 283f8af6..2066fcd9 100644 --- a/apps/server/src/utilities/systray.service.ts +++ b/apps/server/src/utilities/systray.service.ts @@ -4,7 +4,7 @@ import { hideConsole, showConsole } from 'node-hide-console-window'; import open = require('open'); import SysTray, { MenuItem } from 'systray2'; import { join } from 'path'; -import { getExecutablePath } from 'apps/server/src/utilities/pathUtil'; +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { NetworkService } from './network.service'; import serverConfig from '../config/server.config'; import { ShutDownService } from './shutdown.service'; @@ -29,7 +29,7 @@ export class SysTrayService implements OnApplicationShutdown { tooltip: 'Flybywire SimBridge', items: [this.remoteDisplayItem, this.resourcesFolderItem, this.consoleVisibleItem, this.exitItem], }, - copyDir: getExecutablePath(), + copyDir: getSimbridgeDir(), }); this.sysTray.onClick((action) => { @@ -67,7 +67,7 @@ export class SysTrayService implements OnApplicationShutdown { tooltip: 'Open resource folder in your file explorer', enabled: true, click: () => { - open.openApp('explorer', { arguments: [`${getExecutablePath()}\\resources`] }); + open.openApp('explorer', { arguments: [`${getSimbridgeDir()}\\resources`] }); }, }; From 6cb41a20e9107bb47c246e63e701d825b24eddc0 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 10:29:58 +0200 Subject: [PATCH 2/8] use local terrain map from isntaller --- apps/server/src/terrain/processing/maphandler.ts | 13 +++++++------ apps/server/src/utilities/pathUtil.ts | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/server/src/terrain/processing/maphandler.ts b/apps/server/src/terrain/processing/maphandler.ts index a814a311..3d812a42 100644 --- a/apps/server/src/terrain/processing/maphandler.ts +++ b/apps/server/src/terrain/processing/maphandler.ts @@ -1,7 +1,7 @@ import { GPU, IKernelRunShortcut, Texture } from 'gpu.js'; import { readFile } from 'fs/promises'; import { join } from 'path'; -import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; +import { getExecutablePath, getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { AircraftStatus, ElevationProfile, NavigationDisplay, PositionData, TerrainRenderingMode } from '../types'; import { TerrainMap } from '../fileformat/terrainmap'; import { Worldmap } from '../mapdata/worldmap'; @@ -22,7 +22,7 @@ import { bearingWgs84, normalizeHeading, projectWgs84, wgs84toPixelCoordinate } import { ElevationProfileConstants, LocalElevationMapConstants } from './gpu/interfaces'; import { uploadTextureData } from './gpu/upload'; import { Logger } from './logging/logger'; -import { existsSync } from 'fs'; +import { copyFileSync, existsSync } from 'fs'; import execute from '../../terrain'; // defines the maximum dimension length of the world map @@ -148,10 +148,11 @@ export class MapHandler { private async readTerrainMap(): Promise { try { - if(!existsSync(join(getSimbridgeDir(), './terrain/terrain.map'))){ - await execute(); - } - const buffer = await readFile(join(getSimbridgeDir(), './terrain/terrain.map')); +/* if(!existsSync(join(getSimbridgeDir(), '/terrain/terrain.map'))){ + copyFileSync(join(getExecutablePath(), '/terrain/terrain.map'), join(getSimbridgeDir(), '/terrain/terrain.map')); + } */ + // TODO shall we move this as well? Currently the installer downloads the terrain.map file + const buffer = await readFile(join(getExecutablePath(), './terrain/terrain.map')); this.logging.info(`Read MB of terrainmap: ${(Buffer.byteLength(buffer) / (1024 * 1024)).toFixed(2)}`); return new TerrainMap(buffer); } catch (err) { diff --git a/apps/server/src/utilities/pathUtil.ts b/apps/server/src/utilities/pathUtil.ts index 9018269c..b07d7b3a 100644 --- a/apps/server/src/utilities/pathUtil.ts +++ b/apps/server/src/utilities/pathUtil.ts @@ -2,3 +2,6 @@ import { homedir } from 'os' import * as path from 'path'; export const getSimbridgeDir = () => (path.join(homedir() + '/flybywire-externaltools-simbridge')); + +//@ts-expect-error pkg only defined when running as exe +export const getExecutablePath = () => (process.pkg ? path.dirname(process.argv[0]) : process.cwd()); From c3cbf760a7bc9f517592f1b6bca80b41322bcf1c Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 10:30:08 +0200 Subject: [PATCH 3/8] fix --- apps/server/src/terrain/processing/maphandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/terrain/processing/maphandler.ts b/apps/server/src/terrain/processing/maphandler.ts index 3d812a42..b276a90c 100644 --- a/apps/server/src/terrain/processing/maphandler.ts +++ b/apps/server/src/terrain/processing/maphandler.ts @@ -152,7 +152,7 @@ export class MapHandler { copyFileSync(join(getExecutablePath(), '/terrain/terrain.map'), join(getSimbridgeDir(), '/terrain/terrain.map')); } */ // TODO shall we move this as well? Currently the installer downloads the terrain.map file - const buffer = await readFile(join(getExecutablePath(), './terrain/terrain.map')); + const buffer = await readFile(join(getExecutablePath(), '/terrain/terrain.map')); this.logging.info(`Read MB of terrainmap: ${(Buffer.byteLength(buffer) / (1024 * 1024)).toFixed(2)}`); return new TerrainMap(buffer); } catch (err) { From 825e9b6be2364873bdc28bca0076483b152ec584 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 10:30:18 +0200 Subject: [PATCH 4/8] fix pdf conversion --- apps/server/src/utilities/file.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/utilities/file.service.ts b/apps/server/src/utilities/file.service.ts index 36c62f64..f4a79e79 100644 --- a/apps/server/src/utilities/file.service.ts +++ b/apps/server/src/utilities/file.service.ts @@ -4,14 +4,14 @@ import { readFileSync, lstatSync } from 'fs'; import * as xml2js from 'xml2js'; import { getDocument, PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf'; import { join } from 'path'; -import { getSimbridgeDir } from './pathUtil'; +import { getExecutablePath, getSimbridgeDir } from './pathUtil'; import { pdfToPng } from './pdfConversion'; // eslint-disable-next-line @typescript-eslint/no-var-requires const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js'); pdfjsLib.GlobalWorkerOptions.workerSrc = join( - getSimbridgeDir(), + getExecutablePath(), 'node_modules', 'pdfjs-dist', 'build', From 88081de93fd997a993327ecedab4e4a24202ded4 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 10:39:31 +0200 Subject: [PATCH 5/8] fix printer --- apps/server/src/utilities/printer.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/utilities/printer.service.ts b/apps/server/src/utilities/printer.service.ts index 480c53f9..6b8e00da 100644 --- a/apps/server/src/utilities/printer.service.ts +++ b/apps/server/src/utilities/printer.service.ts @@ -5,7 +5,7 @@ import { tmpdir, platform } from 'os'; import * as print from 'pdf-to-printer'; import * as PDFDocument from 'pdfkit'; import { createWriteStream, readFileSync } from 'fs'; -import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; +import { getExecutablePath, getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import printerConfig from '../config/printer.config'; @Injectable() @@ -66,7 +66,7 @@ export class PrinterService { doc.end(); print.print(pdfPath, { printer: foundPrinter.name, - sumatraPdfPath: `${getSimbridgeDir()}/resources/SumatraPDF.exe`, + sumatraPdfPath: `${getExecutablePath()}/resources/SumatraPDF.exe`, }); } } catch (error) { From 969952b1bd2f96742f4d11e5fb04f6c0b5217154 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 20:28:23 +0200 Subject: [PATCH 6/8] use documents folder --- apps/server/src/main.ts | 15 +++++----- apps/server/src/terrain.ts | 31 ++++++++------------ apps/server/src/utilities/file.service.ts | 4 +-- apps/server/src/utilities/pathUtil.ts | 4 +-- apps/server/src/utilities/systray.service.ts | 4 +-- package-lock.json | 21 +++++++++++++ package.json | 1 + 7 files changed, 48 insertions(+), 32 deletions(-) diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 7aa22316..7186b92a 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -75,32 +75,31 @@ bootstrap(); function generateResourceFolders() { dirs.forEach((dir) => { - access(dir, (error) => { - if (error) mkdirSync(path.join(getSimbridgeDir(), dir), { recursive: true }); + const actualDir = path.join(getSimbridgeDir(), dir); + access(actualDir, (error) => { + if (error) mkdirSync(actualDir, { recursive: true }); }); }); } function generateDefaultProperties() { - const propertiesFilePath = path.join(getSimbridgeDir(), '/resources', '/properties.json'); const defaultProperties = { server: { port: 8380, hidden: true, - closeWithMSFS: false + closeWithMSFS: false, }, printer: { enabled: false, printerName: null, fontSize: 19, - paperSize: "A4", - margin: 30 - } + paperSize: 'A4', + margin: 30, + }, }; - if (!existsSync(propertiesFilePath)) { writeFileSync(propertiesFilePath, JSON.stringify(defaultProperties)); } diff --git a/apps/server/src/terrain.ts b/apps/server/src/terrain.ts index eb2120b1..071bfd29 100644 --- a/apps/server/src/terrain.ts +++ b/apps/server/src/terrain.ts @@ -1,15 +1,12 @@ import * as fs from 'fs'; - -import axios from 'axios'; - import * as path from 'path'; -import * as os from 'os'; - +import axios from 'axios'; -const SIMBRIDGE_FOLDER = path.join(os.homedir() + '/flybywire-externaltools-simbridge'); -const TERRAIN_MAP_FOLDER = path.join(SIMBRIDGE_FOLDER, '/terrain'); -const TERRAIN_MAP_PATH = path.join(TERRAIN_MAP_FOLDER, '/terrain.map'); +import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; +const SIMBRIDGE_FOLDER = getSimbridgeDir(); +const TERRAIN_MAP_FOLDER = path.join(SIMBRIDGE_FOLDER, 'terrain'); +const TERRAIN_MAP_PATH = path.join(TERRAIN_MAP_FOLDER, 'terrain.map'); const TERRAIN_MAP_CDN = 'https://cdn.flybywiresim.com/addons/simbridge/terrain-db-binaries/terrain.map'; @@ -18,28 +15,26 @@ const execute = async () => { // Create the folders if they don't exist if (!fs.existsSync(TERRAIN_MAP_FOLDER)) fs.mkdirSync(TERRAIN_MAP_FOLDER); - // Make sure to unlink the old terrain map so we can update it if needed - // if (fs.existsSync(TERRAIN_MAP_PATH)) fs.unlinkSync(TERRAIN_MAP_PATH); - if (!fs.existsSync(TERRAIN_MAP_PATH)) { - // Terrain map is not cached, download it - console.log('Downloading and caching terrain map'); + console.log('Downloading terrain map'); const terrainResponse = await axios.get(TERRAIN_MAP_CDN, { responseType: 'stream' }); return new Promise((resolve, reject) => { const writer = fs.createWriteStream(TERRAIN_MAP_PATH); terrainResponse.data.pipe(writer); + let error: Error = null; - /* writer.on('error', err => { + writer.on('error', (err) => { + error = err; writer.close(); reject(err); - }); */ + }); - writer.on('close', resolve); + writer.on('close', () => { + if (!error) resolve(0); + }); }); - - } } catch (error) { console.error(error); diff --git a/apps/server/src/utilities/file.service.ts b/apps/server/src/utilities/file.service.ts index f4a79e79..200b245f 100644 --- a/apps/server/src/utilities/file.service.ts +++ b/apps/server/src/utilities/file.service.ts @@ -134,11 +134,11 @@ export class FileService { scale: number = 4, ): Promise { // Some PDFs need external cmaps. - const CMAP_URL = `${join(getSimbridgeDir(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`; + const CMAP_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`; const CMAP_PACKED = true; // Where the standard fonts are located. - const STANDARD_FONT_DATA_URL = `${join(getSimbridgeDir(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`; + const STANDARD_FONT_DATA_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`; try { const conversionFilePath = join(getSimbridgeDir(), directory, fileName); diff --git a/apps/server/src/utilities/pathUtil.ts b/apps/server/src/utilities/pathUtil.ts index b07d7b3a..1b7cf5dd 100644 --- a/apps/server/src/utilities/pathUtil.ts +++ b/apps/server/src/utilities/pathUtil.ts @@ -1,7 +1,7 @@ -import { homedir } from 'os' +import getPath from 'platform-folders'; import * as path from 'path'; -export const getSimbridgeDir = () => (path.join(homedir() + '/flybywire-externaltools-simbridge')); +export const getSimbridgeDir = () => path.join(getPath('documents'), 'FlyByWireSim', 'Simbridge'); //@ts-expect-error pkg only defined when running as exe export const getExecutablePath = () => (process.pkg ? path.dirname(process.argv[0]) : process.cwd()); diff --git a/apps/server/src/utilities/systray.service.ts b/apps/server/src/utilities/systray.service.ts index 2066fcd9..2fd17026 100644 --- a/apps/server/src/utilities/systray.service.ts +++ b/apps/server/src/utilities/systray.service.ts @@ -4,7 +4,7 @@ import { hideConsole, showConsole } from 'node-hide-console-window'; import open = require('open'); import SysTray, { MenuItem } from 'systray2'; import { join } from 'path'; -import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; +import { getExecutablePath, getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; import { NetworkService } from './network.service'; import serverConfig from '../config/server.config'; import { ShutDownService } from './shutdown.service'; @@ -29,7 +29,7 @@ export class SysTrayService implements OnApplicationShutdown { tooltip: 'Flybywire SimBridge', items: [this.remoteDisplayItem, this.resourcesFolderItem, this.consoleVisibleItem, this.exitItem], }, - copyDir: getSimbridgeDir(), + copyDir: getExecutablePath(), }); this.sysTray.onClick((action) => { diff --git a/package-lock.json b/package-lock.json index e4b42e62..8ade5feb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "pdf-to-printer": "5.3.0", "pdfjs-dist": "^2.13.216", "pdfkit": "^0.13.0", + "platform-folders": "^0.6.0", "react": "^17.0.0", "react-dom": "^17.0.0", "react-use-websocket": "^2.9.1", @@ -15132,6 +15133,18 @@ "node": ">=8" } }, + "node_modules/platform-folders": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/platform-folders/-/platform-folders-0.6.0.tgz", + "integrity": "sha512-CzaJGN1cbL6kwkge7zM7VbXr/L0Qjkg2Z4IzcprziHHSw8y73oORfB9pMLwjAIhb8JcWUemmWvTXAXvc5hnVlg==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0" + }, + "engines": { + "node": "^8.16.0 || >=10" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -31031,6 +31044,14 @@ } } }, + "platform-folders": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/platform-folders/-/platform-folders-0.6.0.tgz", + "integrity": "sha512-CzaJGN1cbL6kwkge7zM7VbXr/L0Qjkg2Z4IzcprziHHSw8y73oORfB9pMLwjAIhb8JcWUemmWvTXAXvc5hnVlg==", + "requires": { + "bindings": "^1.5.0" + } + }, "pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", diff --git a/package.json b/package.json index ffca673d..83743483 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "pdf-to-printer": "5.3.0", "pdfjs-dist": "^2.13.216", "pdfkit": "^0.13.0", + "platform-folders": "^0.6.0", "react": "^17.0.0", "react-dom": "^17.0.0", "react-use-websocket": "^2.9.1", From f3a9a3a355cfca4fa8546d55ab07d1c81f692a17 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 21:07:19 +0200 Subject: [PATCH 7/8] fix: copy bindings to exe --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 83743483..c2be6e07 100644 --- a/package.json +++ b/package.json @@ -204,6 +204,7 @@ "node_modules/bit-twiddle/**/*.*", "node_modules/@flybywiresim/msfs-nodejs/**/*.*", "node_modules/es-get-iterator/**/*.*", + "node_modules/platform-folders/build/**/*.*", "/node_modules/systray2/traybin/*.exe", "dist/mcdu/**/*", "dist/assets/**/*", From 0e422ec5a4db04d5a679b3d34d954194a6232025 Mon Sep 17 00:00:00 2001 From: Saschl Date: Mon, 30 Sep 2024 22:12:48 +0200 Subject: [PATCH 8/8] cleanup --- apps/server/src/terrain.ts | 44 ------------------- .../src/terrain/processing/maphandler.ts | 5 --- 2 files changed, 49 deletions(-) delete mode 100644 apps/server/src/terrain.ts diff --git a/apps/server/src/terrain.ts b/apps/server/src/terrain.ts deleted file mode 100644 index 071bfd29..00000000 --- a/apps/server/src/terrain.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import axios from 'axios'; - -import { getSimbridgeDir } from 'apps/server/src/utilities/pathUtil'; - -const SIMBRIDGE_FOLDER = getSimbridgeDir(); -const TERRAIN_MAP_FOLDER = path.join(SIMBRIDGE_FOLDER, 'terrain'); -const TERRAIN_MAP_PATH = path.join(TERRAIN_MAP_FOLDER, 'terrain.map'); - -const TERRAIN_MAP_CDN = 'https://cdn.flybywiresim.com/addons/simbridge/terrain-db-binaries/terrain.map'; - -const execute = async () => { - try { - // Create the folders if they don't exist - if (!fs.existsSync(TERRAIN_MAP_FOLDER)) fs.mkdirSync(TERRAIN_MAP_FOLDER); - - if (!fs.existsSync(TERRAIN_MAP_PATH)) { - console.log('Downloading terrain map'); - - const terrainResponse = await axios.get(TERRAIN_MAP_CDN, { responseType: 'stream' }); - - return new Promise((resolve, reject) => { - const writer = fs.createWriteStream(TERRAIN_MAP_PATH); - terrainResponse.data.pipe(writer); - let error: Error = null; - - writer.on('error', (err) => { - error = err; - writer.close(); - reject(err); - }); - - writer.on('close', () => { - if (!error) resolve(0); - }); - }); - } - } catch (error) { - console.error(error); - } -}; - -export default execute; diff --git a/apps/server/src/terrain/processing/maphandler.ts b/apps/server/src/terrain/processing/maphandler.ts index b276a90c..900c810f 100644 --- a/apps/server/src/terrain/processing/maphandler.ts +++ b/apps/server/src/terrain/processing/maphandler.ts @@ -22,8 +22,6 @@ import { bearingWgs84, normalizeHeading, projectWgs84, wgs84toPixelCoordinate } import { ElevationProfileConstants, LocalElevationMapConstants } from './gpu/interfaces'; import { uploadTextureData } from './gpu/upload'; import { Logger } from './logging/logger'; -import { copyFileSync, existsSync } from 'fs'; -import execute from '../../terrain'; // defines the maximum dimension length of the world map const GpuMaxPixelSize = 16384; @@ -148,9 +146,6 @@ export class MapHandler { private async readTerrainMap(): Promise { try { -/* if(!existsSync(join(getSimbridgeDir(), '/terrain/terrain.map'))){ - copyFileSync(join(getExecutablePath(), '/terrain/terrain.map'), join(getSimbridgeDir(), '/terrain/terrain.map')); - } */ // TODO shall we move this as well? Currently the installer downloads the terrain.map file const buffer = await readFile(join(getExecutablePath(), '/terrain/terrain.map')); this.logging.info(`Read MB of terrainmap: ${(Buffer.byteLength(buffer) / (1024 * 1024)).toFixed(2)}`);