1
mirror of https://github.com/thepeacockproject/Peacock synced 2025-02-16 16:34:28 +01:00

feat!: add game version to resolveContract (#519)

This commit is contained in:
Reece Dunham 2024-09-15 14:52:42 -04:00 committed by GitHub
parent 3191f053fb
commit cf953d8f81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 283 additions and 118 deletions

View File

@ -20,7 +20,7 @@ import { Router } from "express"
import { gameDifficulty, nilUuid, ServerVer, uuidRegex } from "../utils"
import { json as jsonMiddleware } from "body-parser"
import { enqueueEvent, newSession } from "../eventHandler"
import { _legacyBull, controller } from "../controller"
import { controller } from "../controller"
import { log, LogLevel } from "../loggingInterop"
import { getConfig } from "../configSwizzleManager"
import type { GameChanger, RequestWithJwt } from "../types/types"
@ -88,11 +88,10 @@ legacyContractRouter.post(
return
}
const contractData =
req.gameVersion === "h1" &&
req.body.id === "42bac555-bbb9-429d-a8ce-f1ffdf94211c"
? _legacyBull
: controller.resolveContract(req.body.id)
const contractData = controller.resolveContract(
req.body.id,
req.gameVersion,
)
if (!contractData) {
log(
@ -193,7 +192,10 @@ legacyContractRouter.post(
return
}
const c = controller.resolveContract(req.body.contractId)
const c = controller.resolveContract(
req.body.contractId,
req.gameVersion,
)
if (!c) {
res.status(404).end()

View File

@ -135,6 +135,7 @@ legacyMenuDataRouter.get(
{
type: ChallengeFilterType.ParentLocation,
parent: parentLocation.Id,
gameVersion: req.gameVersion,
pro1Filter: Pro1FilterType.Exclude,
},
parentLocation.Id,
@ -198,6 +199,7 @@ legacyMenuDataRouter.get(
{
type: ChallengeFilterType.ParentLocation,
parent: parentLocation.Id,
gameVersion: req.gameVersion,
pro1Filter: Pro1FilterType.Only,
},
parentLocation.Id,

View File

@ -49,7 +49,10 @@ legacyProfileRouter.post(
false,
)
const json = controller.resolveContract(req.body.contractId)
const json = controller.resolveContract(
req.body.contractId,
req.gameVersion,
)
if (!json) {
log(

View File

@ -20,6 +20,7 @@ import {
ChallengeProgressionData,
CompiledChallengeIngameData,
CompiledChallengeRuntimeData,
GameVersion,
InclusionData,
MissionManifest,
RegistryChallenge,
@ -115,6 +116,7 @@ export type ChallengeFilterOptions =
type: ChallengeFilterType.Contract
contractId: string
locationId: string
gameVersion: GameVersion
isFeatured?: boolean
difficulty: number
pro1Filter: Pro1FilterType
@ -122,12 +124,14 @@ export type ChallengeFilterOptions =
| {
type: ChallengeFilterType.Contracts
contractIds: string[]
gameVersion: GameVersion
locationId: string
pro1Filter: Pro1FilterType
}
| {
type: ChallengeFilterType.ParentLocation
parent: string
gameVersion: GameVersion
pro1Filter: Pro1FilterType
}
@ -172,6 +176,7 @@ export function isChallengeForDifficulty(
* @param contractId The id of the contract.
* @param locationId The sublocation ID of the challenge.
* @param difficulty The upper bound on the difficulty of the challenges to return.
* @param gameVersion The game version.
* @param challenge The challenge in question.
* @param pro1Filter Settings for handling pro1 challenges.
* @param forCareer Whether the result is used to decide what is shown the CAREER -> CHALLENGES page. Defaulted to false.
@ -181,6 +186,7 @@ function isChallengeInContract(
contractId: string,
locationId: string,
difficulty: number,
gameVersion: GameVersion,
challenge: RegistryChallenge,
pro1Filter: Pro1FilterType,
forCareer = false,
@ -197,7 +203,7 @@ function isChallengeInContract(
return false
}
const contract = controller.resolveContract(contractId, true)
const contract = controller.resolveContract(contractId, gameVersion, true)
if (challenge.Type === "global") {
return inclusionDataCheck(
@ -233,7 +239,9 @@ function isChallengeInContract(
// We have to resolve the non-group contract, `contract` is the group contract
const isForContractType = (
challenge.InclusionData?.ContractTypes || []
).includes(controller.resolveContract(contractId)!.Metadata.Type)
).includes(
controller.resolveContract(contractId, gameVersion)!.Metadata.Type,
)
// Is this a location-wide challenge?
// "location" is more widely used, but "parentlocation" is used in Ambrose and Berlin, as well as some "Discover XX" challenges.
@ -275,6 +283,7 @@ export function filterChallenge(
options.contractId,
options.locationId,
options.difficulty,
options.gameVersion,
challenge,
options.pro1Filter,
)
@ -286,6 +295,7 @@ export function filterChallenge(
contractId,
options.locationId,
gameDifficulty.master, // Get challenges of all difficulties
options.gameVersion,
challenge,
options.pro1Filter,
true,

View File

@ -833,7 +833,11 @@ export class ChallengeService extends ChallengeRegistry {
difficulty = 4,
): GroupIndexedChallengeLists {
const userData = getUserData(userId, gameVersion)
const contractGroup = this.controller.resolveContract(contractId, true)
const contractGroup = this.controller.resolveContract(
contractId,
gameVersion,
true,
)
if (!contractGroup) {
return {}
@ -852,9 +856,17 @@ export class ChallengeService extends ChallengeRegistry {
assert.ok(currentLevel, "expected current level ID in escalation")
contract = this.controller.resolveContract(currentLevel, false)
contract = this.controller.resolveContract(
currentLevel,
gameVersion,
false,
)
} else {
contract = this.controller.resolveContract(contractId, false)
contract = this.controller.resolveContract(
contractId,
gameVersion,
false,
)
}
if (!contract) {
@ -878,6 +890,7 @@ export class ChallengeService extends ChallengeRegistry {
gameVersion !== "h1"
? "LOCATION_ICA_FACILITY_SHIP"
: contract.Metadata.Location,
gameVersion,
isFeatured: contractGroup.Metadata.Type === "featured",
pro1Filter:
contract.Metadata.Difficulty === "pro1"
@ -924,6 +937,7 @@ export class ChallengeService extends ChallengeRegistry {
type: ChallengeFilterType.Contracts,
contractIds: contracts,
locationId: child,
gameVersion,
pro1Filter: Pro1FilterType.Exclude,
},
parent,
@ -936,7 +950,11 @@ export class ChallengeService extends ChallengeRegistry {
// brand new.
const { gameVersion, contractId, challengeContexts } = session
const contractJson = this.controller.resolveContract(contractId, true)
const contractJson = this.controller.resolveContract(
contractId,
gameVersion,
true,
)
const challengeGroups = this.getChallengesForContract(
contractId,
@ -1139,7 +1157,11 @@ export class ChallengeService extends ChallengeRegistry {
): CompiledChallengeTreeCategory[] {
const userData = getUserData(userId, gameVersion)
const contractData = this.controller.resolveContract(contractId, true)
const contractData = this.controller.resolveContract(
contractId,
gameVersion,
true,
)
if (!contractData) {
return []
@ -1164,9 +1186,17 @@ export class ChallengeService extends ChallengeRegistry {
return []
}
levelData = this.controller.resolveContract(order, false)
levelData = this.controller.resolveContract(
order,
gameVersion,
false,
)
} else {
levelData = this.controller.resolveContract(contractId, false)
levelData = this.controller.resolveContract(
contractId,
gameVersion,
false,
)
}
if (!levelData) {
@ -1306,6 +1336,7 @@ export class ChallengeService extends ChallengeRegistry {
{
type: ChallengeFilterType.ParentLocation,
parent: locationParentId,
gameVersion,
pro1Filter: isPro1
? Pro1FilterType.Only
: Pro1FilterType.Exclude,
@ -1535,6 +1566,7 @@ export class ChallengeService extends ChallengeRegistry {
if (challenge.Type === "contract") {
contract = this.controller.resolveContract(
challenge.InclusionData?.ContractIds?.[0] || "",
gameVersion,
)
// This is so we can remove unused data and make it more like official - AF

View File

@ -153,7 +153,10 @@ export class ProgressionService {
location: string,
sniperUnlockable?: string,
): void {
const contract = controller.resolveContract(contractSession.contractId)
const contract = controller.resolveContract(
contractSession.contractId,
contractSession.gameVersion,
)
if (!contract) {
return

View File

@ -70,7 +70,10 @@ contractRoutingRouter.post(
return // user sent some nasty info
}
const contractData = controller.resolveContract(req.body.id)
const contractData = controller.resolveContract(
req.body.id,
req.gameVersion,
)
if (!contractData) {
log(
@ -266,6 +269,7 @@ contractRoutingRouter.post(
const contractData = controller.resolveContract(
sessionDetails.contractId,
sessionDetails.gameVersion,
)
if (!contractData) {
@ -347,7 +351,10 @@ contractRoutingRouter.post(
jsonMiddleware(),
// @ts-expect-error Has jwt props.
(req: RequestWithJwt<never, { contractId: string }>, res) => {
const contract = controller.resolveContract(req.body.contractId)
const contract = controller.resolveContract(
req.body.contractId,
req.gameVersion,
)
if (!contract) {
res.status(400).send("contract not found")

View File

@ -42,6 +42,7 @@ export function contractsModeHome(req: RequestWithJwt, res: Response): void {
const contractCreationTutorial = controller.resolveContract(
contractCreationTutorialId,
req.gameVersion,
)
res.json({

View File

@ -271,7 +271,7 @@ export function generateUserCentric(
uc.Data.EscalationCompletedLevels = p - 1
uc.Data.EscalationTotalLevels = getLevelCount(
controller.resolveContract(eGroupId),
controller.resolveContract(eGroupId, gameVersion),
)
uc.Data.EscalationCompleted =
userData.Extensions.PeacockCompletedEscalations.includes(eGroupId)

View File

@ -133,7 +133,7 @@ export function getPlayEscalationInfo(
}
const totalLevelCount = getLevelCount(
controller.resolveContract(groupContractId),
controller.resolveContract(groupContractId, gameVersion),
)
let nextContractId = "00000000-0000-0000-0000-000000000000"

View File

@ -152,7 +152,7 @@ export class HitsCategoryService {
.for("Elusive_Target_Hits")
.tap(tapName, (contracts, gameVersion) => {
for (const id of orderedETs) {
const contract = controller.resolveContract(id)
const contract = controller.resolveContract(id, gameVersion)
switch (gameVersion) {
case "h1":
@ -243,7 +243,10 @@ export class HitsCategoryService {
missionsInLocations.escalations,
)) {
for (const id of escalations) {
const contract = controller.resolveContract(id)
const contract = controller.resolveContract(
id,
gameVersion,
)
if (!contract) continue

View File

@ -56,7 +56,7 @@ export async function getLeaderboardEntries(
): Promise<GameFacingLeaderboardData | undefined> {
let difficulty = "unset"
const contract = controller.resolveContract(contractId)
const contract = controller.resolveContract(contractId, gameVersion)
if (!contract) {
return undefined

View File

@ -220,7 +220,7 @@ function createPeacockRequire(pluginName: string): NodeRequire {
/**
* Freedom Fighters for Hitman 2016 (objectives are different).
*/
export const _legacyBull: MissionManifest = JSON.parse(LEGACYFF)
const _legacyBull: MissionManifest = JSON.parse(LEGACYFF)
/**
* Ensure a mission has the bare minimum required to work.
@ -330,9 +330,10 @@ export class Controller {
]
>
getContractManifest: SyncBailHook<
[contractId: string],
[contractId: string, gameVersion: GameVersion, isGroup: boolean],
MissionManifest | undefined
>
getContractIdsForGroupDiscovery: SyncHook<[string[]]>
contributeCampaigns: SyncHook<
[
campaigns: Campaign[],
@ -389,6 +390,7 @@ export class Controller {
newEvent: new SyncHook(),
newMetricsEvent: new SyncHook(),
getContractManifest: new SyncBailHook(),
getContractIdsForGroupDiscovery: new SyncHook(),
contributeCampaigns: new SyncHook(),
getSearchResults: new AsyncSeriesHook(),
getNextCampaignMission: new SyncBailHook(),
@ -455,6 +457,18 @@ export class Controller {
)
await this._loadInternalContracts()
// in h1 (legacy), bull is not the same
this.hooks.getContractManifest.tap(
"PeacockH1Bull",
(id, gameVersion) => {
if (
gameVersion === "h1" &&
id === "42bac555-bbb9-429d-a8ce-f1ffdf94211c"
) {
return _legacyBull
}
},
)
this.challengeService = new ChallengeService(this)
this.masteryService = new MasteryService()
@ -492,7 +506,7 @@ export class Controller {
private _getETALocations(): void {
for (const cId of orderedETAs) {
const contract = this.resolveContract(cId, true)
const contract = this.resolveContract(cId, "h3", true)
if (!contract) {
continue
@ -504,7 +518,7 @@ export class Controller {
)
for (const lId of contract.Metadata.GroupDefinition.Order) {
const level = this.resolveContract(lId, false)
const level = this.resolveContract(lId, "h3", false)
if (!level) {
continue
@ -580,13 +594,18 @@ export class Controller {
return manifest
}
private getGroupContract(json: MissionManifest) {
private getGroupContract(
json: MissionManifest,
gameVersion: GameVersion,
): MissionManifest {
if (escalationTypes.includes(json.Metadata.Type)) {
if (!json.Metadata.InGroup) {
return json
}
return this.resolveContract(json.Metadata.InGroup) ?? json
return (
this.resolveContract(json.Metadata.InGroup, gameVersion) ?? json
)
}
return json
@ -596,28 +615,48 @@ export class Controller {
* Get a contract by its ID.
*
* Order of precedence:
* 1. Plugins ({@link addMission} or the `getMissionManifest` hook)
* 2. Peacock internal contracts storage
* 1. Plugins ({@link addMission} or the `getContractManifest` hook).
* 2. Peacock internal contracts storage.
* 3. Files in the `contracts` folder.
*
* @param id The contract's ID.
* @param gameVersion The game version.
* @param getGroup When `id` points one of the levels in a contract group, controls whether to get the group contract instead of the individual mission. Defaulted to false. WARNING: If you set this to true, what is returned is not what is pointed to by the inputted `id`.
* @returns The mission manifest object, or undefined if it wasn't found.
*/
public resolveContract(
id: string | undefined,
gameVersion: GameVersion,
getGroup = false,
): MissionManifest | undefined {
if (!id) {
return undefined
}
const optionalPluginJson = this.hooks.getContractManifest.call(id)
// no matter what, this function is so widely used that it's almost certain
// at some point, it'll be called with either a boolean or undefined as game version,
// because people haven't updated their plugins yet.
// noinspection SuspiciousTypeOfGuard
if (typeof gameVersion === "boolean" || gameVersion === undefined) {
gameVersion = "h3"
log(
LogLevel.WARN,
`Game version not specified. This plugin needs to be updated! Assuming h3.`,
"Contracts",
)
log(LogLevel.TRACE, `No game version.`, "Contracts")
}
const optionalPluginJson = this.hooks.getContractManifest.call(
id,
gameVersion,
getGroup,
)
if (optionalPluginJson) {
return fastClone(
getGroup
? this.getGroupContract(optionalPluginJson)
? this.getGroupContract(optionalPluginJson, gameVersion)
: optionalPluginJson,
)
}
@ -626,7 +665,9 @@ export class Controller {
if (registryJson) {
return fastClone(
getGroup ? this.getGroupContract(registryJson) : registryJson,
getGroup
? this.getGroupContract(registryJson, gameVersion)
: registryJson,
)
}
@ -636,7 +677,9 @@ export class Controller {
if (openCtJson) {
return fastClone(
getGroup ? this.getGroupContract(openCtJson) : openCtJson,
getGroup
? this.getGroupContract(openCtJson, gameVersion)
: openCtJson,
)
}
@ -646,7 +689,9 @@ export class Controller {
if (officialJson) {
return fastClone(
getGroup ? this.getGroupContract(officialJson) : officialJson,
getGroup
? this.getGroupContract(officialJson, gameVersion)
: officialJson,
)
}
@ -1137,11 +1182,15 @@ export class Controller {
scanForGroups(): void {
let groupCount = 0
const discoveryIdPool: string[] = []
this.hooks.getContractIdsForGroupDiscovery.call(discoveryIdPool)
allGroups: for (const contractId of new Set<string>([
...Object.keys(internalContracts),
...this.hooks.getContractManifest.allTapNames,
...discoveryIdPool,
])) {
const contract = this.resolveContract(contractId)
const contract = this.resolveContract(contractId, "h3")
if (!contract?.Metadata?.GroupDefinition) {
continue
@ -1154,7 +1203,7 @@ export class Controller {
const order = contract.Metadata.GroupDefinition.Order
while (i + 1 <= order.length) {
const next = this.resolveContract(order[i])
const next = this.resolveContract(order[i], "h3")
if (!next) {
log(
@ -1208,7 +1257,7 @@ export function contractIdToHitObject(
gameVersion: GameVersion,
userId: string,
): Hit | undefined {
const contract = controller.resolveContract(contractId)
const contract = controller.resolveContract(contractId, gameVersion)
if (!contract) {
return undefined
@ -1249,6 +1298,7 @@ export function contractIdToHitObject(
{
type: ChallengeFilterType.ParentLocation,
parent: parentLocation?.Id,
gameVersion,
pro1Filter: Pro1FilterType.Ignore,
},
parentLocation?.Id,

View File

@ -71,6 +71,7 @@ import {
ManifestScoringModule,
} from "./types/scoring"
import { deepmerge } from "deepmerge-ts"
import assert from "assert"
const eventRouter = Router()
@ -302,12 +303,9 @@ export function newSession(
): void {
const timestamp = new Date()
const contract = controller.resolveContract(contractId)
const contract = controller.resolveContract(contractId, gameVersion)
if (!contract) {
log(LogLevel.ERROR, `Failed to load ${contractId}`)
throw new Error("no ct")
}
assert.ok(contract, `Failed to load ${contractId}`)
if (difficulty === 0 && contractTypes.includes(contract.Metadata.Type)) {
log(
@ -546,7 +544,10 @@ function contractFailed(
): void {
session.markedTargets.clear()
const json = controller.resolveContract(session.contractId)!
const json = controller.resolveContract(
session.contractId,
session.gameVersion,
)!
const userData = getUserData(session.userId, session.gameVersion)
let realName: string
@ -677,7 +678,10 @@ function saveEvents(
session.duration = event.Timestamp
session.lastUpdate = new Date()
const contract = controller.resolveContract(session.contractId)
const contract = controller.resolveContract(
session.contractId,
gameVersion,
)
const contractType = contract?.Metadata?.Type?.toLowerCase()
controller.hooks.newEvent.call(

View File

@ -37,6 +37,7 @@ export class LiveSplitManager {
private _inValidCampaignRun: boolean
private _currentMission: string | undefined
private _currentMissionTotalTime: number
private _currentMissionGameVersion: GameVersion
private _campaignTotalTime: number
private _completedMissions: string[]
private _timeCalcEntries: LiveSplitTimeCalcEntry[]
@ -53,6 +54,7 @@ export class LiveSplitManager {
this._currentMissionTotalTime = 0
this._campaignTotalTime = 0
this._raceMode = undefined
this._currentMissionGameVersion = "h3"
this._liveSplitClient = new LiveSplitClient("127.0.0.1:16834")
}
@ -76,11 +78,13 @@ export class LiveSplitManager {
contractId: string,
gameVersion: GameVersion,
userId: string,
) {
): Promise<void> {
if (!this._checkInit()) {
return
}
this._currentMissionGameVersion = gameVersion
const campaign = getCampaignMissions(contractId, gameVersion, userId)
if (campaign === undefined) {
@ -164,7 +168,7 @@ export class LiveSplitManager {
}
}
async failMission(attemptTime: Seconds) {
async failMission(attemptTime: Seconds): Promise<void> {
if (!this._checkInit()) {
return
}
@ -176,13 +180,18 @@ export class LiveSplitManager {
)
const computedTime = this._addMissionTime(attemptTime)
this._addTimeCalcEntry(this._currentMission, computedTime, false)
this._addTimeCalcEntry(
this._currentMission,
this._currentMissionGameVersion,
computedTime,
false,
)
LiveSplitManager._logAttempt(computedTime)
await this._pushGameTime()
}
}
async completeMission(attemptTime: Seconds) {
async completeMission(attemptTime: Seconds): Promise<void> {
LiveSplitManager._logAttempt(attemptTime)
if (!this._checkInit()) {
@ -196,7 +205,12 @@ export class LiveSplitManager {
)
const computedTime = this._addMissionTime(attemptTime)
this._addTimeCalcEntry(this._currentMission, computedTime, true)
this._addTimeCalcEntry(
this._currentMission,
this._currentMissionGameVersion,
computedTime,
true,
)
log(
LogLevel.INFO,
`Total mission time with resets: ${this._currentMissionTotalTime}`,
@ -256,7 +270,7 @@ export class LiveSplitManager {
* PRIVATE METHODS
*/
async init() {
async init(): Promise<void> {
if (!getFlag("liveSplit")) {
this._initializationAttempted = true
@ -311,7 +325,7 @@ export class LiveSplitManager {
return this._raceMode as boolean
}
private async _invalidateRun(contractId: string) {
private async _invalidateRun(contractId: string): Promise<void> {
log(
LogLevel.INFO,
"Entered invalid mission for current campaign state, invalidating autosplitter run. Start first mission of a campaign to reset and start a new run.",
@ -379,7 +393,7 @@ export class LiveSplitManager {
].includes(startLocationId)
}
private async _setGameTime(totalTime: Seconds) {
private async _setGameTime(totalTime: Seconds): Promise<void> {
// IMPORTANT to floor to int before sending to livesplit or else parsing will fail silently...
const flooredTime = Math.floor(totalTime)
const minutes = Math.floor(flooredTime / 60)
@ -420,8 +434,11 @@ export class LiveSplitManager {
await this._setGameTime(this._campaignTotalTime)
}
private _getMissionLocationName(contractId: string): string | undefined {
const contract = controller.resolveContract(contractId)
private _getMissionLocationName(
contractId: string,
gameVersion: GameVersion,
): string | undefined {
const contract = controller.resolveContract(contractId, gameVersion)
if (!contract) {
return undefined
@ -436,10 +453,11 @@ export class LiveSplitManager {
private _addTimeCalcEntry(
contractId: string,
gameVersion: GameVersion,
time: Seconds,
isCompleted: boolean,
) {
const location = this._getMissionLocationName(contractId)
const location = this._getMissionLocationName(contractId, gameVersion)
if (!location) return

View File

@ -21,14 +21,6 @@ import picocolors from "picocolors"
import winston from "winston"
import "winston-daily-rotate-file"
const isDebug = ["*", "true", "peacock", "yes"].includes(
process.env.DEBUG || "false",
)
const isTest = ["*", "true", "peacock", "yes"].includes(
process.env.TEST || "false",
)
/**
* Represents the different log levels.
*/
@ -51,7 +43,7 @@ export enum LogLevel {
*/
DEBUG = "debug",
/**
* For outputting stacktraces.
* For outputting stack traces.
*/
TRACE = "trace",
/**
@ -62,16 +54,13 @@ export enum LogLevel {
/**
* Represents the different internal log categories used by Peacock.
* @internal
*/
export enum LogCategory {
export const enum LogCategory {
/**
* Remove the category from the log
*/
NONE = "none",
/**
* Set the category to the name of the calling function
*/
CALLER = "caller",
/**
* Used for logging HTTP request
*/
@ -80,9 +69,7 @@ export enum LogCategory {
const LOG_LEVEL_NONE = "none"
// NOTE: Tests run in "strict mode", so only enable CALLER by default for debug-mode.
const LOG_CATEGORY_DEFAULT =
isDebug && !isTest ? LogCategory.CALLER : LogCategory.NONE
const LOG_CATEGORY_DEFAULT = LogCategory.NONE
const fileLogLevel = process.env.LOG_LEVEL_FILE || LogLevel.SILLY
const consoleLogLevel = process.env.LOG_LEVEL_CONSOLE || LogLevel.SILLY
@ -163,6 +150,21 @@ export function logDebug(...args: unknown[]): void {
log(LogLevel.DEBUG, JSON.stringify(args, undefined, " "))
}
function fixMessage(message: string | unknown | null | undefined): string {
switch (typeof message) {
case "string":
return message
case "object":
return JSON.stringify(message, undefined, 4)
case "undefined":
return "undefined"
case "function":
return "function"
default:
return String(message)
}
}
/**
* Outputs a log message to the console.
*
@ -178,11 +180,7 @@ export function log(
data: string | unknown,
category: LogCategory | string = LOG_CATEGORY_DEFAULT,
): void {
if (category === LogCategory.CALLER) {
category = log.caller?.name.toString() || "unknown"
}
const message = data || "No message specified"
const message = fixMessage(data)
const now = new Date()
const stampParts: number[] = [

View File

@ -387,7 +387,11 @@ menuDataRouter.get(
}
const { contractId } = s
const contractData = controller.resolveContract(contractId, true)
const contractData = controller.resolveContract(
contractId,
req.gameVersion,
true,
)
const userData = getUserData(req.jwt.unique_name, req.gameVersion)
assert.ok(contractData, "contract not found")
@ -534,7 +538,10 @@ menuDataRouter.get(
const inventory = createInventory(req.jwt.unique_name, req.gameVersion)
const contractData = controller.resolveContract(req.query.contractId)
const contractData = controller.resolveContract(
req.query.contractId,
req.gameVersion,
)
if (!contractData) {
log(
@ -636,7 +643,10 @@ menuDataRouter.get(
const inventory = createInventory(req.jwt.unique_name, req.gameVersion)
const contractData = controller.resolveContract(req.query.contractId)
const contractData = controller.resolveContract(
req.query.contractId,
req.gameVersion,
)
if (!contractData) {
log(LogLevel.WARN, `Unknown contract: ${req.query.contractId}`)
@ -1066,6 +1076,7 @@ menuDataRouter.get(
menuDataRouter.get("/contractsearchpage", (req: RequestWithJwt, res) => {
const createContractTutorial = controller.resolveContract(
contractCreationTutorialId,
req.gameVersion,
)
res.json({
@ -1121,7 +1132,7 @@ menuDataRouter.post(
for (const contract of specialContracts) {
const userCentric = generateUserCentric(
controller.resolveContract(contract),
controller.resolveContract(contract, req.gameVersion),
req.jwt.unique_name,
req.gameVersion,
)
@ -1250,7 +1261,7 @@ menuDataRouter.get("/contractcreation/create", (req: RequestWithJwt, res) => {
// if for some reason the id is already in use, generate a new one
// the math says this is like a one in a billion chance though, I think
while (controller.resolveContract(cUuid)) {
while (controller.resolveContract(cUuid, req.gameVersion)) {
cUuid = randomUUID()
}
@ -1351,7 +1362,7 @@ const createLoadSaveMiddleware =
if (e && !doneContracts.includes(e)) {
doneContracts.push(e)
const contract = controller.resolveContract(e)
const contract = controller.resolveContract(e, req.gameVersion)
if (!contract) {
log(LogLevel.WARN, `Unknown contract in L/S: ${e}`)

View File

@ -19,12 +19,12 @@
import { contractIdToHitObject, controller } from "../controller"
import type {
Campaign,
GameVersion,
GenSingleMissionFunc,
CampaignMission,
CampaignVideo,
Video,
GameVersion,
GenSingleMissionFunc,
StoryData,
Video,
} from "../types/types"
import { log, LogLevel } from "../loggingInterop"
import { getConfig } from "../configSwizzleManager"
@ -47,7 +47,11 @@ const genSingleMissionFactory = (userId: string): GenSingleMissionFunc => {
"Plugin tried to generate mission with no game version",
)
const actualContractData = controller.resolveContract(contractId, true)
const actualContractData = controller.resolveContract(
contractId,
gameVersion,
true,
)
if (!actualContractData) {
log(LogLevel.ERROR, `Failed to resolve contract ${contractId}!`)

View File

@ -123,6 +123,7 @@ export function getDestinationCompletion(
{
type: ChallengeFilterType.ParentLocation,
parent: parent.Id,
gameVersion,
pro1Filter: Pro1FilterType.Exclude,
},
parent.Id,
@ -298,6 +299,7 @@ export function createLocationsData(
locData.parents[sublocation.Properties.ParentLocation]
const creationContract = controller.resolveContract(
sublocation.Properties.CreateContractId!,
gameVersion,
)
if (!creationContract && excludeIfNoContracts) {

View File

@ -44,7 +44,10 @@ export function withLookupDialog(
return
}
const contract = controller.resolveContract(req.query.contractId)
const contract = controller.resolveContract(
req.query.contractId,
req.gameVersion,
)
if (!contract) {
res.status(404).send("contract does not exist!")
@ -130,7 +133,10 @@ export function directRoute(req: RequestWithJwt, res: Response): void {
return
}
const contract = controller.resolveContract(req.params.contractId)
const contract = controller.resolveContract(
req.params.contractId,
req.gameVersion,
)
if (!contract) {
res.status(404).send("contract does not exist!")

View File

@ -89,7 +89,10 @@ export function getHubData(gameVersion: GameVersion, userId: string) {
const contractCreationTutorial =
gameVersion !== "scpc"
? controller.resolveContract(contractCreationTutorialId)!
? controller.resolveContract(
contractCreationTutorialId,
gameVersion,
)!
: undefined
const locations = getVersionedConfig<PeacockLocationsData>(

View File

@ -28,7 +28,7 @@ import type {
UserProfile,
} from "../types/types"
import { log, LogLevel } from "../loggingInterop"
import { _legacyBull, controller } from "../controller"
import { controller } from "../controller"
import {
escalationTypes,
getLevelCount,
@ -129,16 +129,7 @@ export async function getPlanningData(
}
}
let contractData: MissionManifest | undefined
if (
gameVersion === "h1" &&
contractId === "42bac555-bbb9-429d-a8ce-f1ffdf94211c"
) {
contractData = _legacyBull
} else {
contractData = controller.resolveContract(contractId)
}
let contractData = controller.resolveContract(contractId, gameVersion)
if (!contractData) {
return {
@ -169,7 +160,7 @@ export async function getPlanningData(
// now reassign properties and continue
contractId = group["1"]
contractData = controller.resolveContract(contractId)
contractData = controller.resolveContract(contractId, gameVersion)
}
if (!contractData) {
@ -195,7 +186,10 @@ export async function getPlanningData(
contractData.Metadata.InGroup ?? contractData.Metadata.Id
if (escalation) {
const groupContractData = controller.resolveContract(escalationGroupId)
const groupContractData = controller.resolveContract(
escalationGroupId,
gameVersion,
)
if (!groupContractData) {
log(LogLevel.ERROR, `Not found: ${contractId}, planning esc group`)
@ -225,7 +219,7 @@ export async function getPlanningData(
assert(typeof newLevelId === "string", "newLevelId is not a string")
contractData = controller.resolveContract(newLevelId)
contractData = controller.resolveContract(newLevelId, gameVersion)
}
}

View File

@ -113,7 +113,7 @@ export function createPlayNextMission(
Content: {
ContractId: contractId,
UserCentricContract: generateUserCentric(
controller.resolveContract(contractId),
controller.resolveContract(contractId, gameVersion),
userId,
gameVersion,
),
@ -144,14 +144,15 @@ export type PlayNextCategory = {
/**
* Generates tiles for recommended mission stories given a contract ID.
*
* @param contractId The contract ID.
* @param gameVersion The game's version.
* @returns The tile object.
*/
export function createMainOpportunityTile(
contractId: string,
gameVersion: GameVersion,
): PlayNextCategory {
const contractData = controller.resolveContract(contractId)
const contractData = controller.resolveContract(contractId, gameVersion)
const missionStories = getConfig<Record<string, MissionStory>>(
"MissionStories",
@ -234,7 +235,7 @@ export function getGamePlayNextData(
)
}
cats.push(createMainOpportunityTile(contractId))
cats.push(createMainOpportunityTile(contractId, gameVersion))
}
const pzIdIndex = orderedPZMissions.indexOf(contractId)

View File

@ -134,7 +134,7 @@ export function getModernStashData(
let contractData: MissionManifest | undefined = undefined
if (query.contractid) {
contractData = controller.resolveContract(query.contractid)
contractData = controller.resolveContract(query.contractid, gameVersion)
}
const inventory = createInventory(
@ -264,7 +264,10 @@ export function getLegacyStashData(
return undefined
}
const contractData = controller.resolveContract(query.contractid)
const contractData = controller.resolveContract(
query.contractid,
gameVersion,
)
if (!contractData) {
return undefined

View File

@ -134,7 +134,7 @@ multiplayerMenuDataRouter.get(
Presets: presets,
UserCentricContracts: [...contractIds].map((contractId) => {
return generateUserCentric(
controller.resolveContract(contractId),
controller.resolveContract(contractId, req.gameVersion),
req.jwt.unique_name,
req.gameVersion,
)

View File

@ -110,7 +110,7 @@ multiplayerRouter.post(
const userCentrics = contractIds
.map((id) =>
generateUserCentric(
controller.resolveContract(id),
controller.resolveContract(id, req.gameVersion),
req.jwt.unique_name,
req.gameVersion,
),

View File

@ -476,7 +476,11 @@ profileRouter.post(
return res.status(404).send("invalid contract")
}
const json = controller.resolveContract(req.body.contractId, true)
const json = controller.resolveContract(
req.body.contractId,
req.gameVersion,
true,
)
if (!json) {
log(

View File

@ -637,6 +637,7 @@ export async function getMissionEndData(
// Resolve contract data
const contractData = controller.resolveContract(
sessionDetails.contractId,
gameVersion,
false,
)
@ -661,7 +662,9 @@ export async function getMissionEndData(
IsEscalation: true,
}
const levelCount = getLevelCount(controller.resolveContract(eGroupId))
const levelCount = getLevelCount(
controller.resolveContract(eGroupId, gameVersion),
)
escalationCompletion: if (
userData.Extensions.PeacockEscalations[eGroupId] === levelCount
@ -754,6 +757,7 @@ export async function getMissionEndData(
{
type: ChallengeFilterType.ParentLocation,
parent: locationParentId,
gameVersion,
pro1Filter:
contractData.Metadata.Difficulty === "pro1"
? Pro1FilterType.Only