Old Launcher

A launcher and addon manager for World of Warcraft 3.3.5a clients, built with iced.

Download for Linux Download for Windows

Linux on ARM64 · Arch Linux (AUR)

Each is a single file built from the latest main; nothing to install.

What it does

Every addon in one list, with the search box and the filters above it

The installed addons, among them a manual install and a broken folder to repair

A client's details, and how it starts on Linux: Proton through umu-launcher

Importing addons and saved settings exported on another computer

Shortcuts: Ctrl+F search, F5 rescan, Esc close dialogs, Ctrl+Q quit.

The catalog

index/wotlk.json is the launcher's own catalog, generated by the indexer tool and committed here:

GITHUB_TOKEN=$(gh auth token) cargo run --release -p indexer   # writes index/wotlk.json

The token only raises GitHub's API limits: a run makes about a hundred calls, more than GitHub allows anonymously.

The indexer lists every upstream source, downloads every archive, and records the addon folders each one installs (with their .toc title, version and interface), when it was last updated, and the addon's own page at the source when it has one. Listings that install the same folders are merged into one entry whose downloads are ordered most recently updated first. Entries whose download is dead or holds no addon are left out and reported, and links to dead or hijacked sites are dropped.

The sources, in order of preference (the first one listing an addon describes it):

An addon's update date is the newest file date inside its archive, which for a repository snapshot is its last commit. locus313's zips are rebuilt daily, so for those it is the last commit touching the addon's folder.

The app embeds the copy of index/wotlk.json it was built with, so the catalog shows instantly and offline. It then fetches the published catalog, if there is one:

Sources live in indexer/src/source/, one module each, as types implementing the Source trait (info describes it for the catalog, list returns its listings). Adding one means writing that module and adding it to the ordered list in indexer/src/main.rs; a GitHub maintainer is one more name in github::MAINTAINERS.

How it works

There is no database. Everything is read from the client folder itself:

Archives are extracted to a hidden staging folder inside Interface/AddOns. The walk goes breadth-first until it finds a folder holding a .toc. That folder moves into place under the name its .toc declares, and the walk does not descend into it, since deeper .toc files belong to embedded libraries. So only Name/Name.toc layouts ever reach Interface/AddOns, whatever the archive looks like.

Play starts Wow.exe from the client folder on Windows. Elsewhere it runs the client's command with sh, the way Steam runs launch options: the environment variables are set and exported first, and %command% becomes the quoted path to Wow.exe (wine %command% when there is no command). ~ and $HOME expand as in a terminal.

The only file outside the clients lists the client folders and how each one starts: ~/.config/old_launcher/config.toml (the platform config directory elsewhere), or $OLD_LAUNCHER_CONFIG when set. If it can't be read, the launcher does nothing else until you choose: it shows why, where the file is, and which client folders overwriting it would forget, and offers to edit it (then read it again), quit, or overwrite it with new settings.

Layout

Nothing outside tests may panic on purpose: the workspace denies unwrap, expect, panic!, unreachable!, todo! and unimplemented! through clippy (tests are exempt, see clippy.toml). Whatever goes wrong while the app runs ends up in the error banner at the top, which lists every problem until dismissed.

Tests

cargo clippy --workspace --all-targets   # also enforces the no-panic rule
cargo test --workspace
# Installs real catalog entries into a client, then uninstalls them
# (entries whose folders the client already has are skipped):
OLD_LAUNCHER_TEST_CLIENT="/path/to/World of Warcraft 3.3.5a" \
    cargo test --lib addons -- --ignored