Obsidian 0f635abc25 Cut first-install time by removing redundant payload hashing and copying (#41)
* Stop rehashing and recopying the payload during install

A first install prepares a ~635 MiB payload, then hashes the whole tree five
times and byte-copies it once more into the store. Four of those five passes
re-derive digests the same process just computed, under the same lock.

PreparePayloadFromArchives hashed the tree to write roblox_payload.json and
then immediately verified those hashes against the file it had just written.
PayloadStore::Stage hashed the same workspace again before copying it, and
Promote hashed the published directory a third time.

The guarantee that matters is unchanged: the bytes that land in the immutable
store are still hashed in full after the copy. Stage now records which payload
it verified, so promotion skips the repeat only for a payload this instance
staged; an operator-supplied ID, a directory left by an earlier run, or a fresh
process still gets the complete pass.

CopyTree now reflinks (FICLONE) or uses copy_file_range before falling back to
an ordinary copy, so on btrfs and xfs the staging copy costs no data movement
and no extra space. A hard link would be wrong here: CopyTree makes the result
read-only while the source stays writable.

File hashing moves from the portable foundation::Sha256 to OpenSSL, which
reaches the CPU's SHA extensions, and the asset tree hashes its files across
cores. Measured on this machine: 182 MB/s to 1518 MB/s, and the 1835-file asset
tree of Roblox 2.734.917 reproduces its recorded sha256_tree exactly.

MOCKTAIL_UPDATE_TRACE=1 now reports the elapsed time of each install stage, so
the breakdown is visible instead of inferred.

* Hide the system cursor as soon as the window regains focus

Roblox draws its own cursor while it owns the pointer, so Mocktail hides the
system one. Losing focus reveals it again, which is right, but regaining focus
only set the focused flag and left the re-apply to the next Pump().

Pump() runs at the top of PumpEvents(), before SDL_PollEvent delivers
SDL_EVENT_WINDOW_FOCUS_GAINED, so the desktop arrow stayed on screen for at
least one whole frame after every alt-tab back into the game, and longer
whenever the frame was long. OnFocusGained now pumps the pointer state in the
same call, mirroring OnFocusLost and OnRightButton, and its failure is reported
like the focus-loss path.

An active text field still keeps the system cursor, focused or not.

* Cover promotion of a payload the store did not stage

Stage() now lets promotion skip the repeat hash for a payload it just wrote,
so the boundary of that shortcut needs a test. Nothing covered it: the existing
corruption test rewrites roblox_payload.json, which the metadata check catches
on its own, and payload_store_test.sh exercises the shell store rather than
this class.

The new test corrupts only the content bytes, leaving the metadata describing
the original payload, then promotes from a second PayloadStore instance. It
fails if the shortcut is ever allowed to apply to a payload this instance did
not stage.

* Restore the full hash at the promotion gate

Skipping the rehash for a payload the same instance staged was wrong. The
readiness canaries run between Stage() and Promote(): they spawn the real
client against the staged directory, for up to 150 seconds, twice for a
probation candidate. Trusting the earlier hash across that window drops a
deliberate control, and payload_store_test.sh already spelled out why it is
there.

Promote() and PromoteProbation() call VerifyPreparedPayload() again, as before.
The instance no longer tracks what it staged.

The two tests now tamper with a staged payload from the instance that staged
it, which is the case that was missing: content bytes only, metadata left
describing the original payload, so nothing short of hashing the contents can
detect it. One covers the asset tree, the other libroblox.so.

Promotion still costs a complete pass over ~635 MiB, but OpenSSL and the
parallel asset hash make it 3.9 s -> 0.55 s. Stage is 9.5 s -> 0.93 s. A traced
install reports promote at 656 ms.
2026-08-24 16:22:35 +03:00
2026-08-12 10:50:43 +03:00
2026-08-22 16:03:41 +03:00
2026-08-12 01:58:18 +03:00
2026-08-22 17:06:46 +03:00
2026-08-23 18:31:12 -04:00
2026-08-12 01:58:18 +03:00
2026-08-16 22:05:20 +03:00
2026-08-12 01:58:18 +03:00
2026-08-12 01:58:18 +03:00
2026-08-12 01:58:18 +03:00
2026-08-23 18:31:12 -04:00
2026-08-23 18:31:12 -04:00
2026-08-14 00:34:46 +03:00
2026-08-14 00:34:46 +03:00
2026-08-21 19:13:42 +03:00

Mocktail

CI Stars Downloads License

Mocktail runs the Android x86_64 Roblox client on Linux, including a Linux userspace hosted by FreeBSD's Linuxulator. It provides the Android ABI and JNI pieces the client expects, then connects them to SDL3 and Vulkan or OpenGL on the Linux side.

Mocktail is an independent community project. It is not affiliated with Roblox Corporation or VinegarHQ and does not distribute the Roblox client.

How it works

Roblox APK -> signature and ABI checks -> Bionic + JNI -> SDL3 + Vulkan/OpenGL

The APK is checked before any native code is loaded. It is downloaded on first launch and is not bundled with Mocktail. The last working copy is kept in case an update fails.

Install with Flatpak

Install the stable release from Flathub:

flatpak install flathub space.bigrat.mocktail
flatpak run space.bigrat.mocktail

Nightly builds are produced automatically from the latest main branch:

flatpak install --user https://mocktail.bigrat.space/mocktail.flatpakref
flatpak run space.bigrat.mocktail

Install from the AUR

Arch Linux users can install either the pinned source release (mocktail) or the current development version (mocktail-git) with an AUR helper:

paru -S mocktail-git
# or
yay -S mocktail-git

Use mocktail instead of mocktail-git to build the pinned release from source, or install the stable prebuilt package with paru -S mocktail-bin or yay -S mocktail-bin.

Install with DNF

Fedora 44 users can install either the stable mocktail package or the mocktail-nightly package built from main:

sudo curl -fsSL https://mocktail.bigrat.space/rpm/mocktail.repo \
  -o /etc/yum.repos.d/mocktail.repo
sudo dnf install mocktail
# or
sudo dnf install mocktail-nightly

Install with APT

Ubuntu 26.04 users can install either the stable mocktail package or the mocktail-nightly package built from main:

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://mocktail.bigrat.space/mocktail-packages.gpg \
  -o /etc/apt/keyrings/mocktail.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/mocktail.gpg] https://mocktail.bigrat.space/apt mocktail main" | \
  sudo tee /etc/apt/sources.list.d/mocktail.list >/dev/null
