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,
|
||||
);
|
||||
|
||||
const conditionPromises: Promise<void>[] = [];
|
||||
for (const condition of request.conditions) {
|
||||
// Map conditions to promises for bot generation
|
||||
const conditionPromises = request.conditions.map(async (condition) => {
|
||||
const botGenerationDetails = this.getBotGenerationDetailsForWave(
|
||||
condition,
|
||||
pmcProfile,
|
||||
@ -193,14 +193,11 @@ export class BotController {
|
||||
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 [];
|
||||
}
|
||||
|
||||
@ -301,26 +298,36 @@ export class BotController {
|
||||
|
||||
// Get number of bots we have in cache
|
||||
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;
|
||||
}
|
||||
|
||||
// We're below desired count, add bots to cache
|
||||
const botsToGenerate = botGenerationDetails.botCountToGenerate - botCacheCount;
|
||||
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(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? botGenerationDetails.role ?? ""
|
||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||
);
|
||||
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(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? botGenerationDetails.role ?? ""
|
||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,7 +342,7 @@ export class BotController {
|
||||
sessionId: string,
|
||||
cacheKey: string,
|
||||
): Promise<void> {
|
||||
const botToCache = this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
|
||||
const botToCache = await this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
|
||||
this.botGenerationCacheService.storeBots(cacheKey, [botToCache]);
|
||||
|
||||
// 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
|
||||
if (!this.botGenerationCacheService.cacheHasBotWithKey(cacheKey)) {
|
||||
const botPromises: Promise<void>[] = [];
|
||||
// No bot in cache, generate new and return one
|
||||
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) {
|
||||
botPromises.push(this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey));
|
||||
}
|
||||
// No bot in cache, generate new and store in cache
|
||||
await Promise.all(
|
||||
Array.from({ length: botGenerationDetails.botCountToGenerate }).map(
|
||||
async () => await this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey),
|
||||
),
|
||||
);
|
||||
|
||||
await Promise.all(botPromises).then(() => {
|
||||
this.logger.debug(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? ""
|
||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||
);
|
||||
});
|
||||
this.logger.debug(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? ""
|
||||
}) ${botGenerationDetails.botDifficulty} bots`,
|
||||
);
|
||||
}
|
||||
|
||||
const desiredBot = this.botGenerationCacheService.getBot(cacheKey);
|
||||
|
@ -111,7 +111,10 @@ export class BotGenerator {
|
||||
* @param botGenerationDetails details on how to generate bots
|
||||
* @returns constructed bot
|
||||
*/
|
||||
public prepareAndGenerateBot(sessionId: string, botGenerationDetails: IBotGenerationDetails): IBotBase {
|
||||
public async prepareAndGenerateBot(
|
||||
sessionId: string,
|
||||
botGenerationDetails: IBotGenerationDetails,
|
||||
): Promise<IBotBase> {
|
||||
const preparedBotBase = this.getPreparedBotBase(
|
||||
botGenerationDetails.eventRole ?? botGenerationDetails.role, // Use eventRole if provided,
|
||||
botGenerationDetails.side,
|
||||
@ -122,7 +125,7 @@ export class BotGenerator {
|
||||
const botRole = botGenerationDetails.isPmc
|
||||
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
|
||||
: botGenerationDetails.role;
|
||||
const botJsonTemplateClone = this.cloner.clone(this.botHelper.getBotTemplate(botRole));
|
||||
const botJsonTemplateClone = await this.cloner.cloneAsync(this.botHelper.getBotTemplate(botRole));
|
||||
if (!botJsonTemplateClone) {
|
||||
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> {
|
||||
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()],
|
||||
);
|
||||
const pmcData = profileTemplateClone.character;
|
||||
|
@ -64,8 +64,8 @@ export class App {
|
||||
await onLoad.onLoad();
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
this.update(this.onUpdateComponents);
|
||||
setInterval(async () => {
|
||||
await this.update(this.onUpdateComponents);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
export interface ICloner {
|
||||
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 {
|
||||
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)}`);
|
||||
}
|
||||
|
||||
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 {
|
||||
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