A faithful, open-source FINAL FANTASY XI client — rebuilt in Rust + Bevy, running on a modern engine at 60+ FPS.
Kuluu is a fan-community game-preservation project: a cross-platform, modern, extensible, open-source client for the FINAL FANTASY XI network protocol.
FINAL FANTASY XI is a Square Enix property. Kuluu has no affiliation with, and no endorsement from, Square Enix, and ships no game assets. If you enjoy FFXI, please support the official service. See LEGAL.md.
enhanced label in the backlog), gated behind a feature flag or
build flavor so it never compromises vanilla by default.The workspace builds from a clean checkout once the few build-time vendor submodules are present. Build scripts read data files out of them and translate the values into compile-time Rust constants; no copyrighted asset bytes leave the user's machine, and the submodules are not needed at runtime.
git clone https://github.com/jondwillis/kuluu-ffxi && cd kuluu-ffxi
# Init only the submodules the build actually reads (shallow — history trimmed).
# `server` is large; --depth 1 keeps the working tree without the full history.
git submodule update --init --depth 1 \
vendor/server vendor/POLUtils vendor/AltanaListener
# recastnavigation-rs is vendored in-tree (no submodule).
cargo build
Enable the git hooks (once per clone). A pre-push gate runs the same
fmt + clippy as CI so a red build is caught before you push. It's off until you
opt in (git won't let a repo auto-enable its own hooks):
cargo xtask install-hooks # sets core.hooksPath=.githooks
cargo xtask install-hooks --check # verify it's active (non-zero exit if not)
Bypass a single push with git push --no-verify; PREPUSH_FAST=1 git push
runs fmt only. (scripts/install-hooks.sh does the same thing without a build.)
That's everything the compiler needs. Upstream repos that are not used by
the build — only cited in source comments for reference (xi-model-viewer,
XiEvents, XiPackets, XIClient, xi-tools) — live under research/, not
vendor/. They stay deinitialized; git submodule update --init research/<name> populates one if you want to read the upstream sources.
Phoenix is not a submodule: it is private, so clone it yourself into
research/Phoenix (git-ignored) if you want the server-side divergence
signal.
To actually run the client you also need a user-provided retail install (~19G, never committed — see Getting the game files).
Shallow-clone caveat:
--depth 1works only while the pinned submodule commit is still reachable from its tracked branch tip. If an upstream force-push moves it out of reach, re-run without--depth(or with a larger--depth N) for that submodule.
Native window (the default play mode; GUI ships by default):
cargo run -p kuluu -- play
Headless (JSON-line agent session, useful for protocol work and for driving the client from an automation/LLM harness via the MCP bridge):
cargo run -p kuluu --no-default-features -- play --headless
If any credential env var is unset, the launcher prompts for it and lists characters on the account so you can pick by name.
DLSS Super Resolution is an opt-in enhancement, absent from standard builds and off in Graphics settings until selected. It requires an NVIDIA RTX GPU and Vulkan on x86_64 Windows or Linux; macOS and browser builds do not support it.
The optional vendor/DLSS submodule pins NVIDIA's SDK v310.5.3, matching
dlss_wgpu 4.0.0.
Normal submodule setup skips it. Install the Vulkan SDK and libclang, set
VULKAN_SDK to the SDK root, and set LIBCLANG_PATH if automatic discovery
fails. Then run:
cargo xtask dlss build
This initializes the pinned SDK, builds the release client with dlss, and
stages its matching SR runtime, license, and programming guide (including
upstream attribution notices) beside the executable under
target/<host-target>/release/ (or CARGO_TARGET_DIR). It uses the Windows
MSVC or Linux GNU target. DLSS_SDK can override the pinned SDK directory;
cargo xtask dlss check checks SDK files and Vulkan headers without downloading
or building. The SDK and its runtime remain subject to
NVIDIA's license.
Enable DLSS in Graphics and choose its quality in DLSS Config. It owns
anti-aliasing and render resolution while active. An installed SDK never
changes the normal check gate; explicitly include SR with
KULUU_CHECK_DLSS=1 scripts/checks.sh clippy test build.
Neural Uplift is a separate experimental Windows-only enhancement, gated by
enhanced-neural-uplift and off by default. Its runtime is not included in the
SDK or downloaded by the build helper. To test it, first stage SR with the
helper above, then build the client and forwarder into that same directory:
if (-not $env:DLSS_SDK) { $env:DLSS_SDK = (Resolve-Path vendor/DLSS).Path }
cargo build -p kuluu -p kuluu-ngx-fwd --locked --release --target x86_64-pc-windows-msvc --features native-window,enhanced-neural-uplift
Copy-Item target/x86_64-pc-windows-msvc/release/kuluu_ngx_fwd.dll target/x86_64-pc-windows-msvc/release/nvngx.dll_kuluu.dll
Supply nvngx_dlssnr.dll beside the executable and enable Neural Uplift in
DLSS Config with DLSS active. Adjust these paths when using CARGO_TARGET_DIR.
The forwarder's staged filename must remain nvngx.dll_kuluu.dll. Camera-motion
quality still needs validation; the current NR path supplies zero motion vectors.
The FFXI client DATs (geometry, textures, audio, animations) are Square Enix
copyrighted and must come from a legitimate install — Kuluu never ships or
commits them. The client reads them from vendor/game-files/ by default
(gitignored), from a named client (see below), or wherever FFXI_DAT_PATH
points; the launcher remembers the install you pick, and
kuluu ffxi-client which tells you which one is in effect and why.
Get an install one of these ways:
kuluu ffxi-client setup (or the
launcher's "Get the official client" button, or cargo xtask ffxi-client setup for the checkout) downloads, patches and selects it in one shot,
asking before each step unless told otherwise. Free to download; a
registration code / subscription is needed to play on the official service.~/Games/.../drive_c/.../SquareEnix/FINAL FANTASY XI/..../PlayOnline/SquareEnix/FINAL FANTASY XI/).Kuluu expects this layout (the parent of SquareEnix/):
vendor/game-files/
SquareEnix/
FINAL FANTASY XI/ <- FFXI_DAT_PATH points here
VTABLE.DAT FTABLE.DAT
ROM/ ROM2/ … ROM9/
sound/win/…
Wire an existing install into the checkout with the cross-platform helper,
which detects one (HorizonXI / Lutris / Wine / CrossOver / PlayOnline),
validates it, and symlinks it into vendor/game-files/:
cargo xtask ffxi-client link # auto-detect
cargo xtask ffxi-client link "/path/to/..." # or point it at a known install
cargo xtask ffxi-client link --copy # copy instead of symlink
Don't have an install yet? The same helper downloads Square Enix's official
client installer from the public PlayOnline CDN and unpacks it natively (opt-in
and confirmation-gated). The ffxi-install crate reads the RAR volumes, MSIs
and cabinets itself, decoding each cabinet while the next volume downloads,
then patches the 2019 base image to the current version by speaking the
PlayOnline patch protocol itself: it asks pc001.pol.com for the manifest,
fetches only the files whose checksums differ (whole images or delta chains,
as the viewer would), verifies each one, and writes the manifest as
patch.cfg. No Wine, no installer GUI, no viewer, no account, on any
platform:
cargo xtask ffxi-client setup # asks: name (retail), region (us), then downloads, patches, offers to make it the default
cargo xtask ffxi-client setup --target retail-eu --region eu --yes --default # the same, unattended
cargo xtask ffxi-client update --target retail # re-patch later (--verify re-checks every file)
cargo xtask ffxi-client default hxi # switch what the checkout loads by default (a symlink swap)
setup reuses a target that already carries the name rather than downloading
over it, and default refuses to replace a real directory, so neither can
destroy an install you already have.
update never touches the unnamed default install implicitly: that is
usually a private server's pinned client, which retail patches would break.
(HorizonXI and other flavors must be obtained through their own launchers.)
Or do it by hand — drop/symlink your install at vendor/game-files/, or just
point the client at an existing copy:
export FFXI_DAT_PATH="/path/to/.../SquareEnix/FINAL FANTASY XI"
FFXI_DAT_PATH also overrides at runtime and can be set from the launcher's
settings UI, so you never have to move a large install to use it.
Retail keeps changing its DAT formats (the September 2026 update, for one,
grew every item block from 0xC00 to 0x1400 bytes), and private servers pin
older clients. The latest retail client is the primary target; other
generations stay usable through the same mechanism, not through parallel code
paths. Kuluu identifies an install at startup (ffxi_dat::ClientProfile: the
FFXiMain.dll hash against KNOWN_CLIENTS, plus per-format probes such as the
item block layout) and logs it. Parsers that differ between generations
dispatch on those probed layouts, so an unmeasured build still gets the right
decoder or fails closed instead of reading garbage.
To keep more than one client around, give each a name. Named clients live in
two places that are searched together: vendor/game-files/targets/NAME/ in a
checkout (what cargo xtask ffxi-client manages) and the per-user client
directory the launcher downloads into (~/Library/Application Support/kuluu/clients/NAME
on macOS, ~/.local/share/kuluu/clients/NAME on Linux). Two pointers pick the
active one: the checkout default (a symlink into targets/, which is what
tests, examples and a bare cargo run load) and the launcher's saved choice
in launcher.json (what kuluu play and the launcher load). A shell env var
still wins over both for one-off runs unless the launcher's choice has its
Override tick set; which says which one applied:
cargo xtask ffxi-client link --target retail "/path/to/PlayOnline/SquareEnix/FINAL FANTASY XI"
cargo xtask ffxi-client list # the checkout's installs
cargo xtask ffxi-client default retail # what cargo run / cargo test load with no override
cargo run -p kuluu -- ffxi-client list # every install kuluu can see, with its client profile
cargo run -p kuluu -- ffxi-client setup # download, patch and select the official client
cargo run -p kuluu -- ffxi-client use retail # persist the choice (launcher.json)
cargo run -p kuluu -- ffxi-client which # what will load, and why
FFXI_CLIENT_TARGET=retail cargo run -p kuluu -- play # one-off override by name
cargo run -p ffxi-dat --example dat-client-profile -- "/path/to/FINAL FANTASY XI"
When you measure a new build, add its row to KNOWN_CLIENTS and cite that
row's name (not a date) next to any offset or constant verified on it.
The build-time vendor pins have generations too, and they are not the
client's. vendor/server (LandSandBoat, pinned 2026-04-26) declares
CLIENT_VER = '30260203_0' in settings/default/login.lua with
VER_LOCK = 2, so a stock server at that pin admits retail-2026-09
(30260904_1) and refuses horizonxi-2023 (30230905_0) unless the lock is
off; upstream has since moved to 30260904_1, and a pin bump moves it again.
vendor/POLUtils' ROMFileMappings.xml (pinned 2020-07-19; last edited
2018-08-18 for the Unity dialog tables, before that the 2015-11 Reisenjima
update) keys on absolute file ids up to 86528; every one still resolves on
both installs and retail's table now runs to 109480, so Square Enix appends
and the 2018 mapping stays valid. vendor/AltanaListener's track_names.json
(v1.0.4, 2026-03-11) is a hand-curated 223-track name list, not a
client-derived table, so it has no build to match; the repository is archived
and the pin stays frozen.
The Deck runs the plain x86_64 Linux binary. Launch it from Game Mode, not
Desktop mode: Steam keeps its desktop controller layout active for anything
started outside Game Mode, so the d-pad and left stick arrive as arrow keys
on top of the gamepad. kuluu steam-shortcut registers the binary as a
non-Steam shortcut named Kuluu (with play as its launch options) so Game
Mode can start it under its own controller layout. Run it once from Desktop
mode with Steam fully quit; rerun it after moving the binary.
./kuluu steam-shortcut install # add or update the Kuluu shortcut (Steam must be closed)
./kuluu steam-shortcut install --layout deck.vdf # also install a Steam Input layout for it
./kuluu steam-shortcut install --live # hand the path to a running Steam instead (no rename/layout)
./kuluu steam-shortcut status # which Steam install and account, and whether the entry matches
./kuluu steam-shortcut remove
Without a layout file, pick the Gamepad template in the shortcut's controller settings the first time you launch. In-game the pad follows retail's Pattern E: A confirm, B cancel, X main menu, Y active window, LB autorun, L3 heal/lock, R3 first person, d-pad targets in the field and moves the cursor in menus, left stick moves, right stick is the camera.
Kuluu is, to a first approximation, written by AI coding agents. The large majority of the code in this repository was generated by LLM agents (primarily Claude Code) under human direction, and development continues that way. We'd rather state that plainly than have you infer it.
What that means for you:
Progress is tracked honestly against retail in beads — a git-backed
issue tracker checked into the repo (.beads/issues.jsonl). Parity work carries
the roadmap label, plus vanilla/enhanced and an area label
(hud, combat-action, …); issue status (open / in_progress / closed) is
the source of truth for what's done. The open issues
above are a generated projection of that backlog, published by
scripts/beads-github-publish.py — so the
live counts are not a hand-kept promise. Pick an
open issue and open a PR.
Pick an open issue and open a
PR. The backlog lives in beads (bd ready in a clone, or browse the mirrored
GitHub issues); a feature that has no equivalent in the official FFXI client is
Enhanced / addon and must be gated behind a feature flag.
Remaining vanilla menu / target-interaction gaps are tracked as hud- and
world-interaction-labelled beads (bd list --label=hud); the retail behavior
they're measured against is recorded under
.agents/skills/retail-observe/references/.
For protocol questions, play --headless emits a JSON event stream that's easy
to inspect. For rendering work, the default play GUI window is the fast
iteration loop. New contributors: say hi in Discord.
research/)When re-implementing a feature it helps to read how other community clients
behave. Those upstreams live under research/ as read-only references —
never redistributed by this repo (gitignored or submodule pointers). Study the
behavior and re-express it in our own code; don't copy source in. The most
useful one is XIM, a from-scratch browser FFXI client
(GPL-3). See research/README.md for the full list and
the reference-only policy.
Kuluu is licensed under GPL-3.0-or-later (see LICENSE) — the same copyleft as the upstreams it derives compile-time data from (LandSandBoat, XIM). LEGAL.md covers the no-asset-redistribution policy, trademark disclaimer, and per-source attribution.
Rust
97.4%
Shell
1.7%
A faithful, open-source FINAL FANTASY XI client — rebuilt in Rust + Bevy, running on a modern engine at 60+ FPS.
Kuluu is a fan-community game-preservation project: a cross-platform, modern, extensible, open-source client for the FINAL FANTASY XI network protocol.
FINAL FANTASY XI is a Square Enix property. Kuluu has no affiliation with, and no endorsement from, Square Enix, and ships no game assets. If you enjoy FFXI, please support the official service. See LEGAL.md.
enhanced label in the backlog), gated behind a feature flag or
build flavor so it never compromises vanilla by default.The workspace builds from a clean checkout once the few build-time vendor submodules are present. Build scripts read data files out of them and translate the values into compile-time Rust constants; no copyrighted asset bytes leave the user's machine, and the submodules are not needed at runtime.
git clone https://github.com/jondwillis/kuluu-ffxi && cd kuluu-ffxi
# Init only the submodules the build actually reads (shallow — history trimmed).
# `server` is large; --depth 1 keeps the working tree without the full history.
git submodule update --init --depth 1 \
vendor/server vendor/POLUtils vendor/AltanaListener
# recastnavigation-rs is vendored in-tree (no submodule).
cargo build
Enable the git hooks (once per clone). A pre-push gate runs the same
fmt + clippy as CI so a red build is caught before you push. It's off until you
opt in (git won't let a repo auto-enable its own hooks):
cargo xtask install-hooks # sets core.hooksPath=.githooks
cargo xtask install-hooks --check # verify it's active (non-zero exit if not)
Bypass a single push with git push --no-verify; PREPUSH_FAST=1 git push
runs fmt only. (scripts/install-hooks.sh does the same thing without a build.)
That's everything the compiler needs. Upstream repos that are not used by
the build — only cited in source comments for reference (xi-model-viewer,
XiEvents, XiPackets, XIClient, xi-tools) — live under research/, not
vendor/. They stay deinitialized; git submodule update --init research/<name> populates one if you want to read the upstream sources.
Phoenix is not a submodule: it is private, so clone it yourself into
research/Phoenix (git-ignored) if you want the server-side divergence
signal.
To actually run the client you also need a user-provided retail install (~19G, never committed — see Getting the game files).
Shallow-clone caveat:
--depth 1works only while the pinned submodule commit is still reachable from its tracked branch tip. If an upstream force-push moves it out of reach, re-run without--depth(or with a larger--depth N) for that submodule.
Native window (the default play mode; GUI ships by default):
cargo run -p kuluu -- play
Headless (JSON-line agent session, useful for protocol work and for driving the client from an automation/LLM harness via the MCP bridge):
cargo run -p kuluu --no-default-features -- play --headless
If any credential env var is unset, the launcher prompts for it and lists characters on the account so you can pick by name.
DLSS Super Resolution is an opt-in enhancement, absent from standard builds and off in Graphics settings until selected. It requires an NVIDIA RTX GPU and Vulkan on x86_64 Windows or Linux; macOS and browser builds do not support it.
The optional vendor/DLSS submodule pins NVIDIA's SDK v310.5.3, matching
dlss_wgpu 4.0.0.
Normal submodule setup skips it. Install the Vulkan SDK and libclang, set
VULKAN_SDK to the SDK root, and set LIBCLANG_PATH if automatic discovery
fails. Then run:
cargo xtask dlss build
This initializes the pinned SDK, builds the release client with dlss, and
stages its matching SR runtime, license, and programming guide (including
upstream attribution notices) beside the executable under
target/<host-target>/release/ (or CARGO_TARGET_DIR). It uses the Windows
MSVC or Linux GNU target. DLSS_SDK can override the pinned SDK directory;
cargo xtask dlss check checks SDK files and Vulkan headers without downloading
or building. The SDK and its runtime remain subject to
NVIDIA's license.
Enable DLSS in Graphics and choose its quality in DLSS Config. It owns
anti-aliasing and render resolution while active. An installed SDK never
changes the normal check gate; explicitly include SR with
KULUU_CHECK_DLSS=1 scripts/checks.sh clippy test build.
Neural Uplift is a separate experimental Windows-only enhancement, gated by
enhanced-neural-uplift and off by default. Its runtime is not included in the
SDK or downloaded by the build helper. To test it, first stage SR with the
helper above, then build the client and forwarder into that same directory:
if (-not $env:DLSS_SDK) { $env:DLSS_SDK = (Resolve-Path vendor/DLSS).Path }
cargo build -p kuluu -p kuluu-ngx-fwd --locked --release --target x86_64-pc-windows-msvc --features native-window,enhanced-neural-uplift
Copy-Item target/x86_64-pc-windows-msvc/release/kuluu_ngx_fwd.dll target/x86_64-pc-windows-msvc/release/nvngx.dll_kuluu.dll
Supply nvngx_dlssnr.dll beside the executable and enable Neural Uplift in
DLSS Config with DLSS active. Adjust these paths when using CARGO_TARGET_DIR.
The forwarder's staged filename must remain nvngx.dll_kuluu.dll. Camera-motion
quality still needs validation; the current NR path supplies zero motion vectors.
The FFXI client DATs (geometry, textures, audio, animations) are Square Enix
copyrighted and must come from a legitimate install — Kuluu never ships or
commits them. The client reads them from vendor/game-files/ by default
(gitignored), from a named client (see below), or wherever FFXI_DAT_PATH
points; the launcher remembers the install you pick, and
kuluu ffxi-client which tells you which one is in effect and why.
Get an install one of these ways:
kuluu ffxi-client setup (or the
launcher's "Get the official client" button, or cargo xtask ffxi-client setup for the checkout) downloads, patches and selects it in one shot,
asking before each step unless told otherwise. Free to download; a
registration code / subscription is needed to play on the official service.~/Games/.../drive_c/.../SquareEnix/FINAL FANTASY XI/..../PlayOnline/SquareEnix/FINAL FANTASY XI/).Kuluu expects this layout (the parent of SquareEnix/):
vendor/game-files/
SquareEnix/
FINAL FANTASY XI/ <- FFXI_DAT_PATH points here
VTABLE.DAT FTABLE.DAT
ROM/ ROM2/ … ROM9/
sound/win/…
Wire an existing install into the checkout with the cross-platform helper,
which detects one (HorizonXI / Lutris / Wine / CrossOver / PlayOnline),
validates it, and symlinks it into vendor/game-files/:
cargo xtask ffxi-client link # auto-detect
cargo xtask ffxi-client link "/path/to/..." # or point it at a known install
cargo xtask ffxi-client link --copy # copy instead of symlink
Don't have an install yet? The same helper downloads Square Enix's official
client installer from the public PlayOnline CDN and unpacks it natively (opt-in
and confirmation-gated). The ffxi-install crate reads the RAR volumes, MSIs
and cabinets itself, decoding each cabinet while the next volume downloads,
then patches the 2019 base image to the current version by speaking the
PlayOnline patch protocol itself: it asks pc001.pol.com for the manifest,
fetches only the files whose checksums differ (whole images or delta chains,
as the viewer would), verifies each one, and writes the manifest as
patch.cfg. No Wine, no installer GUI, no viewer, no account, on any
platform:
cargo xtask ffxi-client setup # asks: name (retail), region (us), then downloads, patches, offers to make it the default
cargo xtask ffxi-client setup --target retail-eu --region eu --yes --default # the same, unattended
cargo xtask ffxi-client update --target retail # re-patch later (--verify re-checks every file)
cargo xtask ffxi-client default hxi # switch what the checkout loads by default (a symlink swap)
setup reuses a target that already carries the name rather than downloading
over it, and default refuses to replace a real directory, so neither can
destroy an install you already have.
update never touches the unnamed default install implicitly: that is
usually a private server's pinned client, which retail patches would break.
(HorizonXI and other flavors must be obtained through their own launchers.)
Or do it by hand — drop/symlink your install at vendor/game-files/, or just
point the client at an existing copy:
export FFXI_DAT_PATH="/path/to/.../SquareEnix/FINAL FANTASY XI"
FFXI_DAT_PATH also overrides at runtime and can be set from the launcher's
settings UI, so you never have to move a large install to use it.
Retail keeps changing its DAT formats (the September 2026 update, for one,
grew every item block from 0xC00 to 0x1400 bytes), and private servers pin
older clients. The latest retail client is the primary target; other
generations stay usable through the same mechanism, not through parallel code
paths. Kuluu identifies an install at startup (ffxi_dat::ClientProfile: the
FFXiMain.dll hash against KNOWN_CLIENTS, plus per-format probes such as the
item block layout) and logs it. Parsers that differ between generations
dispatch on those probed layouts, so an unmeasured build still gets the right
decoder or fails closed instead of reading garbage.
To keep more than one client around, give each a name. Named clients live in
two places that are searched together: vendor/game-files/targets/NAME/ in a
checkout (what cargo xtask ffxi-client manages) and the per-user client
directory the launcher downloads into (~/Library/Application Support/kuluu/clients/NAME
on macOS, ~/.local/share/kuluu/clients/NAME on Linux). Two pointers pick the
active one: the checkout default (a symlink into targets/, which is what
tests, examples and a bare cargo run load) and the launcher's saved choice
in launcher.json (what kuluu play and the launcher load). A shell env var
still wins over both for one-off runs unless the launcher's choice has its
Override tick set; which says which one applied:
cargo xtask ffxi-client link --target retail "/path/to/PlayOnline/SquareEnix/FINAL FANTASY XI"
cargo xtask ffxi-client list # the checkout's installs
cargo xtask ffxi-client default retail # what cargo run / cargo test load with no override
cargo run -p kuluu -- ffxi-client list # every install kuluu can see, with its client profile
cargo run -p kuluu -- ffxi-client setup # download, patch and select the official client
cargo run -p kuluu -- ffxi-client use retail # persist the choice (launcher.json)
cargo run -p kuluu -- ffxi-client which # what will load, and why
FFXI_CLIENT_TARGET=retail cargo run -p kuluu -- play # one-off override by name
cargo run -p ffxi-dat --example dat-client-profile -- "/path/to/FINAL FANTASY XI"
When you measure a new build, add its row to KNOWN_CLIENTS and cite that
row's name (not a date) next to any offset or constant verified on it.
The build-time vendor pins have generations too, and they are not the
client's. vendor/server (LandSandBoat, pinned 2026-04-26) declares
CLIENT_VER = '30260203_0' in settings/default/login.lua with
VER_LOCK = 2, so a stock server at that pin admits retail-2026-09
(30260904_1) and refuses horizonxi-2023 (30230905_0) unless the lock is
off; upstream has since moved to 30260904_1, and a pin bump moves it again.
vendor/POLUtils' ROMFileMappings.xml (pinned 2020-07-19; last edited
2018-08-18 for the Unity dialog tables, before that the 2015-11 Reisenjima
update) keys on absolute file ids up to 86528; every one still resolves on
both installs and retail's table now runs to 109480, so Square Enix appends
and the 2018 mapping stays valid. vendor/AltanaListener's track_names.json
(v1.0.4, 2026-03-11) is a hand-curated 223-track name list, not a
client-derived table, so it has no build to match; the repository is archived
and the pin stays frozen.
The Deck runs the plain x86_64 Linux binary. Launch it from Game Mode, not
Desktop mode: Steam keeps its desktop controller layout active for anything
started outside Game Mode, so the d-pad and left stick arrive as arrow keys
on top of the gamepad. kuluu steam-shortcut registers the binary as a
non-Steam shortcut named Kuluu (with play as its launch options) so Game
Mode can start it under its own controller layout. Run it once from Desktop
mode with Steam fully quit; rerun it after moving the binary.
./kuluu steam-shortcut install # add or update the Kuluu shortcut (Steam must be closed)
./kuluu steam-shortcut install --layout deck.vdf # also install a Steam Input layout for it
./kuluu steam-shortcut install --live # hand the path to a running Steam instead (no rename/layout)
./kuluu steam-shortcut status # which Steam install and account, and whether the entry matches
./kuluu steam-shortcut remove
Without a layout file, pick the Gamepad template in the shortcut's controller settings the first time you launch. In-game the pad follows retail's Pattern E: A confirm, B cancel, X main menu, Y active window, LB autorun, L3 heal/lock, R3 first person, d-pad targets in the field and moves the cursor in menus, left stick moves, right stick is the camera.
Kuluu is, to a first approximation, written by AI coding agents. The large majority of the code in this repository was generated by LLM agents (primarily Claude Code) under human direction, and development continues that way. We'd rather state that plainly than have you infer it.
What that means for you:
Progress is tracked honestly against retail in beads — a git-backed
issue tracker checked into the repo (.beads/issues.jsonl). Parity work carries
the roadmap label, plus vanilla/enhanced and an area label
(hud, combat-action, …); issue status (open / in_progress / closed) is
the source of truth for what's done. The open issues
above are a generated projection of that backlog, published by
scripts/beads-github-publish.py — so the
live counts are not a hand-kept promise. Pick an
open issue and open a PR.
Pick an open issue and open a
PR. The backlog lives in beads (bd ready in a clone, or browse the mirrored
GitHub issues); a feature that has no equivalent in the official FFXI client is
Enhanced / addon and must be gated behind a feature flag.
Remaining vanilla menu / target-interaction gaps are tracked as hud- and
world-interaction-labelled beads (bd list --label=hud); the retail behavior
they're measured against is recorded under
.agents/skills/retail-observe/references/.
For protocol questions, play --headless emits a JSON event stream that's easy
to inspect. For rendering work, the default play GUI window is the fast
iteration loop. New contributors: say hi in Discord.
research/)When re-implementing a feature it helps to read how other community clients
behave. Those upstreams live under research/ as read-only references —
never redistributed by this repo (gitignored or submodule pointers). Study the
behavior and re-express it in our own code; don't copy source in. The most
useful one is XIM, a from-scratch browser FFXI client
(GPL-3). See research/README.md for the full list and
the reference-only policy.
Kuluu is licensed under GPL-3.0-or-later (see LICENSE) — the same copyleft as the upstreams it derives compile-time data from (LandSandBoat, XIM). LEGAL.md covers the no-asset-redistribution policy, trademark disclaimer, and per-source attribution.
Rust
97.4%
Shell
1.7%