Server/project/src/loaders/BundleLoader.ts
MadByte a52c81e270 Changed mod path for loading bundles from the server to be relative (!389)
**Before merging: Please test on a real Windows installation if possible!**

I tested this on Linux as well as inside a Windows VM hosted on Linux, but wasn't able to test it on a real Windows installation.

This commit fixes an issue with loading bundles from a native Linux server:

When compiling the server natively for Linux & installing some mods, everything works except for mods trying to load bundles from the server. Reason is a malformed path to the bundle:

```ts
2024-07-30 23:48:16.968 +02:00|0.14.9.1.30626|Error|Default|
EXCEPTION: System.IO.DirectoryNotFoundException: Could not find a part of the path "C:\home\USER\Games\escape-from-tarkov\drive_c\SPTarkov\user\mods\Bloody-Bullet-Wounds\bundles\assets\systems\effects\particlesystems\effects.bundle".
```

`process.cwd()` returns the linux-agnostic path of course, but for some reason it also returns `C:`.
Changing the line to `modpath.slice(0, -1).replace(/\\/g, "/");` seems to work and the bundles seem to get loaded without issues (did a quick test raid), even without passing the absolute path to the  mod.

I tried to check why that is, and I think node is able to [get the cwd and resolve the relative path](4d1d88118b/src/path.cc (L101)) by itself..

Reviewed-on: https://dev.sp-tarkov.com/SPT/Server/pulls/389
Co-authored-by: MadByte <madbyte@noreply.dev.sp-tarkov.com>
Co-committed-by: MadByte <madbyte@noreply.dev.sp-tarkov.com>
(cherry picked from commit 6f010acfca8ec57105a3cd3bc959ed61be206e13)
2024-07-31 20:14:12 +01:00

84 lines
2.6 KiB
TypeScript

import path from "node:path";
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
import { BundleHashCacheService } from "@spt/services/cache/BundleHashCacheService";
import { JsonUtil } from "@spt/utils/JsonUtil";
import { VFS } from "@spt/utils/VFS";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
export class BundleInfo {
modpath: string;
filename: string;
crc: number;
dependencies: string[];
constructor(modpath: string, bundle: BundleManifestEntry, bundleHash: number) {
this.modpath = modpath;
this.filename = bundle.key;
this.crc = bundleHash;
this.dependencies = bundle.dependencyKeys || [];
}
}
@injectable()
export class BundleLoader {
protected bundles: Record<string, BundleInfo> = {};
constructor(
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("VFS") protected vfs: VFS,
@inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("BundleHashCacheService") protected bundleHashCacheService: BundleHashCacheService,
@inject("PrimaryCloner") protected cloner: ICloner,
) {}
/**
* Handle singleplayer/bundles
*/
public getBundles(): BundleInfo[] {
const result: BundleInfo[] = [];
for (const bundle in this.bundles) {
result.push(this.getBundle(bundle));
}
return result;
}
public getBundle(key: string): BundleInfo {
return this.cloner.clone(this.bundles[key]);
}
public addBundles(modpath: string): void {
const bundleManifestArr = this.jsonUtil.deserialize<BundleManifest>(
this.vfs.readFile(`${modpath}bundles.json`),
).manifest;
for (const bundleManifest of bundleManifestArr) {
const relativeModPath = modpath.slice(0, -1).replace(/\\/g, "/");
const bundleLocalPath = `${modpath}bundles/${bundleManifest.key}`.replace(/\\/g, "/");
if (!this.bundleHashCacheService.calculateAndMatchHash(bundleLocalPath)) {
this.bundleHashCacheService.calculateAndStoreHash(bundleLocalPath);
}
const bundleHash = this.bundleHashCacheService.getStoredValue(bundleLocalPath);
this.addBundle(bundleManifest.key, new BundleInfo(relativeModPath, bundleManifest, bundleHash));
}
}
public addBundle(key: string, b: BundleInfo): void {
this.bundles[key] = b;
}
}
export interface BundleManifest {
manifest: BundleManifestEntry[];
}
export interface BundleManifestEntry {
key: string;
dependencyKeys: string[];
}