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:
parent
3191f053fb
commit
cf953d8f81
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -42,6 +42,7 @@ export function contractsModeHome(req: RequestWithJwt, res: Response): void {
|
||||
|
||||
const contractCreationTutorial = controller.resolveContract(
|
||||
contractCreationTutorialId,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
res.json({
|
||||
|
@ -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)
|
||||
|
@ -133,7 +133,7 @@ export function getPlayEscalationInfo(
|
||||
}
|
||||
|
||||
const totalLevelCount = getLevelCount(
|
||||
controller.resolveContract(groupContractId),
|
||||
controller.resolveContract(groupContractId, gameVersion),
|
||||
)
|
||||
|
||||
let nextContractId = "00000000-0000-0000-0000-000000000000"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
||||
|
@ -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[] = [
|
||||
|
@ -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}`)
|
||||
|
@ -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}!`)
|
||||
|
@ -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) {
|
||||
|
@ -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!")
|
||||
|
@ -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>(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user