Most people should not run the manual steps below. A pre-built jk2coop
binary embeds the engine source, so setup needs no clone and no submodule:
./jk2coop setup # extract embedded source + patches + engine build + installOr build jk2coop from a clone first:
git clone --recurse-submodules <repo>
cd jedi-outcast-coop
make build # produces ./jk2coop
./jk2coop setup # same guided setup, from the embedded sourcejk2coop setup does everything the manual sections below describe, in order.
By default it builds in a container inside a throwaway VM (via
vee), so you install neither a C/C++
toolchain nor Docker on the host. If vee is not already on your PATH,
setup downloads a pinned, checksum-verified copy into the config dir
(~/.config/jk2coop/bin) and keeps it for later rebuilds — so the only true
prerequisite is a network connection the first time. See
§ Building in a container (--docker)
below, and build-vm.md for the whole vee/VM story and how to
manage it with jk2coop vee. Override the default with:
--host— build on this machine (needs the cmake/ninja/compiler toolchain;setupprints the exact install command if it is missing);--vm— build in a plain VM (no container) via vee.
When vee cannot be obtained (no network, unsupported platform) and is not
installed, setup falls back to a host build.
By default setup builds from the source embedded in the binary, extracted
to ~/.cache/jk2coop — see embedded-source.md. The manual
sections below operate on the openjk/ submodule directly and are the reference
for patch development; to make setup use the submodule instead of the
embedded source, pass --repo . from inside a checkout.
openjk/— pinned OpenJK submodule (upstream source; co-op changes are applied to it as patches, never committed to it)openjk/build/— build output (gitignored)patches/— this project's source changes, one cumulative diff per file set, applied bytools/apply-patches.shassets/coop-ui/— the original Co-op menu overlay, packed intozz-coop-ui.pk3bytools/build-coop-ui-pk3.shtools/— installers and helper scriptsdocs/— documentation
Requires: cmake, ninja, gcc, SDL2, OpenAL, zlib, libpng, libjpeg.
git clone --recurse-submodules <repo>
cd jedi-outcast-rebuild
tools/apply-patches.sh # apply the co-op patches to the submoduleOr, using the cross-platform jk2coop Go binary (equivalent; see
tooling.md):
go build -mod=vendor -o jk2coop .
./jk2coop dev patches applyThe patches are cumulative and overlap (several touch the same lines — e.g.
one patch sets the sv_maxclients infostring to MAX_CLIENTS and a later one
rewrites that same line to honour the runtime cvar). They apply
cleanly in order to a pristine submodule, but apply-patches.sh is not
idempotent on a dirty tree: re-running it against an already-patched submodule
can fail on an overlapping patch. To re-apply, reset the submodule first:
git -C openjk checkout -- . && git -C openjk clean -fd
tools/apply-patches.shContinuing the build:
cmake -S openjk -B openjk/build -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DBuildJK2SPEngine=ON -DBuildJK2SPGame=ON -DBuildJK2SPRdVanilla=ON \
-DBuildSPEngine=OFF -DBuildSPGame=OFF -DBuildSPRdVanilla=OFF \
-DBuildMPEngine=OFF -DBuildMPRdVanilla=OFF -DBuildMPDed=OFF \
-DBuildMPGame=OFF -DBuildMPCGame=OFF -DBuildMPUI=OFF -DBuildMPRend2=OFF
cmake --build openjk/buildProduces three artifacts:
| Artifact | Role |
|---|---|
openjo_sp.x86_64 |
Engine |
code/rd-vanilla/rdjosp-vanilla_x86_64.so |
OpenGL renderer module |
codeJK2/game/jospgamex86_64.so |
Singleplayer gamecode |
macOS and Windows build with the same -DBuildJK2SP* options; see the
macOS and Windows install guides
for toolchain specifics and artifact names.
jk2coop setup --docker builds the engine inside a container without installing
anything on the host — not a C/C++ toolchain, and not even Docker. It works
by using vee to run a small Linux VM from
its docker template (Alpine + the Docker daemon), then driving that daemon
over the Docker Engine API — vee forwards it to tcp://127.0.0.1:2375 on the
host, and jk2coop talks to it with a tiny built-in HTTP client. The only host
prerequisite is vee itself (which brings QEMU/KVM).
jk2coop setup --docker # build in a container in a vee VM; host stays cleanWhat happens under the hood:
vee create jk2coop-docker --template docker --virtiofs-dir <source> …boots the VM and shares your (already patched) engine source into it over virtiofs.- Inside the VM, the share is mounted and the Docker daemon is started.
jk2coopbuilds a small image (CMake, Ninja, the SDL2/OpenAL/zlib/png/jpeg dev libraries, and the mingw-w64 cross toolchain) via the Engine API.- A container compiles the engine with the bind-mounted source. Because the source is a virtiofs share, the build outputs appear back on the host automatically — there is no copy-out step.
The VM is kept after a successful build (a re-run reuses the warm VM and its
cached image); setup offers to delete it, or run jk2coop vee vm delete (or
vee delete jk2coop-docker). If vee is not already installed, setup
downloads a pinned, checksum-verified copy into ~/.config/jk2coop/bin first.
See build-vm.md for the full vee/VM lifecycle and jk2coop vee.
The build always runs in a Linux container, and produces the binary your host needs:
| Host OS | Output | How |
|---|---|---|
| Linux | Linux ELF (.so, openjo_sp.<arch>) |
native compile in the container |
| Windows | Windows PE (.exe, .dll) |
mingw-w64 cross-compile (the .exe runs on your Windows host) |
| macOS | not supported | a Linux container cannot emit a macOS Mach-O binary, and Apple's SDK is not redistributable — build on the Mac with --host (Xcode), or use the jk2coop-macos CI artifact |
Note: the Docker API inside the VM is plaintext and loopback-only (vee forwards it to
127.0.0.1via user-mode NAT). That is fine for a throwaway local build VM; do not expose it beyond localhost.
The engine reads assets and modules from ~/.local/share/openjo/base/
(note: openjo, not openjk — this is the Jedi Outcast target).
Symlink the retail assets and the freshly built gamecode into place:
mkdir -p ~/.local/share/openjo/base
ln -sfn "<steam>/Jedi Outcast/GameData/base/"assets*.pk3 ~/.local/share/openjo/base/
ln -sfn "$PWD/openjk/build/codeJK2/game/jospgamex86_64.so" ~/.local/share/openjo/base/
# the renderer module is loaded relative to the executable:
ln -sfn "$PWD/openjk/build/code/rd-vanilla/rdjosp-vanilla_x86_64.so" openjk/build/
cd openjk/build && ./openjo_sp.x86_64 +map kejim_posttools/install-coop.sh automates all of this — see
install-linux.md.
RelWithDebInfo and Release both define NDEBUG, which compiles out
assert(). The singleplayer save code carries assertions that Raven left
as deliberate tripwires for exactly the change this project is making, so
test anything touching saves against a Debug tree:
cmake -S openjk -B openjk/build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DBuildJK2SPEngine=ON -DBuildJK2SPGame=ON -DBuildJK2SPRdVanilla=ON
cmake --build openjk/build-debugVerify the assertions are live before trusting a passing test:
nm -u openjk/build-debug/codeJK2/game/jospgamex86_64.so | grep assertGameplay code lives in openjk/codeJK2/game/ and builds as a standalone
shared library. Because the gamecode is symlinked into the engine's
search path, rebuilding that one target is sufficient — no reinstall:
cmake --build openjk/build --target jospgamex86_64Relaunch to pick up the change.
Every change should end with the loopback regression:
cd openjk/build && ./openjo_sp.x86_64 +map kejim_post # exit 0, no errors