Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: in-game options, flags overhaul #506

Merged
merged 14 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
401 changes: 262 additions & 139 deletions components/flags.ts

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ import { multiplayerRouter } from "./multiplayer/multiplayerService"
import { multiplayerMenuDataRouter } from "./multiplayer/multiplayerMenuData"
import { liveSplitManager } from "./livesplit/liveSplitManager"
import { cheapLoadUserData, setupFileStructure } from "./databaseHandler"
import { getFlag } from "./flags"
import { getFlag, saveFlags } from "./flags"

const host = process.env.HOST || "0.0.0.0"
const port = process.env.PORT || 80
Expand Down Expand Up @@ -511,6 +511,9 @@ export async function startServer(options: {
await loadouts.init()
await controller.boot(options.pluginDevHost)

// all plugins had a chance to provide their flags now
saveFlags()

const httpServer = http.createServer(app)

// @ts-expect-error Non-matching method sig
Expand Down
7 changes: 7 additions & 0 deletions components/menus/menuSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@ export class MenuSystemDatabase {
$else: true,
},
}
case "/data/ispeacockdev.json":
return {
"$if $eq (0,0)": {
$then: PEACOCK_DEV,
$else: PEACOCK_DEV,
},
}
case "/pages/hub/modals/peacock/dynresmodal.json":
return getConfig("DynresModal", false)
// This will only get hit by H2
Expand Down
84 changes: 83 additions & 1 deletion components/profileHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import { Router } from "express"
import path from "path"
import querystring from "querystring"
import {
castUserProfile,
getMaxProfileLevel,
Expand All @@ -39,7 +40,7 @@ import {
UpdateUserSaveFileTableBody,
UserProfile,
} from "./types/types"
import { log, LogLevel } from "./loggingInterop"
import { log, logDebug, LogLevel } from "./loggingInterop"
import {
deleteContractSession,
getContractSession,
Expand All @@ -64,6 +65,7 @@ import {
ResolveGamerTagsBody,
} from "./types/gameSchemas"
import assert from "assert"
import { SyncBailHook } from "./hooksImpl"
import { generateCompletionData } from "./contracts/dataGen"

const profileRouter = Router()
Expand Down Expand Up @@ -959,4 +961,84 @@ export async function loadSession(
)
}

export const handleCommand: SyncBailHook<
[/** lastResponse */ unknown, /** command */ string, /** args */ unknown],
unknown
> = new SyncBailHook()

const peacockCommandPrefix = "peacock:"
const commandResponseCache: {
[key: string]: unknown
} = {}

profileRouter.post(
"/ProfileService/GetSemLinkStatus",
jsonMiddleware(),
(_, res) => {
res.json({
CacheBuster: Date.now().toString(),
Commands: commandResponseCache,
IsConfirmed: true,
LinkedEmail: "mail@example.com",
IOIAccountId: nilUuid,
IOIAccountBaseUrl: "https://account.ioi.dk",
})
},
)

profileRouter.post(
"/ProfileService/SubmitSemEmail",
jsonMiddleware(),
// @ts-expect-error Has jwt props.
(req: RequestWithJwt<never, { email: string }>, res) => {
if (req.body.email.startsWith(peacockCommandPrefix)) {
try {
const commands = req.body.email
.substring(peacockCommandPrefix.length)
.split("|")

commands.forEach((c) => {
const commandIndex = c.indexOf("?")

let command = undefined
let args = undefined

if (commandIndex < 0) {
command = c
args = {}
} else {
command = c.substring(0, commandIndex)
args = querystring.parse(c.substring(commandIndex + 1))
LennardF1989 marked this conversation as resolved.
Show resolved Hide resolved
}

if (command === "clear") {
;(args.commands as string)?.split(",").forEach((c2) => {
commandResponseCache[c2] = undefined
})
} else {
commandResponseCache[command] = handleCommand.call(
commandResponseCache[command],
command,
args,
)
}

logDebug(command, args, commandResponseCache)
})
} catch (e) {
log(LogLevel.ERROR, `Failed to handle Peacock command: ${e}`)
}
}

res.json({
CacheBuster: Date.now().toString(),
Commands: commandResponseCache,
IsConfirmed: true,
LinkedEmail: "mail@example.com",
IOIAccountId: nilUuid,
IOIAccountBaseUrl: "https://account.ioi.dk",
})
},
)

export { profileRouter }
22 changes: 18 additions & 4 deletions components/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,10 +1316,24 @@ export interface CompiledChallengeRuntimeData {
export type LoadoutSavingMechanism = "PROFILES" | "LEGACY"
export type ImageLoadingStrategy = "SAVEASREQUESTED" | "ONLINE" | "OFFLINE"

export type Flags = Record<
string,
{ desc: string; default: boolean | string | number }
>
export type Flag = {
category?: string
title: string
desc: string
possibleValues?: string[]
default: boolean | string | number
showIngame?: boolean
requiresGameRestart?: boolean
requiresPeacockRestart?: boolean
}

export type FlagSection = {
title: string
desc: string
flags: Record<string, Flag>
}

export type Flags = Record<string, FlagSection>

/**
* A "hit" object.
Expand Down
75 changes: 75 additions & 0 deletions plugins/elusive-destinations.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Controller } from "@peacockproject/core/controller"
import { LogLevel, log } from "@peacockproject/core/loggingInterop"
import { orderedETs } from "@peacockproject/core/contracts/elusiveTargets"
import { FlagSection } from "@peacockproject/core/types/types"
import { getFlag, registerFlagSection } from "@peacockproject/core/flags"

const pluginFlagSectionKey = "elusiveDestinations"
const pluginFlagSection: FlagSection = {
title: "Plugin - Elusive Destinations",
desc: "Add elusive targets to Destinations",
flags: {
allDifficulties: {
title: "All difficulties",
desc: "When turned on, elusive targets can be played on any difficulty.",
default: true,
requiresPeacockRestart: true,
},
allEntrances: {
title: "All entrances",
desc: "When turned on, any starting location can be chosen for elusive targets, regardless of unlock status.",
default: true,
requiresPeacockRestart: true,
},
smallTiles: {
title: "Small tiles",
desc: "When turned on, show elusive targets as small tiles under Destinations.",
default: true,
requiresPeacockRestart: true,
},
},
}

function initPlugin(controller: Controller): void {
log(LogLevel.INFO, "[Plugin] Elusive destinations", "elusive-destinations")

registerFlagSection(pluginFlagSectionKey, pluginFlagSection)

for (const contractId of orderedETs) {
const contract = controller.resolveContract(contractId)

if (!contract) {
continue
}

const baseContractId =
// @ts-expect-error Indexer
controller.missionsInLocations[contract.Metadata.Location][0]
const baseContract = controller.resolveContract(baseContractId)

if (!baseContract) {
continue
}

if (getFlag(`${pluginFlagSectionKey}.allDifficulties`)) {
contract.Data.GameDifficulties = baseContract.Data.GameDifficulties
}

if (getFlag(`${pluginFlagSectionKey}.allEntrances`)) {
contract.Data.Entrances = baseContract.Data.Entrances
}

if (getFlag(`${pluginFlagSectionKey}.smallTiles`)) {
contract.Metadata.Subtype = "specialassignment"
}

controller.addMission(contract)

// @ts-expect-error Indexer
controller.missionsInLocations[contract.Metadata.Location].push(
contract.Metadata.Id,
)
}
}

module.exports = initPlugin
Loading