Merge branch 'spaceninjaserver:main' into inventoryTypes

This commit is contained in:
AMelonInsideLemon 2024-06-16 15:20:23 +02:00 committed by GitHub
commit 584a70046f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 212 additions and 60 deletions

8
package-lock.json generated
View File

@ -13,7 +13,7 @@
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"mongoose": "^8.1.1", "mongoose": "^8.1.1",
"warframe-items": "^1.1262.74", "warframe-items": "^1.1262.74",
"warframe-public-export-plus": "^0.2.2", "warframe-public-export-plus": "^0.2.3",
"warframe-riven-info": "^0.1.0", "warframe-riven-info": "^0.1.0",
"winston": "^3.11.0", "winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1" "winston-daily-rotate-file": "^4.7.1"
@ -3909,9 +3909,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.2.2", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.2.2.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.2.3.tgz",
"integrity": "sha512-PAsiyiRDqXcsUwZTweihwrSksd+GT3USrbHwS/TrJUC3TqLS0Ng24OfefFKPWOmPfMxDbdkg2zV39uq72iZ/Yg==" "integrity": "sha512-Bl4gb3f1LIdGXLEOJg2XTIFYqrialdTIvVhDqDzVJIRfii0PKsy9jsr9vqM14tWz7oVpQMeCUyvisDkkXijTSg=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.0", "version": "0.1.0",

View File

@ -17,7 +17,7 @@
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"mongoose": "^8.1.1", "mongoose": "^8.1.1",
"warframe-items": "^1.1262.74", "warframe-items": "^1.1262.74",
"warframe-public-export-plus": "^0.2.2", "warframe-public-export-plus": "^0.2.3",
"warframe-riven-info": "^0.1.0", "warframe-riven-info": "^0.1.0",
"winston": "^3.11.0", "winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1" "winston-daily-rotate-file": "^4.7.1"

View File

@ -78,19 +78,18 @@ const inventoryController: RequestHandler = async (request: Request, response: R
} }
} }
if ( if (typeof config.spoofMasteryRank === "number" && config.spoofMasteryRank >= 0) {
typeof config.spoofMasteryRank === "number" &&
config.spoofMasteryRank >= 0 &&
config.spoofMasteryRank <= 5030
) {
inventoryResponse.PlayerLevel = config.spoofMasteryRank; inventoryResponse.PlayerLevel = config.spoofMasteryRank;
inventoryResponse.XPInfo = []; if (!("xpBasedLevelCapDisabled" in request.query)) {
let numFrames = getExpRequiredForMr(config.spoofMasteryRank) / 6000; // This client has not been patched to accept any mastery rank, need to fake the XP.
while (numFrames-- > 0) { inventoryResponse.XPInfo = [];
inventoryResponse.XPInfo.push({ let numFrames = getExpRequiredForMr(Math.min(config.spoofMasteryRank, 5030)) / 6000;
ItemType: "/Lotus/Powersuits/Mag/Mag", while (numFrames-- > 0) {
XP: 1_600_000 inventoryResponse.XPInfo.push({
}); ItemType: "/Lotus/Powersuits/Mag/Mag",
XP: 1_600_000
});
}
} }
} }

View File

@ -7,7 +7,8 @@ const placedDecosSchema = new Schema<IPlacedDecosDatabase>(
{ {
Type: String, Type: String,
Pos: [Number], Pos: [Number],
Rot: [Number] Rot: [Number],
Scale: Number
}, },
{ id: false } { id: false }
); );

View File