sudo apt update
sudo apt install mocktail
# or
sudo apt install mocktail-nightly

Direct downloads

Direct AppImage, DEB, and RPM downloads are available from the Website and GitHub Releases.

Screenshots

Roblox home in Mocktail

Roblox gameplay in Mocktail

Roblox experience in Mocktail

FFlag overrides

Put a JSON object in $XDG_CONFIG_HOME/mocktail/fflags.json (usually ~/.config/mocktail/fflags.json). Mocktail applies it on the next launch.

{
  "FFlagExample": "True",
  "DFIntExample": "120"
}

Building

Linux x86_64 is supported. Experimental FreeBSD 15.1 Linuxulator support has been tested with an x86_64 Fedora 44 userspace. On FreeBSD, Mocktail runs inside Linuxulator; it is not a native FreeBSD binary. Building requires CMake 3.20+, Git, pkg-config, LLD, binutils, a C++17 compiler, SDL 3.4+, SDL3_ttf, Vulkan, EGL, libplacebo, fontconfig, libcurl, OpenSSL, libelf, libyaml, minizip, Capstone 5, utf8proc, nlohmann/json, GTK4, libadwaita 1.6+, and WebKitGTK 6.0.

See the FreeBSD Guide for setup and launch instructions.

Ubuntu 26.04+
sudo apt update
sudo apt install build-essential cmake git ninja-build pkg-config lld \
  libsdl3-dev libsdl3-ttf-dev libcurl4-openssl-dev libssl-dev \
  nlohmann-json3-dev libyaml-dev libelf-dev libminizip-dev \
  libcapstone-dev libgtk-4-dev libadwaita-1-dev libwebkitgtk-6.0-dev \
  libutf8proc-dev libfontconfig1-dev libegl-dev libvulkan-dev \
  libplacebo-dev zlib1g-dev
Arch Linux
sudo pacman -S --needed base-devel cmake git ninja pkgconf lld sdl3 sdl3_ttf \
  curl openssl nlohmann-json libyaml libelf minizip capstone gtk4 \
  libadwaita webkitgtk-6.0 libutf8proc fontconfig libglvnd \
  libplacebo vulkan-headers vulkan-icd-loader zlib
Fedora 44+
sudo dnf install gcc-c++ cmake git ninja-build pkgconf-pkg-config lld \
  SDL3-devel SDL3_ttf-devel libcurl-devel openssl-devel \
  nlohmann-json-devel libyaml-devel elfutils-libelf-devel minizip-ng-compat-devel \
  capstone-devel gtk4-devel libadwaita-devel webkitgtk6.0-devel \
  utf8proc-devel fontconfig-devel libglvnd-devel vulkan-headers \
  vulkan-loader-devel libplacebo-devel zlib-ng-compat-devel
git clone --recurse-submodules https://github.com/komaruworld/mocktail.git
cd mocktail
make build
./build/mocktail

Known limitations

Mocktail does not currently work with hardened_malloc. Using hardened_malloc may cause Mocktail to fail to start or crash during runtime. Run Mocktail without hardened_malloc enabled.

License

Apache License 2.0. Third-party components keep their own licenses.

Support

You can support the project by giving it a star or with cryptocurrency:

  • USDT (TON): UQCi6Yzcc9cOctoij6n_r1K90-OdVxAT0D_xo2UzGKkQaJDY
  • USDT (TRC20): TNPMG9Vig2xiuo2r1QqnXRChPH7Vu28Jmx
2026-10-16 22:00:55 +02:00
Languages
C++ 83.5%
Shell 6.1%
Python 5.1%
C 2.9%
CMake 1.4%
Other 0.8%