Formatting for utils.

This commit is contained in:
Refringe 2023-11-13 11:14:58 -05:00
parent 8576929404
commit 4479f68388
No known key found for this signature in database
GPG Key ID: 64E03E5F892C6F9E
21 changed files with 354 additions and 284 deletions

View File

@ -19,9 +19,9 @@ export class App
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("EncodingUtil") protected encodingUtil: EncodingUtil, @inject("EncodingUtil") protected encodingUtil: EncodingUtil,
@injectAll("OnLoad") protected onLoadComponents: OnLoad[], @injectAll("OnLoad") protected onLoadComponents: OnLoad[],
@injectAll("OnUpdate") protected onUpdateComponents: OnUpdate[] @injectAll("OnUpdate") protected onUpdateComponents: OnUpdate[],
) )
{ } {}
public async load(): Promise<void> public async load(): Promise<void>
{ {
@ -73,7 +73,9 @@ export class App
if (success === void 0 && !(secondsSinceLastRun % warnTime)) if (success === void 0 && !(secondsSinceLastRun % warnTime))
{ {
this.logger.debug(this.localisationService.getText("route_onupdate_no_response", updateable.getRoute())); this.logger.debug(
this.localisationService.getText("route_onupdate_no_response", updateable.getRoute()),
);
} }
} }
} }
@ -82,13 +84,13 @@ export class App
protected logUpdateException(err: any, updateable: OnUpdate): void protected logUpdateException(err: any, updateable: OnUpdate): void
{ {
this.logger.error(this.localisationService.getText("scheduled_event_failed_to_run", updateable.getRoute())); this.logger.error(this.localisationService.getText("scheduled_event_failed_to_run", updateable.getRoute()));
if (err.message) if (err.message)
{ {
this.logger.error(err.message); this.logger.error(err.message);
} }
if (err.stack) if (err.stack)
{ {
this.logger.error(err.stack); this.logger.error(err.stack);
} }
} }
} }

View File

@ -12,21 +12,21 @@ export class AsyncQueue implements IAsyncQueue
// Wait for the right command to execute // Wait for the right command to execute
// This ensures that the commands execute in the right order, thus no data corruption // This ensures that the commands execute in the right order, thus no data corruption
public async waitFor(command: ICommand): Promise<any> public async waitFor(command: ICommand): Promise<any>
{ {
// Add to the queue // Add to the queue
this.commandsQueue.push(command); this.commandsQueue.push(command);
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (this.commandsQueue[0].uuid !== command.uuid) while (this.commandsQueue[0].uuid !== command.uuid)
{ {
await new Promise<void>(resolve => await new Promise<void>((resolve) =>
{ {
setTimeout(resolve, 100); setTimeout(resolve, 100);
}); });
} }
// When the command is ready, execute it // When the command is ready, execute it
return this.commandsQueue.shift().cmd(); return this.commandsQueue.shift().cmd();
} }
} }

View File

