Skip to content

Version 1.3.0 - Tons of bug/crash fixes + Mod Performance and Log files in a new logs folder

Choose a tag to compare

@Treyo1928 Treyo1928 released this 31 Jul 03:49
· 7 commits to master since this release

Changelog

v1.3.0 — release hardening pass (2026-07-30)

Hopefull the final fix pass before public release: every known bug, plus a built-in crash reporter and mod performance log.

Added

  • Crash reports with mod attribution (modloader/crashtrace.c). Fatal
    signals (SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT) now write
    /sdcard/DCIM/AoQMods/logs/crash-<time>.log with the fault address, ARM
    registers, a stack scan, and every code address resolved to its owning
    module — mod .so files are named individually, and libil2cpp.so offsets
    match dump.cs RVAs. Constructor crashes name the mod being dlopened.
    The system tombstone still runs afterwards.
  • Mod performance log (/sdcard/DCIM/AoQMods/logs/modperf.log): each
    mod's load/constructor time, plus frame-time stats (avg/max/slow-frame
    count) every 30 s. Compare windows with a mod disabled to measure its
    per-frame cost.
  • Run log (/sdcard/DCIM/AoQMods/logs/latest.log): the complete logcat
    of the last run — every tag from the game process, captured by a logcat
    child that lives and dies with the game. Capped at ~8 MB (+ one rollover
    file); the previous run is kept as prev.log. Users can attach it to bug
    reports straight from the headset, no adb required.

Fixed — loader

  • scandir failure paths no longer free an uninitialized pointer, and no
    longer latch "loaded" with zero mods on unexpected errors (ENOENT now
    retries like EACCES/EPERM; anything else logs once and retries).
  • load_mods() reports success correctly (missing return 1 was UB).
  • Mod copies to cache are checked and looped (sendfile short writes); a
    full disk now logs an ENOSPC message and skips the mod instead of
    dlopening a truncated file. Non-regular *.so entries are skipped, and
    stale cache copies from previous runs are cleaned before loading.
  • The per-frame "Directories ready." logcat spam while the storage prompt was
    pending is gone.

Fixed — hooking engine

  • The trampoline page's instruction cache is now flushed after writing
    (previously only the patch site was flushed — worked on some devices by
    luck, crashed on others).
  • orig_boundaries[] sized for 6 Thumb-16 prologue instructions (was 4 —
    overflowed on 5-6 instruction prologues).
  • Two silent-corruption relocation bugs fixed: Thumb-16 ADR operator
    precedence, and ADR.W immediate field assembly.
  • INSTALL_HOOK now logs loudly when a hook fails to install (missing
    libil2cpp, bad RVA, engine error) instead of silently doing nothing.
  • Documented that hook installs are main-thread-only (the ptrace freeze is a
    yama no-op on modern Android and patch writes are not atomic).

Fixed — aoqcore

  • aoq_is_master() called the wrong RVA (instance Player.get_IsMasterClient
    as a zero-arg static — guaranteed crash). Now 0x9A80A4, the real static
    PhotonNetwork.get_IsMasterClient.
  • aoq_late_init() only latches on full success: it verifies libil2cpp is
    mapped, drops the dangerous plain-dlopen fallback (which could map a
    second copy), and retries NULL TypeInfos instead of poisoning every helper
    forever when called too early.
  • All per-frame helpers (transform setters, TMP setters, aoq_photon_view/
    aoq_is_mine, aoq_billboard) now gate on aoq_alive() — using a
    destroyed object no longer throws a managed exception through the hook
    frame (process abort).
  • Config getters return the fallback on type mismatch instead of 0;
    aoq_tmp_create destroys its orphan GameObject when AddComponent fails;
    aoq_tap_hold no longer misreads a release-frame hold as a tap.

Fixed — mod manager

  • Configs are saved atomically (write-to-temp + rename), and a config that
    didn't fully load (over 32 entries / unparseable entries) refuses to save
    instead of silently deleting the missing entries on the next +/- press.
    Default-config extraction and aoqmm_ensure_config use the same pattern.
  • cJSON has a nesting depth cap (64) and an iterative delete — a hostile or
    corrupt shared config can no longer stack-overflow the game.
  • A scene change during the initial async bundle load no longer kills the
    Mods button for the whole session: each scene retries a failed bundle
    state, and still-pending async requests are re-adopted instead of
    double-loaded.
  • Menu-scan polling backs off (10 → 300 frames) in scenes with no injectable
    menu, ending the periodic FindObjectsOfType(Transform) GC hitches.
  • The DisabledModsFolder config value is sanitized (no /, \, ..) and
    only committed if the directory could actually be created.
  • aoqmm_register escapes quotes/backslashes in metadata JSON.