fix: generate rewards based on RewardSeed to match what's show in client #1628

Merged
Sainan merged 2 commits from faithful-rewards into main 2025-04-15 09:46:09 -07:00
6 changed files with 23 additions and 9 deletions

View File

@ -1,14 +1,12 @@
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { generateRewardSeed } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
export const getNewRewardSeedController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const rewardSeed = generateRewardSeed();
logger.debug(`generated new reward seed: ${rewardSeed}`);
await Inventory.updateOne(
{
accountOwnerId: accountId

View File

@ -1213,7 +1213,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
accountOwnerId: Schema.Types.ObjectId,
SubscribedToEmails: { type: Number, default: 0 },
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
RewardSeed: Number,
RewardSeed: BigInt,
//Credit
RegularCredits: { type: Number, default: 0 },

View File

@ -9,7 +9,7 @@ import {
} from "warframe-public-export-plus";
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
import { logger } from "@/src/utils/logger";
import { IRngResult, getRandomElement, getRandomReward } from "@/src/services/rngService";
import { IRngResult, SRng, getRandomElement, getRandomReward } from "@/src/services/rngService";
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import {
addBooster,
@ -31,6 +31,7 @@ import {
addShipDecorations,
addStanding,
combineInventoryChanges,
generateRewardSeed,
updateCurrency,
updateSyndicate
} from "@/src/services/inventoryService";
@ -70,7 +71,12 @@ const getRotations = (rotationCount: number, tierOverride: number | undefined):
return rotatedValues;
};
const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
const getRandomRewardByChance = (pool: IReward[], rng?: SRng): IRngResult | undefined => {
if (rng) {
const res = rng.randomReward(pool as IRngResult[]);
rng.randomFloat(); // something related to rewards multiplier
return res;
}
return getRandomReward(pool as IRngResult[]);
};
@ -548,6 +554,11 @@ export const addMissionRewards = async (
return { MissionRewards: [] };
}
if (rewardInfo.rewardSeed) {
// We're using a reward seed, so give the client a new one in the response. On live, missionInventoryUpdate seems to always provide a fresh one in the response.
inventory.RewardSeed = generateRewardSeed();
}
//TODO: check double reward merging
const MissionRewards: IMissionReward[] = getRandomMissionDrops(rewardInfo, wagerTier);
logger.debug("random mission drops:", MissionRewards);
@ -1062,6 +1073,7 @@ function getRandomMissionDrops(RewardInfo: IRewardInfo, tierOverride: number | u
if (rewardManifests.length != 0) {
logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
}
const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn);
rewardManifests.forEach(name => {
const table = ExportRewards[name];
if (!table) {
@ -1070,7 +1082,7 @@ function getRandomMissionDrops(RewardInfo: IRewardInfo, tierOverride: number | u
}
for (const rotation of rotations) {
const rotationRewards = table[rotation];
const drop = getRandomRewardByChance(rotationRewards);
const drop = getRandomRewardByChance(rotationRewards, rng);
if (drop) {
drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
}

View File

@ -31,7 +31,7 @@ const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], per
return item;
}
}
throw new Error("What the fuck?");
return pool[pool.length - 1];
};
export const getRandomReward = <T extends { probability: number }>(pool: T[]): T | undefined => {
@ -142,4 +142,8 @@ export class SRng {
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
return (Number(this.state >> 38n) & 0xffffff) * 0.000000059604645;
}
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
return getRewardAtPercentage(pool, this.randomFloat());
}
}

View File

@ -194,7 +194,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
Mailbox?: IMailboxClient;
SubscribedToEmails: number;
Created: IMongoDate;
RewardSeed: number;
RewardSeed: number | bigint;
RegularCredits: number;
PremiumCredits: number;
PremiumCreditsFree: number;

View File

@ -141,7 +141,7 @@ export interface IRewardInfo {
EOM_AFK?: number;
rewardQualifications?: string; // did a Survival for 5 minutes and this was "1"
PurgatoryRewardQualifications?: string;
rewardSeed?: number;
rewardSeed?: number | bigint;
periodicMissionTag?: string;
// for bounties, only EOM_AFK and node are given from above, plus: