feat: update application icon to band-aid and add GitHub Actions build workflow
65
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
name: Build Windows Executable
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build BetterCurseForge.exe
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install pyinstaller pillow pywin32
|
||||||
|
|
||||||
|
- name: Build executable with PyInstaller
|
||||||
|
run: |
|
||||||
|
python -m PyInstaller BetterCurseForge.spec --noconfirm
|
||||||
|
|
||||||
|
- name: Verify build output
|
||||||
|
run: |
|
||||||
|
if (-not (Test-Path "dist/BetterCurseForge.exe")) {
|
||||||
|
Write-Error "dist/BetterCurseForge.exe was not created!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Get-Item "dist/BetterCurseForge.exe" | Select-Object Name, Length, LastWriteTime
|
||||||
|
|
||||||
|
- name: Upload executable artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: BetterCurseForge-Windows
|
||||||
|
path: dist/BetterCurseForge.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Attach executable to GitHub Release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: dist/BetterCurseForge.exe
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
211
README.md
|
|
@ -1,118 +1,73 @@
|
||||||
# BetterCurseForge
|
# BetterCurseForge
|
||||||
|
|
||||||
<div align="center">
|
<img src="assets/icon_512.png" alt="BetterCurseForge logo" width="96" height="96" />
|
||||||
|
|
||||||
<img src="assets/icon_512.png" alt="BetterCurseForge Logo" width="128" height="128" />
|
A patch for the CurseForge launcher on Windows. It blocks ads, adds a theme and plugin system, unlocks the subscriber-only skins, and re-applies itself after CurseForge updates.
|
||||||
|
|
||||||
### The modern, lightweight client modification for CurseForge Launcher.
|
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/tomasalias/BetterCurseForge)
|
|
||||||
[](#features)
|
|
||||||
[](#-how-auto-repatch-works)
|
|
||||||
|
|
||||||
**Zero Ads • Custom Themes • Unlocked Skins • Extensible Plugins • Autonomous Auto-Repatch**
|
## What it does
|
||||||
|
|
||||||
</div>
|
- **Ad blocking.** Requests to the Overwolf, Adrta and DoubleClick ad servers are blocked at the Electron session level, and some CSS collapses whatever empty ad frames and banners are left. Ad elements are hidden, not removed from the DOM, because removing them breaks React and causes `insertBefore` errors.
|
||||||
|
- **Themes.** Themes show up in the launcher's own Settings > Themes page. CSS is injected live, so you don't need to restart to see changes. One theme is included, Purple Night.
|
||||||
|
- **Subscriber skins.** The official skins that normally need a CurseForge subscription (PixelForge, LifeForge, FantasyForge, Halloween and the rest) are unlocked.
|
||||||
|
- **Plugins.** Plugins are small JS modules with `onEnable` and `onDisable` hooks, scoped styling, and IPC to the main process. There's a sample one called Compact Mode.
|
||||||
|
- **Updates.** When CurseForge updates itself, the patch gets re-applied automatically. More on that [below](#how-the-repatch-works).
|
||||||
|
|
||||||
---
|
## Install
|
||||||
|
|
||||||
## ✨ Features
|
1. Download `BetterCurseForge.exe` from [Releases](https://github.com/tomasalias/BetterCurseForge/releases).
|
||||||
|
2. Run it and click **Patch CurseForge**.
|
||||||
|
3. Start CurseForge as usual.
|
||||||
|
|
||||||
- 🚫 **Complete Zero-Ad Experience**: Multi-layer ad elimination without breaking React Fiber:
|
It also works from PowerShell or Command Prompt:
|
||||||
- Electron `session.webRequest` network request blocker stops Overwolf/Adrta/DoubleClick ad calls before they start.
|
|
||||||
- Fail-safe CSS rules completely neutralize, hide, and collapse all ad frames, banners, and swap buttons to 0px.
|
|
||||||
- Safe non-destructive DOM handling preserves React Fiber reconciliation, preventing `insertBefore` exceptions.
|
|
||||||
- 🎨 **First-Class Theme Engine**:
|
|
||||||
- Seamlessly embedded into CurseForge's native **Settings > Themes** screen.
|
|
||||||
- Live CSS injection with instant hot-reloading (no launcher restarts needed).
|
|
||||||
- Bundled with **Purple Night**—a custom dark theme featuring violet accents, sleek card elevations, and custom badge styling.
|
|
||||||
- 🔓 **Unlocked Native Premium Themes & Skins**:
|
|
||||||
- Unlocks all official CurseForge subscriber skins (PixelForge, LifeForge, FantasyForge, Halloween, etc.) without requiring a CurseForge subscription.
|
|
||||||
- 🧩 **Extensible Plugin System**:
|
|
||||||
- Modular plugin architecture with lifecycle hooks (`onEnable`, `onDisable`), safe IPC communication, and scoped styling.
|
|
||||||
- Includes sample plugins like **Compact Mode**.
|
|
||||||
- 🛡️ **Autonomous Auto-Repatch Engine**:
|
|
||||||
- **Survives in-app CurseForge updates automatically!**
|
|
||||||
- Offline, zero-delay ASAR extraction and repacking (< 3 seconds) using CurseForge's internal Node v24 runtime—no internet connection or external npm/npx required.
|
|
||||||
- Synchronizes Overwolf's binary `OWEASARSIG` integrity hashes directly inside `CurseForge.exe`.
|
|
||||||
- Background watcher daemon ensures your modifications are immediately re-applied when CurseForge updates.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### Easy Installation (GUI)
|
|
||||||
|
|
||||||
1. Download the latest `BetterCurseForge.exe` from [Releases](https://github.com/tomasalias/BetterCurseForge/releases).
|
|
||||||
2. Run `BetterCurseForge.exe`.
|
|
||||||
3. Click **Patch CurseForge**.
|
|
||||||
4. Launch CurseForge Launcher—your modded client is ready to use!
|
|
||||||
|
|
||||||
### Command-Line Interface (CLI)
|
|
||||||
|
|
||||||
`BetterCurseForge.exe` can also be run directly from PowerShell or Command Prompt:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check current patch and watcher status
|
# show patch and watcher status
|
||||||
.\BetterCurseForge.exe status
|
.\BetterCurseForge.exe status
|
||||||
|
|
||||||
# Apply BetterCurseForge patch
|
# apply the patch
|
||||||
.\BetterCurseForge.exe patch
|
.\BetterCurseForge.exe patch
|
||||||
|
|
||||||
# Restore original vanilla CurseForge
|
# go back to stock CurseForge
|
||||||
.\BetterCurseForge.exe unpatch
|
.\BetterCurseForge.exe unpatch
|
||||||
|
|
||||||
# Run background watcher daemon directly
|
# run the background watcher in the foreground
|
||||||
.\BetterCurseForge.exe watch
|
.\BetterCurseForge.exe watch
|
||||||
|
|
||||||
# Trigger the smooth splash updater GUI manually
|
# open the splash updater by hand
|
||||||
.\BetterCurseForge.exe --repatch-splash
|
.\BetterCurseForge.exe --repatch-splash
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## How the repatch works
|
||||||
|
|
||||||
## 🔄 How Auto-Repatch Works
|
When CurseForge downloads an update, the Overwolf installer overwrites `resources/app.asar` with a stock copy and relaunches `CurseForge.exe`. That undoes the patch, and the unpatched launcher would normally appear on screen. BetterCurseForge deals with it like this:
|
||||||
|
|
||||||
Whenever CurseForge downloads an official update, the Overwolf installer replaces `resources/app.asar` with an unpatched vanilla archive and automatically restarts `CurseForge.exe`. BetterCurseForge guarantees a **smooth, zero-delay transition** where you never lose your patch and the unpatched app is never shown on screen:
|
A small watcher process sits in the background and watches CurseForge's install directory. When it sees an update or an unpatched `app.asar`, it polls for a new `CurseForge.exe` every 150 ms and kills the one the installer starts, so the vanilla launcher never gets drawn. Then it opens a splash window with a progress bar and runs the repatch.
|
||||||
|
|
||||||
```mermaid
|
The repatch unpacks and repacks `app.asar` using the Node runtime that ships inside CurseForge (`ELECTRON_RUN_AS_NODE=1`), so it needs no internet connection and no separate Node install. It takes a few seconds. It also updates the `OWEASARSIG` integrity signature stored in `CurseForge.exe` to match the new archive. When that's done, CurseForge relaunches.
|
||||||
flowchart TD
|
|
||||||
A[CurseForge Launcher Update] -->|Overwolf installer replaces files| B[Installer Attempts to Launch Vanilla App]
|
Two named Windows mutexes, `BetterCurseForgePatchMutex` and `BetterCurseForgeSplashMutex`, stop two patch runs or two splash windows from happening at the same time.
|
||||||
B -->|Active Interceptor <50ms| C[Vanilla Launcher Suppressed Silently]
|
|
||||||
C -->|Auto-Repatch Triggered| D[Dedicated Splash Updater GUI Appears]
|
## Themes and plugins
|
||||||
D -->|Real-time Status & Progress| E[Offline Extraction & Patching <3s]
|
|
||||||
E -->|Synchronize OWEASARSIG| F[CurseForge.exe Integrity Signature Updated]
|
Everything user-facing lives in `%APPDATA%\CurseForge\BetterCurseForge\`:
|
||||||
F -->|Autonomous Relaunch| G[Modded BetterCurseForge Launches Ready!]
|
|
||||||
|
```
|
||||||
|
themes/
|
||||||
|
purple-night.css
|
||||||
|
purple-night.json
|
||||||
|
plugins/
|
||||||
|
compact-mode/
|
||||||
|
manifest.json
|
||||||
|
index.js
|
||||||
```
|
```
|
||||||
|
|
||||||
1. **Active Vanilla Interceptor**: When an update or unpatched state is detected, BetterCurseForge polls in tight 150ms slices using lightweight Win32 kernel process enumeration. If the installer starts `CurseForge.exe`, it is terminated within 50ms before Chromium or Electron can render a single frame—guaranteeing the unpatched client never flashes on screen.
|
The GUI has **Open Themes Folder** and **Open Plugins Folder** buttons if you don't want to dig for it.
|
||||||
2. **Dedicated Splash Updater GUI**: Rather than patching silently without visual feedback, BetterCurseForge displays a sleek, centered dark-themed card (`#121214`) featuring the official application icon, an animated purple progress bar, and real-time status updates (*"Waiting for installer..."*, *"Extracting application archive..."*, *"Unlocking native premium themes..."*, *"Injecting zero-ad rules..."*, *"Updating security signatures..."*).
|
|
||||||
3. **Global Mutex Synchronization**: Uses Windows kernel named mutexes (`BetterCurseForgePatchMutex`, `BetterCurseForgeSplashMutex`) to guarantee atomic, single-process patching with zero race conditions or duplicate windows.
|
|
||||||
4. **Resilient Win32 Watcher Daemon**: A silent background process monitors CurseForge's installation directory with 0% CPU consumption during idle. It triggers the splash updater whenever an update is applied and handles all persistence automatically.
|
|
||||||
5. **100% Offline Zero-Delay ASAR Engine**: Unpacking and repacking use bundled tools combined with CurseForge's built-in Node runtime (`ELECTRON_RUN_AS_NODE=1`), completing the entire repatch cycle in 2–3 seconds without requiring an internet connection.
|
|
||||||
|
|
||||||
---
|
### Writing a theme
|
||||||
|
|
||||||
## 🎨 Themes & Plugins
|
A theme is a CSS file plus a JSON manifest with the same name. For example, `themes/my-theme.json`:
|
||||||
|
|
||||||
User configurations, themes, and plugins are stored in your AppData directory:
|
|
||||||
```
|
|
||||||
%APPDATA%\CurseForge\BetterCurseForge\
|
|
||||||
├── themes/
|
|
||||||
│ ├── purple-night.css
|
|
||||||
│ └── purple-night.json
|
|
||||||
└── plugins/
|
|
||||||
└── compact-mode/
|
|
||||||
├── manifest.json
|
|
||||||
└── index.js
|
|
||||||
```
|
|
||||||
|
|
||||||
You can open these folders anytime via the **Open Themes Folder** and **Open Plugins Folder** buttons in the BetterCurseForge GUI.
|
|
||||||
|
|
||||||
### Creating a Custom Theme
|
|
||||||
|
|
||||||
Create a CSS file and manifest in `%APPDATA%\CurseForge\BetterCurseForge\themes\my-theme.css`:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -124,6 +79,8 @@ Create a CSS file and manifest in `%APPDATA%\CurseForge\BetterCurseForge\themes\
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
and `themes/my-theme.css`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
:root {
|
:root {
|
||||||
--surface-brand-brand: #10b981 !important;
|
--surface-brand-brand: #10b981 !important;
|
||||||
|
|
@ -131,32 +88,83 @@ Create a CSS file and manifest in `%APPDATA%\CurseForge\BetterCurseForge\themes\
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### Writing a plugin
|
||||||
|
|
||||||
## 🛠️ Building from Source
|
A plugin lives in its own subfolder in `plugins/<plugin-name>/` with a `manifest.json`, an `index.js`, and an optional companion `styles.css`.
|
||||||
|
|
||||||
### Prerequisites
|
`plugins/my-plugin/manifest.json`:
|
||||||
|
|
||||||
- Windows 10 / 11
|
```json
|
||||||
- Python 3.10+
|
{
|
||||||
- Node.js 20+ (for docs)
|
"name": "My Plugin",
|
||||||
|
"description": "Custom tweak for CurseForge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Your Name",
|
||||||
|
"main": "index.js",
|
||||||
|
"styles": "styles.css"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Build Instructions
|
`plugins/my-plugin/index.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Plugins run inside the Electron renderer with access to BetterCurseForge
|
||||||
|
const { injectCSS, patcher, settings, on, off } = BetterCurseForge;
|
||||||
|
|
||||||
|
console.log("My plugin loaded");
|
||||||
|
|
||||||
|
// Cleanup when toggled off
|
||||||
|
plugin.onDisable = () => {
|
||||||
|
console.log("My plugin disabled");
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### What plugins can do
|
||||||
|
|
||||||
|
- **UI & DOM tweaks.** Inject custom buttons, badges, links, or panels into CurseForge using standard DOM APIs. If a `styles.css` is present in the plugin folder, it is automatically injected when enabled and removed when disabled.
|
||||||
|
- **Method patching.** Intercept, modify, or override internal JavaScript functions with `BetterCurseForge.patcher.before()`, `after()`, or `instead()`.
|
||||||
|
- **Persistent settings.** Store and read user preferences across restarts using `BetterCurseForge.settings.get(key)` and `set(key, value)`.
|
||||||
|
- **Event listening.** Hook into application lifecycle events using `BetterCurseForge.on(event, callback)`.
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
|
||||||
|
You need Windows 10 or 11 and Python 3.10+. Node.js 20+ is only needed if you want to run the docs site.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Clone the repository
|
|
||||||
git clone https://github.com/tomasalias/BetterCurseForge.git
|
git clone https://github.com/tomasalias/BetterCurseForge.git
|
||||||
cd BetterCurseForge
|
cd BetterCurseForge
|
||||||
|
|
||||||
# Build standalone executable with PyInstaller
|
|
||||||
python -m pip install pyinstaller
|
python -m pip install pyinstaller
|
||||||
python -m PyInstaller BetterCurseForge.spec --noconfirm
|
python -m PyInstaller BetterCurseForge.spec --noconfirm
|
||||||
|
|
||||||
# Output executable will be in:
|
|
||||||
# dist/BetterCurseForge.exe
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running VitePress Documentation
|
The executable ends up in `dist/BetterCurseForge.exe`.
|
||||||
|
|
||||||
|
### Syncing changes from GitHub
|
||||||
|
|
||||||
|
To pull down updates made on GitHub:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git pull origin master
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have local changes that conflict:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git stash
|
||||||
|
git pull origin master
|
||||||
|
git stash pop
|
||||||
|
```
|
||||||
|
|
||||||
|
To push your local changes to GitHub:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git add .
|
||||||
|
git commit -m "Description of changes"
|
||||||
|
git push origin master
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the VitePress docs locally:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cd docs
|
cd docs
|
||||||
|
|
@ -164,9 +172,8 @@ npm install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## License
|
||||||
|
|
||||||
## 📄 License
|
[MIT](LICENSE).
|
||||||
|
|
||||||
This project is licensed under the [MIT License](LICENSE).
|
CurseForge and Overwolf are trademarks of their respective owners. This is an independent open-source project and isn't affiliated with or endorsed by either of them.
|
||||||
CurseForge and Overwolf are trademarks of their respective owners. BetterCurseForge is an independent open-source project and is not affiliated with or endorsed by Overwolf or CurseForge.
|
|
||||||
|
|
|
||||||
BIN
assets/icon.ico
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 108 KiB |
BIN
assets/icon.png
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 18 KiB |