Merge pull request #3 from bcorfman/main

Onefile version
This commit is contained in:
csd4ni3l
2025-11-04 19:03:38 +01:00
committed by GitHub
4 changed files with 105 additions and 47 deletions

View File

@@ -8,7 +8,13 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest] include:
- os: ubuntu-22.04
platform: linux
python-version: "3.11"
- os: windows-latest
platform: windows
python-version: "3.11"
steps: steps:
- name: Check-out repository - name: Check-out repository
@@ -35,58 +41,78 @@ jobs:
disable-plugins: tk-inter,dill-compat,eventlet,gevent,pyqt5,pyqt6,pyside2,pyside6,delvewheel,pywebview,matplotlib,spacy,enum-compat,pbr-compat,gevent,pmw-freezer,transformers,upx,kivy,options-nanny,multiprocessing,gi disable-plugins: tk-inter,dill-compat,eventlet,gevent,pyqt5,pyqt6,pyside2,pyside6,delvewheel,pywebview,matplotlib,spacy,enum-compat,pbr-compat,gevent,pmw-freezer,transformers,upx,kivy,options-nanny,multiprocessing,gi
include-data-dir: assets=assets include-data-dir: assets=assets
include-data-files: CREDITS=CREDITS include-data-files: CREDITS=CREDITS
mode: standalone mode: onefile
output-file: GameOfLife output-file: GameOfLife
- name: Zip Build Output - name: Locate and rename executable (Linux)
shell: bash if: matrix.os == 'ubuntu-22.04'
run: | run: |
mkdir -p zip_output set -euo pipefail
if [ "${{ runner.os }}" = "Windows" ]; then echo "Searching for built Linux binary..."
powershell.exe -Command "Compress-Archive -Path 'build/run.dist/*' -DestinationPath 'zip_output/GameOfLife-${{ runner.os }}.zip'" # List to help debugging when paths change
else ls -laR . | head -n 500 || true
cd build/run.dist BIN=$(find . -maxdepth 4 -type f -name 'GameOfLife*' -perm -u+x | head -n1 || true)
zip -r "../../zip_output/GameOfLife-${{ runner.os }}.zip" . if [ -z "${BIN}" ]; then
echo "ERROR: No Linux binary found after build"
exit 1
fi fi
echo "Found: ${BIN}"
mkdir -p build_output
cp "${BIN}" build_output/GameOfLife.bin
chmod +x build_output/GameOfLife.bin
echo "Executable ready: build_output/GameOfLife.bin"
shell: bash
- name: Upload Zipped Build Artifact - name: Locate and rename executable (Windows)
if: matrix.os == 'windows-latest'
run: |
Write-Host "Searching for built Windows binary..."
Get-ChildItem -Recurse -File -Filter 'GameOfLife*.exe' | Select-Object -First 1 | ForEach-Object {
Write-Host ("Found: " + $_.FullName)
New-Item -ItemType Directory -Force -Path build_output | Out-Null
Copy-Item $_.FullName "build_output\GameOfLife.exe"
Write-Host "Executable ready: build_output\GameOfLife.exe"
}
if (!(Test-Path build_output\GameOfLife.exe)) {
Write-Error "ERROR: No Windows binary found after build"
exit 1
}
shell: pwsh
- name: Upload build artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: GameOfLife-${{ runner.os }}.zip name: ${{ matrix.platform }}
path: zip_output/GameOfLife-${{ runner.os }}.zip path: build_output/GameOfLife.*
release: release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Download All Zipped Builds - name: Download All Build Artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
path: downloads path: downloads
- name: Delete Old Release (if exists) - name: Create release (if missing) and upload artifacts to tag
continue-on-error: true
run: gh release delete latest -y
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Delete Git tag (if exists)
continue-on-error: true
run: | run: |
git push origin :refs/tags/latest set -euo pipefail
git tag -d latest TAG="${{ github.ref_name }}"
echo "Target release tag: $TAG"
- name: Recreate Git tag at HEAD if gh release view "$TAG" >/dev/null 2>&1; then
run: | echo "Release $TAG already exists; will upload assets with --clobber"
git tag latest else
git push origin latest gh release create "$TAG" \
--title "$TAG" \
- name: Create the new release --notes "Automated build for $TAG"
run: gh release create latest downloads/**/GameOfLife-*.zip --title "Latest Build" --notes "Most recent multi-platform builds of Game Of Life" fi
env: # Upload the executables directly (no zip files)
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} gh release upload "$TAG" downloads/linux/GameOfLife.bin --clobber
gh release upload "$TAG" downloads/windows/GameOfLife.exe --clobber

View File

@@ -76,7 +76,8 @@ class Game(arcade.gui.UIView):
self.save_button.on_click = lambda event: self.save() self.save_button.on_click = lambda event: self.save()
self.anchor.add(self.save_button, anchor_x="right", anchor_y="bottom", align_x=-5, align_y=5) self.anchor.add(self.save_button, anchor_x="right", anchor_y="bottom", align_x=-5, align_y=5)
if self.window.get_controllers(): # Check if window has controller support (ControllerWindow has get_controllers, regular Window doesn't)
if hasattr(self.window, 'get_controllers') and self.window.get_controllers():
self.spritelist = arcade.SpriteList() self.spritelist = arcade.SpriteList()
self.cursor_sprite = arcade.Sprite(cursor_texture) self.cursor_sprite = arcade.Sprite(cursor_texture)
self.spritelist.append(self.cursor_sprite) self.spritelist.append(self.cursor_sprite)

