diff --git a/.gitea/workflows/gitea-build.yaml b/.gitea/workflows/gitea-build.yaml index f94ed3b..dda2fd5 100644 --- a/.gitea/workflows/gitea-build.yaml +++ b/.gitea/workflows/gitea-build.yaml @@ -83,6 +83,11 @@ jobs: godot --headless --path . --import godot --headless --path . --export-release "Windows Desktop" "build/${PROJECT_NAME}_${GAME_VERSION}.exe" godot --headless --path . --export-release "Linux" "build/${PROJECT_NAME}_${GAME_VERSION}.x86_64" + + # Linux binaries can't hold an icon; the package adds it with a menu entry and installer + - name: Package Linux + run: | + linux/package.sh "build/${PROJECT_NAME}_${GAME_VERSION}.x86_64" "$GAME_VERSION" build ls -la build - name: Upload Artifacts diff --git a/RELEASING.md b/RELEASING.md index d043822..e142d53 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -11,10 +11,19 @@ same version in the About window. The `Build` workflow (`.gitea/workflows/gitea-build.yaml`) then: 1. runs the tests, -2. exports the Windows and Linux builds, +2. exports the Windows and Linux builds, and packages Linux as + `MusicPlayer__linux.tar.gz` with the icon, an app menu entry and `install.sh` + (Linux programs can't carry their own icon the way a Windows .exe does), 3. if no tag exists for the version yet: uploads the builds to the package registry under `MusicPlayer//`, and creates the `` tag and Gitea release with the builds attached. Pushes that don't change the version, and pushes to `develop`, only test and export. Don't create tags or `1.x.y` branches by hand; the workflow makes the tag. + +## Installing on Linux + +Unpack `MusicPlayer__linux.tar.gz` and run `./install.sh` inside the +`MusicPlayer` folder. It installs the game to `~/.local/bin` and adds it, with its icon, +to the app menu. The bare `MusicPlayer_.x86_64` is still published for anyone +who just wants to run the file. diff --git a/export_presets.cfg b/export_presets.cfg index b3391b5..86ddd90 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -8,7 +8,7 @@ dedicated_server=false custom_features="" export_filter="all_resources" include_filter="" -exclude_filter="tests/*, addons/gdUnit4/*" +exclude_filter="tests/*, addons/gdUnit4/*, linux/*" export_path="../../MusicPlayer_1.5.1.exe" patches=PackedStringArray() encryption_include_filters="" @@ -83,7 +83,7 @@ dedicated_server=false custom_features="" export_filter="all_resources" include_filter="" -exclude_filter="tests/*, addons/gdUnit4/*" +exclude_filter="tests/*, addons/gdUnit4/*, linux/*" export_path="./MusicPlayer_0.9_Beta.dmg" patches=PackedStringArray() encryption_include_filters="" @@ -343,7 +343,7 @@ dedicated_server=false custom_features="" export_filter="all_resources" include_filter="" -exclude_filter="tests/*, addons/gdUnit4/*" +exclude_filter="tests/*, addons/gdUnit4/*, linux/*" export_path="web/index.html" patches=PackedStringArray() encryption_include_filters="" @@ -390,7 +390,7 @@ dedicated_server=false custom_features="" export_filter="all_resources" include_filter="" -exclude_filter="tests/*, addons/gdUnit4/*" +exclude_filter="tests/*, addons/gdUnit4/*, linux/*" export_path="./MusicPlayer_1.5.1.x86_64" patches=PackedStringArray() encryption_include_filters="" diff --git a/icon.svg b/icon.svg index adc26df..c60f5f1 100644 --- a/icon.svg +++ b/icon.svg @@ -1 +1,116 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icon.svg.import b/icon.svg.import index 187e4e3..b43d438 100644 --- a/icon.svg.import +++ b/icon.svg.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/linux/install.sh b/linux/install.sh new file mode 100755 index 0000000..b249216 --- /dev/null +++ b/linux/install.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Installs Music Player Randomizer for the current user, with an app menu entry and icon. +# Run it from the unpacked release folder: ./install.sh + +set -euo pipefail +cd "$(dirname "$0")" + +bin_dir="${XDG_BIN_HOME:-$HOME/.local/bin}" +data_dir="${XDG_DATA_HOME:-$HOME/.local/share}" +binary="$bin_dir/music-player-randomizer" + +icon_dir="$data_dir/icons/hicolor/256x256/apps" +mkdir -p "$bin_dir" "$icon_dir" "$data_dir/applications" +install -m755 MusicPlayer.x86_64 "$binary" +install -m644 music-player-randomizer.png "$icon_dir/music-player-randomizer.png" +# The menu entry needs the full path, since ~/.local/bin is not always on PATH +sed "s|^Exec=.*|Exec=$binary|" music-player-randomizer.desktop \ + > "$data_dir/applications/music-player-randomizer.desktop" + +# Refresh menu and icon caches where the tools exist; most desktops also notice on their own +command -v update-desktop-database > /dev/null \ + && update-desktop-database "$data_dir/applications" || true +command -v gtk-update-icon-cache > /dev/null \ + && gtk-update-icon-cache -q "$data_dir/icons/hicolor" || true + +echo "Installed. Start it from your app menu, or run: $binary" +echo "To uninstall, delete:" +echo " $binary" +echo " $data_dir/applications/music-player-randomizer.desktop" +echo " $icon_dir/music-player-randomizer.png" diff --git a/linux/music-player-randomizer.desktop b/linux/music-player-randomizer.desktop new file mode 100644 index 0000000..911aa4d --- /dev/null +++ b/linux/music-player-randomizer.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Music Player Randomizer +Comment=Guess the video game soundtrack with your friends +Exec=music-player-randomizer +Icon=music-player-randomizer +Terminal=false +Categories=Game;Audio; diff --git a/linux/package.sh b/linux/package.sh new file mode 100755 index 0000000..934c720 --- /dev/null +++ b/linux/package.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Packages an exported Linux build with its icon, app menu entry and install script. +# +# Usage: linux/package.sh +# Creates /MusicPlayer__linux.tar.gz. Godot is taken from $GODOT, +# or `godot` on PATH, to render the icon. + +set -euo pipefail + +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi +binary="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" +version="$2" +out_dir="$(mkdir -p "$3" && cd "$3" && pwd)" +linux_dir="$(cd "$(dirname "$0")" && pwd)" +project_dir="$(dirname "$linux_dir")" +godot_bin="${GODOT:-godot}" + +staging="$(mktemp -d)" +trap 'rm -rf "$staging"' EXIT +package_dir="$staging/MusicPlayer" +mkdir -p "$package_dir" + +install -m755 "$binary" "$package_dir/MusicPlayer.x86_64" +install -m755 "$linux_dir/install.sh" "$package_dir/install.sh" +install -m644 "$linux_dir/music-player-randomizer.desktop" "$package_dir/" +# Run outside the project so Godot only renders the icon +(cd "$staging" && "$godot_bin" --headless -s "$linux_dir/render_icon.gd" -- \ + "$project_dir/icon.svg" "$package_dir/music-player-randomizer.png" 256) + +archive="$out_dir/MusicPlayer_${version}_linux.tar.gz" +tar -czf "$archive" -C "$staging" MusicPlayer +echo "Created $archive" diff --git a/linux/render_icon.gd b/linux/render_icon.gd new file mode 100644 index 0000000..ceab257 --- /dev/null +++ b/linux/render_icon.gd @@ -0,0 +1,24 @@ +extends SceneTree +## Renders an SVG to a square PNG, used to package the icon for Linux. +## +## Usage: godot --headless -s render_icon.gd -- + + +func _initialize() -> void: + var args: PackedStringArray = OS.get_cmdline_user_args() + if args.size() != 3: + printerr("Usage: godot --headless -s render_icon.gd -- ") + quit(1) + return + var svg: String = FileAccess.get_file_as_string(args[0]) + var source: Image = Image.new() + # Load at 1x to read the SVG's own size, then scale so the result is pixels + var error: int = source.load_svg_from_string(svg) + if error == OK: + var image: Image = Image.new() + error = image.load_svg_from_string(svg, float(args[2]) / source.get_width()) + if error == OK: + error = image.save_png(args[1]) + if error != OK: + printerr("Could not render %s to %s (error %d)" % [args[0], args[1], error]) + quit(error) diff --git a/linux/render_icon.gd.uid b/linux/render_icon.gd.uid new file mode 100644 index 0000000..ee4934c --- /dev/null +++ b/linux/render_icon.gd.uid @@ -0,0 +1 @@ +uid://b51oybupn7ns7