Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into v7
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyFuller committed May 1, 2024
2 parents 6fa3f3d + 9af665b commit 7271221
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 39 deletions.
11 changes: 4 additions & 7 deletions components/candle/masteryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
getSubLocationByName,
} from "../contracts/dataGen"
import { log, LogLevel } from "../loggingInterop"
import { getVersionedConfig } from "../configSwizzleManager"
import { getConfig } from "../configSwizzleManager"
import { getUserData } from "../databaseHandler"
import {
LocationMasteryData,
Expand Down Expand Up @@ -265,12 +265,9 @@ export class MasteryService {

// TODO: Refactor this into the new inventory system?
const name = isSniper
? getVersionedConfig<Unlockable[]>(
"SniperUnlockables",
gameVersion,
false,
).find((unlockable) => unlockable.Id === subPackageId)?.Properties
.Name
? getConfig<Unlockable[]>("SniperUnlockables", false).find(
(unlockable) => unlockable.Id === subPackageId,
)?.Properties.Name
: undefined

return {
Expand Down
20 changes: 18 additions & 2 deletions components/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ import { readFileSync, writeFileSync } from "fs"
import { pack, unpack } from "msgpackr"
import { log, LogLevel } from "./loggingInterop"
import { startServer } from "./index"
import { PEACOCKVERSTRING } from "./utils"
import * as process from "node:process"

program.description(
"The Peacock Project is a HITMAN™ World of Assassination Trilogy server replacement.",
)

program.option("-v, --version", "print the version number and exit")
program.option(
"--hmr",
"enable experimental hot reloading of contracts",
Expand All @@ -45,9 +48,22 @@ program.option(
"activate plugin development features - requires plugin dev workspace setup",
getFlag("developmentPluginDevHost") as boolean,
)
program.action(startServer)
program.action(
(options: { hmr: boolean; pluginDevHost: boolean; version: boolean }) => {
if (options.version) {
console.log(`Peacock ${PEACOCKVERSTRING}`)
return process.exit()
}

program.command("tools").description("open the tools UI").action(toolsMenu)
return startServer(options)
},
)

program
.command("tools")
.description("open the tools UI")
.option("-s, --skip", "Skip encrypting the debug profile", false)
.action(toolsMenu)

// noinspection RequiredAttributes
program
Expand Down
80 changes: 80 additions & 0 deletions components/contracts/dataGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,84 @@ const noPacifications = {
],
}

const hideAllBodies = {
IsCompleted: true,
ContractConditionType: "PrimarySecondary",
Primary: [
{
Type: "gamechanger",
Properties: {
Id: "c2da52c5-ff3e-41cd-a175-4ed9267f6c95",
Name: "UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_PRIMARY_NAME",
Description:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_PRIMARY_DESC",
LongDescription:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_PRIMARY_DESC",
TileImage:
"images/contractconditions/condition_contrac_hide_all_bodies.jpg",
Icon: "images/challenges/default_challenge_icon.png",
ObjectivesCategory: "primary",
},
},
],
Secondary: [
{
Type: "gamechanger",
Properties: {
Id: "a77cf01e-ab02-4b1c-a4bd-a37fb8be1114",
Name: "UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_SECONDARY_NAME",
Description:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_SECONDARY_DESC",
LongDescription:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_HIDE_ALL_BODIES_SECONDARY_DESC",
TileImage:
"images/contractconditions/condition_contrac_hide_all_bodies.jpg",
Icon: "images/challenges/default_challenge_icon.png",
ObjectivesCategory: "secondary",
},
},
],
}

const doNotGetSpotted = {
IsCompleted: true,
ContractConditionType: "PrimarySecondary",
Primary: [
{
Type: "gamechanger",
Properties: {
Id: "9f409781-0a06-4748-b08d-784e78c6d481",
Name: "UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_PRIMARY_NAME",
Description:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_PRIMARY_DESC",
LongDescription:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_PRIMARY_DESC",
TileImage:
"images/contractconditions/condition_contrac_do_not_be_spotted.jpg",
Icon: "images/challenges/default_challenge_icon.png",
ObjectivesCategory: "primary",
},
},
],
Secondary: [
{
Type: "gamechanger",
Properties: {
Id: "a77cf01e-ab02-4b1c-a4bd-a37fb8be1114",
Name: "UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_SECONDARY_NAME",
Description:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_SECONDARY_DESC",
LongDescription:
"UI_GAMECHANGERS_GLOBAL_CONTRACTCONDITION_DO_NOT_GET_SPOTTED_SECONDARY_DESC",
TileImage:
"images/contractconditions/condition_contrac_do_not_be_spotted.jpg",
Icon: "images/challenges/default_challenge_icon.png",
ObjectivesCategory: "secondary",
},
},
],
}

export function complications(timeString: string) {
return [
{
Expand Down Expand Up @@ -831,5 +909,7 @@ export function complications(timeString: string) {
noMissedShots,
headshotsOnly,
targetsOnly,
hideAllBodies,
doNotGetSpotted,
]
}
78 changes: 75 additions & 3 deletions components/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import picocolors from "picocolors"
import { Filename, npath, PortablePath, ppath, xfs } from "@yarnpkg/fslib"
import { makeEmptyArchive, ZipFS } from "@yarnpkg/libzip"
import { configs } from "./configSwizzleManager"
import * as crypto from "node:crypto"
import * as fs from "node:fs"

const IMAGE_PACK_REPO = "thepeacockproject/ImagePack"

Expand All @@ -45,7 +47,7 @@ const modFrameworkDataPath: string | false =
)) ||
false

export async function toolsMenu() {
export async function toolsMenu(options: { skip: boolean }) {
const init = await prompts({
name: "actions",
message: "Select actions:",
Expand All @@ -72,7 +74,7 @@ export async function toolsMenu() {

switch (init.actions) {
case "debug":
await exportDebugInfo()
await exportDebugInfo(options.skip)
break
case "download-images":
await downloadImagePack()
Expand All @@ -93,7 +95,7 @@ async function copyIntoZip(zip: ZipFS, path: string): Promise<void> {
})
}

async function exportDebugInfo(): Promise<void> {
async function exportDebugInfo(skipEncrypt: boolean): Promise<void> {
const cpus = cpuList().map((cpu, index) => ({
core: index + 1,
...cpu,
Expand Down Expand Up @@ -151,6 +153,76 @@ async function exportDebugInfo(): Promise<void> {

zip.saveAndClose()

encrypt: if (!skipEncrypt) {
const publicKey = crypto.createPublicKey(
"-----BEGIN PUBLIC KEY-----\n" +
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAupOzd1PiMy8M+hWRwt3J\n" +
"/fZPExUwRVlGTd4goZSfrZO2NfXjCzkfTvAIWe8ItU88oUsbfcMu/iQ7JqtoeMdc\n" +
"yd4/hA0Glnc10fREnFJrJTEhetzrv1PUXx8uSYzkfj6L8eO8UO6W/faomYdNontQ\n" +
"F3CEBUvYHXRo31D7ubQKiAXExDLybWCZXDV4f0HL1vaTR0gjB7tCfq5D9jpdaBR5\n" +
"sQPX2RSexUJE4dn5t4Gkbl8G9CaSEwPaBIAhcHnY8BkrNb16cNTp24AUqeKC1AMI\n" +
"e1xgOLjkx0EG+43dGX48lP8oRAixyemA6QDcAwufPPxuCbAVNMN7+NpTEaky2j+U\n" +
"Gay80XtTOAbsW8kgRVUJnw6CbaHvvmDUL19q7rGAe8dOgCiatE1HWpxPCZX8KBrw\n" +
"iCIEmPzftqqulqwmfh1Uf7ZWvdb9ugYhp9wce0cGX1Lb6uO4gd+GAJsgyGHa/ny9\n" +
"Y8nqGHpiCcBXMgZ8ggSQXQlLpJGmGfkxNyw8RRM1uiBBU9y9QxhcghAhkkS814a0\n" +
"F3UAISYursYS337uhm54qvLNKDIHqaOpZHh23El092zQYJlZbCZn5WZiTtBum1Us\n" +
"XvG7eLb7Tulqnk90p0SRhQeIt2zGnb49Zq+ixP9YJX7LK3xm+JJ9j6/1oKJbrdBL\n" +
"1ppbcSy1RGsiYbPT3ohZ59sCAwEAAQ==\n" +
"-----END PUBLIC KEY-----",
)

const options: {
algorithm: string
key: Buffer | string
iv: Buffer | string
} = {
algorithm: "aes-256-cbc",
key: crypto.randomBytes(32),
iv: crypto.randomBytes(16),
}

const cipher = crypto.createCipheriv(
options.algorithm,
options.key,
options.iv,
)

options.key = options.key.toString("base64")
options.iv = options.iv.toString("base64")

const buffer = fs.readFileSync("DEBUG_PROFILE.zip")
const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()])

if (!encrypted) {
log(LogLevel.WARN, "Failed to encrypt debug profile!")
break encrypt
}

fs.rmSync("DEBUG_PROFILE.zip")

const zipFile = ppath.resolve(ppath.cwd(), "DEBUG_PROFILE.zip")

// we'll start by creating an empty zip file
await writeFile(npath.fromPortablePath(zipFile), makeEmptyArchive())

const zip = new ZipFS(zipFile, { create: true })

await zip.writeFilePromise(
zip.resolve("DEBUG_PROFILE.peacock" as Filename),
encrypted,
)

await zip.writeFilePromise(
zip.resolve("key" as Filename),
crypto.publicEncrypt(
publicKey,
Buffer.from(JSON.stringify(options)),
),
)

zip.saveAndClose()
}

log(
LogLevel.INFO,
"Successfully outputted debugging data to DEBUG_PROFILE.zip!",
Expand Down
59 changes: 40 additions & 19 deletions components/webFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ if (PEACOCK_DEV) {
res.set("Access-Control-Allow-Origin", "*")
res.set(
"Access-Control-Allow-Methods",
"GET,HEAD,PUT,PATCH,POST,DELETE",
"GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
)
res.set("Access-Control-Allow-Headers", "Content-Type")
res.set("Access-Control-Allow-Headers", "content-type")
next()
})
}
Expand Down Expand Up @@ -357,6 +357,8 @@ webFeaturesRouter.post(

try {
// Challenge Progression
log(LogLevel.DEBUG, "Getting challenge progression...")

const challengeProgression = await auth._useService<
ChallengeProgressionData[]
>(
Expand Down Expand Up @@ -387,6 +389,8 @@ webFeaturesRouter.post(
)

// Profile Progression
log(LogLevel.DEBUG, "Getting profile progression...")

const exts = await auth._useService<OfficialProfileResponse>(
`https://${remoteService}.hitman.io/authentication/api/userchannel/ProfileService/GetProfile`,
false,
Expand All @@ -405,6 +409,8 @@ webFeaturesRouter.post(
)

if (req.query.gv !== "h1") {
log(LogLevel.DEBUG, "Processing PlayerProfileXP...")

const sublocations = exts.data.Extensions.progression
.PlayerProfileXP
.Sublocations as unknown as OfficialSublocation[]
Expand All @@ -427,32 +433,39 @@ webFeaturesRouter.post(
),
}

log(LogLevel.DEBUG, "Processing opportunity progression...")

userdata.Extensions.opportunityprogression = Object.fromEntries(
Object.keys(
exts.data.Extensions.opportunityprogression,
exts.data.Extensions.opportunityprogression || {},
).map((value) => [value, true]),
)

for (const [unlockId, data] of Object.entries(
exts.data.Extensions.progression.Unlockables ?? {},
)) {
const unlockableId = unlockId.toUpperCase()

if (!(unlockableId in SNIPER_UNLOCK_TO_LOCATION)) continue
;(
userdata.Extensions.progression.Locations[
SNIPER_UNLOCK_TO_LOCATION[unlockableId]
] as SubPackageData
)[unlockableId] = {
Xp: data.Xp,
Level: data.Level,
PreviouslySeenXp: data.PreviouslySeenXp,
if (exts.data.Extensions.progression.Unlockables) {
log(LogLevel.DEBUG, "Processing unlockables...")

for (const [unlockId, data] of Object.entries(
exts.data.Extensions.progression.Unlockables,
)) {
const unlockableId = unlockId.toUpperCase()

if (!(unlockableId in SNIPER_UNLOCK_TO_LOCATION))
continue
;(
userdata.Extensions.progression.Locations[
SNIPER_UNLOCK_TO_LOCATION[unlockableId]
] as SubPackageData
)[unlockableId] = {
Xp: data.Xp,
Level: data.Level,
PreviouslySeenXp: data.PreviouslySeenXp,
}
}
}
}

userdata.Extensions.gamepersistentdata =
exts.data.Extensions.gamepersistentdata
exts.data.Extensions.gamepersistentdata || {}

const sublocations = getSublocations(req.query.gv)
userdata.Extensions.defaultloadout ??= {}
Expand All @@ -467,7 +480,8 @@ webFeaturesRouter.post(
}
}

userdata.Extensions.achievements = exts.data.Extensions.achievements
userdata.Extensions.achievements =
exts.data.Extensions.achievements || []

for (const [locId, data] of Object.entries(
exts.data.Extensions.progression.Locations,
Expand Down Expand Up @@ -508,6 +522,11 @@ webFeaturesRouter.post(
}

// Escalation & Arcade Progression
log(
LogLevel.DEBUG,
`Getting escalation${req.query.gv === "h3" ? " & arcade" : ""} progression...`,
)

const escalations = await getAllHitsCategory(
auth,
remoteService!,
Expand Down Expand Up @@ -546,6 +565,8 @@ webFeaturesRouter.post(
// TODO: Try and see if there is a less intensive way to do this
// GetForPlay2 is quite intensive on IOI's side as it starts a session
if (req.query.gv === "h3") {
log(LogLevel.DEBUG, "Getting freelancer progression...")

await auth._useService(
`https://${remoteService}.hitman.io/authentication/api/configuration/Init?configName=pc-prod&lockedContentDisabled=false&isFreePrologueUser=false&isIntroPackUser=false&isFullExperienceUser=true`,
true,
Expand Down

0 comments on commit 7271221

Please sign in to comment.