@ -46,5 +46,6 @@ interface ILoggerConfig {
export const updateConfig = async (data: string) => { export const updateConfig = async (data: string) => {
amnesia = true; amnesia = true;
return await fsPromises.writeFile(configPath, data); await fsPromises.writeFile(configPath, data);
Object.assign(config, JSON.parse(data));
}; };

View File

@ -115,6 +115,23 @@ export const addItem = async (
FlavourItems: [await addCustomization(typeName, accountId)] FlavourItems: [await addCustomization(typeName, accountId)]
} }
}; };
case "Objects": {
// /Lotus/Objects/Tenno/Props/TnoLisetTextProjector (Note Beacon)
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
}
};
}
case "Types": case "Types":
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
case "AvatarImages": case "AvatarImages":
@ -135,20 +152,40 @@ export const addItem = async (
} }
}; };
case "Items": { case "Items": {
const inventory = await getInventory(accountId); switch (typeName.substr(1).split("/")[3]) {
const miscItemChanges = [ case "ShipDecos": {
{ const inventory = await getInventory(accountId);
ItemType: typeName, const changes = [
ItemCount: quantity {
} satisfies IMiscItem ItemType: typeName,
]; ItemCount: quantity
addMiscItems(inventory, miscItemChanges); } satisfies IMiscItem
await inventory.save(); ];
return { addShipDecorations(inventory, changes);
InventoryChanges: { await inventory.save();
MiscItems: miscItemChanges return {
InventoryChanges: {
ShipDecorations: changes
}
};
} }
}; default: {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
}
}
} }
case "Recipes": case "Recipes":
case "Consumables": { case "Consumables": {
@ -426,6 +463,21 @@ export const addMiscItems = (inventory: IInventoryDatabaseDocument, itemsArray:
}); });
}; };
export const addShipDecorations = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined) => {
const { ShipDecorations } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => {
const itemIndex = ShipDecorations.findIndex(miscItem => miscItem.ItemType === ItemType);
if (itemIndex !== -1) {
ShipDecorations[itemIndex].ItemCount += ItemCount;
inventory.markModified(`ShipDecorations.${itemIndex}.ItemCount`);
} else {
ShipDecorations.push({ ItemCount, ItemType });
}
});
};
export const addConsumables = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined) => { export const addConsumables = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined) => {
const { Consumables } = inventory; const { Consumables } = inventory;

View File

@ -3,6 +3,7 @@ import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService"; import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService";
import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes"; import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { ExportBundles } from "warframe-public-export-plus";
export const getStoreItemCategory = (storeItem: string) => { export const getStoreItemCategory = (storeItem: string) => {
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/"); const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@ -21,26 +22,12 @@ export const getStoreItemTypesCategory = (typesItem: string) => {
export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountId: string) => { export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountId: string) => {
logger.debug("purchase request", purchaseRequest); logger.debug("purchase request", purchaseRequest);
const storeCategory = getStoreItemCategory(purchaseRequest.PurchaseParams.StoreItem);
const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", "");
logger.debug(`store category ${storeCategory}`);
let purchaseResponse; const purchaseResponse = await handleStoreItemAcquisition(
switch (storeCategory) { purchaseRequest.PurchaseParams.StoreItem,
default: accountId,
purchaseResponse = await addItem(accountId, internalName); purchaseRequest.PurchaseParams.Quantity
break; );
case "Types":
purchaseResponse = await handleTypesPurchase(
internalName,
accountId,
purchaseRequest.PurchaseParams.Quantity
);
break;
case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId);
break;
}
if (!purchaseResponse) throw new Error("purchase response was undefined"); if (!purchaseResponse) throw new Error("purchase response was undefined");
@ -58,6 +45,43 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
return purchaseResponse; return purchaseResponse;
}; };
const handleStoreItemAcquisition = async (
storeItemName: string,
accountId: string,
quantity: number
): Promise<{ InventoryChanges: object }> => {
let purchaseResponse = {
InventoryChanges: {}
};
logger.debug(`handling acquision of ${storeItemName}`);
if (storeItemName in ExportBundles) {
const bundle = ExportBundles[storeItemName];
logger.debug("acquiring bundle", bundle);
for (const component of bundle.components) {
purchaseResponse = {
...purchaseResponse,
...(await handleStoreItemAcquisition(component.typeName, accountId, component.purchaseQuantity))
};
}
} else {
const storeCategory = getStoreItemCategory(storeItemName);
const internalName = storeItemName.replace("/StoreItems", "");
logger.debug(`store category ${storeCategory}`);
switch (storeCategory) {
default:
purchaseResponse = await addItem(accountId, internalName);
break;
case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
break;
case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId);
break;
}
}
return purchaseResponse;
};
export const slotPurchaseNameToSlotName: SlotPurchase = { export const slotPurchaseNameToSlotName: SlotPurchase = {
SuitSlotItem: { name: "SuitBin", slotsPerPurchase: 1 }, SuitSlotItem: { name: "SuitBin", slotsPerPurchase: 1 },
TwoSentinelSlotItem: { name: "SentinelBin", slotsPerPurchase: 2 }, TwoSentinelSlotItem: { name: "SentinelBin", slotsPerPurchase: 2 },
@ -122,7 +146,9 @@ const boosterCollection = [
const handleBoostersPurchase = async (boosterStoreName: string, accountId: string) => { const handleBoostersPurchase = async (boosterStoreName: string, accountId: string) => {
const match = boosterStoreName.match(/(\d+)Day/); const match = boosterStoreName.match(/(\d+)Day/);
if (!match) return; if (!match) {
return { InventoryChanges: {} };
}
const extractedDigit = Number(match[1]); const extractedDigit = Number(match[1]);
const ItemType = boosterCollection.find(i => const ItemType = boosterCollection.find(i =>

View File

@ -6,6 +6,7 @@ import {
IShipDecorationsRequest, IShipDecorationsRequest,
IShipDecorationsResponse IShipDecorationsResponse
} from "@/src/types/shipTypes"; } from "@/src/types/shipTypes";
import { logger } from "@/src/utils/logger";
import { Types } from "mongoose"; import { Types } from "mongoose";
export const setShipCustomizations = async (shipCustomization: ISetShipCustomizationsRequest) => { export const setShipCustomizations = async (shipCustomization: ISetShipCustomizationsRequest) => {
@ -37,12 +38,74 @@ export const handleSetShipDecorations = async (
const rooms = placedDecoration.IsApartment ? personalRooms.Apartment.Rooms : personalRooms.Ship.Rooms; const rooms = placedDecoration.IsApartment ? personalRooms.Apartment.Rooms : personalRooms.Ship.Rooms;
const room = rooms.find(room => room.Name === placedDecoration.Room); const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
if (!roomToPlaceIn) {
logger.error("room not found");
throw new Error("room not found");
}
if (placedDecoration.MoveId) {
//moved within the same room
if (placedDecoration.OldRoom === placedDecoration.Room) {
const existingDecorationIndex = roomToPlaceIn?.PlacedDecos?.findIndex(
deco => deco._id.toString() === placedDecoration.MoveId
);
if (existingDecorationIndex === -1) {
logger.error("decoration to be moved not found");
throw new Error("decoration to be moved not found");
}
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Pos = placedDecoration.Pos;
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Rot = placedDecoration.Rot;
if (placedDecoration.Scale) {
roomToPlaceIn.PlacedDecos[existingDecorationIndex].Scale = placedDecoration.Scale;
}
await personalRooms.save();
return {
OldRoom: placedDecoration.OldRoom,
NewRoom: placedDecoration.Room,
IsApartment: placedDecoration.IsApartment,
MaxCapacityIncrease: 0 // TODO: calculate capacity change upon removal
};
}
//moved to a different room
const oldRoom = rooms.find(room => room.Name === placedDecoration.OldRoom);
if (!oldRoom) {
logger.error("old room not found");
throw new Error("old room not found");
}
oldRoom.PlacedDecos.pull({ _id: placedDecoration.MoveId });
const newDecoration = {
Type: placedDecoration.Type,
Pos: placedDecoration.Pos,
Rot: placedDecoration.Rot,
Scale: placedDecoration.Scale || 1,
_id: placedDecoration.MoveId
};
//the new room is still roomToPlaceIn
roomToPlaceIn.PlacedDecos.push(newDecoration);
await personalRooms.save();
return {
OldRoom: placedDecoration.OldRoom,
NewRoom: placedDecoration.Room,
IsApartment: placedDecoration.IsApartment,
MaxCapacityIncrease: 0 // TODO: calculate capacity change upon removal
};
}
//TODO: check whether to remove from shipitems //TODO: check whether to remove from shipitems
if (placedDecoration.RemoveId) { if (placedDecoration.RemoveId) {
room?.PlacedDecos?.pull({ _id: placedDecoration.RemoveId }); roomToPlaceIn.PlacedDecos.pull({ _id: placedDecoration.RemoveId });
await personalRooms.save(); await personalRooms.save();
return { return {
DecoId: placedDecoration.RemoveId, DecoId: placedDecoration.RemoveId,
@ -54,11 +117,13 @@ export const handleSetShipDecorations = async (
// TODO: handle capacity // TODO: handle capacity
//place decoration
const decoId = new Types.ObjectId(); const decoId = new Types.ObjectId();
room?.PlacedDecos?.push({ roomToPlaceIn.PlacedDecos?.push({
Type: placedDecoration.Type, Type: placedDecoration.Type,
Pos: placedDecoration.Pos, Pos: placedDecoration.Pos,
Rot: placedDecoration.Rot, Rot: placedDecoration.Rot,
Scale: placedDecoration.Scale || 1,
_id: decoId _id: decoId
}); });

View File

@ -69,6 +69,7 @@ export interface IPlacedDecosDatabase {
Type: string; Type: string;
Pos: [number, number, number]; Pos: [number, number, number];
Rot: [number, number, number]; Rot: [number, number, number];
Scale: number;
_id: Types.ObjectId; _id: Types.ObjectId;
} }
@ -100,12 +101,17 @@ export interface IShipDecorationsRequest {
Rot: [number, number, number]; Rot: [number, number, number];
Room: string; Room: string;
IsApartment: boolean; IsApartment: boolean;
RemoveId: string; RemoveId?: string;
MoveId?: string;
OldRoom?: string;
Scale?: number;
} }
export interface IShipDecorationsResponse { export interface IShipDecorationsResponse {
DecoId: string; DecoId?: string;
Room: string; Room?: string;
IsApartment: boolean; IsApartment: boolean;
MaxCapacityIncrease?: number; MaxCapacityIncrease?: number;
OldRoom?: string;
NewRoom?: string;
} }

View File

@ -95,6 +95,8 @@ window.itemListPromise = new Promise(resolve => {
"/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" }, "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" },
"/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" }, "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" },
"/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" }, "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" },
// Modular weapons
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
// Missing in data sources // Missing in data sources
"/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" } "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
}; };
@ -121,7 +123,7 @@ window.itemListPromise = new Promise(resolve => {
}); });
function updateInventory() { function updateInventory() {
const req = $.get("/api/inventory.php?" + window.authz); const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => { req.done(data => {
window.itemListPromise.then(itemMap => { window.itemListPromise.then(itemMap => {
document.getElementById("warframe-list").innerHTML = ""; document.getElementById("warframe-list").innerHTML = "";
@ -537,7 +539,7 @@ function doAcquireRiven() {
}) })
}).done(function () { }).done(function () {
// Get riven's assigned id // Get riven's assigned id
$.get("/api/inventory.php?" + window.authz).done(data => { $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1").done(data => {
for (const rawUpgrade of data.RawUpgrades) { for (const rawUpgrade of data.RawUpgrades) {
if (rawUpgrade.ItemType === uniqueName) { if (rawUpgrade.ItemType === uniqueName) {
// Add fingerprint to riven // Add fingerprint to riven