mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 09:50:43 -05:00
Further async improvements (#1070)
- Adds a set of asynchronous cloners able to be used in async methods - Updates setInterval to await the update before processing a new one. - Updates various BotGen methods to remove nested promises and removing a few unnecessary for loops.
This commit is contained in:
parent
ed0e3d64c3
commit
aeee1b8446
@ -182,8 +182,8 @@ export class BotController {
|
|||||||
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
|
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
|
||||||
);
|
);
|
||||||
|
|
||||||
const conditionPromises: Promise<void>[] = [];
|
// Map conditions to promises for bot generation
|
||||||
for (const condition of request.conditions) {
|
const conditionPromises = request.conditions.map(async (condition) => {
|
||||||
const botGenerationDetails = this.getBotGenerationDetailsForWave(
|
const botGenerationDetails = this.getBotGenerationDetailsForWave(
|
||||||
condition,
|
condition,
|
||||||
pmcProfile,
|
pmcProfile,
|
||||||
@ -193,14 +193,11 @@ export class BotController {
|
|||||||
this.botHelper.isBotPmc(condition.Role),
|
this.botHelper.isBotPmc(condition.Role),
|
||||||
);
|
);
|
||||||
|
|
||||||
conditionPromises.push(this.generateWithBotDetails(condition, botGenerationDetails, sessionId));
|
// Generate bots for the current condition
|
||||||
}
|
await this.generateWithBotDetails(condition, botGenerationDetails, sessionId);
|
||||||
|
|
||||||
await Promise.all(conditionPromises)
|
|
||||||
.then((p) => Promise.all(p))
|
|
||||||
.catch((ex) => {
|
|
||||||
this.logger.error(ex);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Promise.all(conditionPromises);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,26 +298,36 @@ export class BotController {
|
|||||||
|
|
||||||
// Get number of bots we have in cache
|
// Get number of bots we have in cache
|
||||||
const botCacheCount = this.botGenerationCacheService.getCachedBotCount(cacheKey);
|
const botCacheCount = this.botGenerationCacheService.getCachedBotCount(cacheKey);
|
||||||
const botPromises: Promise<void>[] = [];
|
|
||||||
if (botCacheCount > botGenerationDetails.botCountToGenerate) {
|
if (botCacheCount >= botGenerationDetails.botCountToGenerate) {
|
||||||
|
this.logger.debug(`Cache already has sufficient bots: ${botCacheCount}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're below desired count, add bots to cache
|
// We're below desired count, add bots to cache
|
||||||
|
const botsToGenerate = botGenerationDetails.botCountToGenerate - botCacheCount;
|
||||||
const progressWriter = new ProgressWriter(botGenerationDetails.botCountToGenerate);
|
const progressWriter = new ProgressWriter(botGenerationDetails.botCountToGenerate);
|
||||||
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) {
|
|
||||||
const detailsClone = this.cloner.clone(botGenerationDetails);
|
|
||||||
botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey));
|
|
||||||
progressWriter.increment();
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(botPromises).then(() => {
|
this.logger.debug(`Generating ${botsToGenerate} bots for cacheKey: ${cacheKey}`);
|
||||||
|
|
||||||
|
const botGenerationPromises = Array.from({ length: botsToGenerate }, async (_, i) => {
|
||||||
|
try {
|
||||||
|
const detailsClone = await this.cloner.cloneAsync(botGenerationDetails);
|
||||||
|
await this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey);
|
||||||
|
progressWriter.increment();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to generate bot #${i + 1}: ${error.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use allSettled here, this allows us to continue even if one of the promises is rejected
|
||||||
|
await Promise.allSettled(botGenerationPromises);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||||
botGenerationDetails.eventRole ?? botGenerationDetails.role ?? ""
|
botGenerationDetails.eventRole ?? botGenerationDetails.role ?? ""
|
||||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -335,7 +342,7 @@ export class BotController {
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
cacheKey: string,
|
cacheKey: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const botToCache = this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
|
const botToCache = await this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
|
||||||
this.botGenerationCacheService.storeBots(cacheKey, [botToCache]);
|
this.botGenerationCacheService.storeBots(cacheKey, [botToCache]);
|
||||||
|
|
||||||
// Store bot details in cache so post-raid PMC messages can use data
|
// Store bot details in cache so post-raid PMC messages can use data
|
||||||
@ -422,19 +429,18 @@ export class BotController {
|
|||||||
|
|
||||||
// Check cache for bot using above key
|
// Check cache for bot using above key
|
||||||
if (!this.botGenerationCacheService.cacheHasBotWithKey(cacheKey)) {
|
if (!this.botGenerationCacheService.cacheHasBotWithKey(cacheKey)) {
|
||||||
const botPromises: Promise<void>[] = [];
|
// No bot in cache, generate new and store in cache
|
||||||
// No bot in cache, generate new and return one
|
await Promise.all(
|
||||||
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) {
|
Array.from({ length: botGenerationDetails.botCountToGenerate }).map(
|
||||||
botPromises.push(this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey));
|
async () => await this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey),
|
||||||
}
|
),
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all(botPromises).then(() => {
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||||
botGenerationDetails.eventRole ?? ""
|
botGenerationDetails.eventRole ?? ""
|
||||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const desiredBot = this.botGenerationCacheService.getBot(cacheKey);
|
const desiredBot = this.botGenerationCacheService.getBot(cacheKey);
|
||||||
|
@ -111,7 +111,10 @@ export class BotGenerator {
|
|||||||
* @param botGenerationDetails details on how to generate bots
|
* @param botGenerationDetails details on how to generate bots
|
||||||
* @returns constructed bot
|
* @returns constructed bot
|
||||||
*/
|
*/
|
||||||
public prepareAndGenerateBot(sessionId: string, botGenerationDetails: IBotGenerationDetails): IBotBase {
|
public async prepareAndGenerateBot(
|
||||||
|
sessionId: string,
|
||||||
|
botGenerationDetails: IBotGenerationDetails,
|
||||||
|
): Promise<IBotBase> {
|
||||||
const preparedBotBase = this.getPreparedBotBase(
|
const preparedBotBase = this.getPreparedBotBase(
|
||||||
botGenerationDetails.eventRole ?? botGenerationDetails.role, // Use eventRole if provided,
|
botGenerationDetails.eventRole ?? botGenerationDetails.role, // Use eventRole if provided,
|
||||||
botGenerationDetails.side,
|
botGenerationDetails.side,
|
||||||
@ -122,7 +125,7 @@ export class BotGenerator {
|
|||||||
const botRole = botGenerationDetails.isPmc
|
const botRole = botGenerationDetails.isPmc
|
||||||
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
|
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
|
||||||
: botGenerationDetails.role;
|
: botGenerationDetails.role;
|
||||||
const botJsonTemplateClone = this.cloner.clone(this.botHelper.getBotTemplate(botRole));
|
const botJsonTemplateClone = await this.cloner.cloneAsync(this.botHelper.getBotTemplate(botRole));
|
||||||
if (!botJsonTemplateClone) {
|
if (!botJsonTemplateClone) {
|
||||||
this.logger.error(`Unable to retrieve: ${botRole} bot template, cannot generate bot of this type`);
|
this.logger.error(`Unable to retrieve: ${botRole} bot template, cannot generate bot of this type`);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ export class CreateProfileService {
|
|||||||
|
|
||||||
public async createProfile(sessionID: string, info: IProfileCreateRequestData): Promise<string> {
|
public async createProfile(sessionID: string, info: IProfileCreateRequestData): Promise<string> {
|
||||||
const account = this.saveServer.getProfile(sessionID).info;
|
const account = this.saveServer.getProfile(sessionID).info;
|
||||||
const profileTemplateClone: ITemplateSide = this.cloner.clone(
|
const profileTemplateClone: ITemplateSide = await this.cloner.cloneAsync(
|
||||||
this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()],
|
this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()],
|
||||||
);
|
);
|
||||||
const pmcData = profileTemplateClone.character;
|
const pmcData = profileTemplateClone.character;
|
||||||
|
@ -64,8 +64,8 @@ export class App {
|
|||||||
await onLoad.onLoad();
|
await onLoad.onLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(async () => {
|
||||||
this.update(this.onUpdateComponents);
|
await this.update(this.onUpdateComponents);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export interface ICloner {
|
export interface ICloner {
|
||||||
clone<T>(obj: T): T;
|
clone<T>(obj: T): T;
|
||||||
|
cloneAsync<T>(obj: T): Promise<T>;
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,14 @@ export class JsonCloner implements ICloner {
|
|||||||
public clone<T>(obj: T): T {
|
public clone<T>(obj: T): T {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cloneAsync<T>(obj: T): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(JSON.stringify(obj)));
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,4 +44,37 @@ export class RecursiveCloner implements ICloner {
|
|||||||
|
|
||||||
throw new Error(`Cant clone ${JSON.stringify(obj)}`);
|
throw new Error(`Cant clone ${JSON.stringify(obj)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cloneAsync<T>(obj: T): Promise<T> {
|
||||||
|
// if null or undefined return it as is
|
||||||
|
if (obj === null || obj === undefined) return obj;
|
||||||
|
|
||||||
|
const typeOfObj = typeof obj;
|
||||||
|
|
||||||
|
// no need to clone these types, they are primitives
|
||||||
|
if (RecursiveCloner.primitives.has(typeOfObj)) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone the object types
|
||||||
|
if (typeOfObj === "object") {
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
const objArr = obj as Array<T>;
|
||||||
|
const clonedArray = await Promise.all(objArr.map(async (v) => await this.cloneAsync(v)));
|
||||||
|
return clonedArray as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newObj: Record<string, T> = {};
|
||||||
|
const clonePromises = Object.keys(obj).map(async (key) => {
|
||||||
|
const value = (obj as Record<string, T>)[key];
|
||||||
|
newObj[key] = await this.cloneAsync(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(clonePromises);
|
||||||
|
return newObj as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle unsupported types
|
||||||
|
throw new Error(`Cannot clone ${JSON.stringify(obj)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,14 @@ export class StructuredCloner implements ICloner {
|
|||||||
public clone<T>(obj: T): T {
|
public clone<T>(obj: T): T {
|
||||||
return structuredClone(obj);
|
return structuredClone(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cloneAsync<T>(obj: T): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(structuredClone(obj));
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user