From b961afddadff3a52e0bfc2a521a79607d6ff3ef3 Mon Sep 17 00:00:00 2001 From: Arnold W Date: Tue, 8 Sep 2026 16:07:39 +0200 Subject: [PATCH] Refocus docs on navigation, terminology, and decisions --- README.md | 10 +- docs/account-pages.md | 266 ++-------------------------------- docs/architecture.md | 32 ++++ docs/backend.md | 246 ------------------------------- docs/build.md | 105 +++----------- docs/cheats.md | 330 ++---------------------------------------- docs/cli.md | 70 +-------- docs/config.md | 181 +++-------------------- docs/decisions.md | 47 ++++++ docs/glossary.md | 29 ++++ docs/platforms.md | 121 +++------------- docs/ui.md | 221 ---------------------------- 12 files changed, 213 insertions(+), 1445 deletions(-) create mode 100644 docs/architecture.md delete mode 100644 docs/backend.md create mode 100644 docs/decisions.md create mode 100644 docs/glossary.md delete mode 100644 docs/ui.md diff --git a/README.md b/README.md index 81e24154..b8edc76a 100644 --- a/README.md +++ b/README.md @@ -123,13 +123,15 @@ npm run validate ## Documentation -- [Backend Architecture](docs/backend.md) - Server, CDP, and module structure. +- [Architecture Map](docs/architecture.md) - Runtime boundaries and source entry points. +- [Domain Language](docs/glossary.md) - Shared project terminology. +- [Design Decisions](docs/decisions.md) - Choices, alternatives, and trade-offs. - [Build Guide](docs/build.md) - Building from source for all platforms. -- [Cheats Guide](docs/cheats.md) - Writing and registering cheats. +- [Cheats Guide](docs/cheats.md) - Usage safety and contributor entry points. - [CLI Reference](docs/cli.md) - Console commands and autocomplete. -- [Configuration](docs/config.md) - Config files, schema, and validation. +- [Configuration](docs/config.md) - Applying settings, saving overrides, and safety. - [Platforms](docs/platforms.md) - Steam, web, and OS-specific setup. -- [Web UI](docs/ui.md) - VanJS dashboard and components. +- [Account Pages](docs/account-pages.md) - Editing safety and source entry points. ## Contributing diff --git a/docs/account-pages.md b/docs/account-pages.md index ee9f5365..5fb5eb15 100644 --- a/docs/account-pages.md +++ b/docs/account-pages.md @@ -1,258 +1,18 @@ -# Account pages playbook +# Account pages -Guide for building and maintaining Account feature tabs. +Account editors can change progression and other save-sensitive values. Check the field's meaning and warning +before writing. An inferred label is not proof that a value is safe to change. -This document covers the refactored Account tab system in `src/ui/components/views/account/`. It is for new top-level Account tabs, world tabs, nested world panels, and world sub-tabs. It does not cover raw `OptionsListAccount` schema/editor changes. +For terminology, see the [glossary](glossary.md). For the rendering trade-off, see +[preserve editable row identity](decisions.md#preserve-editable-row-identity). -## 1) Source of truth +## Where to start -Read these files before adding or changing an Account feature tab: +- [Account.js](../src/ui/components/views/Account.js): Account navigation. +- [Account feature directory](../src/ui/components/views/account/): world and feature editors. +- [Shared components](../src/ui/components/views/account/components/): editable rows and page controls. +- [accountShared.js](../src/ui/components/views/account/accountShared.js): shared read/write helpers. +- [Account schema](../src/ui/config/optionsAccountSchema.json): raw option labels and warnings. -- `src/ui/components/views/Account.js` - Top-level Account shell, `ACCOUNT_TABS`, and lazy top-level pane mounting. -- `src/ui/components/views/account/W1Tab.js` ... `W7Tab.js` - World tab shells and sub-tab registries. -- `src/ui/components/views/account/tabShared.js` - Shared world/nested tab factories, tab navigation, lazy panes, and persistent panes. -- `src/ui/components/views/account/accountLoadPolicy.js` - `useAccountLoad()` for standardized load state and load failure logging. -- `src/ui/components/views/account/accountShared.js` - Shared value helpers, Haxe unwrapping, verified writes, bulk writes, stable state helpers, and write status. -- `src/ui/components/views/account/components/` - Shared chrome, rows, sections, page shells, and collection helpers. - -Current structure: - -- Top-level Account tabs include Account Options, Upgrade Vault, and W1-W7. -- W1-W7 contain implemented feature tabs. -- W2 has a nested Alchemy panel: Brewing, Liquid, Vials, Pay 2 Win, Sigils. -- W3 has a nested Construction panel: Buildings, Cogs. - -## 2) Shared contracts - -### Page chrome - -- Prefer `PersistentAccountListPage(...)` for most editable Account features. -- Use `RefreshButton`, `WarningBanner`, `NoticeBanner`, `AccountSection`, and `AccountRow` before adding tab-local chrome. -- Keep tab-specific selectors out of shared CSS unless they define a reusable primitive. - -### Loading - -- Use `useAccountLoad({ label })` for account-page reads. -- Keep reads inline in the tab. The shared hook owns state transitions and logging. -- Call `load()` once when the component is constructed. Because Account panes lazy-mount, this loads the tab only when first opened. -- Use `Promise.all` for independent reads. -- Normalize indexed game payloads with `toIndexedArray(raw)` from `src/ui/utils/index.js`. -- Use `readLevelDefinitions(...)` when pairing a GGA levels array with a `cList` definition table. - -### Writes - -- Use `useWriteStatus()` for row-level and bulk write actions. -- Use `writeVerified(path, value)` when one GGA write must be confirmed. -- Use `writeManyVerified(writes)` for custom batches. -- Use `runBulkSet(...)` when many rows share a target-value and local-state update flow. -- Use shared row/action components so loading, success, and error states render consistently. -- Do not hand-roll status timers. `useWriteStatus()` owns the success/error clear timing. - -### Feedback classes - -- Current shared Account rows emit `account-row--success` and `account-row--error`. -- Action buttons use the shared button/status classes from `ActionButton`. -- Do not introduce old `feature-row--success`, `feature-row--error`, or tab-local feedback class contracts. - -## 3) Pattern selection - -Pick one rendering pattern per tab and stay consistent within that tab. - -### Pattern A: Persistent list page - -Use this for editable row tabs, dense lists, bulk actions, and any UI where input focus, row status, or scroll position needs to survive writes. - -Typical examples: - -- `w1/AnvilTab.js` -- `w2/VialTab.js` -- `UpgradeVaultTab.js` - -Shape: - -```js -export const MyFeatureTab = () => { - const { loading, error, run } = useAccountLoad({ label: "My Feature" }); - const listNode = div({ class: "account-list" }); - - const load = async () => - run(async () => { - // Read game data, normalize it, then update existing state. - }); - - load(); - - return PersistentAccountListPage({ - title: "MY FEATURE", - description: "Short operational description.", - actions: RefreshButton({ onRefresh: load }), - state: { loading, error }, - loadingText: "READING MY FEATURE", - errorTitle: "MY FEATURE READ FAILED", - initialWrapperClass: "account-list", - body: listNode, - }); -}; -``` - -### Pattern B: Simple rebuild body - -Use when the tab is small, mostly read-only, and remounting content after load is acceptable. - -Pass a small reactive `body` to `PersistentAccountListPage` when the surrounding page should keep persistent Account chrome. - -### Pattern C: Cached collection or card UI - -Use when the data shape can change but existing rows or cards should survive writes and refreshes. - -Typical techniques: - -- `createIndexedStateGetter()` for sparse numeric/list index state. -- `getOrCreateState(map, key, initial)` for keyed row/card state. -- `createStaticRowReconciler(container)` when rows should rebuild only after a signature changes. -- Stable arrays of row/card nodes when refreshing values should not rebuild UI identity. - -## 4) Shared UI primitives - -Use these before creating new one-off helpers: - -- `EditableNumberRow` - Focus-safe numeric row. Keeps committed value state separate from draft input text. -- `ClampedLevelRow` - Thin adapter for single-path level fields with min/max clamping. -- `BulkActionBar`, `SetAllNumberControl`, `SetAllSelectControl` - Header action strip and set-all controls. -- `AddFromListSection` - Add-from-dropdown collection section. -- `RemovableStoredRow` - Removable collection row with row-local write status. -- `AccountRow` - Non-numeric row shell with status-aware classes. -- `AccountSection` - Standard grouped section header/body. - -Do not extract tiny one-off helpers when the logic is only a couple of lines and used once. Inline the logic unless a helper materially improves reuse or readability. - -## 5) Reactivity safety - -The most common regression is remounting rows or cards after a value changes. That can lose focus or hide success/error feedback. - -Rules: - -- Keep VanJS reactive function scope as small as possible. -- Do not return arrays directly from reactive children; return one node or wrap multiple nodes in a container. -- Do not wrap an input in a reactive block that depends on that input's value. -- Do not read mutable `state.val` while constructing row or card components inside a reactive list renderer if that subscribes the parent renderer. -- Build persistent rows or cards once where possible, then update backing `van.state` values in place. -- For dynamic lists, rebuild only when the list shape changes, not when an individual row value changes. - -Bad: - -```js -const Row = ({ valueState }) => { - const inputValue = van.state(String(valueState.val ?? 0)); - return input({ value: inputValue }); -}; -``` - -Good: - -```js -const Row = ({ valueState }) => { - const inputValue = van.state("0"); - - van.derive(() => { - inputValue.val = String(valueState.val ?? 0); - }); - - return input({ value: inputValue }); -}; -``` - -Prefer `EditableNumberRow` when this pattern fits; it already handles draft text and focus-safe syncing. - -## 6) Wiring new tabs - -### Add a feature tab under an existing world - -1. Create `src/ui/components/views/account/wN/MyFeatureTab.js`. -2. Import it in `src/ui/components/views/account/WNTab.js`. -3. Add a `WN_SUBTABS` entry with a stable kebab-case `id`, uppercase `label`, and `component`. -4. If the world has a nested panel, add the tab to that nested registry instead of the outer world registry. -5. Add feature CSS at `src/ui/styles/tabs/wN/_my-feature.css` if needed. -6. Import that CSS from `src/ui/styles/tabs/wN/_index.css`. - -### Add a nested panel tab - -1. Find the nested registry, such as `ALCHEMY_SUBTABS` in `W2Tab.js` or `CONSTRUCTION_SUBTABS` in `W3Tab.js`. -2. Add the new tab to that nested array. -3. Keep the registry wired through the existing `createNestedTab(...)` component. -4. Keep panel-specific CSS near the world's existing tab CSS. - -### Add a top-level Account tab - -1. Create `src/ui/components/views/account/MyTopLevelTab.js`. -2. Import it in `src/ui/components/views/Account.js`. -3. Add an `ACCOUNT_TABS` entry with `isWorld: false`. -4. Add styles only if the shared Account primitives are insufficient. - -### Add a new world tab - -1. Create `src/ui/components/views/account/WNTab.js`. -2. Register it in `src/ui/components/views/Account.js` with `isWorld: true` and `worldNum`. -3. Define its sub-tab registry and export it with `createWorldTab(...)`. -4. Add world-specific styles and imports. - -## 7) CSS rules - -- Shared Account primitives live in `src/ui/styles/_account-pages.css`. -- Top account/world navigation lives in `src/ui/styles/_world-tabs.css`. -- Feature-specific CSS belongs in `src/ui/styles/tabs/wN/_feature-name.css`. -- Import feature CSS from the matching `src/ui/styles/tabs/wN/_index.css`. -- Use existing classes first: `tab-container`, `scroll-container`, `account-list`, `account-row`, `account-section`, `account-header__actions`, `account-setall-row`, `warning-banner`, `tab-add-row`. -- Keep dimensions stable for row controls, cards, grids, status labels, and action buttons so writes do not shift layout. - -## 8) Validation checklist - -Run the full validation when code changes: - -```powershell -npm run validate -``` - -If the change is narrow and full validation is too expensive, run the tightest checks that cover changed files: - -```powershell -node --check src/ui/components/views/account/wN/MyFeatureTab.js -npx eslint src/ui/components/views/account/ src/ui/styles/ -``` - -Manual checks: - -- The tab appears at the correct nav level. -- Lazy mounting loads the tab only when first opened. -- Initial loading and initial failure states use the shared page chrome. -- Refresh reloads data without destroying stable rows unnecessarily. -- Write actions show loading, success, and error feedback on the correct row/action. -- Changed and unchanged verified writes both show success feedback. -- Inputs keep focus while typing and do not reset on each keystroke. -- Bulk actions use `useWriteStatus()` and verified writes. -- Placeholder tabs remain passive. -- CSS is imported and scoped to the feature/world. - -## 9) Anti-patterns - -- Importing or referencing removed `featureShared.js` helpers. -- Manual load-state boilerplate when `useAccountLoad()` fits. -- Manual status timers instead of `useWriteStatus()`. -- Rebuilding rows/cards on every value write. -- Constructor-time reads of mutable `*.val` in row/card builders when they cause parent remounts. -- Returning arrays from reactive VanJS children. -- Putting feature-specific selectors in shared Account CSS. -- Adding a new helper for logic that is short, local, and used once. +Read the relevant feature and shared component before changing an editor. Their source owns the rendering +and write contracts. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..a277abe4 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,32 @@ +# Architecture map + +Use this map to choose where to start reading. Source owns implementation details and contracts. +See the [glossary](glossary.md) for terminology and [decisions](decisions.md) for design rationale. + +## Runtime boundaries + +| Area | Responsibility | Start here | +| ------------------ | --------------------------------------------------------- | ----------------------------------------------------- | +| Node process | Orchestration, platform attachment, local server, and CLI | [main.js](../src/main.js), [modules](../src/modules/) | +| Injected game code | Commands and hooks operating in the game runtime | [cheats/main.js](../src/cheats/main.js) | +| Web UI | User interaction with the injector | [App.js](../src/ui/components/App.js) | + +The Node process connects to the game through CDP. The Web UI communicates with the Node server. +Injected code runs in the game context; Node and UI code have separate environments. + +## Find the relevant area + +| Task area | Source | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------- | +| Platform attachment and injection | [game modules](../src/modules/game/) | +| Configuration loading | [configManager.js](../src/modules/config/configManager.js) | +| HTTP and WebSocket contracts | [apiRoutes.js](../src/modules/server/apiRoutes.js), [wsServer.js](../src/modules/server/wsServer.js) | +| Console interaction | [cliInterface.js](../src/modules/cli/cliInterface.js) | +| Command registration and state | [cheats/core](../src/cheats/core/) | +| Commands and game hooks | [cheats/cheats](../src/cheats/cheats/), [proxies](../src/cheats/proxies/) | +| UI workspaces and shared components | [views](../src/ui/components/views/), [components](../src/ui/components/) | +| UI state and communication | [state](../src/ui/state/), [services](../src/ui/services/) | +| Styles | [style.css](../src/ui/entry/style.css), [styles](../src/ui/styles/) | +| Bundling and packaging | [rollup.config.mjs](../rollup.config.mjs), [package.json](../package.json) | + +See the [Account page guide](account-pages.md) and [cheats guide](cheats.md) for those areas. diff --git a/docs/backend.md b/docs/backend.md deleted file mode 100644 index c52d40b7..00000000 --- a/docs/backend.md +++ /dev/null @@ -1,246 +0,0 @@ -# Backend Architecture - -How the Node.js backend attaches to Idleon, injects the cheat bundle, and serves the UI/API/CLI. - -## Repository layout - -- `src/main.js`: Application entry point and orchestration. -- `src/modules/config/configManager.js`: Loads `config.js` and `config.custom.js` and exposes accessors. -- `src/modules/game/`: CDP attachment and injection pipeline. -- `src/modules/server/`: Web server, API routes, and WebSocket updates. -- `src/modules/server/tinyRouter.js`: Lightweight router with `.json()` and `.status()` helpers. -- `src/modules/server/wsServer.js`: WebSocket lifecycle, cheat-state broadcasts, and value monitor sync. -- `src/modules/cli/cliInterface.js`: Interactive CLI prompt. -- `src/modules/updateChecker.js`: GitHub release check for update hints. -- `src/modules/utils/`: Logging and helper utilities. - -## Startup sequence - -`src/main.js` drives the runtime in this order: - -1. `printHeader()` shows version info and update check. -2. `loadConfiguration()` merges defaults with `config.custom.js` overrides. -3. `createWebServer()` prepares the TinyRouter instance (UI optional). -4. `attachToTarget()` connects to Steam or Web targets via CDP. -5. `setupIntercept()` installs request interception and injects `cheats.js`. -6. Registers `Page.loadEventFired` handler to initialize the cheat context. -7. `Page.reload({ ignoreCache: true })` triggers the first intercepted load. -8. On first page load, starts API routes and the web server (if UI is enabled). -9. `initializeCheatContext()` runs `setup.call(context)` in the game. -10. Starts CLI once cheat setup succeeds. - -The `uiServicesStarted` and `servicesStarted` flags prevent duplicate startups on page reload. - -`handleError()` logs fatal failures, prints CDP hints, and waits for Enter before exiting. - -## Configuration loading - -`configManager.js` loads two files in order from the runtime base directory: - -- Packaged builds: executable directory. -- Source runs: `process.cwd()`. - -- `config.js` (defaults) -- `config.custom.js` (overrides, optional) - -Merge behavior: - -- `injectorConfig` is deep-merged. -- `startupCheats` is unioned to avoid duplicates. -- `cheatConfig` is deep-merged. -- `defaultConfig` is stored as the pristine `config.js` copy (used for UI diffs and saves). - -Ports are fixed by helpers: - -- `getCdpPort()` returns the constant `32123`. -- `getWebPort()` returns `injectorConfig.webPort` or falls back to `8080`. -- `getLinuxTimeout()` returns `injectorConfig.onLinuxTimeout` (Linux attach timeout). - -## CDP attachment - -`src/modules/game/gameAttachment.js` handles platform-specific attachment. - -`waitForCdpEndpoint()` polls `http://localhost:/json/version` until a WebSocket URL appears. - -### Steam target - -Windows: - -- `findIdleonExe()` checks `injectorConfig.gameExePath`, then standard Steam paths. -- `attach(exePath)` spawns `LegendsOfIdleon.exe --remote-debugging-port=32123`. -- If direct launch fails or times out, it falls back to Steam protocol: - -```text -steam://run/1476970//--remote-debugging-port=32123 -``` - -Linux: - -- `autoAttachLinux()` tries `steam -applaunch 1476970 --remote-debugging-port=32123`. -- If Steam auto-launch fails, it waits for manual launch and polls the CDP endpoint. - -macOS: - -- Steam target is blocked; use the web target. - -### Web target - -`attachToWeb()` launches a Chromium-based browser and attaches to the Idleon tab. - -- `resolveBrowserPath()` checks `injectorConfig.browserPath` or common install locations by OS. -- Uses `injectorConfig.browserUserDataDir` or defaults to `idleon-web-profile` in the runtime base directory. -- Spawns the browser with CDP and browser safety flags: - -```text ---remote-debugging-port=32123 ---user-data-dir= ---no-first-run ---no-default-browser-check ---remote-allow-origins=* ---site-per-process ---disable-extensions ---new-window - -``` - -- Linux adds `--disable-gpu` for stability. -- `waitForIdleonTarget()` scans CDP targets and matches host or URL against `injectorConfig.webUrl`. -- The returned hook is `target.webSocketDebuggerUrl` (or the target itself when provided). - -## Injection pipeline - -`src/modules/game/cheatInjection.js` installs a CDP interceptor that patches the game bundle and injects cheats. - -Key steps: - -1. Read `cheats.js` from disk. -2. Prepend runtime config values used by cheats: - -```js -let startupCheats = ["wide mtx"]; -let cheatConfig = { ... }; -let webPort = 8080; -``` - -3. Register interception using `injectorConfig.interceptPattern` (default `*N.js`). -4. Disable cache and bypass CSP to keep interception reliable. -5. For each intercepted response: - - Download the body (`Network.getResponseBodyForInterception`). - - Match `injectorConfig.injreg` (default `\w+\.ApplicationMain\s*?=`) to capture the game root variable. - - Evaluate the cheat bundle in the page context (`Runtime.evaluate`) before patching the script. - - Inject the game root reference into `window.__idleon_cheats__`. - - Return the modified response via `rawResponse` (full headers + body). -6. If injection fails, the interceptor continues the original request to avoid a hanging load. - -Relevant snippet: - -```js -const replacementRegex = new RegExp(config.injreg); -const newBody = originalBody.replace(replacementRegex, `window.__idleon_cheats__=${AppVar[0]};$&`); -``` - -## Cheat context and runtime init - -`createCheatContext()` builds the expression used by both UI and CLI: - -```js -window.__idleon_cheats__ || window.document.querySelector("iframe")?.contentWindow?.__idleon_cheats__; -``` - -`initializeCheatContext()` checks the context exists, then executes: - -```js -setup.call(context); -``` - -`initializeCheatContext()` uses `allowUnsafeEvalBlockedByCSP` and returns false if the context is missing. - -## Web server and API - -`src/modules/server/webServer.js` creates a lightweight HTTP server: - -- `TinyRouter` handles JSON API routes. -- Static assets are served from `src/ui` when `injectorConfig.enableUI` is true (root `/` maps to `entry/index.html`). -- WebSocket server is attached for live cheat-state updates. - -`tinyRouter.js` polyfills `res.status()`, `res.json()`, and `req.json()` for JSON bodies. - -`src/modules/server/apiRoutes.js` defines all REST endpoints. The core ones are: - -- `GET /api/heartbeat`: `{ status: "online", timestamp }` -- `GET /api/cheats`: autocomplete list from `getAutoCompleteSuggestions`. -- `POST /api/toggle`: executes a cheat command. -- `GET /api/config`: startupCheats + cheatConfig + injectorConfig + defaultConfig. -- `POST /api/config/update`: updates runtime config in memory and in-game. -- `POST /api/config/save`: writes `config.custom.js` with diffs only in the runtime base directory. -- `GET /api/options-account`: reads `OptionsListAccount` from game memory. -- `POST /api/options-account/index`: writes a single options list entry. -- `GET /api/cheat-states`: returns current cheat states via `cheatStateList`. -- `GET /api/devtools-url`: returns the CDP DevTools URL for the attached target. -- `POST /api/open-url`: opens a local browser tab for help/devtools links. - -Example payload: - -```json -POST /api/toggle -{ "action": "wide mtx" } -``` - -Successful responses return `{ result: "..." }` with the command output string. - -## WebSocket updates - -`src/modules/server/wsServer.js` pushes cheat-state and monitor updates to UI clients, and accepts updates from the game runtime. - -Message format: - -```json -{ - "type": "cheat-states", - "data": { "wide": { "mtx": true } } -} -``` - -`broadcastCheatStates()` is called after cheats run so the UI stays in sync without polling. - -Monitor messages: - -```json -{ "type": "identify", "clientType": "ui" } -{ "type": "monitor-subscribe", "id": "gga-GemsOwned", "path": "gga.GemsOwned" } -{ "type": "monitor-unsubscribe", "id": "gga-GemsOwned" } -{ "type": "monitor-update", "id": "gga-GemsOwned", "value": 123, "ts": 1700000000000 } -{ "type": "monitor-state", "data": { "gga-GemsOwned": { "path": "gga.GemsOwned", "history": [] } } } -``` - -Notes: - -- Clients default to `ui`; the game runtime re-identifies with `identify`. -- Monitor history stores the last 10 values per id, broadcast as `monitor-state`. -- `monitor-subscribe` and `monitor-unsubscribe` evaluate `window.monitorWrap` and `window.monitorUnwrap` in the game context. - -On connection, clients receive current cheat state and monitor state immediately. - -## CLI integration - -`src/modules/cli/cliInterface.js` uses the same CDP context as the UI: - -- Autocomplete list comes from `getAutoCompleteSuggestions.call(context)`. -- Commands execute via `cheat.call(context, '')`. -- A built-in `chromedebug` command opens the DevTools URL directly. -- The CLI uses Enquirer autocomplete with token matching and a two-step confirm for parameterized cheats. - -## Error handling and troubleshooting - -- `No inspectable targets` usually means Steam is not running or the game is already open without `--remote-debugging-port`. -- `Injection regex did not match` indicates `injectorConfig.injreg` is stale after a game update. -- `Timeout waiting for debugger WebSocket URL` means the target never opened the CDP port. -- `webUrl is required when target is 'web'` means a URL is missing for web attach. -- `Could not find a compatible Chromium-based browser` means auto-detection failed. -- `Cheat context not found` means the page loaded but `window.__idleon_cheats__` was never created. - -When adding API routes, wrap runtime calls in `try/catch` and return JSON errors: - -```js -res.status(500).json({ error: error.message }); -``` diff --git a/docs/build.md b/docs/build.md index e4846098..5c9b90b6 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,98 +1,37 @@ -# Build and Release +# Build and release -Bundling cheats, validating changes, and packaging for each platform. +## Development -## Prerequisites +Install dependencies, build the cheat bundle, then start the injector: -- Run `npm install`. -- Use Node 18 for packaging (`pkg` targets Node 18). -- Build `cheats.js` before running (build once or use the watcher). - -## Development loop - -```bash +```sh npm install npm run build:cheats npm run start ``` -- `npm run start` launches the injector and web server using the existing `cheats.js` bundle. -- Start does not rebuild cheats; rebuild and restart after changes. -- No hot reload for the injected game context. - -For cheat development, run the watcher to keep `cheats.js` current: - -```bash -npm run watch:cheats -``` - -Keep the watcher running in one terminal and run `npm run start` in another. - -## Bundling cheats - -`rollup.config.mjs` builds the browser bundle: - -- Input: `src/cheats/main.js`. -- Output: `cheats.js` (IIFE format). -- `strict: false` to avoid issues in the game context. -- Injects a banner with version info and date. -- Prints bundle stats (chunks, size, modules) after build. -- Bundles all cheat modules with `moduleSideEffects: true` for safety. -- The cheat bundle is required for both dev and packaged builds. - -Build once: - -```bash -npm run build:cheats -``` - -Note: `cheats.js` is gitignored and generated during builds. - -## Validation - -`npm run validate` runs these checks: - -1. `npm run build:cheats` (ensures bundle is current). -2. `node --check cheats.js config.js` (syntax check). -3. `npx eslint .` (lint). -4. `node -e "require('./src/ui/config/optionsAccountSchema.json')"` (schema validity). +After changing injected code, rebuild and restart. For repeated edits, run `npm run watch:cheats` in another +terminal; restart the injector to use the rebuilt bundle. Edit the source, not generated `cheats.js`. -Run this before packaging or releasing. - -To lint only cheats: `npx eslint src/cheats/`. -To check formatting with Prettier: `npm run format:check`. -To auto-format all files: `npm run format`. - -## Packaging binaries - -`pkg` bundles Node 18 with the app. Commands in `package.json`: - -```bash -npm run build # Windows: InjectCheatsUI.exe -npm run build-unix # Linux: InjectCheatsUI-linux -npm run build-macos-x64 # macOS Intel: InjectCheatsUI-macos-x64 -npm run build-macos-arm64 # macOS Apple Silicon: InjectCheatsUI-macos-arm64 -``` +## Before submitting -Each script runs `npm run build:cheats` first, then packages with `--compress Gzip`. +Follow the checks in [CONTRIBUTING.md](../CONTRIBUTING.md). +Inspect [package.json](../package.json) for the current scripts and +[rollup.config.mjs](../rollup.config.mjs) for bundling details. -`pkg` includes `src/ui/**/*` as assets so the web UI ships with the binary. -New UI assets should go under `src/ui` or be added to `pkg.assets`. +## Packaging -## Release checklist +Install dependencies before packaging. The packaging targets use Node 18. -1. Run `npm run validate`. -2. Rebuild cheats if not using the watcher. -3. Build target binaries. -4. Run `InjectCheatsUI` and verify: - - UI loads at `http://localhost:8080`. - - Cheats list and config load. - - A sample cheat executes. - - CLI autocomplete works. +| Platform | Command | +| ------------------- | --------------------------- | +| Windows | `npm run build` | +| Linux | `npm run build-unix` | +| macOS Intel | `npm run build-macos-x64` | +| macOS Apple Silicon | `npm run build-macos-arm64` | -## Troubleshooting +Before a release, run the contribution checks, build the intended binaries, and try the packaged application +on the target platform. Check attachment, UI loading, and a command whose effect you understand. -- Syntax errors in `cheats.js` or `config.js` will fail `node --check`. -- `ENOENT: cheats.js` means the bundle was not built (run `npm run build:cheats`). -- If `pkg` fails, check Node 18 compatibility and that dependencies are installed. -- If the UI fails to load in packaged builds, check that `src/ui/**/*` is in `pkg.assets`. +If startup reports a missing `cheats.js`, rebuild the bundle. For packaging failures, check the terminal +error against the packaging configuration in `package.json`. diff --git a/docs/cheats.md b/docs/cheats.md index d71bbab4..654ccb60 100644 --- a/docs/cheats.md +++ b/docs/cheats.md @@ -1,322 +1,20 @@ -# Cheat Development Guide +# Cheats -For contributors adding new cheats. Assumes familiarity with the browser runtime and game scripts. +Use the CLI or Cheats workspace to find commands and their descriptions. The +[project wiki](https://github.com/MrJoiny/Idleon-Injector/wiki) provides a user-facing command list. +See the [glossary](glossary.md) for the distinction between commands, state, configuration, and proxies. -## Cheat system overview +Commands can alter save-sensitive game data. Check the command description and relevant configuration warnings +before running an unfamiliar command. -- Cheats are ES modules in `src/cheats/` bundled into `cheats.js` via `npm run build:cheats`. -- Runtime entry is `src/cheats/main.js`; see "Runtime globals" for exposed helpers and getters. -- `src/cheats/core/setup.js` waits for `gameReady` before installing proxies and running `startupCheats`. +## Contributor entry points -## Where code goes +- [Command implementations](../src/cheats/cheats/) and [registration](../src/cheats/core/registration.js). +- [Game globals](../src/cheats/core/globals.js) and [traversal utilities](../src/cheats/utils/traverse.js). +- [Game hooks](../src/cheats/proxies/) and [proxy utilities](../src/cheats/utils/proxy.js). -- `src/cheats/cheats/`: command cheats (toggles + parameterized commands). -- `src/cheats/proxies/`: hooks into game logic. -- `src/cheats/helpers/` and `src/cheats/utils/`: shared helpers. -- `src/cheats/core/`: state, registration, globals. +Exact parameters and helper contracts belong in those files. The +[base-first decision](decisions.md#preserve-original-method-side-effects) explains the method-hook constraint. -## Adding a command cheat - -1. Create a new module in `src/cheats/cheats/`. -2. Register it with `registerCheat` or `registerCheats`. -3. Import it in `src/cheats/cheats/register.js` so rollup includes it. -4. Rebuild with `npm run build:cheats` (or `npm run watch:cheats`). -5. Restart the app. - -### Registration API - -#### registerCheat structure - -From `src/cheats/cheats/utility.js`: - -```js -import { registerCheat } from "../core/registration.js"; - -registerCheat({ - name: "gga", - message: "The attribute you want to get, separated by spaces", - needsParam: true, - fn: (params) => gg_func(params, 0), -}); -``` - -Fields: - -- `name`: command string (`"gga"`, `"buy"`, `"list monster"`, etc). -- `message`: help text for UI/CLI. -- `fn`: command handler. It runs with `this` bound to the game context. -- `category`: optional category override (defaults to `"general"`). -- `needsParam`: whether the CLI expects parameters. - -#### registerCheats structure - -From `src/cheats/cheats/wide.js`: - -```js -import { registerCheats } from "../core/registration.js"; -import { firebase } from "../core/globals.js"; - -registerCheats({ - name: "wide", - message: "all account-wide cheats", - allowToggleChildren: true, - subcheats: [ - { - name: "gembuylimit", - message: "set max gem item purchases", - configurable: true, - }, - { name: "mtx", message: "gem shop cost nullification" }, - { - name: "guildpoints", - message: "Adds 1200 guild points to the guild.", - fn: function () { - firebase.guildPointAdjust(1200); - return "Added 1200 guild points to the guild."; - }, - }, - ], -}); -``` - -Fields: - -- `name`: command namespace (`"wide"`). -- `message`: help text. -- `category`: optional category override (inherits for subcheats if not set). -- `fn`: custom handler (overrides default toggle). When provided, you control `cheatState` updates manually. -- `subcheats`: array of subcheat definitions (same shape as a `registerCheats` node). -- `configurable`: allows numeric/boolean input; writes to `cheatConfig` at the same path. -- `allowToggleChildren`: toggles all subcheats when called without args. -- `registerParent`: optional (default `true`). Set `false` to register only subcommands and keep the parent as a namespace. -- `needsParam`: optional override for parameter expectation. - -### Namespace-only parent example - -Use `registerParent: false` when only subcommands should be callable: - -```js -registerCheats({ - name: "qnty", - message: "Change first inventory/chest slot quantity", - registerParent: false, - subcheats: [ - { name: "inv", needsParam: true, fn: () => {} }, - { name: "chest", needsParam: true, fn: () => {} }, - ], -}); -``` - -With this setup, `qnty inv` and `qnty chest` are valid, but plain `qnty` is not registered. - -### Parameterized command example - -From `src/cheats/cheats/wide.js`: - -```js -registerCheat({ - name: "buy", - message: "Buy gem shop packs. You get items from the pack, but no gems and no pets.", - needsParam: true, - fn: function (params) { - const code = params[0]; - if (!code) { - return "No code was given, provide a code"; - } - - firebase.addToMessageQueue("SERVER_CODE", "SERVER_ITEM_BUNDLE", code); - return `${code} has been sent!`; - }, -}); -``` - -### Configurable cheats - -From `src/cheats/cheats/wide.js`: - -```js -registerCheats({ - name: "wide", - message: "all account-wide cheats", - allowToggleChildren: true, - subcheats: [ - { - name: "gembuylimit", - message: "set max gem item purchases", - configurable: true, - }, - ], -}); -``` - -`config.js` provides the defaults that map to the same path: - -```js -exports.cheatConfig = { - wide: { - gembuylimit: 0, - }, -}; -``` - -### UI categories - -`category` controls grouping in the UI. If not specified, top-level cheat names become the category when using `registerCheats` with subcheats. - -## Game globals and context - -- `src/cheats/core/globals.js` exposes `gga`, `itemDefs`, `monsterDefs`, `cList`, `customMaps`, `dialogueDefs`, and others once `gameReady` completes. -- Import the globals from `core/globals.js` instead of walking window properties directly. - -```js -import { gga, cList } from "../core/globals.js"; -``` - -### Runtime globals - -`src/cheats/main.js` exposes these globals in the game context: - -- `window.cheat(action)`: main dispatcher (runs setup if needed). -- `window.setup()`: runs cheat setup explicitly. -- `window.updateCheatConfig()`: updates config at runtime. -- `window.getAutoCompleteSuggestions()`: UI suggestions API. -- `window.getOptionsListAccount()` / `window.setOptionsListAccountIndex()`: account options list helpers. -- `window.cheatStateList`: snapshot for UI/state lists. -- `window.monitorWrap(id, path)`: start monitoring a value for Web UI. -- `window.monitorUnwrap(id)`: stop monitoring a value. -- `window.monitorList()`: list active monitor ids and paths. -- `window.cheats` / `window.cheatState`: command registry and live state. -- `window.bEngine`, `window.itemDefs`, `window.monsterDefs`, `window.cList`, `window.behavior`, `window.events`: game - getters defined with `Object.defineProperty`. - -## Proxy patterns (advanced) - -Use a proxy to intercept game logic consistently (per tick or per call). All proxies are wired in `src/cheats/proxies/setup.js`, which imports per-module setup functions (e.g., `setupEvents012Proxies`, `setupFirebaseProxy`). Prefer helpers in `src/cheats/utils/proxy.js` (`createMethodProxy`, `createProxy`, `createConfigLookupProxy`, `nullifyListCost`) for consistency. - -### Base-first helper pattern (recommended) - -Use `createMethodProxy` to standardize base-first behavior. - -```js -import { cheatState } from "../core/state.js"; -import { createMethodProxy } from "../utils/proxy.js"; - -createMethodProxy(ActorEvents12, "_customBlock_PlayerReach", (base) => { - if (cheatState.godlike.reach) return 666; - return base; -}); -``` - -Parameters: - -- `target`: object that owns the method. -- `methodName`: method name to wrap. -- `handler`: `(baseResult, ...args) => newResult`, called after the original method runs (base-first). - -Best used for: intercepting methods that must run for side effects while selectively overriding return values. - -### Property proxy helper - -Use `createProxy` for data objects or list entries that return a modified value while a cheat is enabled. - -```js -import { cheatState } from "../core/state.js"; -import { cList } from "../core/globals.js"; -import { createProxy } from "../utils/proxy.js"; - -createProxy(cList, "AlchemyVialItemsPCT", (original) => { - if (cheatState.w2.vialrng) return new Array(original.length).fill(99); - return original; -}); -``` - -Parameters: - -- `targetObj`: object owning the property to proxy. -- `index`: property name or array index to intercept. -- `callback`: either a simple getter (`(original) => newValue`) or `{ get, set }` handlers for more control. - -Best used for: static data lookups (defs, cList entries) where you want conditional values without changing the object structure. - -### Config lookup helper - -Use `createConfigLookupProxy` when a method should look up overrides from `cheatConfig` based on keys or args. - -```js -import { events } from "../core/globals.js"; -import { createConfigLookupProxy } from "../utils/proxy.js"; - -const ActorEvents189 = events(189); -createConfigLookupProxy(ActorEvents189, "_customBlock_CauldronStats", [{ state: "w2.alchemy" }]); -``` - -Parameters: - -- `target`: object that owns the method. -- `methodName`: method name to wrap. -- `mappings`: array of configs describing how to read `cheatState` and `cheatConfig`. - -Mapping fields: - -- `state`: dot-path in `cheatState` that enables the override. -- `config`: optional dot-path in `cheatConfig` (defaults to `state`). -- `fixedKey`: optional key to match against `args[0]` before applying. -- `value`: optional constant to return when `fixedKey` matches. - -Best used for: method hooks that map key-based lookups to `cheatConfig` functions without large if/else blocks. - -### List cost helper - -Use `nullifyListCost` to zero out nested list values when a cheat is enabled. - -```js -import { cList } from "../core/globals.js"; -import { nullifyListCost } from "../utils/proxy.js"; - -nullifyListCost(cList.MTXinfo, 3, [3, 7], "wide.mtx", 0); -``` - -Parameters: - -- `list`: root list to traverse. -- `depth`: how deep to traverse before applying proxies. -- `indices`: index or array of indices to replace at the target level. -- `statePath`: dot-path in `cheatState` that enables the override. -- `zeroValue`: optional replacement value (defaults to "0"). - -Best used for: list-style costs and requirements (MTX, prayers, tasks) that flip to a constant when enabled. - -### Traversal Utilities - -For complex data structures, use the utilities in `src/cheats/utils/traverse.js`: - -- `traverse(obj, depth, worker)`: Visit nodes at a specific depth. **Automatically unwraps Haxe `.h` properties.** This is the preferred way to apply proxies to large lists like `cList`. -- `traverseAll(obj, worker)`: Visit every node in a tree with path tracking. Used for diagnostics and search. Does NOT unwrap `.h` to show true object structure. -- `buildPath(segments)`: Formats an array of keys into a JS property access string (e.g., `foo.bar[0].baz`). - -### Patch guards with \_isPatched - -From `src/cheats/proxies/items.js`: - -```js -export function setupItemProxies() { - if (itemDefs._isPatched) return; - Object.defineProperty(itemDefs, "_isPatched", { - value: true, - enumerable: false, - }); - - for (const item of Object.values(itemDefs)) { - if (!item.h) continue; - // ... apply createProxy helpers per item - } -} -``` - -The `_isPatched` flag prevents double-wrapping if setup runs more than once. Character selection recreates game data objects (lists, item defs, etc.), so `setupFirebaseProxy()` re-runs select setup functions on play button to restore lost proxies. The guard makes those setups idempotent: it skips re-wrapping when the same object is still in memory, but allows re-application when the game has replaced the underlying object. The re-run functions are `setupCListProxy`, `setupGameAttributeProxies`, and `setupItemProxies`. - -## Build and verify - -- `npm run build:cheats` or `npm run watch:cheats` -- `npm run start` -- Restart the app after changes (no hot reload) +Follow the [build workflow](build.md) after changes and the +[contribution guidance](../CONTRIBUTING.md) when submitting a command. diff --git a/docs/cli.md b/docs/cli.md index 6555a96d..36e71979 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,66 +1,12 @@ -# CLI Reference +# Console usage -Interactive prompt that executes cheat commands. Lives in `src/modules/cli/cliInterface.js` and runs alongside the web server. +Use the console prompt to find and run cheat commands. Search by command name or description. -## How the CLI works +For a command that takes a parameter, select the command, append the value, then confirm execution. +Read its description before running it. Use Ctrl+Up and Ctrl+Down to revisit commands from the current session. -1. The injector evaluates `getAutoCompleteSuggestions.call(context)` in the game context. -2. Suggestions map to Enquirer autocomplete choices, with `message` including the description. -3. The prompt loops until exit; selecting a command runs `cheat.call(context, '')`. -4. The CLI starts after first successful injection and shares the CDP Runtime client with the UI. +Run `chromedebug` to open the attached game's DevTools. If commands are unavailable, check the terminal for +attachment or injection errors. -Any cheat registered in `src/cheats/` appears in both CLI and UI. - -## Autocomplete behavior - -- Filtering checks both command value and description. -- Case-insensitive with multi-word matching (space-separated tokens must all match). -- Parameterized commands show a `[+param]` hint (e.g., `buy [+param] (Purchase items)`). -- First match selected by default when input is empty. -- Custom commands not in the list are appended and executed as-is. - -## Command History - -History of executed commands during the session: - -- **Ctrl+Up**: Navigate backwards through previous commands. -- **Ctrl+Down**: Navigate forwards (clears input at end). -- Consecutive duplicates not stored. -- Autocomplete updates as you scroll through history. - -## Parameterized cheats - -Commands with parameters set `needsParam` in registration. The CLI asks for a second Enter to confirm. - -Example flow (command with a parameter): - -```text -Action: buy - -> the prompt locks to "buy" so you can append parameters -Action: buy bun_c - -> command executes -``` - -The final string passes as a single action; enter multi-word parameters exactly as needed. - -## Built-in command - -`chromedebug` is a CLI-only command that opens DevTools for the current CDP target: - -- Uses the same CDP port as the injector (default `32123`). -- Windows uses `start`, macOS uses `open`, Linux uses `xdg-open`. -- Builds the DevTools URL from `Target.getTargetInfo()` and includes `experiment=true`. - -## Output and errors - -- Successful commands log the result string from the cheat. -- Errors print with context so the prompt continues. -- Auto-recovers from prompt errors by retrying after a short delay. -- If autocomplete fails to load, the CLI stops (usually injection issues). - -## Tips - -- Use keywords from command name or description for faster filtering. -- Add frequent cheats to `startupCheats` in `config.custom.js` for auto-execution. -- Use `chromedebug` to inspect the live game context when debugging. -- If autocomplete fails, verify injection succeeded and the UI is reachable. +For automatic commands, use Startup Cheats in [configuration](config.md). +The [CLI implementation](../src/modules/cli/cliInterface.js) owns completion and execution details. diff --git a/docs/config.md b/docs/config.md index d9681adb..c56ea2bd 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,170 +1,33 @@ -# Configuration and Schema +# Configuration -Layered configuration plus UI schema metadata for rendering and validating settings. +Keep personal overrides in `config.custom.js`; leave the committed defaults in +[config.js](../config.js) unchanged. The first-run setup wizard creates the override file. -## Config files +For option help, use the Config editor. The maintained descriptions live in +[configDescriptions.js](../src/ui/config/configDescriptions.js). -Backend loads configuration in `src/modules/config/configManager.js`: +## Applying and saving -- `config.js`: defaults committed to the repo. -- `config.custom.js`: user overrides (gitignored). +Apply cheat configuration to RAM when experimenting in the current session. Save to disk when you want +the configuration available on later runs. Treat applying and saving as separate choices. +Restart after changing injector settings. -`config.custom.js` is created by the setup wizard on first run. -Config files are loaded from the runtime base directory: +Use Startup Cheats for commands you want to run on startup. Check command descriptions before making +an action automatic. -- Packaged builds: directory of the injector executable. -- Source runs (`npm run start`): `process.cwd()`. +## Safety -Merge rules: +The Web UI permits command execution and game-state editing. Keep access local unless you deliberately need +remote access on a trusted network. Remote access has no login; configure the listening address and allowed +origins carefully, then restart. -- `injectorConfig` is deep-merged. -- `startupCheats` arrays are unioned. -- `cheatConfig` is deep-merged. -- `defaultConfig` is a deep clone of `config.js`, used for UI diffs and saves. -- **Type Validation**: `config.custom.js` is validated against `config.js` types. Mismatches (e.g., string where function expected) are logged and reverted to defaults. +Account values and cheat settings can affect save data. Read field warnings before editing. +Keep the dangerous `chng` command disabled unless you understand the consequences. -## Top-level structure +## Source entry points -`config.js` exports three keys: +- [Defaults](../config.js) and [configuration loading](../src/modules/config/configManager.js). +- [Config workspace](../src/ui/components/views/Config.js). +- [Account option labels and warnings](../src/ui/config/optionsAccountSchema.json). -```js -exports.startupCheats = []; - -exports.cheatConfig = { - unban: true, - maxval: { bones: 1e20 }, - w1: { stampcost: (t) => t / 4 }, -}; - -exports.injectorConfig = { - logLevel: "info", - injreg: "\\w+\\.ApplicationMain\\s*?=", - interceptPattern: "*N.js", - enableUI: true, - webPort: 8080, - onLinuxTimeout: 30000, - target: "web", - webUrl: "https://www.legendsofidleon.com/ytGl5oc/", - browserPath: "", - browserUserDataDir: "", -}; -``` - -`config.custom.js` can omit any export; only provided keys override defaults. - -The web UI is local-only by default. Remote access requires a non-loopback -`injectorConfig.webHost` and the exact browser origins in `webAllowedOrigins`. -Restart the injector after changing these settings. Remote access grants command -execution and game-state editing without a login, so use it only on a trusted network. - -## Startup cheats - -`startupCheats` is an array of command strings run after injection. - -Example (`config.custom.js`): - -```js -exports.startupCheats = ["wide mtx", "unlock quickref", "wide autoloot"]; -``` - -These map to the same commands shown in the Cheats tab and CLI. - -In the Web UI Startup tab, commands marked with `needsParam` display a separate `Value` field for clarity. The saved config format is unchanged: each row is still stored as a single command string (for example, `"drop Copper 100"`). - -## Cheat config values - -`cheatConfig` controls parameterized cheats and proxy overrides. Values can be: - -- Primitives (`true`, `false`, numbers, strings). -- Functions taking the original game value (`t`) and returning a modified value. -- Functions with extra arguments (`(t, args) => ...`) for cheats that pass parameters. - -Example overrides: - -```js -exports.cheatConfig = { - w1: { - stampcost: (t) => t / 4, - anvil: { productionspeed: (t) => t * 4 }, - }, - w5: { - gaming: { FertilizerUpgCosts: (t) => 0 }, - }, -}; -``` - -### Function editing in the UI - -The Config view recognizes simple function patterns and exposes sliders/dropdowns via `FunctionInput`: - -- Multiply: `(t) => t * 2` -- Divide: `(t) => t / 2` -- Fixed: `(t) => 0` -- Pass-through: `(t) => t` -- Min/Max: `(t) => Math.min(t, 1)` / `(t) => Math.max(t, 10)` -- Complex: any other form, shown as raw source. - -Parsing logic lives in `src/ui/utils/functionParser.js` and feeds `FunctionInput`. -Multiply/divide functions get slider presets (1, 2, 4, 5, 10, 20) with default range 1-20. - -## Injector config - -`injectorConfig` controls attachment and injection: - -- `logLevel`: `debug`, `info`, `warn`, `error`. -- `injreg`: regex that finds the injection point in `N.js`. -- `interceptPattern`: CDP script match (default `*N.js`). -- `enableUI`: toggle the web UI. -- `webPort`: UI port (default 8080). -- `onLinuxTimeout`: Linux attach timeout in ms. -- `target`: `steam` or `web`. -- `webUrl`: Idleon web URL for browser injection. -- `browserPath`: explicit Chromium executable. -- `browserUserDataDir`: custom profile directory (defaults to `idleon-web-profile` in the runtime base directory). -- `gameExePath`: optional Windows override for the Steam exe search. - -Example `config.custom.js` targeting Steam: - -```js -exports.injectorConfig = { - target: "steam", - logLevel: "debug", -}; -``` - -## Runtime updates vs saved config - -Two update modes: - -- Session-only updates: `POST /api/config/update` updates in-memory config and calls `updateCheatConfig` in the game context. -- Persistent updates: `POST /api/config/save` writes `config.custom.js` with only the diff from defaults. - -`apiRoutes.js` uses `prepareConfigForJson` to serialize functions, then `parseConfigFromJson` to restore them. Uses `filterByTemplate` and `getDeepDiff` to persist only valid keys. - -## Descriptions and schema - -Two files control UI labels and warnings: - -- `src/ui/config/configDescriptions.js`: path-to-description map for `cheatConfig` and `injectorConfig` fields. -- `src/ui/config/optionsAccountSchema.json`: labels for `OptionsListAccount` indices. - -Schema fields: - -- `name`: display label. -- `description`: help text. -- `type`: `number`, `string`, `boolean`, etc. -- `warning`: optional warning string. -- `AI`: marks entries that are auto-generated or inferred. - -## Adding new config fields - -1. Add the default to `config.js`. -2. Add a tooltip description in `src/ui/config/configDescriptions.js`. -3. If user-editable, ensure `ConfigNode` renders it correctly (number, boolean, or function). -4. Keys not in `config.js` are dropped when saving via the UI (template filtering). -5. Document risks in description or warning fields. - -## Safety notes - -- `cheatConfig.chng_enabled` exposes the dangerous `chng` command. Keep `false` unless you understand the risk. -- Some cheats use hard caps (like `maxval`) to prevent save corruption. Adjust carefully. +See [platform setup](platforms.md) for target selection and the [glossary](glossary.md) for configuration terms. diff --git a/docs/decisions.md b/docs/decisions.md new file mode 100644 index 00000000..381b4a2e --- /dev/null +++ b/docs/decisions.md @@ -0,0 +1,47 @@ +# Design decisions + +Record a decision, its reason, and its trade-off here. Include alternatives when they are known. +Do not infer historical intent from the implementation or maintain a second description of its behavior. + +## Keep documentation thin + +Decision: use docs for navigation, domain language, design rationale, and user workflows or safety guidance. +Keep implementation contracts beside their source when an explanation is necessary. + +Why: descriptions of algorithms, field lists, startup order, and copied code can drift independently of the code. +Names and structure should make the implementation understandable. + +Alternative: maintain a separate implementation reference or tutorial for each module. + +Trade-off: contributors must read source for exact behavior. A small [architecture map](architecture.md) +provides entry points without duplicating it. + +Update the map when a top-level area or runtime boundary changes. Define new terms once in the +[glossary](glossary.md). Update an existing decision when its trade-off changes. + +## Preserve original method side effects + +Decision: method hooks follow the base-first convention. + +Why: the original game method may update state as well as return a value. +Replacing its result must not silently skip those side effects. + +Alternative: return a cheat value before invoking the original method. + +Trade-off: the original work still runs even when its result will be overridden. + +Source: [proxy utilities](../src/cheats/utils/proxy.js). + +## Preserve editable row identity + +Decision: keep editable Account rows stable when their values change. +Choose simpler rebuilding only where losing transient input or feedback state is acceptable. + +Why: rebuilding an active row can lose focus, draft text, or write feedback. + +Alternative: rebuild the whole section whenever any row changes. + +Trade-off: stable rows require explicit value updates and collection reconciliation. + +Source: [Account components](../src/ui/components/views/account/components/), +[Account shared helpers](../src/ui/components/views/account/accountShared.js). diff --git a/docs/glossary.md b/docs/glossary.md new file mode 100644 index 00000000..6bbb36f2 --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,29 @@ +# Domain language + +These terms distinguish concepts used throughout the project. Follow the linked source for implementation details. + +| Term | Meaning | +| ------------------- | -------------------------------------------------------------------------------------------------- | +| Target | The Steam or web game client selected for attachment. | +| CDP | Chrome DevTools Protocol, the debugging connection used to access the game runtime. | +| Injection | Introducing the cheat runtime into the running game. | +| Game context | The game-side environment where commands and hooks access game objects. | +| Cheat command | A named action in the command registry. It may perform an action or toggle an ongoing effect. | +| Cheat state | Whether a stateful cheat is enabled, distinct from its configured values. | +| Cheat configuration | Values that customize cheat effects. | +| Startup cheats | Commands selected to run when the cheat runtime starts. | +| Proxy | A hook around a game method or property that can alter its result. | +| GGA | The game's attributes map, exposed as `gga`. | +| cList | The game's custom lists, including definition and lookup data. | +| Haxe wrapper | An object representation whose data may be stored under an `.h` property. | +| Account options | The game's indexed `OptionsListAccount` values. | +| Account page | An editor for an account feature or world system. The raw Account Options editor is one such page. | +| Monitor | A subscription to observe a game value over time. | +| Session update | A configuration change applied to the running session. | +| Saved configuration | User overrides written to disk for later runs. | + +Source entry points: [game globals](../src/cheats/core/globals.js), +[command registration](../src/cheats/core/registration.js), +[cheat state](../src/cheats/core/state.js), +[account schema](../src/ui/config/optionsAccountSchema.json), +[configuration loading](../src/modules/config/configManager.js). diff --git a/docs/platforms.md b/docs/platforms.md index 84e93af6..9b132207 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -1,110 +1,29 @@ -# Platform and Injection Modes +# Platform setup -Two targets: Steam and Web. Select via `injectorConfig.target` in `config.custom.js`. +Choose Steam or Web during first-run setup. Use the Config editor or your personal override file to change +the target later, then restart the injector. -## Common constants +| Platform | Target | +| -------- | ---------------------------- | +| Windows | Steam or Web | +| Linux | Steam through Proton, or Web | +| macOS | Web | -- CDP port fixed at `32123` (`getCdpPort()` in `configManager.js`). -- UI port defaults to `8080` unless `injectorConfig.webPort` overrides. -- Attach timeout defaults to 30s; Linux uses `injectorConfig.onLinuxTimeout`. +## Steam -## Steam target +Install Idleon and keep Steam running. If the Windows game location is not detected, set its executable path in your personal override file. On Linux, follow the terminal's manual launch instructions when automatic launch fails. -Set: +## Web -```js -exports.injectorConfig = { target: "steam" }; -``` - -### Windows flow - -1. `findIdleonExe()` checks `injectorConfig.gameExePath` and common Steam install paths. -2. `attach(exePath)` spawns `LegendsOfIdleon.exe --remote-debugging-port=32123`. -3. If direct launch fails or times out, falls back to Steam protocol: - -```text -steam://run/1476970//--remote-debugging-port=32123 -``` - -4. Polls `http://localhost:32123/json/version` until CDP is ready. - -### Linux flow - -1. `autoAttachLinux()` searches for `steam.sh` in common paths. -2. Spawns `steam -applaunch 1476970 --remote-debugging-port=32123`. -3. If auto-launch fails, waits for manual launch and polls CDP. -4. Timeout is controlled by `injectorConfig.onLinuxTimeout`. - -### macOS - -Steam target not supported on macOS. Use web target instead. -Entry point throws an error if `target` is not `web` on macOS. - -## Web target - -Set: - -```js -exports.injectorConfig = { - target: "web", - webUrl: "https://www.legendsofidleon.com/ytGl5oc/", -}; -``` - -`webUrl` is required for web mode; errors if missing. - -### Browser resolution - -`resolveBrowserPath()` picks a Chromium-based browser: - -1. Uses `injectorConfig.browserPath` if set. -2. Falls back to known locations for Chrome, Edge, Brave, or Opera. - -If no executable found, throws "Could not find a compatible Chromium-based browser". - -### Browser launch arguments - -Spawns the browser with: - -```text ---remote-debugging-port=32123 ---user-data-dir= ---no-first-run ---no-default-browser-check ---remote-allow-origins=* ---site-per-process ---disable-extensions ---new-window - -``` - -Linux adds `--disable-gpu` for stability. - -If `injectorConfig.browserUserDataDir` is empty, defaults to `idleon-web-profile` in the runtime base directory (executable directory in packaged builds, `process.cwd()` in source runs). - -### Target matching - -`waitForIdleonTarget()` selects a CDP page target that matches `webUrl` exactly or shares the same host. - -If the Idleon page never appears, throws `Timeout waiting for Idleon page`. - -Web attach flow: - -1. Launch the browser with CDP enabled. -2. Poll `/json/version` until the CDP WebSocket URL is available. -3. Find the Idleon page target and return its `webSocketDebuggerUrl`. - -## Injection tuning - -If a game update changes the bootstrap script, update these `injectorConfig` fields: - -- `interceptPattern` (default `*N.js`). -- `injreg` (default `\w+\.ApplicationMain\s*?=`). +Install a Chromium-based browser and configure the Idleon web URL. If browser detection fails, set the browser +executable path explicitly. A dedicated profile keeps this session separate from ordinary browsing. ## Troubleshooting -- `No inspectable targets`: Steam not running or game launched without CDP. -- `Timeout waiting for debugger WebSocket URL`: target did not open CDP on port 32123. -- `Timeout waiting for Idleon page`: wrong `webUrl`, slow load, or browser profile lock. -- `Configured browserPath does not exist`: fix path or clear to auto-detect. -- `webUrl is required when target is 'web'`: add a valid Idleon URL. +- If the game does not start, check the selected target and its installation. +- If browser detection fails, check the configured executable path or clear it to retry detection. +- If attachment times out, inspect the terminal output and confirm the game or browser launched successfully. +- If the game opens but commands are unavailable, inspect injection errors before changing game data. + +Platform attachment code lives in [game modules](../src/modules/game/). +See [configuration](config.md) for settings and access safety. diff --git a/docs/ui.md b/docs/ui.md deleted file mode 100644 index a5b557e6..00000000 --- a/docs/ui.md +++ /dev/null @@ -1,221 +0,0 @@ -# UI Development - -How the VanJS-based UI is structured, syncs to the backend, and how to extend it. - -## Directory map - -- `src/ui/entry/`: HTML entry point and CSS imports. -- `src/ui/components/`: UI building blocks and view containers. -- `src/ui/components/views/`: Cheats, Config, Account, Search, and DevTools workspaces. -- `src/ui/services/`: API and WebSocket clients. -- `src/ui/state/`: Reactive store and constants. -- `src/ui/styles/`: CSS partials (imported by `entry/style.css`). -- `src/ui/config/`: Config descriptions and account schema. -- `src/ui/assets/`: Icon set and UI assets. -- `src/ui/vendor/`: `van` and `van-x` bundles. - -## Entry point - -`src/ui/entry/index.html` mounts the app: - -```js -import van from "/vendor/van-1.6.0.js"; -import { App } from "/components/App.js"; - -van.add(document.body, App()); -``` - -`src/ui/entry/style.css` is the CSS entry. Add new partials in `src/ui/styles/` and import there. - -`src/ui/components/App.js` initializes heartbeat monitoring and keyboard shortcuts, mounts workspace content, and keeps global Toast, Activity, and update surfaces available. - -## State management - -`src/ui/state/store.js` uses VanX reactivity (`vanX.reactive`) and exposes a simple service-style API. - -State buckets: - -- `store.app`: UI state (active workspace, loading state, heartbeat, toast, drawers, and update state). -- `store.data`: data from the backend (cheats, config, account options, cheat states, monitor values). - -Notable `store.app` UI flags include `configForcedPath` (focused config path from cheat gear icon) and `configDrawerOpen` (side drawer state while on Cheats). - -Persisted UI settings: - -- Sidebar collapsed state is stored in `localStorage`. -- Cheat favorites, recent commands, Search key favorites, selected Search keys, and saved Search results are stored in `localStorage`. -- Config, Activity, Search key, and Search inspector drawers are session-only. - -Core flows: - -- `store.initHeartbeat()` opens the WebSocket and checks `/api/heartbeat`. -- `store.loadCheats()` requests `/api/cheats` and lazily fetches `/api/config` if not loaded. -- `store.loadConfig()` fetches `/api/config` for the Config tab. -- `store.loadAccountOptions()` fetches `/api/options-account` and `/config/optionsAccountSchema.json`. - -Heartbeat details: - -- WebSocket connection status is the primary heartbeat signal. -- 10s interval falls back to `/api/heartbeat` when WS is disconnected. -- Electron mode uses the same WebSocket + heartbeat flow as browser UI. - -## Services layer - -API requests centralized in `src/ui/services/api.js`: - -- `fetchCheatsData()` -> `GET /api/cheats` -- `executeCheatAction()` -> `POST /api/toggle` -- `fetchConfig()` -> `GET /api/config` -- `saveConfigFile()` -> `POST /api/config/save` -- `updateSessionConfig()` -> `POST /api/config/update` -- `fetchOptionsAccount()` -> `GET /api/options-account` -- `updateOptionAccountIndex()` -> `POST /api/options-account/index` -- `fetchDevToolsUrl()` -> `GET /api/devtools-url` -- `fetchCheatStates()` -> `GET /api/cheat-states` -- `openExternalUrl()` -> `POST /api/open-url` - -WebSocket updates live in `src/ui/services/ws.js` and push cheat-state changes into `store.data.activeCheatStates`. -Monitor subscriptions use the same socket, pushing `monitor-state` updates into `store.data.monitorValues`. - -WebSocket client auto-reconnects every 10s in all runtimes. - -## Core views - -### Cheats view - -`src/ui/components/views/AtlasCheats.js` is the main cheat explorer. - -Features: - -- Scope navigation covers all, active, favorite, recent, and category-filtered commands. -- The command table filters by command, description, or category and paginates at 50 rows. -- Row selection is available by pointer, Enter/Space, and table Arrow/J/K navigation. -- Execution remains separate from selection; stateful commands use switches and one-shot commands use Run. -- Parameterized commands collect their value in the inspector and block execution until it is supplied. -- Favorites and recents preserve complete parameterized command strings in `localStorage`. -- The inspector exposes command details and linked config without hiding the command table. -- Linked config can also open the shared Config drawer beside the Cheats workspace. - -Useful helpers: - -- `API.executeCheatAction(action)` triggers `/api/toggle`; Atlas records the result through `store.notify()`. -- `store.navigateToCheatConfig(cheatValue)` focuses Config for that cheat path (opens the side drawer when on Cheats, otherwise switches to full Config tab). -- `store.data.activeCheatStates` receives WebSocket state updates that Atlas flattens for its switches and Active scope. - -### Config view - -`src/ui/components/views/Config.js` edits `startupCheats`, `cheatConfig`, and `injectorConfig` through the shared draft in `views/config/configDraft.js`. - -Key behaviors: - -- Uses one reactive draft and separate RAM/disk baselines to avoid direct edits on live config. -- Sub-tabs: Cheat Config, Startup Cheats, Injector Config. -- Startup Cheats auto-show a separate `Value` input when a selected command has `needsParam`. -- Supports category filtering and search on cheat config keys. -- Uses `ConfigNode` to recursively render object trees. -- Uses forced-path mode when coming from Cheats gear icon, with "SHOWING" banner. -- Can run as a right-side drawer while Cheats stays open; close from drawer header or toggle button in Cheats. -- Saves explicitly apply the cheat config to RAM (`/api/config/update`) or persist the full draft to disk (`/api/config/save`). -- `Ctrl+S` saves the shared config draft from the Cheats or Config workspace. -- Injector config shows "restart required" warning banner. - -Function values (like `(t) => t * 2`) are edited through `FunctionInput`: - -- Parsing logic lives in `src/ui/utils/functionParser.js`. -- Recognizes multiply, divide, fixed, passthrough, min, max, and complex forms. -- Sliders provided for multiply/divide values; raw editor for complex. - -Save behavior: - -- Session updates call `/api/config/update`. -- Persistent saves call `/api/config/save` and write `config.custom.js`. - -### Account view - -`src/ui/components/views/Account.js` exposes `OptionsListAccount` editing. - -- Users must confirm a warning before data loads. -- Uses `src/ui/config/optionsAccountSchema.json` for labels, types, warnings, and AI flags. -- `Hide AI` filters out `schema.AI` entries for easier manual editing. -- Rows render number inputs, boolean toggles, or raw JSON based on value type. -- Each "SET" writes to memory via `/api/options-account/index` with optimistic updates. - -### Search view - -`src/ui/components/views/Search.js` provides a tool for finding values in the game's internal data (`gga`). - -Features: - -- **Key Whitelist**: Select top-level game attribute categories to search (e.g., `PlayerDATABASE`, `SkillLevels`). -- **Favorites**: Curated defaults and user-edited key favorites persist independently; an intentionally empty list stays empty. -- **Value Matching**: - - Supports strings (case-insensitive contains). - - Supports numbers (exact or rounding tolerance for floats). - - Supports ranges (e.g., `100-200`). - - Supports `true`, `false`, `null`, `undefined`. -- **Result inspector**: Selecting a result exposes its path, value editor, and monitor state. -- **Path Copying**: Copy actions produce the full Haxe access path and immediate toast feedback. -- **Saved monitors**: Saved results can subscribe or pause their live monitor in the Search inspector. -- **Performance**: "Load more" pattern handles large result sets without freezing. - -### Activity drawer - -`src/ui/components/ActivityDrawer.js` keeps notifications and active value monitors available from every workspace. - -Features: - -- Activity lists the same success and error events shown immediately by Toast. -- Monitors show current values and recent updates received over WebSocket. -- Monitor subscriptions are owned by saved Search results and can be paused or removed there. - -### DevTools view - -The in-game inspector supports console evaluation and inspection of the main game. -Pauses are skipped while it is connected. Use **Open externally** for breakpoints, -child frames, and workers. Opening externally disconnects the embedded inspector; -**Reconnect here** returns to in-game inspection. - -## Components and patterns - -Common components in `src/ui/components/`: - -- `AtlasHeader` + `Sidebar`: global status, workspace navigation, and workspace-specific context. -- `WorkspaceContext`: contextual navigation supplied by the active workspace. -- `ActivityDrawer`: notification history and live monitor output. -- `SearchBar`: shared filter input used by Cheats and Account. -- `ConfigNode`: recursive config renderer with tooltips. -- `Toast` + `Tooltip`: global immediate feedback helpers. - -Typical component pattern: - -```js -const { div, button } = van.tags; - -export const MyWidget = () => { - const count = van.state(0); - - return div(button({ onclick: () => count.val++ }, "Increment"), () => `Count: ${count.val}`); -}; -``` - -## Adding a new view - -1. Create a view component in `src/ui/components/views/`. -2. Add its metadata to `src/ui/state/constants.js` in `VIEWS` and `VIEW_ORDER`. -3. Register it in `src/ui/components/App.js` under `viewFactories`. -4. Add any API calls to `src/ui/services/api.js` and expose them via `store.js`. -5. Add CSS in `src/ui/styles/_yourfile.css` and import it in `entry/style.css`. - -Example registration in `App.js`: - -```js -const viewFactories = { - [VIEWS.CHEATS.id]: Cheats, - [VIEWS.CONFIG.id]: Config, - [VIEWS.MYVIEW.id]: MyView, -}; -``` - -## Embedded vs desktop behavior - -`IS_ELECTRON` in `src/ui/state/constants.js` handles Electron-specific UI behavior (like external link handling). WebSocket updates available in Electron and browser modes.