Compare commits

..

3 Commits

Author SHA1 Message Date
3e80d96b39 use inventory projection in contributeToVaultController
All checks were successful
Build / build (18) (push) Successful in 1m16s
Build / build (18) (pull_request) Successful in 43s
Build / build (22) (push) Successful in 40s
Build / build (20) (push) Successful in 1m13s
Build / build (20) (pull_request) Successful in 1m14s
Build / build (22) (pull_request) Successful in 1m12s
2025-03-31 03:30:32 +02:00
eadf7a530d prettier
All checks were successful
Build / build (18) (push) Successful in 42s
Build / build (20) (push) Successful in 1m12s
Build / build (22) (push) Successful in 1m9s
Build / build (18) (pull_request) Successful in 43s
Build / build (20) (pull_request) Successful in 1m13s
Build / build (22) (pull_request) Successful in 1m17s
2025-03-31 03:23:39 +02:00
12130c1eac chore: run save operatons in parallel where possible
Some checks failed
Build / build (22) (push) Failing after 38s
Build / build (18) (push) Failing after 1m16s
Build / build (20) (push) Failing after 1m3s
Build / build (18) (pull_request) Failing after 42s
Build / build (20) (pull_request) Failing after 1m12s
Build / build (22) (pull_request) Failing after 1m5s
2025-03-31 03:18:43 +02:00
27 changed files with 78 additions and 329 deletions

View File

@ -1,20 +0,0 @@
import { GuildAd } from "@/src/models/guildModel";
import { getGuildForRequestEx, hasGuildPermission } from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
export const cancelGuildAdvertisementController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory);
if (!(await hasGuildPermission(guild, accountId, GuildPermission.Advertiser))) {
res.status(400).end();
return;
}
await GuildAd.deleteOne({ GuildId: guild._id });
res.end();
};

View File

@ -99,11 +99,6 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
inventory.SpectreLoadouts.push(inventory.PendingSpectreLoadouts[pendingLoadoutIndex]);
inventory.PendingSpectreLoadouts.splice(pendingLoadoutIndex, 1);
}
} else if (recipe.secretIngredientAction == "SIA_UNBRAND") {
inventory.BrandedSuits!.splice(
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
1
);
}
let InventoryChanges = {};
@ -116,23 +111,15 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
]);
}
if (req.query.rush) {
const end = Math.trunc(pendingRecipe.CompletionDate.getTime() / 1000);
const start = end - recipe.buildTime;
const secondsElapsed = Math.trunc(Date.now() / 1000) - start;
const progress = secondsElapsed / recipe.buildTime;
logger.debug(`rushing recipe at ${Math.trunc(progress * 100)}% completion`);
const cost = Math.round(recipe.skipBuildTimePrice * (1 - (progress - 0.5)));
InventoryChanges = {
...InventoryChanges,
...updateCurrency(inventory, cost, true)
};
}
if (recipe.secretIngredientAction != "SIA_UNBRAND") {
InventoryChanges = {
...InventoryChanges,
...(await addItem(inventory, recipe.resultType, recipe.num, false))
...updateCurrency(inventory, recipe.skipBuildTimePrice, true)
};
}
InventoryChanges = {
...InventoryChanges,
...(await addItem(inventory, recipe.resultType, recipe.num, false))
};
await inventory.save();
res.json({ InventoryChanges });
}

View File

@ -1,7 +1,6 @@
import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import {
addGuildMemberMiscItemContribution,
getDojoClient,
getGuildForRequestEx,
hasAccessToDojo,
@ -142,7 +141,8 @@ const processContribution = (
ItemCount: ingredientContribution.ItemCount * -1
});
addGuildMemberMiscItemContribution(guildMember, ingredientContribution);
guildMember.MiscItemsContributed ??= [];
guildMember.MiscItemsContributed.push(ingredientContribution);
}
addMiscItems(inventory, miscItemChanges);
inventoryChanges.MiscItems = miscItemChanges;

