Building from source - BEEmod/BEE2-items GitHub Wiki
You can build BEE2 from source on both Windows and Linux.
Below is a detailed step-by-step guide.
- 1. Install Python
- 2. Download the Repository
- 3. Install Python Dependencies
- 4. Enable Sound Effects (FFmpeg)
- 5. Update Git Submodules
- 6. Compile with PyInstaller
- 7. Add Items to BEE2
- 8. Fix Puzzle Maker on Linux
- 9. Run BEE2
- 10. Add a Desktop Shortcut
- 11. Fully Automated Build Script
Linux:
-
Install via your package manager:
sudo pacman -S python python-pip git
(or equivalent) -
Also install via your package manager:
sudo pacman -S spatialindex
(or equivalent, may also be called libspatialindex) -
Make virtual environment (needed on linux):
python -m venv ./venv source ./venv/bin/activate
Windows:
-
Download Python or use
winget install Python.Python.3.13
-
Download Git or use
winget install Git.Git
-
If you like to use a venv, you can use (in cmd):
python -m venv ./venv venv/Scripts/activate
-
Clone the repo:
git clone --recurse-submodules https://github.com/BEEmod/BEE2.4.git cd BEE2.4
-
To use the dev branch:
git clone -b dev --recurse-submodules https://github.com/BEEmod/BEE2.4.git
pip install -r requirements.txt
If you're using the dev branch, also run:
pip install -r dev-requirements.txt
-
Download shared FFmpeg builds:
-
Extract the
bin/
contents into:-
lib-64/
orlib-32/
inside the BEE2.4 folder (depending on your platform)
-
On Linux, sound effects may still not work, to be save copy them anyway.
From inside the BEE2.4/
directory run:
git submodule update --init
From inside the BEE2.4/
directory run:
cd src
pyinstaller --distpath ../dist/64bit/ --workpath ../build_tmp compiler.spec
pyinstaller --distpath ../dist/64bit/ --workpath ../build_tmp BEE2.spec
You can add item packages to BEE2 using stable precompiled zips or by generating dev packages manually.
-
Download the latest:
-
Extract the contents into:
BEE2.4/dist/64bit/BEE2/packages/
You may need to manually create the
packages/
folder if it doesnβt exist.
Option B (Click to see): Use Dev Packages (compile from source, mostly needed for dev version of BEE)
This method is useful if you're working with or testing the latest unreleased item changes / using the dev version of BEE.
-
Clone or download the dev branches:
git clone -b dev https://github.com/BEEmod/BEE2-items.git dev-items git clone -b dev https://github.com/BEEmod/BEE2-music.git music-dev-items
Alternatively, you can click the green "Code" β "Download ZIP" button on each repo, unzip, and rename to
dev-items
andmusic-dev-items
. -
Install requirements:
cd dev-items pip install -r requirements.txt
-
Compile the packages:
mkdir -p zips/items python ./compile_packages.py imput . -c --zip --overwrite -o zips/items
-
Compile the music packages:
mkdir -p zips/music python ./compile_packages.py imput ../music-dev-items/. -c --zip --overwrite -o zips/music
-
Link or move the compiled
.bee_pack
files fromzips/items/
andzips/music/
to:BEE2.4/dist/64bit/BEE2/packages/
You can also use symbolic links to avoid duplicating data.
The Portal 2 Puzzle Maker is broken on Linux by default (still true in 2025).
You can fix it by copying a working filesystem_stdio.so
from an old depot:
steam -console
# In Steam console:
download_depot 620 661 2854055004190207766
Then run:
cp "$HOME/.steam/steam/ubuntu12_32/steamapps/content/app_620/depot_661/bin/linux32/filesystem_stdio.so" \
"$HOME/.steam/steam/steamapps/common/Portal 2/bin/linux32/filesystem_stdio.so"
More details: ValveSoftware/portal2#403
Navigate to:
BEE2.4/dist/64bit/BEE2/
Then run:
./BEE2 # On Linux
BEE2.exe # On Windows
Windows:
- Right-click
BEE2.exe
, then choose "Create shortcut".
Linux:
Create a file named BEE2.desktop
with:
[Desktop Entry]
Exec=%yourbeeroot%/dist/64bit/BEE2/BEE2
GenericName=BEE2
Icon=%yourbeeroot%/icon/BEE2.ico
Name=BEE2
Path=%yourbeeroot%/dist/64bit/BEE2
StartupNotify=true
StartupWMClass=BEE2
Terminal=false
Type=Application
Replace %yourbeeroot%
with the absolute path to your BEE2.4
directory.
If you're building on Linux, this script can automate nearly everything from cloning the repo to packaging, FFmpeg handling, and shortcuts.
-
First: Prepare a build folder (e.g.,
$HOME/Documents/Coding/Git/Build-Bee2
) -
And: Fix Puzzle Maker on Linux (Only download_depo part is needed)
-
Then: Install required system tools (via your distroβs package manager):
sudo pacman -S git curl jq tar unzip awk pyinstaller bash
(or equivalent) -
After: Follow the script creation bellow
Create a file named bee-builder.sh
in your build root, and paste the following into it:
=== Click to expand the full script ===
#!/bin/bash
# Set default python environment path
DEFAULT_PYTHON_ENV="./venv"
pythonenv="${pythonenv:-$DEFAULT_PYTHON_ENV}"
customver="${customver:-}"
scriptdir="$(dirname "$(realpath "$0")")"
main() {
show_help
parse_input "$@"
[[ "$goexit" == true ]] && echo "Exit selected, exiting." && exit
post_cli_seperator
setup_python_env
prepare_build_dir
clone_repo
fix_portal_bug
download_icon
download_ffmpeg
update_pip
install_requirements
update_submodules
clean_old_builds
run_pyinstaller
download_packages
download_dev_packages
download_dev_music_packages
build_packages
copy_pac_and_ffmpeg
create_shortcut_if_needed
create_link_if_needed
run_bee_if_needed
}
show_help() {
cat <<EOF
Usage: $0 [options]
Options or User input choices (comma-separated, e.g., '0,1,2,3'):
0 - Use only CLI args (no interactive input)
1 - Delete old build directory (have a fresh directory for building; its faster to leave it as is)
2 - Run BEE2 after build
3 - Don't Create a shortcut next to this script
4 - Disable pip update
5 - Build from main branch instead of dev
6 - Skip downloading pip requirements
7 - Skip downloading pip requirements for building packages
8 - Don't get the icon (Only downloded when icon is missing)
9 - Skip Portal 2 fix for Linux (Still needed in 2025, skipping not recomended)
10 - Use current FFmpeg (by default it will auto update when needed)
11 - Use current package items (by default they will auto update when needed)
12 - Don't create symlink to main BEE2 directory
13 - Skip building packages (for testing only)
14 - Skip building (for testing only)
15 - Exit
EOF
}
parse_input() {
if [[ "$1" != *",0" && "$1" != "0,"* && "$1" != *",0,"* && "$1" != "0" ]]; then
read -p "Enter your choices: " choice
fi
local combined="${1},${choice}"
IFS=',' read -ra user_choices <<< "$combined"
set_defaults
for choice in "${user_choices[@]}"; do
case "$choice" in
0) ;;
1) fresh=true ;;
2) run=true ;;
3) shortcut=false ;;
4) pipupdate=false ;;
5) dev=false ;;
6) requirements=false ;;
7) pac_requirements=false ;;
8) geticon=false ;;
9) portallinuxbug=false ;;
10) newffmpeg=false ;;
11) newpackages=false ;;
12) distlink=false ;;
13) pac_build=false ;;
14) build=false ;;
15) goexit=true ;;
*) [[ -n "$choice" ]] && echo "Invalid choice: $choice" ;;
esac
done
}
set_defaults() {
goexit=false
fresh=false
run=false
shortcut=true
pipupdate=true
dev=true
distlink=true
requirements=true
pac_requirements=false
geticon=true
portallinuxbug=true
newffmpeg=true
newpackages=true
build=true
pac_build=true
}
post_cli_seperator() {
echo "---"
echo ""
}
setup_python_env() {
cd "$scriptdir" || exit
echo "Checking for python environment..."
if [[ "$pythonenv" == false ]]; then
echo "Python environment is disabled."
post_cli_seperator
return
fi
if [[ ! -f "$pythonenv" ]]; then
echo "Python environment not found, creating one..."
python3 -m venv "$pythonenv"
fi
echo "Activating environment: $pythonenv"
source "$pythonenv/bin/activate"
post_cli_seperator
}
prepare_build_dir() {
echo "Peparing build directory..."
cd "$scriptdir" || exit
mainbuildir="BEE2.4"
[[ "$dev" == false ]] && mainbuildir="MainBEE2.4"
[[ -n "$customver" ]] && mainbuildir="$customver$mainbuildir"
if [[ "$fresh" == true ]]; then
echo "Removing old directory: $mainbuildir"
rm -rf "$mainbuildir"
fi
post_cli_seperator
}
clone_repo() {
echo "Getting Repo..."
cd "$scriptdir" || exit
local branch="dev"
[[ "$dev" == false ]] && branch=""
local repo_url="https://github.com/BEEmod/BEE2.4.git"
if [[ -d "$mainbuildir/.git" ]]; then
echo "Updating existing repo in $mainbuildir"
cd "$mainbuildir" || exit
if [[ -n "$branch" ]]; then
git fetch origin "$branch"
git reset --hard "origin/$branch"
else
git fetch origin
git reset --hard "origin"
fi
[[ -n "$customver" ]] && git checkout "$customver"
git pull
else
echo "Cloning BEE2.4 repository"
if [[ -n "$branch" ]]; then
git clone -b "$branch" --recurse-submodules "$repo_url" temp
else
git clone --recurse-submodules "$repo_url" temp
fi
if [[ -n "$customver" ]]; then
echo "Checking out custom version: $customver"
cd temp || exit
git checkout "$customver"
cd "$scriptdir"
fi
echo "Copying repo to $mainbuildir"
mkdir -p "$mainbuildir"
cp -rfa temp/. "$mainbuildir"
rm -rf temp
fi
post_cli_seperator
}
fix_portal_bug() {
[[ "$portallinuxbug" != true ]] && return
cd "$scriptdir" || exit
echo "Applying Portal 2 Linux fix..."
fixf="$HOME/.steam/steam/ubuntu12_32/steamapps/content/app_620/depot_661/bin/linux32/filesystem_stdio.so"
targetf="$HOME/.steam/steam/steamapps/common/Portal 2/bin/linux32/filesystem_stdio.so"
if cp "$fixf" "$targetf"; then
echo "File was copyed successfully."
else
echo "File could not be copyed."
echo "Please run the following:"
echo "- 'steam -console' or 'steam-native -console' in the teminal."
echo "After that run in the steam console:"
echo "download_depot 620 661 2854055004190207766"
echo "After that the fix should work fine"
fi
post_cli_seperator
}
download_icon() {
[[ "$geticon" != true ]] && return
cd "$scriptdir" || exit
echo "Getting Icon..."
if [[ -f "icon/BEE2.ico" ]]; then
echo "Icon already exists, skipping download."
post_cli_seperator
return
fi
echo "Downloading icon..."
mkdir -p icon
curl -L -o "icon/BEE2.ico" "https://raw.githubusercontent.com/BEEmod/BEE2.4/master/BEE2.ico"
post_cli_seperator
}
download_ffmpeg() {
[[ "$newffmpeg" != true ]] && return
cd "$scriptdir" || exit
echo "Checking FFmpeg version..."
mkdir -p bin "$mainbuildir/lib-64"
local url release_id id_file="bin/ffmpeg_release_id.txt"
release_json=$(curl -s 'https://api.github.com/repos/BtbN/FFmpeg-Builds/releases/latest')
release_id=$(echo "$release_json" | jq -r '.id')
if [[ -f "$id_file" && "$(cat "$id_file")" == "$release_id" ]]; then
echo "Latest FFmpeg already downloaded (release ID: $release_id)"
post_cli_seperator
return
fi
url=$(echo "$release_json" | jq -r '.assets[] | select(.name | endswith("linux64-lgpl-shared.tar.xz")) | .browser_download_url' | head -n 1)
echo "Downloading FFmpeg (release ID: $release_id)..."
curl -L -o bin/ffmpeg.tar.xz "$url"
# Extract only the bin/* contents directly into bin/
tar --wildcards --strip-components=2 -xvf bin/ffmpeg.tar.xz -C bin "ffmpeg-*/bin/*"
rm bin/ffmpeg.tar.xz
echo "$release_id" > "$id_file"
post_cli_seperator
}
update_pip() {
[[ "$pipupdate" != true ]] && return
cd "$scriptdir" || exit
echo "Updating pip..."
pip install --upgrade pip
post_cli_seperator
}
install_requirements() {
[[ "$requirements" != true ]] && return
cd "$scriptdir/$mainbuildir" || exit
echo "Grabbing requirements..."
pip install -r requirements.txt
[[ "$dev" == true ]] && pip install -r dev-requirements.txt
post_cli_seperator
}
update_submodules() {
cd "$scriptdir" || exit
echo "Updating submodules..."
cd "$mainbuildir" || exit
git submodule update --init
cd "$scriptdir"
post_cli_seperator
}
clean_old_builds() {
[[ "$build" != true ]] && return
cd "$scriptdir/$mainbuildir" || exit
echo "Deleting old build files"
[[ -d "./dist/64bit/compiler" ]] && rm -rf ./dist/64bit/compiler
[[ -d "./dist/64bit/BEE2" ]] && rm -rf ./dist/64bit/BEE2
post_cli_seperator
}
run_pyinstaller() {
cd "$scriptdir/$mainbuildir/src" || exit
echo "Checking for building requirements..."
if [[ "$build" != true ]] && [[ ! -f "$scriptdir/$mainbuildir/dist/64bit/BEE2/BEE2" ]]; then
echo "Building can't be skipped the first time. Forcing build..."
build=true
fi
if [[ "$build" == true ]]; then
pyinstaller --distpath ../dist/64bit/ --workpath ../build_tmp compiler.spec || {
echo "Compiler build failed. Ensure 'tk' is installed."
exit 1
}
pyinstaller --distpath ../dist/64bit/ --workpath ../build_tmp BEE2.spec || {
echo "BEE2 build failed."
[[ ! -f "$scriptdir/$mainbuildir/dist/64bit/BEE2/BEE2" ]] && echo "Can't find BEE. Closing..." && exit 1
echo "BEE found. Continuing anyway..."
}
else
echo "Building skipped (for testing). Continuing..."
fi
post_cli_seperator
}
download_packages() {
[[ $newpackages != true ]] && return
[[ "$dev" == true ]] && return
cd "$scriptdir" || exit
echo "Grabbing new packages..."
version_file="packages/items_version.txt"
mkdir -p bin
api_json=$(curl -s 'https://api.github.com/repos/BEEmod/BEE2-items/releases/latest')
release_id=$(echo "$api_json" | jq -r '.id')
# Check if we already have this version
if [[ -f "$version_file" && "$(cat "$version_file")" == "$release_id" ]]; then
echo "Latest packages already downloaded (release id: $release_id)"
else
echo "New release detected (release id: $release_id)"
ZIP_URLS=$(echo "$api_json" | jq -r '.assets[] | select(.name | endswith(".zip")) | .browser_download_url')
for url in $ZIP_URLS; do
filename=$(echo "$url" | awk -F'/' '{split($NF,a,"_"); printf "%s", a[3]; for(i=4;i<=length(a);i++) printf "_%s", a[i];}')
foldername=${filename%.*}
mkdir -p "$foldername"
curl -L -o "$foldername/$filename" "$url"
unzip -o "$foldername/$filename" -d "$foldername"
rm "$foldername/$filename"
done
echo "$release_id" > "$version_file"
echo "Packages downloaded"
fi
post_cli_seperator
}
download_dev_packages() {
[[ $newpackages != true ]] && return
[[ "$dev" != true ]] && return
cd "$scriptdir" || exit
echo "Grabbing new dev packages..."
local repo_url="https://github.com/BEEmod/BEE2-items.git"
if [[ -d "dev-items/.git" ]]; then
echo "Updating existing repo in dev-items"
cd "$scriptdir/dev-items" || exit
git fetch origin dev
git reset --hard "origin/dev"
[[ -n "$customver" ]] && git checkout "$customver"
git pull
else
echo "Cloning BEE2.4 repository"
git clone -b "dev" --recurse-submodules "$repo_url" temp
if [[ -n "$customver" ]]; then
echo "Checking out custom version: $customver"
cd "$scriptdir/temp" || exit
git checkout "$customver"
cd "$scriptdir"
fi
echo "Copying repo to dev-items"
mkdir -p "dev-items"
cp -rfa temp/. "dev-items"
rm -rf temp
fi
post_cli_seperator
}
download_dev_music_packages() {
[[ $newpackages != true ]] && return
[[ "$dev" != true ]] && return
cd "$scriptdir" || exit
echo "Grabbing new music dev packages..."
local music_repo_url="https://github.com/BEEmod/BEE2-music.git"
if [[ -d "music-dev-items/.git" ]]; then
echo "Updating existing repo in dev-items"
cd "$scriptdir/music-dev-items" || exit
git fetch origin dev
git reset --hard "origin/dev"
[[ -n "$customver" ]] && git checkout "$customver"
git pull
cd "$scriptdir"
else
echo "Cloning BEE2.4 repository"
git clone -b "dev" --recurse-submodules "$music_repo_url" temp
if [[ -n "$customver" ]]; then
echo "Checking out custom version: $customver"
cd "$scriptdir/temp" || exit
git checkout "$customver"
cd "$scriptdir"
fi
echo "Copying repo to dev-items"
mkdir -p "music-dev-items"
cp -rfa temp/. "music-dev-items"
rm -rf temp
fi
post_cli_seperator
}
build_packages() {
[[ "$dev" != true ]] && return
[[ "$pac_build" != true ]] && return
cd "$scriptdir/dev-items" || exit
echo "Building item packages..."
[[ "$pac_requirements" == true ]] && pip install -r requirements.txt
mkdir -p "zips/items"
python ./compile_packages.py imput . -c --zip --overwrite -o "zips/items"
mkdir -p "zips/music"
python ./compile_packages.py imput ../music-dev-items/. -c --zip --overwrite -o "zips/music"
post_cli_seperator
}
copy_pac_and_ffmpeg() {
cd "$scriptdir" || exit
mkdir -p "$mainbuildir/dist/64bit/BEE2/lib-64/"
for f in bin/*; do
ln -sf "$scriptdir/$f" "$mainbuildir/dist/64bit/BEE2/lib-64/"
done
if [[ "$dev" != true ]]; then
mkdir -p "$mainbuildir/dist/64bit/BEE2/packages/"
for f in packages/*; do
ln -sf "$scriptdir/$f" "$mainbuildir/dist/64bit/BEE2/packages/"
done
for f in music_packages/*; do
ln -sf "$scriptdir/$f" "$mainbuildir/dist/64bit/BEE2/packages/"
done
fi
if [[ "$dev" == true ]]; then
mkdir -p "$mainbuildir/dist/64bit/BEE2/packages/"
for f in dev-items/zips/items/*; do
ln -sf "$scriptdir/$f" "$mainbuildir/dist/64bit/BEE2/packages/"
done
for f in dev-items/zips/music/*; do
ln -sf "$scriptdir/$f" "$mainbuildir/dist/64bit/BEE2/packages/"
done
fi
}
create_shortcut_if_needed() {
cd "$scriptdir" || exit
[[ "$shortcut" != true ]] && return
echo "Creating shortcut..."
if [[ -f "$scriptdir/icon/BEE2.ico" ]]
then
geticon="$scriptdir/icon/BEE2.ico"
else
geticon="$scriptdir/$mainbuildir/dist/64bit/BEE2/bin/BEE2.ico"
fi
echo "[Desktop Entry]
Exec=$scriptdir/$mainbuildir/dist/64bit/BEE2/BEE2
GenericName=$mainbuildir
Icon=$geticon
Name=BEE2
Path=$scriptdir/$mainbuildir/dist/64bit/BEE2
StartupNotify=true
StartupWMClass=BEE2
Terminal=false
Type=Application" > $mainbuildir.desktop
echo "Shortcut created"
post_cli_seperator
}
create_link_if_needed() {
cd "$scriptdir" || exit
[[ "$distlink" != true ]] && return
echo "Making link..."
# Delete old link
rm "./LN-$mainbuildir"
# Create a symlink in the root (scriptdir) pointing to the BEE2 folder in the build dir
ln -sf "$mainbuildir/dist/64bit/BEE2" "./LN-$mainbuildir"
post_cli_seperator
}
run_bee_if_needed() {
cd "$scriptdir" || exit
[[ "$run" != true ]] && return
echo "Running BEE.."
cd "$mainbuildir/dist/64bit/BEE2/"
./BEE2
post_cli_seperator
}
main "$@"
Make it executable:
chmod +x bee-builder.sh
Click to see some Script Features
- Clones and updates BEE2.4 (dev or main)
- Builds BEE2 using PyInstaller (if needed)
- Auto-downloads FFmpeg (and only updates when changed)
- Auto-downloads package items (or pulls dev zips and builds them)
- Symlinks package items and ffmpeg instead of copying for efficiency
- Adds Linux puzzlemaker fix
- Creates a
.desktop
shortcut - Maintains a
LN-BEE2.4
symlink for easy access
Run the script so it asks for options:
./bee-builder.sh
Or even non-interactively with the same options:
./bee-builder.sh "0,2"
This will not ask and just do the defaults with nr 2.
Options:
-
0
β Use only CLI args (no interactive input) -
1
β Delete old build directory -
2
β Run BEE2 after build -
3
β Don't create shortcut -
4
β Disable pip update -
5
β Build from main branch instead of dev -
6
β Skip installing pip requirements -
7
β Skip package build requirements -
8
β Skip icon download -
9
β Skip Portal 2 Linux puzzlemaker fix -
10
β Use current FFmpeg -
11
β Use current item packages -
12
β Skip symlink creation -
13
β Skip package build (for testing) -
14
β Skip full build (for testing) -
15
β Exit immediately
-
You can link the created
.desktop
shortcut to your desktop or startmenu if you like -
At this point there is nothing to to but to use BEE2 to create some chambers