1
mirror of https://github.com/thepeacockproject/Peacock synced 2025-03-01 14:43:02 +01:00
Peacock/components/menus/menuSystem.ts
AnthonyFuller 926168bbd1 Fix elusive targets
(cherry picked from commit c8838f755ba47cc4199e111e79490230aec578db)
2023-01-28 16:58:22 +00:00

271 lines
9.1 KiB
TypeScript

/*
* The Peacock Project - a HITMAN server replacement.
* Copyright (C) 2021-2023 The Peacock Project Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { NextFunction, Response, Router } from "express"
import serveStatic from "serve-static"
import { join } from "path"
import md5File from "md5-file"
import { getConfig } from "../configSwizzleManager"
import { readFile } from "atomically"
import { GameVersion, RequestWithJwt } from "../types/types"
import { log, LogLevel } from "../loggingInterop"
import { imageFetchingMiddleware } from "./imageHandler"
import { SyncBailHook, SyncHook } from "../hooksImpl"
/**
* Router triggered before {@link menuSystemRouter}.
*/
const menuSystemPreRouter = Router()
const menuSystemRouter = Router()
// /resources-8-11/
/**
* A class for managing the menu system's fetched JSON data.
*/
export class MenuSystemDatabase {
/**
* The hooks.
*/
hooks: {
/**
* A hook for getting a list of configurations which the game should
* fetch from the server.
*
* Params:
* - configs: The configurations list (mutable). These should be full paths,
* for instance, `/menusystem/data/testing.json`.
* - gameVersion: The game's version.
*/
getDatabaseDiff: SyncHook<
[/** configs */ string[], /** gameVersion */ GameVersion]
>
/**
* A hook for getting the requested configuration.
*
* Params:
* - configName: The requested file's name.
* - gameVersion: The game's version.
*
* Returns: The file as an object.
*/
getConfig: SyncBailHook<
[/** configName */ string, /** gameVersion */ GameVersion],
unknown
>
}
constructor() {
this.hooks = {
getDatabaseDiff: new SyncHook(),
getConfig: new SyncBailHook(),
}
this.hooks.getDatabaseDiff.tap(
"PeacockInternal",
(configs, gameVersion) => {
if (gameVersion === "h3") {
configs.push(
"menusystem/elements/settings/data/isnonvroptionvisible.json",
)
configs.push(
"menusystem/elements/contract/hitscategory_elusive.json",
)
}
if (gameVersion === "h3" || gameVersion === "h1") {
configs.push("menusystem/pages/hub/hub_page.json")
}
configs.push("menusystem/data/ishitman3available.json")
configs.push("menusystem/pages/hub/modals/roadmap/modal.json")
configs.push(
"menusystem/pages/hub/data/isfullmenuavailable.json",
)
if (["h3", "h2"].includes(gameVersion)) {
configs.push(
"menusystem/pages/hub/dashboard/dashboard.json",
)
configs.push(
"menusystem/pages/hub/dashboard/category_escalation/result.json",
)
}
if (gameVersion === "h2") {
configs.push("menusystem/data/ismultiplayeravailable.json")
configs.push(
"menusystem/pages/multiplayer/content/lobbyslim.json",
)
}
},
)
this.hooks.getConfig.tap("PeacockInternal", (name, gameVersion) => {
switch (name) {
case "/elements/settings/data/isnonvroptionvisible.json":
return {
$if: {
$condition: {
$and: ["$isingame", "$not $isineditor"],
},
$then: "$eq($vrmode,off)",
$else: true,
},
}
case "/elements/contract/hitscategory_elusive.json":
return {
$include:
"menusystem/elements/contract/hitscategory.json",
}
case "/data/ishitman3available.json":
return {
"$if $eq (0,0)": {
$then: "$isonline",
$else: false,
},
}
case "/pages/hub/modals/roadmap/modal.json":
return getConfig("Roadmap", false)
case "/pages/hub/hub_page.json":
return getConfig("HubPageData", false)
case "/pages/hub/data/isfullmenuavailable.json":
return {
"$if $not $isuser freeprologue": {
$then: true,
$else: {
$and: [
"$not $eq($platform,izumo)",
"$isonline",
],
},
},
}
case "/pages/hub/dashboard/dashboard.json":
if (gameVersion === "h3") {
return getConfig("EiderDashboard", false)
} else if (gameVersion === "h2") {
return getConfig("H2DashboardTemplate", false)
}
return undefined
case "/pages/hub/dashboard/category_escalation/result.json":
return getConfig("DashboardCategoryEscalation", false)
case "/data/ismultiplayeravailable.json":
return {
"$if $eq ($platform,stadia)": {
$then: false,
$else: true,
},
}
case "/pages/multiplayer/content/lobbyslim.json":
return getConfig("LobbySlimTemplate", false)
default:
return undefined
}
})
}
/**
* @internal
*/
_getNamedConfig(configName: string, gameVersion: GameVersion): unknown {
return this.hooks.getConfig.call(configName, gameVersion)
}
/**
* Express middleware for fetching configurations.
*
* @param req The request.
* @param res The response.
* @param next The next function.
*/
static configMiddleware(
req: RequestWithJwt,
res: Response,
next: NextFunction,
): void {
const config = menuSystemDatabase._getNamedConfig(
req.path,
req.gameVersion,
)
if (config) {
res.json(config)
return
}
log(LogLevel.DEBUG, `Unable to resolve config ${req.path}, skipping...`)
next()
}
}
export const menuSystemDatabase = new MenuSystemDatabase()
menuSystemRouter.get(
"/dynamic_resources_pc_release_rpkg",
async (req: RequestWithJwt, res) => {
const dynamicResourceName = `dynamic_resources_${req.gameVersion}.rpkg`
const dynamicResourcePath = join(
PEACOCK_DEV ? process.cwd() : __dirname,
"resources",
dynamicResourceName,
)
log(
LogLevel.DEBUG,
`Serving dynamic resources from file ${dynamicResourceName}.`,
)
const hash = await md5File(dynamicResourcePath)
res.set("Content-Type", "application/octet-stream")
res.set("Content-MD5", Buffer.from(hash, "hex").toString("base64"))
res.send(await readFile(dynamicResourcePath))
},
)
menuSystemRouter.use("/menusystem/", MenuSystemDatabase.configMiddleware)
// Miranda Jamison's image path in the repository is escaped for some reason
menuSystemPreRouter.get(
"/images%5Cactors%5Celusive_goldendoublet_face.jpg",
(req, res, next) => {
req.url = "/images/actors/elusive_goldendoublet_face.jpg"
next("router")
},
)
// Sully Bowden is the same (come on IOI!)
menuSystemPreRouter.get(
"/images%5Cactors%5Celusive_redsnapper_face.jpg",
(req, res, next) => {
req.url = "/images/actors/elusive_redsnapper_face.jpg"
next("router")
},
)
menuSystemRouter.use(
"/images/",
serveStatic("images", { fallthrough: true }),
imageFetchingMiddleware,
)
export { menuSystemRouter, menuSystemPreRouter }