mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 09:50:43 -05:00
# Conflicts: # project/.vscode/launch.json # project/assets/database/locations/bigmap/base.json # project/assets/database/locations/interchange/base.json # project/assets/database/locations/rezervbase/base.json # project/gulpfile.mjs # project/package.json # project/src/ErrorHandler.ts # project/src/Program.ts # project/src/callbacks/DataCallbacks.ts # project/src/callbacks/DialogueCallbacks.ts # project/src/callbacks/GameCallbacks.ts # project/src/callbacks/HandbookCallbacks.ts # project/src/callbacks/HealthCallbacks.ts # project/src/callbacks/HttpCallbacks.ts # project/src/callbacks/LauncherCallbacks.ts # project/src/callbacks/LocationCallbacks.ts # project/src/callbacks/MatchCallbacks.ts # project/src/callbacks/ModCallbacks.ts # project/src/callbacks/NotifierCallbacks.ts # project/src/callbacks/PresetCallbacks.ts # project/src/callbacks/ProfileCallbacks.ts # project/src/callbacks/RagfairCallbacks.ts # project/src/callbacks/TraderCallbacks.ts # project/src/context/ApplicationContext.ts # project/src/context/ContextVariableType.ts # project/src/controllers/BotController.ts # project/src/controllers/CustomizationController.ts # project/src/controllers/DialogueController.ts # project/src/controllers/GameController.ts # project/src/controllers/HealthController.ts # project/src/controllers/HideoutController.ts # project/src/controllers/InraidController.ts # project/src/controllers/InsuranceController.ts # project/src/controllers/InventoryController.ts # project/src/controllers/LauncherController.ts # project/src/controllers/LocationController.ts # project/src/controllers/MatchController.ts # project/src/controllers/QuestController.ts # project/src/controllers/RagfairController.ts # project/src/controllers/RepeatableQuestController.ts # project/src/controllers/TradeController.ts # project/src/di/Container.ts # project/src/di/Router.ts # project/src/generators/BotEquipmentModGenerator.ts # project/src/generators/BotLevelGenerator.ts # project/src/generators/BotWeaponGenerator.ts # project/src/generators/LocationGenerator.ts # project/src/generators/LootGenerator.ts # project/src/generators/RepeatableQuestGenerator.ts # project/src/generators/WeatherGenerator.ts # project/src/generators/weapongen/InventoryMagGen.ts # project/src/generators/weapongen/implementations/BarrelInventoryMagGen.ts # project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts # project/src/helpers/AssortHelper.ts # project/src/helpers/BotGeneratorHelper.ts # project/src/helpers/InRaidHelper.ts # project/src/helpers/ProfileHelper.ts # project/src/helpers/RagfairHelper.ts # project/src/helpers/RagfairOfferHelper.ts # project/src/helpers/TraderHelper.ts # project/src/loaders/ModLoadOrder.ts # project/src/loaders/PostDBModLoader.ts # project/src/loaders/PreAkiModLoader.ts # project/src/models/eft/common/IGlobals.ts # project/src/models/eft/common/ILocationBase.ts # project/src/models/eft/common/tables/IBotBase.ts # project/src/models/eft/common/tables/IProfileTemplate.ts # project/src/models/eft/common/tables/ITemplateItem.ts # project/src/models/eft/dialog/IAcceptFriendRequestData.ts # project/src/models/eft/dialog/IDeleteFriendRequest.ts # project/src/models/eft/game/IGameConfigResponse.ts # project/src/models/eft/game/IGameKeepAliveResponse.ts # project/src/models/eft/game/IGameStartResponse.ts # project/src/models/eft/match/IJoinMatchResult.ts # project/src/models/eft/notifier/INotifier.ts # project/src/models/eft/profile/GetProfileStatusResponseData.ts # project/src/models/eft/trade/IProcessBuyTradeRequestData.ts # project/src/models/eft/trade/IProcessSellTradeRequestData.ts # project/src/models/enums/WildSpawnTypeNumber.ts # project/src/models/spt/bots/BotGenerationDetails.ts # project/src/models/spt/config/IBotConfig.ts # project/src/models/spt/config/IBotDurability.ts # project/src/models/spt/config/IInRaidConfig.ts # project/src/models/spt/config/ILocationConfig.ts # project/src/models/spt/config/IQuestConfig.ts # project/src/models/spt/config/ISeasonalEventConfig.ts # project/src/models/spt/server/ILocations.ts # project/src/models/spt/utils/IUuidGenerator.ts # project/src/routers/dynamic/BotDynamicRouter.ts # project/src/routers/dynamic/BundleDynamicRouter.ts # project/src/routers/dynamic/CustomizationDynamicRouter.ts # project/src/routers/dynamic/DataDynamicRouter.ts # project/src/routers/dynamic/HttpDynamicRouter.ts # project/src/routers/dynamic/InraidDynamicRouter.ts # project/src/routers/dynamic/LocationDynamicRouter.ts # project/src/routers/dynamic/NotifierDynamicRouter.ts # project/src/routers/dynamic/TraderDynamicRouter.ts # project/src/routers/save_load/InsuranceSaveLoadRouter.ts # project/src/routers/save_load/ProfileSaveLoadRouter.ts # project/src/routers/serializers/NotifySerializer.ts # project/src/routers/static/BotStaticRouter.ts # project/src/routers/static/BundleStaticRouter.ts # project/src/routers/static/ClientLogStaticRouter.ts # project/src/routers/static/CustomizationStaticRouter.ts # project/src/routers/static/DataStaticRouter.ts # project/src/routers/static/DialogStaticRouter.ts # project/src/routers/static/GameStaticRouter.ts # project/src/routers/static/HealthStaticRouter.ts # project/src/routers/static/InraidStaticRouter.ts # project/src/routers/static/InsuranceStaticRouter.ts # project/src/routers/static/ItemEventStaticRouter.ts # project/src/routers/static/LauncherStaticRouter.ts # project/src/routers/static/LocationStaticRouter.ts # project/src/routers/static/MatchStaticRouter.ts # project/src/routers/static/NotifierStaticRouter.ts # project/src/routers/static/PresetStaticRouter.ts # project/src/routers/static/ProfileStaticRouter.ts # project/src/routers/static/QuestStaticRouter.ts # project/src/routers/static/RagfairStaticRouter.ts # project/src/routers/static/TraderStaticRouter.ts # project/src/routers/static/WeatherStaticRouter.ts # project/src/services/BotEquipmentFilterService.ts # project/src/services/BotGenerationCacheService.ts # project/src/services/BotWeaponModLimitService.ts # project/src/services/PaymentService.ts # project/src/services/ProfileFixerService.ts # project/src/services/RagfairOfferService.ts # project/src/services/RagfairTaxService.ts # project/src/services/RepairService.ts # project/src/services/SeasonalEventService.ts # project/src/utils/RagfairOfferHolder.ts # project/src/utils/TimeUtil.ts # project/src/utils/UUidGenerator.ts # project/src/utils/VFS.ts # project/src/utils/collections/queue/Queue.ts # project/src/utils/logging/AbstractWinstonLogger.ts # project/src/utils/logging/WinstonMainLogger.ts # project/src/utils/logging/WinstonRequestLogger.ts # project/tests/utils/TimeUtil.test.ts Manually resolved by Refringe.
355 lines
13 KiB
TypeScript
355 lines
13 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
|
|
import { PlayerScavGenerator } from "@spt-aki/generators/PlayerScavGenerator";
|
|
import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper";
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
|
import { QuestHelper } from "@spt-aki/helpers/QuestHelper";
|
|
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
|
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
|
import { TemplateSide } from "@spt-aki/models/eft/common/tables/IProfileTemplate";
|
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
|
import { IMiniProfile } from "@spt-aki/models/eft/launcher/IMiniProfile";
|
|
import { IAkiProfile, Inraid, Vitality } from "@spt-aki/models/eft/profile/IAkiProfile";
|
|
import { IProfileChangeNicknameRequestData } from "@spt-aki/models/eft/profile/IProfileChangeNicknameRequestData";
|
|
import { IProfileChangeVoiceRequestData } from "@spt-aki/models/eft/profile/IProfileChangeVoiceRequestData";
|
|
import { IProfileCreateRequestData } from "@spt-aki/models/eft/profile/IProfileCreateRequestData";
|
|
import { ISearchFriendRequestData } from "@spt-aki/models/eft/profile/ISearchFriendRequestData";
|
|
import { ISearchFriendResponse } from "@spt-aki/models/eft/profile/ISearchFriendResponse";
|
|
import { IValidateNicknameRequestData } from "@spt-aki/models/eft/profile/IValidateNicknameRequestData";
|
|
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
|
import { QuestStatus } from "@spt-aki/models/enums/QuestStatus";
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
import { MailSendService } from "@spt-aki/services/MailSendService";
|
|
import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService";
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
|
|
|
@injectable()
|
|
export class ProfileController
|
|
{
|
|
constructor(
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
|
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
|
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
|
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
|
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
|
@inject("QuestHelper") protected questHelper: QuestHelper,
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
|
)
|
|
{}
|
|
|
|
/**
|
|
* Handle /launcher/profiles
|
|
*/
|
|
public getMiniProfiles(): IMiniProfile[]
|
|
{
|
|
const miniProfiles: IMiniProfile[] = [];
|
|
|
|
for (const sessionIdKey in this.saveServer.getProfiles())
|
|
{
|
|
miniProfiles.push(this.getMiniProfile(sessionIdKey));
|
|
}
|
|
|
|
return miniProfiles;
|
|
}
|
|
|
|
/**
|
|
* Handle launcher/profile/info
|
|
*/
|
|
public getMiniProfile(sessionID: string): any
|
|
{
|
|
const maxlvl = this.profileHelper.getMaxLevel();
|
|
const profile = this.saveServer.getProfile(sessionID);
|
|
const pmc = profile.characters.pmc;
|
|
|
|
// make sure character completed creation
|
|
if (!(pmc?.Info?.Level))
|
|
{
|
|
return {
|
|
username: profile.info.username,
|
|
nickname: "unknown",
|
|
side: "unknown",
|
|
currlvl: 0,
|
|
currexp: 0,
|
|
prevexp: 0,
|
|
nextlvl: 0,
|
|
maxlvl: maxlvl,
|
|
akiData: this.profileHelper.getDefaultAkiDataObject(),
|
|
};
|
|
}
|
|
|
|
const currlvl = pmc.Info.Level;
|
|
const nextlvl = this.profileHelper.getExperience(currlvl + 1);
|
|
const result = {
|
|
username: profile.info.username,
|
|
nickname: pmc.Info.Nickname,
|
|
side: pmc.Info.Side,
|
|
currlvl: pmc.Info.Level,
|
|
currexp: pmc.Info.Experience ?? 0,
|
|
prevexp: (currlvl === 0) ? 0 : this.profileHelper.getExperience(currlvl),
|
|
nextlvl: nextlvl,
|
|
maxlvl: maxlvl,
|
|
akiData: profile.aki,
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/list
|
|
*/
|
|
public getCompleteProfile(sessionID: string): IPmcData[]
|
|
{
|
|
return this.profileHelper.getCompleteProfile(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/create
|
|
*/
|
|
public createProfile(info: IProfileCreateRequestData, sessionID: string): void
|
|
{
|
|
const account = this.saveServer.getProfile(sessionID).info;
|
|
const profile: TemplateSide =
|
|
this.databaseServer.getTables().templates.profiles[account.edition][info.side.toLowerCase()];
|
|
const pmcData = profile.character;
|
|
|
|
// Delete existing profile
|
|
this.deleteProfileBySessionId(sessionID);
|
|
|
|
// PMC
|
|
pmcData._id = `pmc${sessionID}`;
|
|
pmcData.aid = account.aid;
|
|
pmcData.savage = `scav${sessionID}`;
|
|
pmcData.sessionId = sessionID;
|
|
pmcData.Info.Nickname = info.nickname;
|
|
pmcData.Info.LowerNickname = info.nickname.toLowerCase();
|
|
pmcData.Info.RegistrationDate = this.timeUtil.getTimestamp();
|
|
pmcData.Info.Voice = this.databaseServer.getTables().templates.customization[info.voiceId]._name;
|
|
pmcData.Stats = this.profileHelper.getDefaultCounters();
|
|
pmcData.Info.NeedWipeOptions = [];
|
|
pmcData.Customization.Head = info.headId;
|
|
pmcData.Health.UpdateTime = this.timeUtil.getTimestamp();
|
|
pmcData.Quests = [];
|
|
pmcData.Hideout.Seed = this.timeUtil.getTimestamp() + (8 * 60 * 60 * 24 * 365); // 8 years in future why? who knows, we saw it in live
|
|
pmcData.RepeatableQuests = [];
|
|
pmcData.CarExtractCounts = {};
|
|
pmcData.CoopExtractCounts = {};
|
|
|
|
if (!pmcData.UnlockedInfo)
|
|
{
|
|
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] };
|
|
}
|
|
|
|
// Change item id's to be unique
|
|
pmcData.Inventory.items = this.itemHelper.replaceIDs(
|
|
pmcData,
|
|
pmcData.Inventory.items,
|
|
null,
|
|
pmcData.Inventory.fastPanel,
|
|
);
|
|
pmcData.Inventory.hideoutAreaStashes = {};
|
|
|
|
// Create profile
|
|
const profileDetails: IAkiProfile = {
|
|
info: account,
|
|
characters: { pmc: pmcData, scav: {} as IPmcData },
|
|
suits: profile.suits,
|
|
userbuilds: profile.userbuilds,
|
|
dialogues: profile.dialogues,
|
|
aki: this.profileHelper.getDefaultAkiDataObject(),
|
|
vitality: {} as Vitality,
|
|
inraid: {} as Inraid,
|
|
insurance: [],
|
|
traderPurchases: {},
|
|
};
|
|
|
|
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
|
|
this.profileFixerService.addMissingHideoutBonusesToProfile(profileDetails.characters.pmc);
|
|
|
|
this.saveServer.addProfile(profileDetails);
|
|
|
|
if (profile.trader.setQuestsAvailableForStart)
|
|
{
|
|
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
|
|
}
|
|
|
|
// Profile is flagged as wanting quests set to ready to hand in and collect rewards
|
|
if (profile.trader.setQuestsAvailableForFinish)
|
|
{
|
|
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [
|
|
QuestStatus.AvailableForStart,
|
|
QuestStatus.Started,
|
|
QuestStatus.AvailableForFinish,
|
|
]);
|
|
|
|
// Make unused response so applyQuestReward works
|
|
const response = this.eventOutputHolder.getOutput(sessionID);
|
|
|
|
// Add rewards for starting quests to profile
|
|
this.givePlayerStartingQuestRewards(profileDetails, sessionID, response);
|
|
}
|
|
|
|
this.saveServer.getProfile(sessionID).characters.scav = this.generatePlayerScav(sessionID);
|
|
|
|
this.resetAllTradersInProfile(sessionID);
|
|
|
|
// Store minimal profile and reload it
|
|
this.saveServer.saveProfile(sessionID);
|
|
this.saveServer.loadProfile(sessionID);
|
|
|
|
// Completed account creation
|
|
this.saveServer.getProfile(sessionID).info.wipe = false;
|
|
this.saveServer.saveProfile(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Delete a profile
|
|
* @param sessionID Id of profile to delete
|
|
*/
|
|
protected deleteProfileBySessionId(sessionID: string): void
|
|
{
|
|
if (sessionID in this.saveServer.getProfiles())
|
|
{
|
|
this.saveServer.deleteProfileById(sessionID);
|
|
}
|
|
else
|
|
{
|
|
this.logger.warning(
|
|
this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID),
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iterate over all quests in player profile, inspect rewards for the quests current state (accepted/completed)
|
|
* and send rewards to them in mail
|
|
* @param profileDetails Player profile
|
|
* @param sessionID Session id
|
|
* @param response Event router response
|
|
*/
|
|
protected givePlayerStartingQuestRewards(
|
|
profileDetails: IAkiProfile,
|
|
sessionID: string,
|
|
response: IItemEventRouterResponse,
|
|
): void
|
|
{
|
|
for (const quest of profileDetails.characters.pmc.Quests)
|
|
{
|
|
const questFromDb = this.questHelper.getQuestFromDb(quest.qid, profileDetails.characters.pmc);
|
|
|
|
// Get messageId of text to send to player as text message in game
|
|
// Copy of code from QuestController.acceptQuest()
|
|
const messageId = this.questHelper.getMessageIdForQuestStart(
|
|
questFromDb.startedMessageText,
|
|
questFromDb.description,
|
|
);
|
|
const itemRewards = this.questHelper.applyQuestReward(
|
|
profileDetails.characters.pmc,
|
|
quest.qid,
|
|
QuestStatus.Started,
|
|
sessionID,
|
|
response,
|
|
);
|
|
|
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
|
sessionID,
|
|
this.traderHelper.getTraderById(questFromDb.traderId),
|
|
MessageType.QUEST_START,
|
|
messageId,
|
|
itemRewards,
|
|
this.timeUtil.getHoursAsSeconds(100),
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For each trader reset their state to what a level 1 player would see
|
|
* @param sessionID Session id of profile to reset
|
|
*/
|
|
protected resetAllTradersInProfile(sessionID: string): void
|
|
{
|
|
for (const traderID in this.databaseServer.getTables().traders)
|
|
{
|
|
this.traderHelper.resetTrader(sessionID, traderID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a player scav object
|
|
* PMC profile MUST exist first before pscav can be generated
|
|
* @param sessionID
|
|
* @returns IPmcData object
|
|
*/
|
|
public generatePlayerScav(sessionID: string): IPmcData
|
|
{
|
|
return this.playerScavGenerator.generate(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/nickname/validate
|
|
*/
|
|
public validateNickname(info: IValidateNicknameRequestData, sessionID: string): string
|
|
{
|
|
if (info.nickname.length < 3)
|
|
{
|
|
return "tooshort";
|
|
}
|
|
|
|
if (this.profileHelper.isNicknameTaken(info, sessionID))
|
|
{
|
|
return "taken";
|
|
}
|
|
|
|
return "OK";
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/nickname/change event
|
|
* Client allows player to adjust their profile name
|
|
*/
|
|
public changeNickname(info: IProfileChangeNicknameRequestData, sessionID: string): string
|
|
{
|
|
const output = this.validateNickname(info, sessionID);
|
|
|
|
if (output === "OK")
|
|
{
|
|
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
|
|
|
pmcData.Info.Nickname = info.nickname;
|
|
pmcData.Info.LowerNickname = info.nickname.toLowerCase();
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/voice/change event
|
|
*/
|
|
public changeVoice(info: IProfileChangeVoiceRequestData, sessionID: string): void
|
|
{
|
|
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
|
pmcData.Info.Voice = info.voice;
|
|
}
|
|
|
|
/**
|
|
* Handle client/game/profile/search
|
|
*/
|
|
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[]
|
|
{
|
|
return [{ _id: this.hashUtil.generate(), Info: { Level: 1, Side: "Bear", Nickname: info.nickname } }];
|
|
}
|
|
}
|