Added logic to generate require table for plugins

Fixed issue where non-development builds would try to load TypeScript-based plugins
Fixed issue of non-exported interface FakePlayer
This commit is contained in:
Lennard Fonteijn 2023-04-16 20:11:49 +02:00 committed by Lennard Fonteijn
parent acd269a4ea
commit 4551059a53
11 changed files with 395 additions and 59 deletions

View File

@ -20,7 +20,6 @@ import { existsSync, readdirSync, readFileSync } from "fs"
import { readdir, readFile, writeFile } from "fs/promises"
import * as atomically from "atomically"
import { join } from "path"
import * as dataGen from "./contracts/dataGen"
import {
generateUserCentric,
getSubLocationFromContract,
@ -52,12 +51,10 @@ import {
getVersionedConfig,
swizzle,
} from "./configSwizzleManager"
import * as logging from "./loggingInterop"
import { log, LogLevel } from "./loggingInterop"
import * as axios from "axios"
import * as ini from "js-ini"
import * as statemachineParser from "@peacockproject/statemachine-parser"
import * as utils from "./utils"
import {
addDashesToPublicId,
fastClone,
@ -65,13 +62,8 @@ import {
hitmapsUrl,
versions,
} from "./utils"
import * as sessionSerialization from "./sessionSerialization"
import * as databaseHandler from "./databaseHandler"
import * as playnext from "./menus/playnext"
import * as hooksImpl from "./hooksImpl"
import { AsyncSeriesHook, SyncBailHook, SyncHook } from "./hooksImpl"
import * as hitsCategoryServiceMod from "./contracts/hitsCategoryService"
import { MenuSystemDatabase, menuSystemDatabase } from "./menus/menuSystem"
import { menuSystemDatabase } from "./menus/menuSystem"
import { parse } from "json5"
import { userAuths } from "./officialServerAuth"
// @ts-expect-error Ignore JSON import
@ -93,6 +85,7 @@ import { ChallengeFilterType } from "./candle/challengeHelpers"
import { MasteryService } from "./candle/masteryService"
import { MasteryPackage } from "./types/mastery"
import { ProgressionService } from "./candle/progressionService"
import generatedPeacockRequireTable from "./generatedPeacockRequireTable"
/**
* An array of string arrays that contains the IDs of the featured contracts.
@ -193,38 +186,7 @@ export const featuredContractGroups: string[][] = [
]
const peacockRequireTable = {
"@peacockproject/core/contracts/dataGen": { __esModule: true, ...dataGen },
"@peacockproject/core/databaseHandler": {
__esModule: true,
...databaseHandler,
},
"@peacockproject/core/utils": {
__esModule: true,
...utils,
},
"@peacockproject/core/loggingInterop": { __esModule: true, ...logging },
"@peacockproject/core/sessionSerialization": {
__esModule: true,
...sessionSerialization,
},
"@peacockproject/statemachine-parser": statemachineParser,
"@peacockproject/core/menus/playnext": {
__esModule: true,
...playnext,
},
"@peacockproject/core/hooksImpl": {
__esModule: true,
...hooksImpl,
},
"@peacockproject/core/contracts/hitsCategoryService": {
__esModule: true,
hitsCategoryService: hitsCategoryServiceMod,
},
"@peacockproject/core/menus/menuSystem": {
__esModule: true,
MenuSystemDatabase,
menuSystemDatabase,
},
axios,
ini,
atomically,
@ -248,6 +210,15 @@ function createPeacockRequire(pluginName: string): NodeRequire {
return peacockRequireTable[specifier]
}
if (
Object.prototype.hasOwnProperty.call(
generatedPeacockRequireTable,
specifier,
)
) {
return generatedPeacockRequireTable[specifier]
}
try {
return require(specifier)
} catch (e) {
@ -563,7 +534,7 @@ export class Controller {
await this._loadPlugins()
if (pluginDevHost) {
if (PEACOCK_DEV && pluginDevHost) {
await this._loadWorkspacePlugins()
}

View File

@ -0,0 +1,272 @@
/*
* 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 * as webFeatures from "./webFeatures"
import * as utils from "./utils"
import * as tools from "./tools"
import * as sessionSerialization from "./sessionSerialization"
import * as scoreHandler from "./scoreHandler"
import * as profileHandler from "./profileHandler"
import * as platformEntitlements from "./platformEntitlements"
import * as ownership from "./ownership"
import * as officialServerAuth from "./officialServerAuth"
import * as oauthToken from "./oauthToken"
import * as menuData from "./menuData"
import * as loggingInterop from "./loggingInterop"
import * as loadouts from "./loadouts"
import * as inventory from "./inventory"
import * as hotReloadService from "./hotReloadService"
import * as hooksImpl from "./hooksImpl"
import * as flags from "./flags"
import * as evergreen from "./evergreen"
import * as eventHandler from "./eventHandler"
import * as entitlementStrategies from "./entitlementStrategies"
import * as discordRp from "./discordRp"
import * as databaseHandler from "./databaseHandler"
import * as controller from "./controller"
import * as configSwizzleManager from "./configSwizzleManager"
import * as types from "./types/types"
import * as sniperModules from "./types/sniperModules"
import * as scoring from "./types/scoring"
import * as score from "./types/score"
import * as mastery from "./types/mastery"
import * as livesplit from "./types/livesplit"
import * as gameSchemas from "./types/gameSchemas"
import * as events from "./types/events"
import * as challenges from "./types/challenges"
import * as contractCreation from "./statemachines/contractCreation"
import * as contextListeners from "./statemachines/contextListeners"
import * as multiplayerUtils from "./multiplayer/multiplayerUtils"
import * as multiplayerService from "./multiplayer/multiplayerService"
import * as multiplayerMenuData from "./multiplayer/multiplayerMenuData"
import * as sniper from "./menus/sniper"
import * as playnext from "./menus/playnext"
import * as planning from "./menus/planning"
import * as menuSystem from "./menus/menuSystem"
import * as imageHandler from "./menus/imageHandler"
import * as favoriteContracts from "./menus/favoriteContracts"
import * as destinations from "./menus/destinations"
import * as campaigns from "./menus/campaigns"
import * as liveSplitManager from "./livesplit/liveSplitManager"
import * as liveSplitClient from "./livesplit/liveSplitClient"
import * as ipc from "./discord/ipc"
import * as client from "./discord/client"
import * as reportRouting from "./contracts/reportRouting"
import * as missionsInLocation from "./contracts/missionsInLocation"
import * as hitsCategoryService from "./contracts/hitsCategoryService"
import * as elusiveTargets from "./contracts/elusiveTargets"
import * as dataGen from "./contracts/dataGen"
import * as contractsModeRouting from "./contracts/contractsModeRouting"
import * as contractRouting from "./contracts/contractRouting"
import * as escalationService from "./contracts/escalations/escalationService"
import * as progressionService from "./candle/progressionService"
import * as masteryService from "./candle/masteryService"
import * as challengeService from "./candle/challengeService"
import * as challengeHelpers from "./candle/challengeHelpers"
import * as legacyProfileRouter from "./2016/legacyProfileRouter"
import * as legacyMenuSystem from "./2016/legacyMenuSystem"
import * as legacyMenuData from "./2016/legacyMenuData"
import * as legacyEventRouter from "./2016/legacyEventRouter"
import * as legacyContractHandler from "./2016/legacyContractHandler"
export default {
"@peacockproject/core/webFeatures": { __esModule: true, ...webFeatures },
"@peacockproject/core/utils": { __esModule: true, ...utils },
"@peacockproject/core/tools": { __esModule: true, ...tools },
"@peacockproject/core/sessionSerialization": {
__esModule: true,
...sessionSerialization,
},
"@peacockproject/core/scoreHandler": { __esModule: true, ...scoreHandler },
"@peacockproject/core/profileHandler": {
__esModule: true,
...profileHandler,
},
"@peacockproject/core/platformEntitlements": {
__esModule: true,
...platformEntitlements,
},
"@peacockproject/core/ownership": { __esModule: true, ...ownership },
"@peacockproject/core/officialServerAuth": {
__esModule: true,
...officialServerAuth,
},
"@peacockproject/core/oauthToken": { __esModule: true, ...oauthToken },
"@peacockproject/core/menuData": { __esModule: true, ...menuData },
"@peacockproject/core/loggingInterop": {
__esModule: true,
...loggingInterop,
},
"@peacockproject/core/loadouts": { __esModule: true, ...loadouts },
"@peacockproject/core/inventory": { __esModule: true, ...inventory },
"@peacockproject/core/hotReloadService": {
__esModule: true,
...hotReloadService,
},
"@peacockproject/core/hooksImpl": { __esModule: true, ...hooksImpl },
"@peacockproject/core/flags": { __esModule: true, ...flags },
"@peacockproject/core/evergreen": { __esModule: true, ...evergreen },
"@peacockproject/core/eventHandler": { __esModule: true, ...eventHandler },
"@peacockproject/core/entitlementStrategies": {
__esModule: true,
...entitlementStrategies,
},
"@peacockproject/core/discordRp": { __esModule: true, ...discordRp },
"@peacockproject/core/databaseHandler": {
__esModule: true,
...databaseHandler,
},
"@peacockproject/core/controller": { __esModule: true, ...controller },
"@peacockproject/core/configSwizzleManager": {
__esModule: true,
...configSwizzleManager,
},
"@peacockproject/core/types/types": { __esModule: true, ...types },
"@peacockproject/core/types/sniperModules": {
__esModule: true,
...sniperModules,
},
"@peacockproject/core/types/scoring": { __esModule: true, ...scoring },
"@peacockproject/core/types/score": { __esModule: true, ...score },
"@peacockproject/core/types/mastery": { __esModule: true, ...mastery },
"@peacockproject/core/types/livesplit": { __esModule: true, ...livesplit },
"@peacockproject/core/types/gameSchemas": {
__esModule: true,
...gameSchemas,
},
"@peacockproject/core/types/events": { __esModule: true, ...events },
"@peacockproject/core/types/challenges": {
__esModule: true,
...challenges,
},
"@peacockproject/core/statemachines/contractCreation": {
__esModule: true,
...contractCreation,
},
"@peacockproject/core/statemachines/contextListeners": {
__esModule: true,
...contextListeners,
},
"@peacockproject/core/multiplayer/multiplayerUtils": {
__esModule: true,
...multiplayerUtils,
},
"@peacockproject/core/multiplayer/multiplayerService": {
__esModule: true,
...multiplayerService,
},
"@peacockproject/core/multiplayer/multiplayerMenuData": {
__esModule: true,
...multiplayerMenuData,
},
"@peacockproject/core/menus/sniper": { __esModule: true, ...sniper },
"@peacockproject/core/menus/playnext": { __esModule: true, ...playnext },
"@peacockproject/core/menus/planning": { __esModule: true, ...planning },
"@peacockproject/core/menus/menuSystem": {
__esModule: true,
...menuSystem,
},
"@peacockproject/core/menus/imageHandler": {
__esModule: true,
...imageHandler,
},
"@peacockproject/core/menus/favoriteContracts": {
__esModule: true,
...favoriteContracts,
},
"@peacockproject/core/menus/destinations": {
__esModule: true,
...destinations,
},
"@peacockproject/core/menus/campaigns": { __esModule: true, ...campaigns },
"@peacockproject/core/livesplit/liveSplitManager": {
__esModule: true,
...liveSplitManager,
},
"@peacockproject/core/livesplit/liveSplitClient": {
__esModule: true,
...liveSplitClient,
},
"@peacockproject/core/discord/ipc": { __esModule: true, ...ipc },
"@peacockproject/core/discord/client": { __esModule: true, ...client },
"@peacockproject/core/contracts/reportRouting": {
__esModule: true,
...reportRouting,
},
"@peacockproject/core/contracts/missionsInLocation": {
__esModule: true,
...missionsInLocation,
},
"@peacockproject/core/contracts/hitsCategoryService": {
__esModule: true,
...hitsCategoryService,
},
"@peacockproject/core/contracts/elusiveTargets": {
__esModule: true,
...elusiveTargets,
},
"@peacockproject/core/contracts/dataGen": { __esModule: true, ...dataGen },
"@peacockproject/core/contracts/contractsModeRouting": {
__esModule: true,
...contractsModeRouting,
},
"@peacockproject/core/contracts/contractRouting": {
__esModule: true,
...contractRouting,
},
"@peacockproject/core/contracts/escalations/escalationService": {
__esModule: true,
...escalationService,
},
"@peacockproject/core/candle/progressionService": {
__esModule: true,
...progressionService,
},
"@peacockproject/core/candle/masteryService": {
__esModule: true,
...masteryService,
},
"@peacockproject/core/candle/challengeService": {
__esModule: true,
...challengeService,
},
"@peacockproject/core/candle/challengeHelpers": {
__esModule: true,
...challengeHelpers,
},
"@peacockproject/core/2016/legacyProfileRouter": {
__esModule: true,
...legacyProfileRouter,
},
"@peacockproject/core/2016/legacyMenuSystem": {
__esModule: true,
...legacyMenuSystem,
},
"@peacockproject/core/2016/legacyMenuData": {
__esModule: true,
...legacyMenuData,
},
"@peacockproject/core/2016/legacyEventRouter": {
__esModule: true,
...legacyEventRouter,
},
"@peacockproject/core/2016/legacyContractHandler": {
__esModule: true,
...legacyContractHandler,
},
}

View File

@ -19,6 +19,9 @@
/* eslint-disable no-inner-declarations */
// noinspection RequiredAttributes
// load as soon as possible to prevent dependency issues
import "./generatedPeacockRequireTable"
// load flags as soon as possible
import { getFlag, loadFlags } from "./flags"

View File

@ -62,7 +62,7 @@ const profileRouter = Router()
// /authentication/api/userchannel/
interface FakePlayer {
export interface FakePlayer {
id: string
name: string
platformId: string

View File

@ -10,7 +10,7 @@
"prettier": "pprettier --write \"**/*.{js,json,ts,md,tsx,css,mjs,html}\"",
"prettier:check": "pprettier --check \"**/*.{js,json,ts,md,tsx,css,mjs,html}\"",
"build": "yarn webui:build && node packaging/build.mjs",
"build-plugin": "tsc --project ./plugins/tsconfig.json",
"build-plugins": "node packaging/buildPlugins.mjs",
"typecheck-ws": "tsc",
"typecheck": "yarn workspaces foreach -A run typecheck-ws",
"lint": "eslint --format=pretty .",

View File

@ -19,10 +19,11 @@
import * as e from "esbuild"
import { createRequire } from "module"
import { esbuildPluginLicense } from "./esbuild-plugin-license.mjs"
import { packResources } from "./buildTasks.mjs"
import { generateRequireTable, packResources } from "./buildTasks.mjs"
const require = createRequire(import.meta.url)
await generateRequireTable()
await packResources()
const { version, revisionIdent } = require("../package.json")

View File

@ -0,0 +1,33 @@
/*
* 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 * as e from "esbuild"
import glob from "glob"
const plugins = glob.sync("plugins/*.plugin.ts", {
cwd: ".",
})
await e.build({
entryPoints: plugins,
bundle: true,
outdir: "plugins",
platform: "node",
target: "es2021",
external: ["@peacockproject/core"],
})

View File

@ -17,7 +17,7 @@
*/
import { existsSync } from "fs"
import { join, basename } from "path"
import { join, basename, sep } from "path"
import millis from "ms"
import { mkdir, readdir, readFile, unlink, writeFile } from "fs/promises"
import { createHash } from "crypto"
@ -25,6 +25,7 @@ import { Packr } from "msgpackr"
import { brotliCompress } from "zlib"
import { promisify } from "util"
import glob from "glob"
import prettier from "prettier"
const packer = new Packr({
bundleStrings: true,
@ -50,6 +51,65 @@ async function handleFile(resources, name, contents) {
await writeFile(join(resources, `${targetName}.prp`), packer.pack(contents))
}
export async function generateRequireTable() {
const files = glob.sync("**/*.ts", {
cwd: "./components",
ignore: [
"index.ts",
"generatedPeacockRequireTable.ts",
"types/globals.d.ts",
],
})
const imports = []
const requiresTable = []
files.forEach((e) => {
const variable = basename(e, ".ts")
const importPath = e.replaceAll(sep, "/").replace(/\.ts$/, "")
imports.push(`import * as ${variable} from "./${importPath}"`)
requiresTable.push(
`"@peacockproject/core/${importPath}": { __esModule: true, ...${variable}}`,
)
})
const prettierConfig = await prettier.resolveConfig()
prettierConfig.parser = "babel"
const generatedPeacockRequireTableFile = prettier.format(
`/*
* 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/>.
*/
${imports.join("\n")}
export default {
${requiresTable.join(",\n")}
}`,
prettierConfig,
)
await writeFile(
"components/generatedPeacockRequireTable.ts",
generatedPeacockRequireTableFile,
)
}
export async function packResources() {
const challengesResources = join("resources", "challenges")
await createResourcesFolder(challengesResources)

View File

@ -17,7 +17,7 @@
*/
import picocolors from "picocolors"
import { packResources } from "./buildTasks.mjs"
import { generateRequireTable, packResources } from "./buildTasks.mjs"
import { createRequire, Module } from "module"
import { readFileSync } from "fs"
@ -25,6 +25,7 @@ import { readFileSync } from "fs"
// TS files as if they were JS in a CommonJS environment
const require = createRequire(import.meta.url)
await generateRequireTable()
await packResources()
const { version, revisionIdent } = require("../package.json")

View File

@ -1,17 +1,13 @@
{
"extends": ["../tsconfig.json"],
"compilerOptions": {
"declaration": false,
"emitDeclarationOnly": false,
"rootDir": ".",
"outDir": "./build",
"composite": false
},
"references": [
{
"path": ".."
// Reset rootDir to default to make rootDirs take effect
"rootDir": null,
"rootDirs": ["../components", "."],
"paths": {
"@peacockproject/core/*": ["./components/*"]
}
],
},
"include": ["*.ts"],
"exclude": []
}

View File

@ -21,9 +21,8 @@
"rootDir": "components",
"outDir": "./build",
"isolatedModules": true,
"stripInternal": true,
"composite": true
"stripInternal": true
},
"include": ["components"],
"exclude": ["packaging", "chunk0.js", "webui", "tests"]
"exclude": ["packaging", "chunk0.js", "webui", "tests", "plugins"]
}