This checkout had no git history at all, so this one commit carries the whole
of the Everwood work as a single import on top of chairmanpow/benilla-octo's
`main` (706cd71). The two trees have genuinely diverged — each has files the
other does not — so this is a fork point, not a catch-up.
Today's work, which is the reason a baseline exists at all:
- Booth previews no longer cast world shadows (`NO_BOOTH_SHADOW`). Portrait
characters were entering the sun's shadow cascades, so every skinned-rig
shadow specialization compiled live mid-frame; the hitch detector logged
"main thread blocked 1316 ms" beside a `compiled LIVE` warning. This was
the Fashion/Tmog stutter.
- Per-pane dress-up: each `DressUpModel` pane assembles and stages its own
tried-on look instead of every pane mirroring the live player.
- Item template cache is append-only JSONL written on the IO pool, replacing
an 11.6 MB whole-map JSON rewrite on the main thread every 10 s.
- `WOW_SYSTEM_TIMING` rebuilt per-thread: it took 2 mutexes per system enter
and 4 per exit, costing 15 ms a frame (590 fps -> 60) and corrupting the
measurements it was taken to produce.
- The visibility-churn sweep moved to its own `WOW_FLASH_CHURN` switch, so a
frame-cost reading no longer pays 2.7 ms a frame for it.
Local state (`benilla-config/`, `logs/`, `target/`) is excluded by the
existing .gitignore; the account credential it holds is not in this commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
192 lines
8.8 KiB
Bash
Executable File
192 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Import addon settings (SavedVariables) from a real WoW install's WTF folder into benilla.
|
|
#
|
|
# scripts/import-wtf.sh <WTF-dir> <ACCOUNT> [<Realm> <Character>] # dry run, prints a plan
|
|
# scripts/import-wtf.sh <WTF-dir> <ACCOUNT> <Realm> <Character> --apply
|
|
#
|
|
# scripts/import-wtf.sh ~/Games/Octo-wow/WTF GROMGOSH "C'Thun" Gromgosh --apply
|
|
#
|
|
# Why a script and not a copy: benilla does not use the WTF layout. It keeps saved variables in
|
|
# `benilla-config/saved/`, and the two differ in both shape and naming:
|
|
#
|
|
# WTF/Account/<ACC>/SavedVariables/<Addon>.lua → saved/<Addon>.lua
|
|
# WTF/Account/<ACC>/<Realm>/<Char>/SavedVariables/<A>.lua → saved/<Realm>-<Char>/<A>.lua
|
|
#
|
|
# and the per-character directory name is sanitised the way the client does it
|
|
# (`local_state.rs`'s `file_token`: every non-alphanumeric character becomes `_`), so the realm
|
|
# `C'Thun` is the directory `C_Thun`. Getting that wrong leaves the files in place but unread,
|
|
# which looks exactly like "the import did nothing".
|
|
#
|
|
# Key bindings come across too, and they are NOT a copy — the two formats are transposed:
|
|
#
|
|
# bindings-cache.wtf bind <KEY> <COMMAND> one line per key
|
|
# bindings/account.txt bind <COMMAND> <key...> one line per command
|
|
#
|
|
# So `bind W MOVEFORWARD` copied verbatim is read by benilla as "the command W, bound to the key
|
|
# MOVEFORWARD" — it parses without complaint and binds nothing usable. This transposes properly:
|
|
# it groups every key by the command it runs and emits one line per command, key order preserved.
|
|
# benilla stores a diff against its own defaults and an absent command keeps those defaults, so a
|
|
# full snapshot is safe to write; the one asymmetry is that a command you *deliberately unbound*
|
|
# in WoW is simply absent from the cache, and will come back at benilla's default rather than
|
|
# staying unbound.
|
|
#
|
|
# Flags:
|
|
# --apply actually write (default is a dry run that changes nothing)
|
|
# --all import settings for every addon in the WTF folder, not just the ones installed
|
|
# under benilla-config/AddOns. Default is installed-only: benilla ignores saved
|
|
# variables for an addon it does not load, so the rest are clutter.
|
|
set -euo pipefail
|
|
|
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
dest="$root/benilla-config/saved"
|
|
|
|
wtf="${1:-}"; account="${2:-}"; realm="${3:-}"; character="${4:-}"
|
|
apply=0; all=0
|
|
for a in "$@"; do
|
|
[ "$a" = "--apply" ] && apply=1
|
|
[ "$a" = "--all" ] && all=1
|
|
done
|
|
|
|
if [ -z "$wtf" ] || [ -z "$account" ]; then
|
|
sed -n '2,30p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
exit 2
|
|
fi
|
|
acct_dir="$wtf/Account/$account"
|
|
[ -d "$acct_dir" ] || { echo "import: no account dir at $acct_dir" >&2; exit 1; }
|
|
|
|
# `file_token`: every non-alphanumeric character becomes `_`, matching local_state.rs exactly.
|
|
token() { printf '%s' "$1" | sed 's/[^[:alnum:]]/_/g'; }
|
|
|
|
installed() {
|
|
# An addon counts as installed if benilla-config/AddOns has a directory of that name. The
|
|
# saved-variable file is named after the addon, which is the same key.
|
|
[ "$all" = "1" ] && return 0
|
|
[ -d "$root/benilla-config/AddOns/$1" ]
|
|
}
|
|
|
|
copied=0; skipped=0; backed=0
|
|
tmp_known="$(mktemp)"
|
|
trap 'rm -f "$tmp_known"' EXIT
|
|
do_one() {
|
|
local src="$1" out="$2" addon
|
|
addon="$(basename "$src" .lua)"
|
|
case "$src" in *.bak) return 0;; esac
|
|
if ! installed "$addon"; then
|
|
skipped=$((skipped+1)); return 0
|
|
fi
|
|
if [ "$apply" = "1" ]; then
|
|
mkdir -p "$(dirname "$out")"
|
|
# Back up rather than clobber: benilla may already hold settings from play sessions, and
|
|
# this import is the kind of thing a person runs twice by accident.
|
|
if [ -f "$out" ]; then
|
|
cp -p "$out" "$out.pre-import.bak"; backed=$((backed+1))
|
|
fi
|
|
cp -p "$src" "$out"
|
|
fi
|
|
copied=$((copied+1))
|
|
printf ' %-34s → %s\n' "$addon" "${out#$root/}"
|
|
}
|
|
|
|
echo "import: account-wide (## SavedVariables)"
|
|
if [ -d "$acct_dir/SavedVariables" ]; then
|
|
for f in "$acct_dir/SavedVariables"/*.lua; do
|
|
[ -e "$f" ] || continue
|
|
do_one "$f" "$dest/$(basename "$f")"
|
|
done
|
|
fi
|
|
|
|
if [ -n "$realm" ] && [ -n "$character" ]; then
|
|
key="$(token "$realm")-$(token "$character")"
|
|
echo "import: per-character (## SavedVariablesPerCharacter) → $key"
|
|
cdir="$acct_dir/$realm/$character/SavedVariables"
|
|
if [ -d "$cdir" ]; then
|
|
for f in "$cdir"/*.lua; do
|
|
[ -e "$f" ] || continue
|
|
do_one "$f" "$dest/$key/$(basename "$f")"
|
|
done
|
|
else
|
|
echo " (no such character dir: $cdir)" >&2
|
|
fi
|
|
fi
|
|
|
|
# ── key bindings ──────────────────────────────────────────────────────────────────────────────
|
|
# Transpose `bind <KEY> <COMMAND>` into `bind <COMMAND> <key...>`, grouping by command and keeping
|
|
# the order the keys appeared in, which is the order WoW itself lists a command's primary and
|
|
# secondary binding.
|
|
# benilla's command registry, read from the source so this cannot drift from it.
|
|
known_commands() {
|
|
local f="$root/crates/benilla-app/src/bindings/commands.rs"
|
|
# `spec!("NAME", ...)` on one line, plus the rustfmt-wrapped form with the name on the next.
|
|
{ grep -ohE 'spec!\("[A-Z0-9_]+"' "$f" | grep -oE '"[A-Z0-9_]+"' | tr -d '"'
|
|
awk '/spec!\($/{getline; gsub(/[ \t",]/,""); print}' "$f"; } | sort -u
|
|
}
|
|
|
|
# Transpose `bind <KEY> <COMMAND>` into `bind <COMMAND> <key...>`, grouping by command and keeping
|
|
# the order the keys appeared in — the order WoW itself lists a command's primary then secondary.
|
|
#
|
|
# Two things here are load-bearing:
|
|
#
|
|
# * `gsub(/\r/, "")` — `bindings-cache.wtf` is a Windows file with CRLF endings, so every command
|
|
# name carries a trailing CR. Without this, *nothing* matches the registry and the import looks
|
|
# like it worked while binding nothing (verified: 0 of 161 matched before stripping, 147 after).
|
|
# * the registry filter — benilla does not implement all 161 commands, and `store.rs`'s `resolve`
|
|
# *keeps* an unknown command's row (for addon commands registered later at world entry) after
|
|
# having already stolen its keys from whatever held them. So importing a command benilla lacks
|
|
# takes a key away from a working binding and gives it to something that never exists. The
|
|
# stock snapshot contains exactly that hazard: `CAMERAORSELECTORMOVE <- BUTTON1` and
|
|
# `TURNORACTION <- BUTTON2` would steal left- and right-click mouselook.
|
|
transpose_bindings() {
|
|
known_commands > "$tmp_known"
|
|
awk -v knownfile="$tmp_known" '
|
|
BEGIN { while ((getline k < knownfile) > 0) known[k] = 1 }
|
|
{ gsub(/\r/, "") }
|
|
$1 == "bind" && NF >= 3 {
|
|
cmd = $3
|
|
if (!(cmd in known)) { skipped[cmd] = 1; next }
|
|
if (!(cmd in keys)) { order[++n] = cmd; keys[cmd] = $2 }
|
|
else { keys[cmd] = keys[cmd] " " $2 }
|
|
}
|
|
END {
|
|
print "# benilla key bindings — imported from bindings-cache.wtf (transposed)"
|
|
for (i = 1; i <= n; i++) print "bind " order[i] " " keys[order[i]]
|
|
ns = 0; for (c in skipped) ns++
|
|
if (ns > 0) {
|
|
printf "# %d command(s) in the source file are not in benilla'"'"'s registry and were\n", ns
|
|
print "# left out on purpose: importing one steals its key from a working binding."
|
|
for (c in skipped) print "# " c
|
|
}
|
|
}
|
|
' "$1"
|
|
}
|
|
|
|
bind_one() {
|
|
local src="$1" out="$2" label="$3" lines
|
|
[ -f "$src" ] || return 0
|
|
lines=$(awk '$1 == "bind" && NF >= 3' "$src" | wc -l)
|
|
cmds=$(transpose_bindings "$src" | grep -c '^bind ' || true)
|
|
unknown=$(transpose_bindings "$src" | grep -c '^# ' || true)
|
|
printf ' %-22s %s key line(s) → %s command(s)%s → %s\n' \
|
|
"$label" "$lines" "$cmds" \
|
|
"$([ "$unknown" -gt 0 ] && printf ', %s not in registry (listed in the file)' "$unknown")" \
|
|
"${out#$root/}"
|
|
if [ "$apply" = "1" ]; then
|
|
mkdir -p "$(dirname "$out")"
|
|
[ -f "$out" ] && { cp -p "$out" "$out.pre-import.bak"; backed=$((backed+1)); }
|
|
transpose_bindings "$src" > "$out"
|
|
fi
|
|
}
|
|
|
|
echo "import: key bindings (transposed, not copied)"
|
|
bind_one "$acct_dir/bindings-cache.wtf" "$root/benilla-config/bindings/account.txt" "account-wide"
|
|
if [ -n "$realm" ] && [ -n "$character" ]; then
|
|
bind_one "$acct_dir/$realm/$character/bindings-cache.wtf" \
|
|
"$root/benilla-config/bindings/$(token "$realm")-$(token "$character").txt" \
|
|
"$character"
|
|
fi
|
|
|
|
echo
|
|
echo "import: $copied file(s)$([ "$all" = "1" ] || echo ", $skipped skipped as not-installed")$([ "$backed" -gt 0 ] && echo ", $backed existing backed up to *.pre-import.bak")"
|
|
if [ "$apply" != "1" ]; then
|
|
echo "import: DRY RUN — nothing written. Re-run with --apply to do it."
|
|
fi
|