38
run.py
View File

@@ -1,8 +1,15 @@
import pyglet import pyglet
pyglet.options['shadow_window'] = False # Fix double window issue on Wayland
pyglet.options.debug_gl = False pyglet.options.debug_gl = False
import logging, datetime, os, json, sys, arcade import logging, datetime, os, json, sys, arcade, platform
# Set up paths BEFORE importing modules that load assets
script_dir = os.path.dirname(os.path.abspath(__file__))
pyglet.resource.path.append(script_dir)
pyglet.font.add_directory(os.path.join(script_dir, 'assets', 'fonts'))
from utils.utils import get_closest_resolution, print_debug_info, on_exception from utils.utils import get_closest_resolution, print_debug_info, on_exception
from utils.constants import log_dir, menu_background_color from utils.constants import log_dir, menu_background_color
@@ -12,9 +19,6 @@ from arcade.experimental.controller_window import ControllerWindow
sys.excepthook = on_exception sys.excepthook = on_exception
pyglet.resource.path.append(os.getcwd())
pyglet.font.add_directory('./assets/fonts')
__builtins__.print = lambda *args, **kwargs: logging.debug(" ".join(map(str, args))) __builtins__.print = lambda *args, **kwargs: logging.debug(" ".join(map(str, args)))
if not log_dir in os.listdir(): if not log_dir in os.listdir():
@@ -44,6 +48,13 @@ if os.path.exists('settings.json'):
else: else:
antialiasing = 0 antialiasing = 0
# Wayland workaround (can be overridden with environment variable)
if (platform.system() == "Linux" and
os.environ.get("WAYLAND_DISPLAY") and
not os.environ.get("ARCADE_FORCE_MSAA")):
logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)")
antialiasing = 0
fullscreen = settings['window_mode'] == 'Fullscreen' fullscreen = settings['window_mode'] == 'Fullscreen'
style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT
vsync = settings['vsync'] vsync = settings['vsync']
@@ -51,6 +62,14 @@ if os.path.exists('settings.json'):
else: else:
resolution = get_closest_resolution() resolution = get_closest_resolution()
antialiasing = 4 antialiasing = 4
# Wayland workaround (can be overridden with environment variable)
if (platform.system() == "Linux" and
os.environ.get("WAYLAND_DISPLAY") and
not os.environ.get("ARCADE_FORCE_MSAA")):
logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)")
antialiasing = 0
fullscreen = False fullscreen = False
style = arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_DEFAULT
vsync = True vsync = True
@@ -73,8 +92,12 @@ else:
if settings.get("music", True): if settings.get("music", True):
theme_sound.play(volume=settings.get("music_volume", 50) / 100, loop=True) theme_sound.play(volume=settings.get("music_volume", 50) / 100, loop=True)
window = ControllerWindow(width=resolution[0], height=resolution[1], title='Game Of Life', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style) try:
window = ControllerWindow(width=resolution[0], height=resolution[1], title='Game Of Life', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
except (FileNotFoundError, PermissionError) as e:
logging.warning(f"Controller support unavailable: {e}. Falling back to regular window.")
window = arcade.Window(width=resolution[0], height=resolution[1], title='Game Of Life', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
if vsync: if vsync:
window.set_vsync(True) window.set_vsync(True)
display_mode = window.display.get_default_screen().get_mode() display_mode = window.display.get_default_screen().get_mode()
@@ -98,6 +121,9 @@ main = Main()
window.show_view(main) window.show_view(main)
# Make window visible after all setup is complete (helps prevent double window on Wayland)
window.set_visible(True)
logging.debug('Game started.') logging.debug('Game started.')
arcade.run() arcade.run()

View File

@@ -1,9 +1,14 @@
import arcade.gui, arcade import arcade.gui, arcade
import os
button_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button.png")) # Get the directory where this module is located
button_hovered_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button_hovered.png")) _module_dir = os.path.dirname(os.path.abspath(__file__))
cursor_texture = arcade.load_texture("assets/graphics/cursor.png") _assets_dir = os.path.join(os.path.dirname(_module_dir), 'assets')
create_sound = arcade.Sound("assets/sound/create.mp3") button_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture(os.path.join(_assets_dir, 'graphics', 'button.png')))
destroy_sound = arcade.Sound("assets/sound/destroy.mp3") button_hovered_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture(os.path.join(_assets_dir, 'graphics', 'button_hovered.png')))
theme_sound = arcade.Sound("assets/sound/music.mp3") cursor_texture = arcade.load_texture(os.path.join(_assets_dir, 'graphics', 'cursor.png'))
create_sound = arcade.Sound(os.path.join(_assets_dir, 'sound', 'create.mp3'))
destroy_sound = arcade.Sound(os.path.join(_assets_dir, 'sound', 'destroy.mp3'))
theme_sound = arcade.Sound(os.path.join(_assets_dir, 'sound', 'music.mp3'))