View File

@ -1,9 +1,4 @@
import { GuildMember } from "@/src/models/guildModel";
import {
addGuildMemberMiscItemContribution,
addVaultMiscItems,
getGuildForRequestEx
} from "@/src/services/guildService";
import { addVaultMiscItems, getGuildForRequestEx } from "@/src/services/guildService";
import {
addFusionTreasures,
@ -38,8 +33,14 @@ export const contributeToVaultController: RequestHandler = async (req, res) => {
if (request.MiscItems.length) {
addVaultMiscItems(guild, request.MiscItems);
guildMember.MiscItemsContributed ??= [];
for (const item of request.MiscItems) {
addGuildMemberMiscItemContribution(guildMember, item);
const miscItemContribution = guildMember.MiscItemsContributed.find(x => x.ItemType == item.ItemType);
if (miscItemContribution) {
miscItemContribution.ItemCount += item.ItemCount;
} else {
guildMember.MiscItemsContributed.push(item);
}
addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
}

View File

@ -17,7 +17,7 @@ export const evolveWeaponController: RequestHandler = async (req, res) => {
recipe.ingredients.map(x => ({ ItemType: x.ItemType, ItemCount: x.ItemCount * -1 }))
);
const item = inventory[payload.Category].id(req.query.ItemId as string)!;
const item = inventory[payload.Category].find(item => item._id.toString() == (req.query.ItemId as string))!;
item.Features ??= 0;
item.Features |= EquipmentFeatures.INCARNON_GENESIS;
@ -39,7 +39,7 @@ export const evolveWeaponController: RequestHandler = async (req, res) => {
}
]);
const item = inventory[payload.Category].id(req.query.ItemId as string)!;
const item = inventory[payload.Category].find(item => item._id.toString() == (req.query.ItemId as string))!;
item.Features! &= ~EquipmentFeatures.INCARNON_GENESIS;
} else {
throw new Error(`unexpected evolve weapon action: ${payload.Action}`);

View File

@ -18,15 +18,17 @@ export const focusController: RequestHandler = async (req, res) => {
case FocusOperation.InstallLens: {
const request = JSON.parse(String(req.body)) as ILensInstallRequest;
const inventory = await getInventory(accountId);
const item = inventory[request.Category].id(request.WeaponId);
if (item) {
item.FocusLens = request.LensType;
addMiscItems(inventory, [
{
ItemType: request.LensType,
ItemCount: -1
} satisfies IMiscItem
]);
for (const item of inventory[request.Category]) {
if (item._id.toString() == request.WeaponId) {
item.FocusLens = request.LensType;
addMiscItems(inventory, [
{
ItemType: request.LensType,
ItemCount: -1
} satisfies IMiscItem
]);
break;
}
}
await inventory.save();
res.json({

View File

@ -1,6 +1,5 @@
import { RequestHandler } from "express";
import {
addGuildMemberMiscItemContribution,
getGuildForRequestEx,
getGuildVault,
hasAccessToDojo,
@ -147,7 +146,8 @@ export const guildTechController: RequestHandler = async (req, res) => {
ItemCount: miscItem.ItemCount * -1
});
addGuildMemberMiscItemContribution(guildMember, miscItem);
guildMember.MiscItemsContributed ??= [];
guildMember.MiscItemsContributed.push(miscItem);
}
}
addMiscItems(inventory, miscItemChanges);

View File

@ -28,7 +28,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
// shard installation
const request = getJSONfromString<IShardInstallRequest>(String(req.body));
const inventory = await getInventory(accountId);
const suit = inventory.Suits.id(request.SuitId.$oid)!;
const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!;
if (!suit.ArchonCrystalUpgrades || suit.ArchonCrystalUpgrades.length != 5) {
suit.ArchonCrystalUpgrades = [{}, {}, {}, {}, {}];
}
@ -56,7 +56,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
// shard removal
const request = getJSONfromString<IShardUninstallRequest>(String(req.body));
const inventory = await getInventory(accountId);
const suit = inventory.Suits.id(request.SuitId.$oid)!;
const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!;
const miscItemChanges: IMiscItem[] = [];
if (suit.ArchonCrystalUpgrades![request.Slot].Color) {

View File

@ -12,7 +12,9 @@ export const nameWeaponController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const body = getJSONfromString<INameWeaponRequest>(String(req.body));
const item = inventory[req.query.Category as string as TEquipmentKey].id(req.query.ItemId as string)!;
const item = inventory[req.query.Category as string as TEquipmentKey].find(
item => item._id.toString() == (req.query.ItemId as string)
)!;
if (body.ItemName != "") {
item.ItemName = body.ItemName;
} else {

View File

@ -1,75 +0,0 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { GuildAd, GuildMember } from "@/src/models/guildModel";
import {
addGuildMemberMiscItemContribution,
addVaultMiscItems,
getGuildForRequestEx,
getVaultMiscItemCount,
hasGuildPermissionEx
} from "@/src/services/guildService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getVendorManifestByTypeName, preprocessVendorManifest } from "@/src/services/serversideVendorsService";
import { GuildPermission } from "@/src/types/guildTypes";
import { IPurchaseParams } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
export const postGuildAdvertisementController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "GuildId MiscItems");
const guild = await getGuildForRequestEx(req, inventory);
const guildMember = (await GuildMember.findOne({ accountId, guildId: guild._id }))!;
if (!hasGuildPermissionEx(guild, guildMember, GuildPermission.Advertiser)) {
res.status(400).end();
return;
}
const payload = getJSONfromString<IPostGuildAdvertisementRequest>(String(req.body));
// Handle resource cost
const vendor = preprocessVendorManifest(
getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest")!
);
const offer = vendor.VendorInfo.ItemManifest.find(x => x.StoreItem == payload.PurchaseParams.StoreItem)!;
if (getVaultMiscItemCount(guild, offer.ItemPrices![0].ItemType) >= offer.ItemPrices![0].ItemCount) {
addVaultMiscItems(guild, [
{
ItemType: offer.ItemPrices![0].ItemType,
ItemCount: offer.ItemPrices![0].ItemCount * -1
}
]);
} else {
const miscItem = inventory.MiscItems.find(x => x.ItemType == offer.ItemPrices![0].ItemType);
if (!miscItem || miscItem.ItemCount < offer.ItemPrices![0].ItemCount) {
res.status(400).json("Insufficient funds");
return;
}
miscItem.ItemCount -= offer.ItemPrices![0].ItemCount;
addGuildMemberMiscItemContribution(guildMember, offer.ItemPrices![0]);
await guildMember.save();
await inventory.save();
}
// Create or update ad
await GuildAd.findOneAndUpdate(
{ GuildId: guild._id },
{
Emblem: guild.Emblem,
Expiry: new Date(Date.now() + 12 * 3600 * 1000),
Features: payload.Features,
GuildName: guild.Name,
MemberCount: await GuildMember.countDocuments({ guildId: guild._id, status: 0 }),
RecruitMsg: payload.RecruitMsg,
Tier: guild.Tier
},
{ upsert: true }
);
res.end();
};
interface IPostGuildAdvertisementRequest {
Features: number;
RecruitMsg: string;
Languages: string[];
PurchaseParams: IPurchaseParams;
}

View File

@ -13,42 +13,7 @@ import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
export const sellController: RequestHandler = async (req, res) => {
const payload = JSON.parse(String(req.body)) as ISellRequest;
const accountId = await getAccountIdForRequest(req);
const requiredFields = new Set();
if (payload.SellCurrency == "SC_RegularCredits") {
requiredFields.add("RegularCredits");
} else if (payload.SellCurrency == "SC_FusionPoints") {
requiredFields.add("FusionPoints");
} else {
requiredFields.add("MiscItems");
}
for (const key of Object.keys(payload.Items)) {
requiredFields.add(key);
}
if (requiredFields.has("Upgrades")) {
requiredFields.add("RawUpgrades");
}
if (payload.Items.Suits) {
requiredFields.add(InventorySlot.SUITS);
}
if (payload.Items.LongGuns || payload.Items.Pistols || payload.Items.Melee) {
requiredFields.add(InventorySlot.WEAPONS);
}
if (payload.Items.SpaceSuits) {
requiredFields.add(InventorySlot.SPACESUITS);
}
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
requiredFields.add(InventorySlot.SPACEWEAPONS);
}
if (payload.Items.Sentinels || payload.Items.SentinelWeapons) {
requiredFields.add(InventorySlot.SENTINELS);
}
if (payload.Items.OperatorAmps) {
requiredFields.add(InventorySlot.AMPS);
}
if (payload.Items.Hoverboards) {
requiredFields.add(InventorySlot.SPACESUITS);
}
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
const inventory = await getInventory(accountId);
// Give currency
if (payload.SellCurrency == "SC_RegularCredits") {

View File

@ -6,10 +6,12 @@ import { WeaponTypeInternal } from "@/src/services/itemDataService";
export const setWeaponSkillTreeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, req.query.Category as string);
const inventory = await getInventory(accountId);
const payload = getJSONfromString<ISetWeaponSkillTreeRequest>(String(req.body));
const item = inventory[req.query.Category as WeaponTypeInternal].id(req.query.ItemId as string)!;
const item = inventory[req.query.Category as WeaponTypeInternal].find(
item => item._id.toString() == (req.query.ItemId as string)
)!;
item.SkillTree = payload.SkillTree;
await inventory.save();

View File

@ -111,8 +111,6 @@ export const startRecipeController: RequestHandler = async (req, res) => {
inventory.PendingSpectreLoadouts.push(spectreLoadout);
logger.debug("pending spectre loadout", spectreLoadout);
}
} else if (recipe.secretIngredientAction == "SIA_UNBRAND") {
pr.SuitToUnbrand = new Types.ObjectId(startRecipeRequest.Ids[recipe.ingredients.length + 0]);
}
await inventory.save();

View File

@ -5,7 +5,7 @@ import { getInventory } from "@/src/services/inventoryService";
export const popArchonCrystalUpgradeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const suit = inventory.Suits.id(req.query.oid as string);
const suit = inventory.Suits.find(suit => suit._id.toString() == (req.query.oid as string));
if (suit && suit.ArchonCrystalUpgrades) {
suit.ArchonCrystalUpgrades = suit.ArchonCrystalUpgrades.filter(
x => x.UpgradeType != (req.query.type as string)

View File

@ -5,7 +5,7 @@ import { getInventory } from "@/src/services/inventoryService";
export const pushArchonCrystalUpgradeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const suit = inventory.Suits.id(req.query.oid as string);
const suit = inventory.Suits.find(suit => suit._id.toString() == (req.query.oid as string));
if (suit) {
suit.ArchonCrystalUpgrades ??= [];
const count = (req.query.count as number | undefined) ?? 1;

View File

@ -1,26 +0,0 @@
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { GuildAd } from "@/src/models/guildModel";
import { IGuildAdInfoClient } from "@/src/types/guildTypes";
import { RequestHandler } from "express";
export const getGuildAdsController: RequestHandler = async (req, res) => {
const ads = await GuildAd.find(req.query.tier ? { Tier: req.query.tier } : {});
const guildAdInfos: IGuildAdInfoClient[] = [];
for (const ad of ads) {
guildAdInfos.push({
_id: toOid(ad.GuildId),
CrossPlatformEnabled: true,
Emblem: ad.Emblem,
Expiry: toMongoDate(ad.Expiry),
Features: ad.Features,
GuildName: ad.GuildName,
MemberCount: ad.MemberCount,
OriginalPlatform: 0,
RecruitMsg: ad.RecruitMsg,
Tier: ad.Tier
});
}
res.json({
GuildAdInfos: guildAdInfos
});
};

View File

@ -10,8 +10,7 @@ import {
IGuildLogRoomChange,
IGuildLogEntryRoster,
IGuildLogEntryContributable,
IDojoLeaderboardEntry,
IGuildAdDatabase
IDojoLeaderboardEntry
} from "@/src/types/guildTypes";
import { Document, Model, model, Schema, Types } from "mongoose";
import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel";
@ -166,7 +165,6 @@ const guildSchema = new Schema<IGuildDatabase>(
Ranks: { type: [guildRankSchema], default: defaultRanks },
TradeTax: { type: Number, default: 0 },
Tier: { type: Number, default: 1 },
Emblem: { type: Boolean },
DojoComponents: { type: [dojoComponentSchema], default: [] },
DojoCapacity: { type: Number, default: 100 },
DojoEnergy: { type: Number, default: 5 },
@ -227,19 +225,3 @@ const guildMemberSchema = new Schema<IGuildMemberDatabase>({
guildMemberSchema.index({ accountId: 1, guildId: 1 }, { unique: true });
export const GuildMember = model<IGuildMemberDatabase>("GuildMember", guildMemberSchema);
const guildAdSchema = new Schema<IGuildAdDatabase>({
GuildId: { type: Schema.Types.ObjectId, required: true },
Emblem: Boolean,
Expiry: { type: Date, required: true },
Features: { type: Number, required: true },
GuildName: { type: String, required: true },
MemberCount: { type: Number, required: true },
RecruitMsg: { type: String, required: true },
Tier: { type: Number, required: true }
});
guildAdSchema.index({ GuildId: 1 }, { unique: true });
guildAdSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 });
export const GuildAd = model<IGuildAdDatabase>("GuildAd", guildAdSchema);

View File

@ -903,8 +903,7 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
CompletionDate: Date,
LongGuns: { type: [EquipmentSchema], default: undefined },
Pistols: { type: [EquipmentSchema], default: undefined },
Melee: { type: [EquipmentSchema], default: undefined },
SuitToUnbrand: { type: Schema.Types.ObjectId, default: undefined }
Melee: { type: [EquipmentSchema], default: undefined }
},
{ id: false }
);
@ -921,7 +920,6 @@ pendingRecipeSchema.set("toJSON", {
delete returnedObject.LongGuns;
delete returnedObject.Pistols;
delete returnedObject.Melees;
delete returnedObject.SuitToUnbrand;
(returnedObject as IPendingRecipeClient).CompletionDate = {
$date: { $numberLong: (returnedObject as IPendingRecipeDatabase).CompletionDate.getTime().toString() }
};
@ -1486,9 +1484,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
EchoesHexConquestHardModeStatus: { type: Number, default: undefined },
EchoesHexConquestCacheScoreMission: { type: Number, default: undefined },
EchoesHexConquestActiveFrameVariants: { type: [String], default: undefined },
EchoesHexConquestActiveStickers: { type: [String], default: undefined },
BrandedSuits: { type: [Schema.Types.ObjectId], default: undefined }
EchoesHexConquestActiveStickers: { type: [String], default: undefined }
},
{ timestamps: { createdAt: "Created", updatedAt: false } }
);
@ -1520,9 +1516,6 @@ inventorySchema.set("toJSON", {
if (inventoryDatabase.EntratiVaultCountResetDate) {
inventoryResponse.EntratiVaultCountResetDate = toMongoDate(inventoryDatabase.EntratiVaultCountResetDate);
}
if (inventoryDatabase.BrandedSuits) {
inventoryResponse.BrandedSuits = inventoryDatabase.BrandedSuits.map(toOid);
}
}
});

View File

@ -9,7 +9,6 @@ import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonContro
import { archonFusionController } from "@/src/controllers/api/archonFusionController";
import { artifactsController } from "@/src/controllers/api/artifactsController";
import { artifactTransmutationController } from "@/src/controllers/api/artifactTransmutationController";
import { cancelGuildAdvertisementController } from "@/src/controllers/api/cancelGuildAdvertisementController";
import { changeDojoRootController } from "@/src/controllers/api/changeDojoRootController";
import { changeGuildRankController } from "@/src/controllers/api/changeGuildRankController";
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
@ -81,7 +80,6 @@ import { nameWeaponController } from "@/src/controllers/api/nameWeaponController
import { nemesisController } from "@/src/controllers/api/nemesisController";
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
import { purchaseController } from "@/src/controllers/api/purchaseController";
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
@ -133,7 +131,6 @@ const apiRouter = express.Router();
// get
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
@ -231,7 +228,6 @@ apiRouter.post("/nameWeapon.php", nameWeaponController);
apiRouter.post("/nemesis.php", nemesisController);
apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
apiRouter.post("/playerSkills.php", playerSkillsController);
apiRouter.post("/postGuildAdvertisement.php", postGuildAdvertisementController);
apiRouter.post("/projectionManager.php", projectionManagerController);
apiRouter.post("/purchase.php", purchaseController);
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);

View File

@ -1,13 +1,11 @@
import express from "express";
import { aggregateSessionsController } from "@/src/controllers/dynamic/aggregateSessionsController";
import { getGuildAdsController } from "@/src/controllers/dynamic/getGuildAdsController";
import { getProfileViewingDataController } from "@/src/controllers/dynamic/getProfileViewingDataController";
import { worldStateController } from "@/src/controllers/dynamic/worldStateController";
const dynamicController = express.Router();
dynamicController.get("/aggregateSessions.php", aggregateSessionsController);
dynamicController.get("/getGuildAds.php", getGuildAdsController);
dynamicController.get("/getProfileViewingData.php", getProfileViewingDataController);
dynamicController.get("/worldState.php", worldStateController);

View File

@ -1,7 +1,7 @@
import { Request } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addRecipes, getInventory } from "@/src/services/inventoryService";
import { Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { Guild, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import {
GuildPermission,
@ -298,10 +298,6 @@ const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoCon
}
};
export const getVaultMiscItemCount = (guild: TGuildDatabaseDocument, itemType: string): number => {
return guild.VaultMiscItems?.find(x => x.ItemType == itemType)?.ItemCount ?? 0;
};
export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITypeCount[]): void => {
guild.VaultMiscItems ??= [];
for (const miscItem of miscItems) {
@ -314,16 +310,6 @@ export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITyp
}
};
export const addGuildMemberMiscItemContribution = (guildMember: IGuildMemberDatabase, item: ITypeCount): void => {
guildMember.MiscItemsContributed ??= [];
const miscItemContribution = guildMember.MiscItemsContributed.find(x => x.ItemType == item.ItemType);
if (miscItemContribution) {
miscItemContribution.ItemCount += item.ItemCount;
} else {
guildMember.MiscItemsContributed.push(item);
}
};
export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument, build: IDojoBuild): void => {
if (build.guildXpValue) {
guild.ClaimedXP ??= [];
@ -527,6 +513,4 @@ export const deleteGuild = async (guildId: Types.ObjectId): Promise<void> => {
contextInfo: guildId.toString(),
acceptAction: "GUILD_INVITE"
});
await GuildAd.deleteOne({ GuildId: guildId });
};

View File

@ -24,6 +24,7 @@ import {
ExportGear,
ExportKeys,
ExportRecipes,
ExportRegions,
ExportResources,
ExportSentinels,
ExportWarframes,
@ -33,6 +34,7 @@ import {
IMissionReward,
IPowersuit,
IRecipe,
IRegion,
TReward
} from "warframe-public-export-plus";
import questCompletionItems from "@/static/fixed_responses/questCompletionRewards.json";
@ -190,6 +192,16 @@ export const getLevelKeyRewards = (
};
};
export const getNode = (nodeName: string): IRegion => {
const node = ExportRegions[nodeName];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!node) {
throw new Error(`Node ${nodeName} not found`);
}
return node;
};
export const getQuestCompletionItems = (questKey: string): ITypeCount[] | undefined => {
if (questKey in questCompletionItems) {
return questCompletionItems[questKey as keyof typeof questCompletionItems];

View File

@ -4,7 +4,6 @@ import {
ExportRegions,
ExportRewards,
IMissionReward as IMissionRewardExternal,
IRegion,
IReward
} from "warframe-public-export-plus";
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
@ -31,9 +30,9 @@ import {
updateSyndicate
} from "@/src/services/inventoryService";
import { updateQuestKey } from "@/src/services/questService";
import { HydratedDocument, Types } from "mongoose";
import { HydratedDocument } from "mongoose";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService";
import { getLevelKeyRewards, getNode, toStoreItem } from "@/src/services/itemDataService";
import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
@ -46,7 +45,6 @@ import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePe
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
import { getInfNodes } from "@/src/helpers/nemesisHelpers";
import { Loadout } from "../models/inventoryModels/loadoutModel";
const getRotations = (rotationCount: number): number[] => {
if (rotationCount === 0) return [0];
@ -91,31 +89,6 @@ export const addMissionInventoryUpdates = async (
if (inventoryUpdates.RewardInfo && inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
}
if (
inventoryUpdates.MissionFailed &&
inventoryUpdates.MissionStatus == "GS_FAILURE" &&
inventoryUpdates.EndOfMatchUpload &&
inventoryUpdates.ObjectiveReached
) {
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!;
const config = loadout.NORMAL.id(inventory.CurrentLoadOutIds[0].$oid)!;
const SuitId = new Types.ObjectId(config.s!.ItemId.$oid);
inventory.BrandedSuits ??= [];
if (!inventory.BrandedSuits.find(x => x.equals(SuitId))) {
inventory.BrandedSuits.push(SuitId);
await createMessage(inventory.accountOwnerId.toString(), [
{
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
msg: "/Lotus/Language/G1Quests/BrandedMessage",
sub: "/Lotus/Language/G1Quests/BrandedTitle",
att: ["/Lotus/Types/Recipes/Components/BrandRemovalBlueprint"],
highPriority: true // I cannot find any content of this within the last 10 years so I can only assume that highPriority is set (it certainly would make sense), but I just don't know for sure that it is so on live.
}
]);
}
}
for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
if (value === undefined) {
logger.error(`Inventory update key ${key} has no value `);
@ -470,15 +443,15 @@ export const addMissionRewards = async (
}
}
// ignoring tags not in ExportRegions, because it can just be garbage:
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1013
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1365
if (missions && missions.Tag in ExportRegions) {
const node = ExportRegions[missions.Tag];
if (
missions &&
missions.Tag != "" // https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1013
) {
const node = getNode(missions.Tag);
//node based credit rewards for mission completion
if (node.missionIndex !== 28) {
const levelCreditReward = getLevelCreditRewards(node);
const levelCreditReward = getLevelCreditRewards(missions.Tag);
missionCompletionCredits += levelCreditReward;
inventory.RegularCredits += levelCreditReward;
logger.debug(`levelCreditReward ${levelCreditReward}`);
@ -661,8 +634,8 @@ export const addFixedLevelRewards = (
return missionBonusCredits;
};
function getLevelCreditRewards(node: IRegion): number {
const minEnemyLevel = node.minEnemyLevel;
function getLevelCreditRewards(nodeName: string): number {
const minEnemyLevel = getNode(nodeName).minEnemyLevel;
return 1000 + (minEnemyLevel - 1) * 100;

View File

@ -84,7 +84,9 @@ export const handleInventoryItemConfigChange = async (
continue;
}
const oldLoadoutConfig = loadout[loadoutSlot].id(loadoutId);
const oldLoadoutConfig = loadout[loadoutSlot].find(
loadout => loadout._id.toString() === loadoutId
);
const { ItemId, ...loadoutConfigItemIdRemoved } = loadoutConfig;
const loadoutConfigDatabase: ILoadoutConfigDatabase = {

View File

@ -61,17 +61,19 @@ export const handleSetShipDecorations = async (
if (placedDecoration.MoveId) {
//moved within the same room
if (placedDecoration.OldRoom === placedDecoration.Room) {
const existingDecoration = roomToPlaceIn.PlacedDecos.id(placedDecoration.MoveId);
const existingDecorationIndex = roomToPlaceIn.PlacedDecos.findIndex(
deco => deco._id.toString() === placedDecoration.MoveId
);
if (!existingDecoration) {
if (existingDecorationIndex === -1) {
throw new Error("decoration to be moved not found");
}
existingDecoration.Pos = placedDecoration.Pos;
existingDecoration.Rot = placedDecoration.Rot;
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Pos = placedDecoration.Pos;
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Rot = placedDecoration.Rot;
if (placedDecoration.Scale) {
existingDecoration.Scale = placedDecoration.Scale;
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Scale = placedDecoration.Scale;
}
await personalRooms.save();

View File

@ -28,7 +28,6 @@ export interface IGuildDatabase {
Ranks: IGuildRank[];
TradeTax: number;
Tier: number;
Emblem?: boolean;
DojoComponents: IDojoComponentDatabase[];
DojoCapacity: number;
@ -224,27 +223,3 @@ export interface IDojoLeaderboardEntry {
r: number; // rank
n: string; // displayName
}
export interface IGuildAdInfoClient {
_id: IOid; // Guild ID
CrossPlatformEnabled: boolean;
Emblem?: boolean;
Expiry: IMongoDate;
Features: number;
GuildName: string;
MemberCount: number;
OriginalPlatform: number;
RecruitMsg: string;
Tier: number;
}
export interface IGuildAdDatabase {
GuildId: Types.ObjectId;
Emblem?: boolean;
Expiry: Date;
Features: number;
GuildName: string;
MemberCount: number;
RecruitMsg: string;
Tier: number;
}

View File

@ -44,7 +44,6 @@ export interface IInventoryDatabase
| "NextRefill"
| "Nemesis"
| "EntratiVaultCountResetDate"
| "BrandedSuits"
| TEquipmentKey
>,
InventoryDatabaseEquipment {
@ -74,7 +73,6 @@ export interface IInventoryDatabase
NextRefill?: Date;
Nemesis?: INemesisDatabase;
EntratiVaultCountResetDate?: Date;
BrandedSuits?: Types.ObjectId[];
}
export interface IQuestKeyDatabase {
@ -348,7 +346,6 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
EchoesHexConquestCacheScoreMission?: number;
EchoesHexConquestActiveFrameVariants?: string[];
EchoesHexConquestActiveStickers?: string[];
BrandedSuits?: IOid[];
}
export interface IAffiliation {
@ -860,11 +857,10 @@ export interface IPendingRecipeDatabase {
LongGuns?: IEquipmentDatabase[];
Pistols?: IEquipmentDatabase[];
Melee?: IEquipmentDatabase[];
SuitToUnbrand?: Types.ObjectId;
}
export interface IPendingRecipeClient
extends Omit<IPendingRecipeDatabase, "CompletionDate" | "LongGuns" | "Pistols" | "Melee" | "SuitToUnbrand"> {
extends Omit<IPendingRecipeDatabase, "CompletionDate" | "LongGuns" | "Pistols" | "Melee"> {
CompletionDate: IMongoDate;
}