@ -33,7 +33,7 @@ export class DatabaseImporter implements OnLoad
@inject("EncodingUtil") protected encodingUtil: EncodingUtil, @inject("EncodingUtil") protected encodingUtil: EncodingUtil,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("ImporterUtil") protected importerUtil: ImporterUtil, @inject("ImporterUtil") protected importerUtil: ImporterUtil,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
@ -45,25 +45,28 @@ export class DatabaseImporter implements OnLoad
*/ */
public getSptDataPath(): string public getSptDataPath(): string
{ {
return (globalThis.G_RELEASE_CONFIGURATION) return (globalThis.G_RELEASE_CONFIGURATION) ?
? "Aki_Data/Server/" "Aki_Data/Server/" :
: "./assets/"; "./assets/";
} }
public async onLoad(): Promise<void> public async onLoad(): Promise<void>
{ {
this.filepath = this.getSptDataPath(); this.filepath = this.getSptDataPath();
if (globalThis.G_RELEASE_CONFIGURATION) if (globalThis.G_RELEASE_CONFIGURATION)
{ {
try try
{ {
// Reading the dynamic SHA1 file // Reading the dynamic SHA1 file
const file = "checks.dat"; const file = "checks.dat";
const fileWithPath = `${this.filepath}${file}`; const fileWithPath = `${this.filepath}${file}`;
if (this.vfs.exists(fileWithPath)) if (this.vfs.exists(fileWithPath))
{ {
this.hashedFile = this.jsonUtil.deserialize(this.encodingUtil.fromBase64(this.vfs.readFile(fileWithPath)), file); this.hashedFile = this.jsonUtil.deserialize(
this.encodingUtil.fromBase64(this.vfs.readFile(fileWithPath)),
file,
);
} }
else else
{ {
@ -88,7 +91,7 @@ export class DatabaseImporter implements OnLoad
"/files/Hideout/", "/files/Hideout/",
"/files/launcher/", "/files/launcher/",
"/files/quest/icon/", "/files/quest/icon/",
"/files/trader/avatar/" "/files/trader/avatar/",
]); ]);
} }
@ -99,42 +102,50 @@ export class DatabaseImporter implements OnLoad
protected async hydrateDatabase(filepath: string): Promise<void> protected async hydrateDatabase(filepath: string): Promise<void>
{ {
this.logger.info(this.localisationService.getText("importing_database")); this.logger.info(this.localisationService.getText("importing_database"));
const dataToImport = await this.importerUtil.loadAsync<IDatabaseTables>( const dataToImport = await this.importerUtil.loadAsync<IDatabaseTables>(
`${filepath}database/`, `${filepath}database/`,
this.filepath, this.filepath,
(fileWithPath: string, data: string) => this.onReadValidate(fileWithPath, data) (fileWithPath: string, data: string) => this.onReadValidate(fileWithPath, data),
); );
const validation = (this.valid === VaildationResult.FAILED || this.valid === VaildationResult.NOT_FOUND) ? "." : ""; const validation = (this.valid === VaildationResult.FAILED || this.valid === VaildationResult.NOT_FOUND) ?
"." :
"";
this.logger.info(`${this.localisationService.getText("importing_database_finish")}${validation}`); this.logger.info(`${this.localisationService.getText("importing_database_finish")}${validation}`);
this.databaseServer.setTables(dataToImport); this.databaseServer.setTables(dataToImport);
} }
protected onReadValidate(fileWithPath: string, data: string): void protected onReadValidate(fileWithPath: string, data: string): void
{ {
// Validate files // Validate files
if (globalThis.G_RELEASE_CONFIGURATION && this.hashedFile && !this.validateFile(fileWithPath, data)) if (globalThis.G_RELEASE_CONFIGURATION && this.hashedFile && !this.validateFile(fileWithPath, data))
{
this.valid = VaildationResult.FAILED; this.valid = VaildationResult.FAILED;
}
} }
public getRoute(): string public getRoute(): string
{ {
return "aki-database"; return "aki-database";
} }
protected validateFile(filePathAndName: string, fileData: any): boolean protected validateFile(filePathAndName: string, fileData: any): boolean
{ {
try try
{ {
const finalPath = filePathAndName.replace(this.filepath, "").replace(".json", ""); const finalPath = filePathAndName.replace(this.filepath, "").replace(".json", "");
let tempObject; let tempObject;
for (const prop of finalPath.split("/")) for (const prop of finalPath.split("/"))
{ {
if (!tempObject) if (!tempObject)
{
tempObject = this.hashedFile[prop]; tempObject = this.hashedFile[prop];
}
else else
{
tempObject = tempObject[prop]; tempObject = tempObject[prop];
}
} }
if (tempObject !== this.hashUtil.generateSha1ForData(fileData)) if (tempObject !== this.hashUtil.generateSha1ForData(fileData))
@ -196,9 +207,9 @@ export class DatabaseImporter implements OnLoad
} }
enum VaildationResult enum VaildationResult
{ {
SUCCESS = 0, SUCCESS = 0,
FAILED = 1, FAILED = 1,
NOT_FOUND = 2, NOT_FOUND = 2,
UNDEFINED = 3 UNDEFINED = 3,
} }

View File

@ -3,7 +3,6 @@ import { injectable } from "tsyringe";
@injectable() @injectable()
export class EncodingUtil export class EncodingUtil
{ {
public encode(value: string, encode: EncodeType): string public encode(value: string, encode: EncodeType): string
{ {
return Buffer.from(value).toString(encode); return Buffer.from(value).toString(encode);
@ -33,14 +32,13 @@ export class EncodingUtil
{ {
return this.encode(value, EncodeType.HEX); return this.encode(value, EncodeType.HEX);
} }
} }
export enum EncodeType export enum EncodeType
{ {
BASE64 = "base64", BASE64 = "base64",
HEX = "hex", HEX = "hex",
ASCII = "ascii", ASCII = "ascii",
BINARY = "binary", BINARY = "binary",
UTF8 = "utf8" UTF8 = "utf8",
} }

View File

@ -7,9 +7,9 @@ import { TimeUtil } from "@spt-aki/utils/TimeUtil";
export class HashUtil export class HashUtil
{ {
constructor( constructor(
@inject("TimeUtil") protected timeUtil: TimeUtil @inject("TimeUtil") protected timeUtil: TimeUtil,
) )
{ } {}
/** /**
* Create a 24 character id using the sha256 algorithm + current timestamp * Create a 24 character id using the sha256 algorithm + current timestamp

View File

@ -8,7 +8,7 @@ import { HttpServerHelper } from "@spt-aki/helpers/HttpServerHelper";
export class HttpFileUtil export class HttpFileUtil
{ {
constructor( constructor(
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper @inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
) )
{ {
} }
@ -16,13 +16,14 @@ export class HttpFileUtil
public sendFile(resp: ServerResponse, file: any): void public sendFile(resp: ServerResponse, file: any): void
{ {
const pathSlic = file.split("/"); const pathSlic = file.split("/");
const type = this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1)) || this.httpServerHelper.getMimeText("txt"); const type = this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1)) ||
this.httpServerHelper.getMimeText("txt");
const fileStream = fs.createReadStream(file); const fileStream = fs.createReadStream(file);
fileStream.on("open", function () fileStream.on("open", function()
{ {
resp.setHeader("Content-Type", type); resp.setHeader("Content-Type", type);
fileStream.pipe(resp); fileStream.pipe(resp);
}); });
} }
} }

View File

@ -10,12 +10,11 @@ import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
export class HttpResponseUtil export class HttpResponseUtil
{ {
constructor( constructor(
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("LocalisationService") protected localisationService: LocalisationService @inject("LocalisationService") protected localisationService: LocalisationService,
) )
{ } {}
protected clearString(s: string): any protected clearString(s: string): any
{ {
@ -29,8 +28,8 @@ export class HttpResponseUtil
/** /**
* Return passed in data as JSON string * Return passed in data as JSON string
* @param data * @param data
* @returns * @returns
*/ */
public noBody(data: any): any public noBody(data: any): any
{ {
@ -47,7 +46,7 @@ export class HttpResponseUtil
return this.jsonUtil.serialize({ return this.jsonUtil.serialize({
err: err, err: err,
errmsg: errmsg, errmsg: errmsg,
data: data data: data,
}); });
} }
@ -66,12 +65,16 @@ export class HttpResponseUtil
return this.getBody([]); return this.getBody([]);
} }
public appendErrorToOutput(output: IItemEventRouterResponse, message = this.localisationService.getText("http-unknown_error"), errorCode = BackendErrorCodes.NONE): IItemEventRouterResponse public appendErrorToOutput(
output: IItemEventRouterResponse,
message = this.localisationService.getText("http-unknown_error"),
errorCode = BackendErrorCodes.NONE,
): IItemEventRouterResponse
{ {
output.warnings = [{ output.warnings = [{
index: 0, index: 0,
errmsg: message, errmsg: message,
code: errorCode.toString() code: errorCode.toString(),
}]; }];
return output; return output;

View File

@ -11,7 +11,7 @@ export class ImporterUtil
{ {
constructor( constructor(
@inject("VFS") protected vfs: VFS, @inject("VFS") protected vfs: VFS,
@inject("JsonUtil") protected jsonUtil: JsonUtil @inject("JsonUtil") protected jsonUtil: JsonUtil,
) )
{} {}
@ -22,8 +22,10 @@ export class ImporterUtil
*/ */
public async loadRecursiveAsync<T>( public async loadRecursiveAsync<T>(
filepath: string, filepath: string,
onReadCallback: (fileWithPath: string, data: string) => void = () => {}, onReadCallback: (fileWithPath: string, data: string) => void = () =>
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {} {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
): Promise<T> ): Promise<T>
{ {
const result = {} as T; const result = {} as T;
@ -39,7 +41,7 @@ export class ImporterUtil
{ {
const filename = this.vfs.stripExtension(file); const filename = this.vfs.stripExtension(file);
const filePathAndName = `${filepath}${file}`; const filePathAndName = `${filepath}${file}`;
await this.vfs.readFileAsync(filePathAndName).then(fileData => await this.vfs.readFileAsync(filePathAndName).then((fileData) =>
{ {
onReadCallback(filePathAndName, fileData); onReadCallback(filePathAndName, fileData);
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName); const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
@ -57,7 +59,7 @@ export class ImporterUtil
// set all loadRecursive to be executed asynchronously // set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result); const resEntries = Object.entries(result);
const resResolved = await Promise.all(resEntries.map(ent => ent[1])); const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
for (let resIdx = 0; resIdx < resResolved.length; resIdx++) for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
{ {
resEntries[resIdx][1] = resResolved[resIdx]; resEntries[resIdx][1] = resResolved[resIdx];
@ -67,16 +69,17 @@ export class ImporterUtil
return Object.fromEntries(resEntries) as T; return Object.fromEntries(resEntries) as T;
} }
/** /**
* Load files into js objects recursively (synchronous) * Load files into js objects recursively (synchronous)
* @param filepath Path to folder with files * @param filepath Path to folder with files
* @returns * @returns
*/ */
public loadRecursive<T>( public loadRecursive<T>(
filepath: string, filepath: string,
onReadCallback: (fileWithPath: string, data: string) => void = () => {}, onReadCallback: (fileWithPath: string, data: string) => void = () =>
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {} {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
): T ): T
{ {
const result = {} as T; const result = {} as T;
@ -112,8 +115,10 @@ export class ImporterUtil
public async loadAsync<T>( public async loadAsync<T>(
filepath: string, filepath: string,
strippablePath = "", strippablePath = "",
onReadCallback: (fileWithPath: string, data: string) => void = () => {}, onReadCallback: (fileWithPath: string, data: string) => void = () =>
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {} {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
{},
): Promise<T> ): Promise<T>
{ {
const directoriesToRead = new Queue<string>(); const directoriesToRead = new Queue<string>();
@ -125,15 +130,15 @@ export class ImporterUtil
const files = this.vfs.getFiles(filepath); const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath); const directories = this.vfs.getDirs(filepath);
directoriesToRead.enqueueAll(directories.map(d => `${filepath}${d}`)); directoriesToRead.enqueueAll(directories.map((d) => `${filepath}${d}`));
filesToProcess.enqueueAll(files.map(f => new VisitNode(filepath, f))); filesToProcess.enqueueAll(files.map((f) => new VisitNode(filepath, f)));
while (!directoriesToRead.isEmpty()) while (!directoriesToRead.isEmpty())
{ {
const directory = directoriesToRead.dequeue(); const directory = directoriesToRead.dequeue();
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map(f => new VisitNode(`${directory}/`, f))); filesToProcess.enqueueAll(this.vfs.getFiles(directory).map((f) => new VisitNode(`${directory}/`, f)));
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map(d => `${directory}/${d}`)); directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map((d) => `${directory}/${d}`));
} }
while (!filesToProcess.isEmpty()) while (!filesToProcess.isEmpty())
@ -144,25 +149,28 @@ export class ImporterUtil
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`; const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;
promises.push( promises.push(
this.vfs.readFileAsync(filePathAndName) this.vfs.readFileAsync(filePathAndName)
.then(async fileData => { .then(async (fileData) =>
{
onReadCallback(filePathAndName, fileData); onReadCallback(filePathAndName, fileData);
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName); return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
}) })
.then(async fileDeserialized => { .then(async (fileDeserialized) =>
{
onObjectDeserialized(filePathAndName, fileDeserialized); onObjectDeserialized(filePathAndName, fileDeserialized);
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, ""); const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath); this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
}) }),
); );
} }
} }
await Promise.all(promises).catch(e => console.error(e)); await Promise.all(promises).catch((e) => console.error(e));
return result; return result;
} }
protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string):void { protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string): void
{
const strippedFinalPath = strippedFilePath.replace(strippablePath, ""); const strippedFinalPath = strippedFilePath.replace(strippablePath, "");
let temp = result; let temp = result;
const propertiesToVisit = strippedFinalPath.split("/"); const propertiesToVisit = strippedFinalPath.split("/");
@ -177,7 +185,9 @@ export class ImporterUtil
else else
{ {
if (!temp[property]) if (!temp[property])
{
temp[property] = {}; temp[property] = {};
}
temp = temp[property]; temp = temp[property];
} }
} }
@ -188,7 +198,7 @@ class VisitNode
{ {
constructor( constructor(
public filePath: string, public filePath: string,
public fileName: string public fileName: string,
){} )
{}
} }

View File

@ -18,9 +18,9 @@ export class JsonUtil
constructor( constructor(
@inject("VFS") protected vfs: VFS, @inject("VFS") protected vfs: VFS,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("WinstonLogger") protected logger: ILogger @inject("WinstonLogger") protected logger: ILogger,
) )
{ } {}
/** /**
* From object to string * From object to string
@ -47,23 +47,27 @@ export class JsonUtil
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
* @returns string * @returns string
*/ */
public serializeAdvanced(data: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string public serializeAdvanced(
data: any,
replacer?: (this: any, key: string, value: any) => any,
space?: string | number,
): string
{ {
return JSON.stringify(data, replacer, space); return JSON.stringify(data, replacer, space);
} }
/** /**
* From object to string * From object to string
* @param data object to turn into JSON * @param data object to turn into JSON
* @param filename Name of file being serialized * @param filename Name of file being serialized
* @param options Stringify options or a replacer. * @param options Stringify options or a replacer.
* @returns The string converted from the JavaScript value * @returns The string converted from the JavaScript value
*/ */
public serializeJsonC( public serializeJsonC(
data: any, data: any,
filename?: string | null, filename?: string | null,
options?: IStringifyOptions | Reviver): string options?: IStringifyOptions | Reviver,
): string
{ {
try try
{ {
@ -71,14 +75,17 @@ export class JsonUtil
} }
catch (error) catch (error)
{ {
this.logger.error(`unable to stringify jsonC file: ${filename} message: ${error.message}, stack: ${error.stack}`); this.logger.error(
`unable to stringify jsonC file: ${filename} message: ${error.message}, stack: ${error.stack}`,
);
} }
} }
public serializeJson5( public serializeJson5(
data: any, data: any,
filename?: string | null, filename?: string | null,
prettify = false): string prettify = false,
): string
{ {
try try
{ {
@ -93,7 +100,9 @@ export class JsonUtil
} }
catch (error) catch (error)
{ {
this.logger.error(`unable to stringify json5 file: ${filename} message: ${error.message}, stack: ${error.stack}`); this.logger.error(
`unable to stringify json5 file: ${filename} message: ${error.message}, stack: ${error.stack}`,
);
} }
} }
@ -111,7 +120,9 @@ export class JsonUtil
} }
catch (error) catch (error)
{ {
this.logger.error(`unable to parse json file: ${filename} message: ${error.message}, stack: ${error.stack}`); this.logger.error(
`unable to parse json file: ${filename} message: ${error.message}, stack: ${error.stack}`,
);
} }
} }
@ -130,7 +141,9 @@ export class JsonUtil
} }
catch (error) catch (error)
{ {
this.logger.error(`unable to parse jsonC file: ${filename} message: ${error.message}, stack: ${error.stack}`); this.logger.error(
`unable to parse jsonC file: ${filename} message: ${error.message}, stack: ${error.stack}`,
);
} }
} }
@ -142,13 +155,15 @@ export class JsonUtil
} }
catch (error) catch (error)
{ {
this.logger.error(`unable to parse json file: ${filename} message: ${error.message}, stack: ${error.stack}`); this.logger.error(
`unable to parse json file: ${filename} message: ${error.message}, stack: ${error.stack}`,
);
} }
} }
public async deserializeWithCacheCheckAsync<T>(jsonString: string, filePath: string): Promise<T> public async deserializeWithCacheCheckAsync<T>(jsonString: string, filePath: string): Promise<T>
{ {
return new Promise((resolve) => return new Promise((resolve) =>
{ {
resolve(this.deserializeWithCacheCheck<T>(jsonString, filePath)); resolve(this.deserializeWithCacheCheck<T>(jsonString, filePath));
}); });
@ -174,9 +189,9 @@ export class JsonUtil
{ {
try try
{ {
const { data, changed } = fixJson(jsonString); const {data, changed} = fixJson(jsonString);
if (changed) // data invalid, return it if (changed)
{ { // data invalid, return it
this.logger.error(`${filePath} - Detected faulty json, please fix your json file using VSCodium`); this.logger.error(`${filePath} - Detected faulty json, please fix your json file using VSCodium`);
} }
else else
@ -206,7 +221,6 @@ export class JsonUtil
return this.deserialize<T>(jsonString); return this.deserialize<T>(jsonString);
} }
/** /**
* Create file if nothing found * Create file if nothing found
* @param jsonCachePath path to cache * @param jsonCachePath path to cache
@ -228,7 +242,7 @@ export class JsonUtil
* Read contents of json cache and add to class field * Read contents of json cache and add to class field
* @param jsonCachePath Path to cache * @param jsonCachePath Path to cache
*/ */
protected hydrateJsonCache(jsonCachePath: string) : void protected hydrateJsonCache(jsonCachePath: string): void
{ {
// Get all file hashes // Get all file hashes
if (!this.fileHashes) if (!this.fileHashes)

View File

@ -5,9 +5,9 @@ export class MathUtil
{ {
/** /**
* Helper to create the sum of all array elements * Helper to create the sum of all array elements
* @param {array} values The array with numbers of which to calculate the sum * @param {array} values The array with numbers of which to calculate the sum
* @return {number} sum(values) * @return {number} sum(values)
*/ */
public arraySum(values: number[]): number public arraySum(values: number[]): number
{ {
// sum with initial value being 0 // sum with initial value being 0
@ -24,7 +24,7 @@ export class MathUtil
{ {
// curried function for cumulative sum: (cum, x) => cum += x // curried function for cumulative sum: (cum, x) => cum += x
// and 0 being the initial value for the map // and 0 being the initial value for the map
return values.map((cum => x => cum += x)(0)); return values.map(((cum) => (x) => cum += x)(0));
} }
/** /**
@ -34,7 +34,7 @@ export class MathUtil
*/ */
public arrayProd(values: number[], factor: number): number[] public arrayProd(values: number[], factor: number): number[]
{ {
return values.map(x => x * factor); return values.map((x) => x * factor);
} }
/** /**
@ -44,7 +44,7 @@ export class MathUtil
*/ */
public arrayAdd(values: number[], summand: number): number[] public arrayAdd(values: number[], summand: number): number[]
{ {
return values.map(x => x + summand); return values.map((x) => x + summand);
} }
/** /**
@ -72,14 +72,14 @@ export class MathUtil
} }
/** /**
* Linear interpolation * Linear interpolation
* e.g. used to do a continuous integration for quest rewards which are defined for specific support centers of pmcLevel * e.g. used to do a continuous integration for quest rewards which are defined for specific support centers of pmcLevel
* *
* @param {string} xp the point of x at which to interpolate * @param {string} xp the point of x at which to interpolate
* @param {array} x support points in x (of same length as y) * @param {array} x support points in x (of same length as y)
* @param {array} y support points in y (of same length as x) * @param {array} y support points in y (of same length as x)
* @return {number} y(xp) * @return {number} y(xp)
*/ */
public interp1(xp: number, x: number[], y: number[]): number public interp1(xp: number, x: number[], y: number[]): number
{ {
if (xp > x[x.length - 1]) if (xp > x[x.length - 1])
@ -101,4 +101,4 @@ export class MathUtil
} }
} }
} }
} }

View File

@ -7,9 +7,9 @@ import { TimeUtil } from "@spt-aki/utils/TimeUtil";
export class ObjectId export class ObjectId
{ {
constructor( constructor(
@inject("TimeUtil") protected timeUtil: TimeUtil @inject("TimeUtil") protected timeUtil: TimeUtil,
) )
{ } {}
protected randomBytes = crypto.randomBytes(5); protected randomBytes = crypto.randomBytes(5);
protected constglobalCounter = 0; protected constglobalCounter = 0;
@ -58,4 +58,4 @@ export class ObjectId
return this.toHexString(objectIdBinary); return this.toHexString(objectIdBinary);
} }
} }

View File

@ -84,7 +84,7 @@ export class RagfairOfferHolder
this.removeOffer(offer); this.removeOffer(offer);
} }
} }
public removeOfferByTrader(traderId: string): void public removeOfferByTrader(traderId: string): void
{ {
if (this.offersByTrader.has(traderId)) if (this.offersByTrader.has(traderId))
@ -99,7 +99,7 @@ export class RagfairOfferHolder
*/ */
public getStaleOffers(time: number): Array<IRagfairOffer> public getStaleOffers(time: number): Array<IRagfairOffer>
{ {
return this.getOffers().filter(o => this.isStale(o, time)); return this.getOffers().filter((o) => this.isStale(o, time));
} }
protected addOfferByTemplates(template: string, offer: IRagfairOffer): void protected addOfferByTemplates(template: string, offer: IRagfairOffer): void
@ -134,4 +134,4 @@ export class RagfairOfferHolder
{ {
return offer.endTime < time || offer.items[0].upd.StackObjectsCount < 1; return offer.endTime < time || offer.items[0].upd.StackObjectsCount < 1;
} }
} }

View File

@ -5,32 +5,35 @@ import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
/** /**
* Array of ProbabilityObjectArray which allow to randomly draw of the contained objects * Array of ProbabilityObjectArray which allow to randomly draw of the contained objects
* based on the relative probability of each of its elements. * based on the relative probability of each of its elements.
* The probabilities of the contained element is not required to be normalized. * The probabilities of the contained element is not required to be normalized.
* *
* Example: * Example:
* po = new ProbabilityObjectArray( * po = new ProbabilityObjectArray(
* new ProbabilityObject("a", 5), * new ProbabilityObject("a", 5),
* new ProbabilityObject("b", 1), * new ProbabilityObject("b", 1),
* new ProbabilityObject("c", 1) * new ProbabilityObject("c", 1)
* ); * );
* res = po.draw(10000); * res = po.draw(10000);
* // count the elements which should be distributed according to the relative probabilities * // count the elements which should be distributed according to the relative probabilities
* res.filter(x => x==="b").reduce((sum, x) => sum + 1 , 0) * res.filter(x => x==="b").reduce((sum, x) => sum + 1 , 0)
*/ */
export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObject<K, V>> export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityObject<K, V>>
{ {
constructor( constructor(
private mathUtil: MathUtil, private mathUtil: MathUtil,
private jsonUtil: JsonUtil, private jsonUtil: JsonUtil,
...items: ProbabilityObject<K, V>[]) ...items: ProbabilityObject<K, V>[]
)
{ {
super(); super();
this.push(...items); this.push(...items);
} }
filter(callbackfn: (value: ProbabilityObject<K, V>, index: number, array: ProbabilityObject<K, V>[]) => any): ProbabilityObjectArray<K, V> filter(
callbackfn: (value: ProbabilityObject<K, V>, index: number, array: ProbabilityObject<K, V>[]) => any,
): ProbabilityObjectArray<K, V>
{ {
return new ProbabilityObjectArray(this.mathUtil, this.jsonUtil, ...super.filter(callbackfn)); return new ProbabilityObjectArray(this.mathUtil, this.jsonUtil, ...super.filter(callbackfn));
} }
@ -56,7 +59,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
{ {
const clone = this.jsonUtil.clone(this); const clone = this.jsonUtil.clone(this);
const probabliltyObjects = new ProbabilityObjectArray<K, V>(this.mathUtil, this.jsonUtil); const probabliltyObjects = new ProbabilityObjectArray<K, V>(this.mathUtil, this.jsonUtil);
for (const ci of clone) for (const ci of clone)
{ {
probabliltyObjects.push(new ProbabilityObject(ci.key, ci.relativeProbability, ci.data)); probabliltyObjects.push(new ProbabilityObject(ci.key, ci.relativeProbability, ci.data));
} }
@ -71,7 +74,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
*/ */
drop(key: K): ProbabilityObjectArray<K, V> drop(key: K): ProbabilityObjectArray<K, V>
{ {
return this.filter(r => r.key !== key); return this.filter((r) => r.key !== key);
} }
/** /**
@ -81,7 +84,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
*/ */
data(key: K): V data(key: K): V
{ {
return this.filter(r => r.key === key)[0]?.data; return this.filter((r) => r.key === key)[0]?.data;
} }
/** /**
@ -96,7 +99,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
*/ */
probability(key: K): number probability(key: K): number
{ {
return this.filter(r => r.key === key)[0].relativeProbability; return this.filter((r) => r.key === key)[0].relativeProbability;
} }
/** /**
@ -110,7 +113,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
*/ */
maxProbability(): number maxProbability(): number
{ {
return Math.max(...this.map(x => x.relativeProbability)); return Math.max(...this.map((x) => x.relativeProbability));
} }
/** /**
@ -124,7 +127,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
*/ */
minProbability(): number minProbability(): number
{ {
return Math.min(...this.map(x => x.relativeProbability)); return Math.min(...this.map((x) => x.relativeProbability));
} }
/** /**
@ -146,17 +149,17 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
let probCumsum = this.cumulativeProbability(probArray); let probCumsum = this.cumulativeProbability(probArray);
const drawnKeys = []; const drawnKeys = [];
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
{ {
const rand = Math.random(); const rand = Math.random();
const randomIndex = probCumsum.findIndex(x => x > rand); const randomIndex = probCumsum.findIndex((x) => x > rand);
// We cannot put Math.random() directly in the findIndex because then it draws anew for each of its iteration // We cannot put Math.random() directly in the findIndex because then it draws anew for each of its iteration
if (replacement || locklist.includes(keyArray[randomIndex])) if (replacement || locklist.includes(keyArray[randomIndex]))
{ {
// Add random item from possible value into return array // Add random item from possible value into return array
drawnKeys.push(keyArray[randomIndex]); drawnKeys.push(keyArray[randomIndex]);
} }
else else
{ {
// We draw without replacement -> remove the key and its probability from array // We draw without replacement -> remove the key and its probability from array
const key = keyArray.splice(randomIndex, 1)[0]; const key = keyArray.splice(randomIndex, 1)[0];
@ -164,7 +167,7 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
drawnKeys.push(key); drawnKeys.push(key);
probCumsum = this.cumulativeProbability(probArray); probCumsum = this.cumulativeProbability(probArray);
// If we draw without replacement and the ProbabilityObjectArray is exhausted we need to break // If we draw without replacement and the ProbabilityObjectArray is exhausted we need to break
if (keyArray.length < 1) if (keyArray.length < 1)
{ {
break; break;
} }
@ -176,20 +179,20 @@ export class ProbabilityObjectArray<K, V=undefined> extends Array<ProbabilityObj
} }
/** /**
* A ProbabilityObject which is use as an element to the ProbabilityObjectArray array * A ProbabilityObject which is use as an element to the ProbabilityObjectArray array
* It contains a key, the relative probability as well as optional data. * It contains a key, the relative probability as well as optional data.
*/ */
export class ProbabilityObject<K,V=undefined> export class ProbabilityObject<K, V = undefined>
{ {
key: K; key: K;
relativeProbability: number; relativeProbability: number;
data: V; data: V;
/** /**
* Constructor for the ProbabilityObject * Constructor for the ProbabilityObject
* @param {string} key The key of the element * @param {string} key The key of the element
* @param {number} relativeProbability The relative probability of this element * @param {number} relativeProbability The relative probability of this element
* @param {any} data Optional data attached to the element * @param {any} data Optional data attached to the element
*/ */
constructor(key: K, relativeProbability: number, data: V = null) constructor(key: K, relativeProbability: number, data: V = null)
{ {
this.key = key; this.key = key;
@ -201,9 +204,9 @@ export class ProbabilityObject<K,V=undefined>
@injectable() @injectable()
export class RandomUtil export class RandomUtil
{ {
constructor ( constructor(
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("WinstonLogger") protected logger: ILogger @inject("WinstonLogger") protected logger: ILogger,
) )
{ {
} }
@ -261,7 +264,7 @@ export class RandomUtil
return this.getArrayValue(Object.keys(node)); return this.getArrayValue(Object.keys(node));
} }
public getKeyValue(node: { [x: string]: any; }): any public getKeyValue(node: {[x: string]: any;}): any
{ {
return node[this.getKey(node)]; return node[this.getKey(node)];
} }
@ -276,8 +279,14 @@ export class RandomUtil
{ {
let u = 0; let u = 0;
let v = 0; let v = 0;
while (u === 0) u = Math.random(); //Converting [0,1) to (0,1) while (u === 0)
while (v === 0) v = Math.random(); {
u = Math.random(); // Converting [0,1) to (0,1)
}
while (v === 0)
{
v = Math.random();
}
const w = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v); const w = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
return mu + w * sigma; return mu + w * sigma;
} }
@ -291,11 +300,11 @@ export class RandomUtil
*/ */
public randInt(low: number, high?: number): number public randInt(low: number, high?: number): number
{ {
if (high) if (high)
{ {
return low + Math.floor(Math.random() * (high - low)); return low + Math.floor(Math.random() * (high - low));
} }
else else
{ {
return Math.floor(Math.random() * low); return Math.floor(Math.random() * low);
} }
@ -311,20 +320,20 @@ export class RandomUtil
*/ */
public drawRandomFromList<T>(list: Array<T>, count = 1, replacement = true): Array<T> public drawRandomFromList<T>(list: Array<T>, count = 1, replacement = true): Array<T>
{ {
if (!replacement) if (!replacement)
{ {
list = this.jsonUtil.clone(list); list = this.jsonUtil.clone(list);
} }
const results = []; const results = [];
for (let i = 0; i < count; i++) for (let i = 0; i < count; i++)
{ {
const randomIndex = this.randInt(list.length); const randomIndex = this.randInt(list.length);
if (replacement) if (replacement)
{ {
results.push(list[randomIndex]); results.push(list[randomIndex]);
} }
else else
{ {
results.push(list.splice(randomIndex, 1)[0]); results.push(list.splice(randomIndex, 1)[0]);
} }
@ -366,7 +375,7 @@ export class RandomUtil
{ {
throw { throw {
name: "Invalid arguments", name: "Invalid arguments",
message: `Bounded random number generation max is smaller than min (${max} < ${min})` message: `Bounded random number generation max is smaller than min (${max} < ${min})`,
}; };
} }
@ -374,7 +383,7 @@ export class RandomUtil
{ {
throw { throw {
name: "Invalid argument", name: "Invalid argument",
message: `'n' must be 1 or greater (received ${n})` message: `'n' must be 1 or greater (received ${n})`,
}; };
} }
@ -390,7 +399,9 @@ export class RandomUtil
* A shift that is equal to the available range only has a 50% chance of rolling correctly, theoretically halving performance. * A shift that is equal to the available range only has a 50% chance of rolling correctly, theoretically halving performance.
* Shifting even further drops the success chance very rapidly - so we want to warn against that */ * Shifting even further drops the success chance very rapidly - so we want to warn against that */
this.logger.warning("Bias shift for random number generation is greater than the range of available numbers.\nThis can have a very severe performance impact!"); this.logger.warning(
"Bias shift for random number generation is greater than the range of available numbers.\nThis can have a very severe performance impact!",
);
this.logger.info(`min -> ${min}; max -> ${max}; shift -> ${shift}`); this.logger.info(`min -> ${min}; max -> ${max}; shift -> ${shift}`);
} }
@ -433,19 +444,21 @@ export class RandomUtil
{ {
let currentIndex = array.length; let currentIndex = array.length;
let randomIndex: number; let randomIndex: number;
// While there remain elements to shuffle. // While there remain elements to shuffle.
while (currentIndex !== 0) while (currentIndex !== 0)
{ {
// Pick a remaining element. // Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex); randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--; currentIndex--;
// And swap it with the current element. // And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [ [array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]]; array[randomIndex],
array[currentIndex],
];
} }
return array; return array;
} }
} }

View File

@ -1,5 +1,5 @@
import { injectable } from "tsyringe";
import { formatInTimeZone } from "date-fns-tz"; import { formatInTimeZone } from "date-fns-tz";
import { injectable } from "tsyringe";
/** /**
* Utility class to handle time related operations. * Utility class to handle time related operations.

View File

@ -1,30 +1,33 @@
import "reflect-metadata"; import "reflect-metadata";
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import fs from "node:fs";
import crypto from "node:crypto";
import { promisify } from "node:util";
import path, { resolve } from "node:path";
import { writeFileSync } from "atomically";
import lockfile from "proper-lockfile";
import { IAsyncQueue } from "@spt-aki/models/spt/utils/IAsyncQueue"; import { IAsyncQueue } from "@spt-aki/models/spt/utils/IAsyncQueue";
import { writeFileSync } from "atomically";
import crypto from "node:crypto";
import fs from "node:fs";
import path, { resolve } from "node:path";
import { promisify } from "node:util";
import lockfile from "proper-lockfile";
@injectable() @injectable()
export class VFS export class VFS
{ {
accessFilePromisify: (path: fs.PathLike, mode?: number) => Promise<void>; accessFilePromisify: (path: fs.PathLike, mode?: number) => Promise<void>;
copyFilePromisify: (src: fs.PathLike, dst: fs.PathLike, flags?: number) => Promise<void>; copyFilePromisify: (src: fs.PathLike, dst: fs.PathLike, flags?: number) => Promise<void>;
mkdirPromisify: (path: fs.PathLike, options: fs.MakeDirectoryOptions & { recursive: true; }) => Promise<string>; mkdirPromisify: (path: fs.PathLike, options: fs.MakeDirectoryOptions & {recursive: true;}) => Promise<string>;
readFilePromisify: (path: fs.PathLike) => Promise<Buffer>; readFilePromisify: (path: fs.PathLike) => Promise<Buffer>;
writeFilePromisify: (path: fs.PathLike, data: string, options?: any) => Promise<void>; writeFilePromisify: (path: fs.PathLike, data: string, options?: any) => Promise<void>;
readdirPromisify: (path: fs.PathLike, options?: BufferEncoding | { encoding: BufferEncoding; withFileTypes?: false; }) => Promise<string[]>; readdirPromisify: (
statPromisify: (path: fs.PathLike, options?: fs.StatOptions & { bigint?: false; }) => Promise<fs.Stats>; path: fs.PathLike,
options?: BufferEncoding | {encoding: BufferEncoding; withFileTypes?: false;},
) => Promise<string[]>;
statPromisify: (path: fs.PathLike, options?: fs.StatOptions & {bigint?: false;}) => Promise<fs.Stats>;
unlinkPromisify: (path: fs.PathLike) => Promise<void>; unlinkPromisify: (path: fs.PathLike) => Promise<void>;
rmdirPromisify: (path: fs.PathLike) => Promise<void>; rmdirPromisify: (path: fs.PathLike) => Promise<void>;
renamePromisify: (oldPath: fs.PathLike, newPath: fs.PathLike) => Promise<void>; renamePromisify: (oldPath: fs.PathLike, newPath: fs.PathLike) => Promise<void>;
constructor( constructor(
@inject("AsyncQueue") protected asyncQueue: IAsyncQueue @inject("AsyncQueue") protected asyncQueue: IAsyncQueue,
) )
{ {
this.accessFilePromisify = promisify(fs.access); this.accessFilePromisify = promisify(fs.access);
@ -51,7 +54,7 @@ export class VFS
// Create the command to add to the queue // Create the command to add to the queue
const command = { const command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.accessFilePromisify(filepath) cmd: async () => await this.accessFilePromisify(filepath),
}; };
// Wait for the command completion // Wait for the command completion
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
@ -75,21 +78,22 @@ export class VFS
{ {
const command = { const command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.copyFilePromisify(filepath, target) cmd: async () => await this.copyFilePromisify(filepath, target),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
public createDir(filepath: string): void public createDir(filepath: string): void
{ {
fs.mkdirSync(filepath.substr(0, filepath.lastIndexOf("/")), { recursive: true }); fs.mkdirSync(filepath.substr(0, filepath.lastIndexOf("/")), {recursive: true});
} }
public async createDirAsync(filepath: string): Promise<void> public async createDirAsync(filepath: string): Promise<void>
{ {
const command = { const command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.mkdirPromisify(filepath.substr(0, filepath.lastIndexOf("/")), { recursive: true }) cmd: async () =>
await this.mkdirPromisify(filepath.substr(0, filepath.lastIndexOf("/")), {recursive: true}),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
@ -148,7 +152,9 @@ export class VFS
{ {
const read = fs.readFileSync(...args); const read = fs.readFileSync(...args);
if (this.isBuffer(read)) if (this.isBuffer(read))
{
return read.toString(); return read.toString();
}
return read; return read;
} }
@ -156,7 +162,9 @@ export class VFS
{ {
const read = await this.readFilePromisify(path); const read = await this.readFilePromisify(path);
if (this.isBuffer(read)) if (this.isBuffer(read))
{
return read.toString(); return read.toString();
}
return read; return read;
} }
@ -167,7 +175,7 @@ export class VFS
public writeFile(filepath: any, data = "", append = false, atomic = true): void public writeFile(filepath: any, data = "", append = false, atomic = true): void
{ {
const options = append ? { flag: "a" } : { flag: "w" }; const options = append ? {flag: "a"} : {flag: "w"};
if (!this.exists(filepath)) if (!this.exists(filepath))
{ {
@ -194,7 +202,7 @@ export class VFS
public async writeFileAsync(filepath: any, data = "", append = false, atomic = true): Promise<void> public async writeFileAsync(filepath: any, data = "", append = false, atomic = true): Promise<void>
{ {
const options = append ? { flag: "a" } : { flag: "w" }; const options = append ? {flag: "a"} : {flag: "w"};
if (!await this.exists(filepath)) if (!await this.exists(filepath))
{ {
@ -230,7 +238,6 @@ export class VFS
}); });
} }
public getDirs(filepath: string): string[] public getDirs(filepath: string): string[]
{ {
return fs.readdirSync(filepath).filter((item) => return fs.readdirSync(filepath).filter((item) =>
@ -377,7 +384,7 @@ export class VFS
return files; return files;
} }
const dirents = fs.readdirSync(directory, { encoding: "utf-8", withFileTypes: true }); const dirents = fs.readdirSync(directory, {encoding: "utf-8", withFileTypes: true});
for (const dirent of dirents) for (const dirent of dirents)
{ {
const res = resolve(directory, dirent.name); const res = resolve(directory, dirent.name);

View File

@ -15,7 +15,7 @@ export class WatermarkLocale
protected modding: string[]; protected modding: string[];
constructor( constructor(
@inject("LocalisationService") protected localisationService: LocalisationService @inject("LocalisationService") protected localisationService: LocalisationService,
) )
{ {
this.description = [ this.description = [
@ -23,7 +23,7 @@ export class WatermarkLocale
"", "",
this.localisationService.getText("watermark-free_of_charge"), this.localisationService.getText("watermark-free_of_charge"),
this.localisationService.getText("watermark-paid_scammed"), this.localisationService.getText("watermark-paid_scammed"),
this.localisationService.getText("watermark-commercial_use_prohibited") this.localisationService.getText("watermark-commercial_use_prohibited"),
]; ];
this.warning = [ this.warning = [
"", "",
@ -33,14 +33,14 @@ export class WatermarkLocale
`${this.localisationService.getText("watermark-report_issues_to")}:`, `${this.localisationService.getText("watermark-report_issues_to")}:`,
this.localisationService.getText("watermark-issue_tracker_url"), this.localisationService.getText("watermark-issue_tracker_url"),
"", "",
this.localisationService.getText("watermark-use_at_own_risk") this.localisationService.getText("watermark-use_at_own_risk"),
]; ];
this.modding = [ this.modding = [
"", "",
this.localisationService.getText("watermark-modding_disabled"), this.localisationService.getText("watermark-modding_disabled"),
"", "",
this.localisationService.getText("watermark-not_an_issue"), this.localisationService.getText("watermark-not_an_issue"),
this.localisationService.getText("watermark-do_not_report") this.localisationService.getText("watermark-do_not_report"),
]; ];
} }
@ -71,7 +71,7 @@ export class Watermark
@inject("WinstonLogger") protected logger: ILogger, @inject("WinstonLogger") protected logger: ILogger,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("WatermarkLocale") protected watermarkLocale?: WatermarkLocale @inject("WatermarkLocale") protected watermarkLocale?: WatermarkLocale,
) )
{ {
this.akiConfig = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE); this.akiConfig = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE);
@ -110,9 +110,9 @@ export class Watermark
*/ */
public getVersionTag(withEftVersion = false): string public getVersionTag(withEftVersion = false): string
{ {
const versionTag = (globalThis.G_DEBUG_CONFIGURATION) const versionTag = (globalThis.G_DEBUG_CONFIGURATION) ?
? `${this.akiConfig.akiVersion} - ${this.localisationService.getText("bleeding_edge_build")}` `${this.akiConfig.akiVersion} - ${this.localisationService.getText("bleeding_edge_build")}` :
: this.akiConfig.akiVersion; this.akiConfig.akiVersion;
if (withEftVersion) if (withEftVersion)
{ {
@ -130,9 +130,9 @@ export class Watermark
*/ */
public getInGameVersionLabel(): string public getInGameVersionLabel(): string
{ {
const versionTag = (globalThis.G_DEBUG_CONFIGURATION) const versionTag = (globalThis.G_DEBUG_CONFIGURATION) ?
? `${this.akiConfig.akiVersion} - BLEEDINGEDGE` `${this.akiConfig.akiVersion} - BLEEDINGEDGE` :
: this.akiConfig.akiVersion; this.akiConfig.akiVersion;
return `${this.akiConfig.projectName} ${versionTag}`; return `${this.akiConfig.projectName} ${versionTag}`;
} }

View File

@ -3,19 +3,19 @@ export class LinkedList<T>
private head: LinkedListNode<T>; private head: LinkedListNode<T>;
private tail: LinkedListNode<T>; private tail: LinkedListNode<T>;
public add(t: T): void public add(t: T): void
{ {
if (!this.head) if (!this.head)
{ {
const node = new LinkedListNode(t); const node = new LinkedListNode(t);
this.head = node; this.head = node;
this.tail = node; this.tail = node;
} }
else else
{ {
let ref = this.head; let ref = this.head;
let next = this.head.getNextNode(); let next = this.head.getNextNode();
while (next) while (next)
{ {
ref = next; ref = next;
next = ref.getNextNode(); next = ref.getNextNode();
@ -26,34 +26,34 @@ export class LinkedList<T>
} }
} }
public addRange(list: T[]): void public addRange(list: T[]): void
{ {
for (const item of list) for (const item of list)
{ {
this.add(item); this.add(item);
} }
} }
public getHead(): LinkedListNode<T> public getHead(): LinkedListNode<T>
{ {
return this.head; return this.head;
} }
public getTail(): LinkedListNode<T> public getTail(): LinkedListNode<T>
{ {
return this.tail; return this.tail;
} }
public isEmpty(): boolean public isEmpty(): boolean
{ {
return this.head === undefined || this.head === null; return this.head === undefined || this.head === null;
} }
public getSize(): number public getSize(): number
{ {
let size = 0; let size = 0;
let next = this.head; let next = this.head;
while (next) while (next)
{ {
size++; size++;
next = next.getNextNode(); next = next.getNextNode();
@ -61,41 +61,47 @@ export class LinkedList<T>
return size; return size;
} }
public removeFirst(): LinkedListNode<T> public removeFirst(): LinkedListNode<T>
{ {
if (!this.head) return undefined; if (!this.head)
{
return undefined;
}
const node = this.head; const node = this.head;
if (this.head.getNextNode()) if (this.head.getNextNode())
{ {
this.head = this.head.getNextNode(); this.head = this.head.getNextNode();
this.head.setPreviousNode(undefined); this.head.setPreviousNode(undefined);
} }
else else
{ {
this.head = undefined; this.head = undefined;
} }
return node; return node;
} }
public removeLast(): LinkedListNode<T> public removeLast(): LinkedListNode<T>
{ {
if (!this.tail) return undefined; if (!this.tail)
{
return undefined;
}
const node = this.tail; const node = this.tail;
if (this.tail.getPreviousNode()) if (this.tail.getPreviousNode())
{ {
this.tail = this.tail.getPreviousNode(); this.tail = this.tail.getPreviousNode();
this.tail.setNextNode(undefined); this.tail.setNextNode(undefined);
} }
else else
{ {
this.tail = undefined; this.tail = undefined;
} }
return node; return node;
} }
public indexOf(func: (t:T) => boolean): number public indexOf(func: (t: T) => boolean): number
{ {
const node = this.head; const node = this.head;
let index = 0; let index = 0;
@ -110,7 +116,7 @@ export class LinkedList<T>
return undefined; return undefined;
} }
public contains(func: (t:T) => boolean): boolean public contains(func: (t: T) => boolean): boolean
{ {
let node = this.head; let node = this.head;
while (node) while (node)
@ -124,7 +130,7 @@ export class LinkedList<T>
return false; return false;
} }
public forEachNode(func: (t:LinkedListNode<T>) => void): void public forEachNode(func: (t: LinkedListNode<T>) => void): void
{ {
let node = this.head; let node = this.head;
while (node) while (node)
@ -134,7 +140,7 @@ export class LinkedList<T>
} }
} }
public forEachValue(func: (t:T) => void): void public forEachValue(func: (t: T) => void): void
{ {
let node = this.head; let node = this.head;
while (node) while (node)
@ -144,7 +150,7 @@ export class LinkedList<T>
} }
} }
public findFirstNode(func: (t:LinkedListNode<T>) => boolean): LinkedListNode<T> public findFirstNode(func: (t: LinkedListNode<T>) => boolean): LinkedListNode<T>
{ {
let node = this.head; let node = this.head;
while (node) while (node)
@ -158,7 +164,7 @@ export class LinkedList<T>
return undefined; return undefined;
} }
public findFirstValue(func: (t:T) => boolean): T public findFirstValue(func: (t: T) => boolean): T
{ {
let node = this.head; let node = this.head;
while (node) while (node)
@ -191,35 +197,35 @@ export class LinkedListNode<T>
private value: T; private value: T;
private next: LinkedListNode<T>; private next: LinkedListNode<T>;
constructor(value: T, previous: LinkedListNode<T> = undefined, next: LinkedListNode<T> = undefined) constructor(value: T, previous: LinkedListNode<T> = undefined, next: LinkedListNode<T> = undefined)
{ {
this.value = value; this.value = value;
this.previous = previous; this.previous = previous;
this.next = next; this.next = next;
} }
public getValue(): T public getValue(): T
{ {
return this.value; return this.value;
} }
public getNextNode(): LinkedListNode<T> public getNextNode(): LinkedListNode<T>
{ {
return this.next; return this.next;
} }
public setNextNode(node: LinkedListNode<T>): void public setNextNode(node: LinkedListNode<T>): void
{ {
this.next = node; this.next = node;
} }
public getPreviousNode(): LinkedListNode<T> public getPreviousNode(): LinkedListNode<T>
{ {
return this.previous; return this.previous;
} }
public setPreviousNode(node: LinkedListNode<T>): void public setPreviousNode(node: LinkedListNode<T>): void
{ {
this.previous = node; this.previous = node;
} }
} }

View File

@ -3,21 +3,20 @@ export class Queue<T>
private elements: Record<number, T>; private elements: Record<number, T>;
private head: number; private head: number;
private tail: number; private tail: number;
constructor() constructor()
{ {
this.elements = {}; this.elements = {};
this.head = 0; this.head = 0;
this.tail = 0; this.tail = 0;
} }
public enqueue(element: T): void public enqueue(element: T): void
{ {
this.elements[this.tail] = element; this.elements[this.tail] = element;
this.tail++; this.tail++;
} }
public enqueueAll(elements: T[]): void public enqueueAll(elements: T[]): void
{ {
for (const element of elements) for (const element of elements)
{ {
@ -25,7 +24,7 @@ export class Queue<T>
} }
} }
public dequeue(): T public dequeue(): T
{ {
const item = this.elements[this.head]; const item = this.elements[this.head];
delete this.elements[this.head]; delete this.elements[this.head];
@ -33,11 +32,11 @@ export class Queue<T>
return item; return item;
} }
public peek():T public peek(): T
{ {
return this.elements[this.head]; return this.elements[this.head];
} }
public getLength(): number public getLength(): number
{ {
return this.tail - this.head; return this.tail - this.head;
} }
@ -46,4 +45,4 @@ export class Queue<T>
{ {
return this.getLength() === 0; return this.getLength() === 0;
} }
} }

View File

@ -1,5 +1,5 @@
import fs from "node:fs";
import crypto from "node:crypto"; import crypto from "node:crypto";
import fs from "node:fs";
import { promisify } from "node:util"; import { promisify } from "node:util";
import winston, { createLogger, format, transports } from "winston"; import winston, { createLogger, format, transports } from "winston";
import DailyRotateFile from "winston-daily-rotate-file"; import DailyRotateFile from "winston-daily-rotate-file";
@ -23,7 +23,7 @@ export abstract class AbstractWinstonLogger implements ILogger
succ: 2, succ: 2,
info: 3, info: 3,
custom: 4, custom: 4,
debug: 5 debug: 5,
}, },
colors: { colors: {
error: "red", error: "red",
@ -31,7 +31,7 @@ export abstract class AbstractWinstonLogger implements ILogger
succ: "green", succ: "green",
info: "white", info: "white",
custom: "black", custom: "black",
debug: "gray" debug: "gray",
}, },
bgColors: { bgColors: {
default: "", default: "",
@ -42,14 +42,14 @@ export abstract class AbstractWinstonLogger implements ILogger
blueBG: "blueBG", blueBG: "blueBG",
magentaBG: "magentaBG", magentaBG: "magentaBG",
cyanBG: "cyanBG", cyanBG: "cyanBG",
whiteBG: "whiteBG" whiteBG: "whiteBG",
} },
}; };
protected logger: winston.Logger & SptLogger; protected logger: winston.Logger & SptLogger;
protected writeFilePromisify: (path: fs.PathLike, data: string, options?: any) => Promise<void>; protected writeFilePromisify: (path: fs.PathLike, data: string, options?: any) => Promise<void>;
constructor( constructor(
protected asyncQueue: IAsyncQueue protected asyncQueue: IAsyncQueue,
) )
{ {
this.filePath = `${this.getFilePath()}${this.getFileName()}`; this.filePath = `${this.getFilePath()}${this.getFileName()}`;
@ -57,7 +57,7 @@ export abstract class AbstractWinstonLogger implements ILogger
this.showDebugInConsole = globalThis.G_DEBUG_CONFIGURATION; this.showDebugInConsole = globalThis.G_DEBUG_CONFIGURATION;
if (!fs.existsSync(this.getFilePath())) if (!fs.existsSync(this.getFilePath()))
{ {
fs.mkdirSync(this.getFilePath(), { recursive: true }); fs.mkdirSync(this.getFilePath(), {recursive: true});
} }
const transportsList: winston.transport[] = []; const transportsList: winston.transport[] = [];
@ -68,13 +68,13 @@ export abstract class AbstractWinstonLogger implements ILogger
new transports.Console({ new transports.Console({
level: this.showDebugInConsole ? "debug" : "custom", level: this.showDebugInConsole ? "debug" : "custom",
format: format.combine( format: format.combine(
format.colorize({ all: true, colors: this.logLevels.colors }), format.colorize({all: true, colors: this.logLevels.colors}),
format.printf(({ message }) => format.printf(({message}) =>
{ {
return `${message}`; return `${message}`;
}) }),
) ),
}) }),
); );
} }
if (this.isLogToFile()) if (this.isLogToFile())
@ -91,19 +91,19 @@ export abstract class AbstractWinstonLogger implements ILogger
format.timestamp(), format.timestamp(),
format.align(), format.align(),
format.json(), format.json(),
format.printf(({ timestamp, level, message }) => format.printf(({timestamp, level, message}) =>
{ {
return `[${timestamp}] ${level}: ${message}`; return `[${timestamp}] ${level}: ${message}`;
}) }),
) ),
}) }),
); );
} }
winston.addColors(this.logLevels.colors); winston.addColors(this.logLevels.colors);
this.logger = createLogger({ this.logger = createLogger({
levels: this.logLevels.levels, levels: this.logLevels.levels,
transports: [...transportsList] transports: [...transportsList],
}); });
if (this.isLogExceptions()) if (this.isLogExceptions())
@ -140,39 +140,41 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.writeFilePromisify(this.filePath, `${data}\n`, true) cmd: async () => await this.writeFilePromisify(this.filePath, `${data}\n`, true),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
public async log(data: string | Error | Record<string, unknown>, color: string, backgroundColor = "" ): Promise<void> public async log(data: string | Error | Record<string, unknown>, color: string, backgroundColor = ""): Promise<void>
{ {
const textColor = `${color} ${backgroundColor}`.trimEnd(); const textColor = `${color} ${backgroundColor}`.trimEnd();
const tmpLogger = createLogger({ const tmpLogger = createLogger({
levels: { custom: 0 }, levels: {custom: 0},
level: "custom", level: "custom",
transports: [new transports.Console({ transports: [
format: format.combine( new transports.Console({
format.colorize({ all: true, colors: { custom: textColor } }), format: format.combine(
format.printf(({ message }) => message) format.colorize({all: true, colors: {custom: textColor}}),
) format.printf(({message}) => message),
})] ),
}),
],
}); });
let command: ICommand; let command: ICommand;
if (typeof (data) === "string") if (typeof data === "string")
{ {
command = { command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await tmpLogger.log("custom", data) cmd: async () => await tmpLogger.log("custom", data),
}; };
} }
else else
{ {
command = { command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await tmpLogger.log("custom", JSON.stringify(data, null, 4)) cmd: async () => await tmpLogger.log("custom", JSON.stringify(data, null, 4)),
}; };
} }
@ -183,7 +185,7 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.logger.error(data) cmd: async () => await this.logger.error(data),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
@ -192,7 +194,7 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.logger.warn(data) cmd: async () => await this.logger.warn(data),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
@ -201,7 +203,7 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.logger.succ(data) cmd: async () => await this.logger.succ(data),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
@ -210,7 +212,7 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.logger.info(data) cmd: async () => await this.logger.info(data),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
} }
@ -221,11 +223,15 @@ export abstract class AbstractWinstonLogger implements ILogger
* @param textColor color of text * @param textColor color of text
* @param backgroundColor color of background * @param backgroundColor color of background
*/ */
public async logWithColor(data: string | Record<string, unknown>, textColor: LogTextColor, backgroundColor = LogBackgroundColor.DEFAULT): Promise<void> public async logWithColor(
data: string | Record<string, unknown>,
textColor: LogTextColor,
backgroundColor = LogBackgroundColor.DEFAULT,
): Promise<void>
{ {
const command: ICommand = { const command: ICommand = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.log(data, textColor.toString(), backgroundColor.toString()) cmd: async () => await this.log(data, textColor.toString(), backgroundColor.toString()),
}; };
await this.asyncQueue.waitFor(command); await this.asyncQueue.waitFor(command);
@ -239,14 +245,14 @@ export abstract class AbstractWinstonLogger implements ILogger
{ {
command = { command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.log(data, this.logLevels.colors.debug) cmd: async () => await this.log(data, this.logLevels.colors.debug),
}; };
} }
else else
{ {
command = { command = {
uuid: crypto.randomUUID(), uuid: crypto.randomUUID(),
cmd: async () => await this.logger.debug(data) cmd: async () => await this.logger.debug(data),
}; };
} }

View File

@ -7,7 +7,7 @@ import { AbstractWinstonLogger } from "@spt-aki/utils/logging/AbstractWinstonLog
export class WinstonMainLogger extends AbstractWinstonLogger export class WinstonMainLogger extends AbstractWinstonLogger
{ {
constructor( constructor(
@inject("AsyncQueue") protected asyncQueue: IAsyncQueue @inject("AsyncQueue") protected asyncQueue: IAsyncQueue,
) )
{ {
super(asyncQueue); super(asyncQueue);

View File

@ -7,7 +7,7 @@ import { AbstractWinstonLogger } from "@spt-aki/utils/logging/AbstractWinstonLog
export class WinstonRequestLogger extends AbstractWinstonLogger export class WinstonRequestLogger extends AbstractWinstonLogger
{ {
constructor( constructor(
@inject("AsyncQueue") protected asyncQueue: IAsyncQueue @inject("AsyncQueue") protected asyncQueue: IAsyncQueue,
) )
{ {
super(asyncQueue); super(asyncQueue);