Fix id-namespace regression, add StrikeRequest type, kill full-panel
rerender on assign/alive/shell, add Windows build tooling - Board.add_target/add_ally's id auto-assignment used a bare next(c for c in string.ascii_uppercase if c not in used), which raises StopIteration once 26 entities of a group exist -- a real crash confirmed via a live traceback, and a direct regression from moving that sequence from per-type to per-group. This was the actual cause of "Accept as"/"Accept all" silently doing nothing. Fixed with _next_free_id(), which rolls over to two-letter ids instead of raising. - New TargetType.STRIKE_REQUEST: the bearing/distance-offset "taking fire" fire-support request (see the earlier two-entity split) now creates this instead of reusing STRIKE, so a radioed-in request is never confused with a strike the player placed themselves. Same crosshair icon, excluded from type pickers/dedupe like STRIKE. - The "Accept as..." popover on a detected map marker now uses the same icon grid the entity-edit "Change type" popover does (was a plain unfiltered text list of every TargetType, which also wrongly offered STRIKE/STRIKE_REQUEST as pickable). - Firing panel: _cycle_assignment/_toggle_alive/_pick_shell no longer route through app.py's full solver+dedupe+canvas+panel refresh -- none of the three can affect the solver or dedupe, and none change which cards exist or their order (except _toggle_alive in hide/sort_later mode). New FiringPanel._rebuild_one() rebuilds just the one changed card; on_visual_change is a new, lighter callback (just a map redraw) for the two of these three that actually affect it. This was a real, confirmed lag source with many units on the board: every click on any of these was previously rebuilding every card of every target. - Map right-click entity menu: added "Mark destroyed"/"Mark alive", reusing the same cheap-refresh path (new FiringPanel.refresh_after_alive_change). - packaging/windows/: a from-scratch (untested against a real boot) MSYS2 + WiX .msi build pipeline for Windows, driven from Linux via dockur/windows (KVM-in-container), no Windows machine or GitHub required. See its own README for status/caveats. - New/updated tests: id-namespace sharing + the 26-entity overflow regression (tests/test_models.py), StrikeRequest split (tests/test_ocr.py), warp_to_map's img_scale param (tests/test_map_vision_warp.py). 44/44 passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
@echo off
|
||||
REM Actual per-build packaging. Triggered by watch_build.bat once
|
||||
REM install.bat's one-time provisioning has already put MSYS2/GTK4/
|
||||
REM libadwaita/WiX in place. Reads source from Z:\src, writes
|
||||
REM FEnigma-<version>.msi to Z:\dist, and Z:\BUILD_DONE (or
|
||||
REM Z:\BUILD_FAILED, with the log copied alongside it) when finished.
|
||||
REM
|
||||
REM UNTESTED (see install.bat's note) -- the WiX harvest/link step in
|
||||
REM particular is likely to need iteration: bulk-copying all of
|
||||
REM mingw64\ is the "make it work first" approach, not a lean one, and
|
||||
REM heat.exe's default harvest options may need tuning to actually
|
||||
REM produce a working component set for a tree this size.
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
set LOG=Z:\build.log
|
||||
echo [build.bat] starting > %LOG%
|
||||
|
||||
if exist Z:\BUILD_VERSION (
|
||||
set /p APPVER=<Z:\BUILD_VERSION
|
||||
) else (
|
||||
set APPVER=0.1.0
|
||||
)
|
||||
echo [build.bat] version %APPVER% >> %LOG%
|
||||
|
||||
rd /s /q C:\build 2>nul
|
||||
mkdir C:\build\src
|
||||
mkdir C:\build\dist\src
|
||||
mkdir C:\build\dist\mingw64
|
||||
|
||||
echo [build.bat] copying source from Z:\src ... >> %LOG%
|
||||
xcopy /e /i /q Z:\src C:\build\src >> %LOG% 2>&1
|
||||
|
||||
echo [build.bat] sanity import check ... >> %LOG%
|
||||
set PYTHONPATH=C:\build\src\src
|
||||
C:\msys64\mingw64\bin\python3.exe -c "import fenigma.app" >> %LOG% 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [build.bat] FAILED: fenigma.app failed to import, see log >> %LOG%
|
||||
copy %LOG% Z:\build.log.failed >nul
|
||||
echo FAILED > Z:\BUILD_FAILED
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [build.bat] assembling dist tree ... >> %LOG%
|
||||
xcopy /e /i /q C:\build\src\src C:\build\dist\src >> %LOG% 2>&1
|
||||
REM Bulk-copy the whole mingw64 runtime rather than hand-tracing the DLL/
|
||||
REM typelib/icon-theme/schema dependency closure -- bloated (likely 1GB+)
|
||||
REM but reliable; trimming this down is a known follow-up, not attempted
|
||||
REM here (see this file's top-of-file note).
|
||||
robocopy C:\msys64\mingw64 C:\build\dist\mingw64 /e /xd include share\doc share\man share\gtk-doc /nfl /ndl /njh /njs >> %LOG% 2>&1
|
||||
|
||||
echo [build.bat] harvesting WiX components ... >> %LOG%
|
||||
C:\wix\heat.exe dir C:\build\dist -cg AppFiles -gg -scom -sreg -sfrag -srd -sw5150 -dr INSTALLFOLDER -var var.DistDir -out C:\build\files.wxs >> %LOG% 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [build.bat] FAILED: heat.exe harvest failed >> %LOG%
|
||||
copy %LOG% Z:\build.log.failed >nul
|
||||
echo FAILED > Z:\BUILD_FAILED
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
copy /y C:\OEM\product.wxs C:\build\product.wxs >nul
|
||||
|
||||
echo [build.bat] compiling (candle) ... >> %LOG%
|
||||
C:\wix\candle.exe -dDistDir=C:\build\dist -dAppVersion=%APPVER% -out C:\build\ C:\build\product.wxs C:\build\files.wxs >> %LOG% 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [build.bat] FAILED: candle.exe failed >> %LOG%
|
||||
copy %LOG% Z:\build.log.failed >nul
|
||||
echo FAILED > Z:\BUILD_FAILED
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [build.bat] linking (light) ... >> %LOG%
|
||||
C:\wix\light.exe -ext WixUIExtension -sice:ICE60 -sice:ICE61 -out C:\build\FEnigma-%APPVER%.msi C:\build\product.wixobj C:\build\files.wixobj >> %LOG% 2>&1
|
||||
if errorlevel 1 (
|
||||
echo [build.bat] FAILED: light.exe failed >> %LOG%
|
||||
copy %LOG% Z:\build.log.failed >nul
|
||||
echo FAILED > Z:\BUILD_FAILED
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist Z:\dist mkdir Z:\dist
|
||||
copy /y C:\build\FEnigma-%APPVER%.msi Z:\dist\ >> %LOG% 2>&1
|
||||
copy /y %LOG% Z:\dist\build.log >nul
|
||||
|
||||
echo [build.bat] done >> %LOG%
|
||||
echo DONE > Z:\BUILD_DONE
|
||||
endlocal
|
||||
@@ -0,0 +1,81 @@
|
||||
@echo off
|
||||
REM One-time provisioning, auto-run by dockur/windows during the final step
|
||||
REM of Windows's own unattended setup (see its README's /oem mechanism).
|
||||
REM Everything here happens exactly once and lands on the VM's persistent
|
||||
REM disk -- later builds just boot this already-provisioned VM and run
|
||||
REM build.bat, no re-provisioning.
|
||||
REM
|
||||
REM UNTESTED end to end: written from MSYS2's documented CI bootstrap
|
||||
REM sequence (the same one msys2/setup-msys2 uses) and WiX's own docs, not
|
||||
REM verified against a live dockur/windows boot. Expect to debug this on
|
||||
REM the actual first run -- watch it happen at http://localhost:8006.
|
||||
REM
|
||||
REM Every step also echoes to Z:\install_progress.log (best-effort, only
|
||||
REM if the Z:\ shared drive happens to be up already at this point in
|
||||
REM setup) purely so build_windows.sh on the host has SOMETHING to show
|
||||
REM besides silence during the one-time provisioning run.
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
call :log "starting FEnigma build-VM provisioning"
|
||||
|
||||
REM -- MSYS2: the "base" self-extracting archive, not the GUI installer --
|
||||
REM (the GUI installer has no reliable non-interactive/silent flag across
|
||||
REM versions; the base sfx archive is what CI pipelines actually use).
|
||||
REM Discover the current filename by scraping the repo listing, since it's
|
||||
REM datestamped and there's no stable "latest" URL.
|
||||
call :log "finding current MSYS2 base archive..."
|
||||
powershell -NoProfile -Command ^
|
||||
"$ProgressPreference='SilentlyContinue';" ^
|
||||
"$html = Invoke-WebRequest -Uri 'https://repo.msys2.org/distrib/x86_64/' -UseBasicParsing;" ^
|
||||
"$name = ($html.Links | Where-Object { $_.href -match '^msys2-base-x86_64-.*\.sfx\.exe$' } | Select-Object -Last 1).href;" ^
|
||||
"Invoke-WebRequest -Uri ('https://repo.msys2.org/distrib/x86_64/' + $name) -OutFile 'C:\msys2-base.sfx.exe' -UseBasicParsing"
|
||||
if not exist C:\msys2-base.sfx.exe (
|
||||
call :log "FAILED: could not download MSYS2 base archive"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :log "extracting MSYS2 to C:\msys64 ..."
|
||||
C:\msys2-base.sfx.exe -y -oC:\ >> C:\OEM\install.log 2>&1
|
||||
del C:\msys2-base.sfx.exe
|
||||
|
||||
REM First bash launch finalizes the base install and kills itself off
|
||||
REM mid-update (documented MSYS2 behavior) -- run it, ignore its exit
|
||||
REM code, then run the real update.
|
||||
call :log "bootstrapping MSYS2 (pacman -Syuu, twice) ..."
|
||||
C:\msys64\usr\bin\bash.exe -lc "exit 0" >> C:\OEM\install.log 2>&1
|
||||
C:\msys64\usr\bin\bash.exe -lc "pacman -Syuu --noconfirm" >> C:\OEM\install.log 2>&1
|
||||
C:\msys64\usr\bin\bash.exe -lc "pacman -Syuu --noconfirm" >> C:\OEM\install.log 2>&1
|
||||
|
||||
call :log "installing GTK4/libadwaita/PyGObject/build deps ..."
|
||||
C:\msys64\usr\bin\bash.exe -lc "pacman -S --noconfirm --needed mingw-w64-x86_64-python mingw-w64-x86_64-python-pip mingw-w64-x86_64-python-gobject mingw-w64-x86_64-gtk4 mingw-w64-x86_64-libadwaita mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-pillow mingw-w64-x86_64-opencv mingw-w64-x86_64-tesseract-ocr" >> C:\OEM\install.log 2>&1
|
||||
|
||||
call :log "pip install pytesseract (pure python, no wheel needed) ..."
|
||||
C:\msys64\mingw64\bin\python3.exe -m pip install pytesseract >> C:\OEM\install.log 2>&1
|
||||
|
||||
REM -- WiX v3 toolset (candle/light/heat), a plain zip of standalone exes,
|
||||
REM no installer needed. Fixed versioned URL, no scraping required.
|
||||
call :log "fetching WiX v3.11 ..."
|
||||
powershell -NoProfile -Command ^
|
||||
"$ProgressPreference='SilentlyContinue';" ^
|
||||
"Invoke-WebRequest -Uri 'https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip' -OutFile 'C:\wix311-binaries.zip' -UseBasicParsing;" ^
|
||||
"Expand-Archive -Path 'C:\wix311-binaries.zip' -DestinationPath 'C:\wix' -Force"
|
||||
del C:\wix311-binaries.zip
|
||||
|
||||
REM -- Register the build watcher to run at every boot from here on, plus
|
||||
REM kick it off right now too (ONSTART won't retroactively fire for this
|
||||
REM already-in-progress boot). Runs as SYSTEM so it works with no user
|
||||
REM logged in.
|
||||
call :log "registering build watcher ..."
|
||||
schtasks /create /tn "FenigmaBuildWatcher" /sc onstart /ru SYSTEM /rl HIGHEST /tr "C:\OEM\watch_build.bat" /f >> C:\OEM\install.log 2>&1
|
||||
start "" cmd /c C:\OEM\watch_build.bat
|
||||
|
||||
call :log "provisioning done"
|
||||
echo DONE > C:\OEM\provisioned.marker
|
||||
if exist Z:\ echo DONE > Z:\PROVISIONED
|
||||
endlocal
|
||||
exit /b 0
|
||||
|
||||
:log
|
||||
echo [install.bat] %~1 >> C:\OEM\install.log
|
||||
if exist Z:\ echo [install.bat] %~1 >> Z:\install_progress.log
|
||||
exit /b 0
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Hand-authored shell: directory layout, the Start Menu shortcut, and the
|
||||
PYTHONPATH environment variable the shortcut relies on (see run.sh's
|
||||
equivalent `PYTHONPATH=src python -m fenigma.app`). The actual app/
|
||||
runtime files are a separate auto-harvested fragment (files.wxs, built
|
||||
by heat.exe in build.bat) referenced here only by its ComponentGroup id.
|
||||
|
||||
UpgradeCode below is a fixed, generated-once GUID: DO NOT regenerate
|
||||
it, that's what lets a newer .msi upgrade an older install in place
|
||||
instead of installing side by side. ProductCode is left as "*" (auto-
|
||||
generated per build), which is the normal WiX pattern.
|
||||
|
||||
UNTESTED (see build.bat's top-of-file note).
|
||||
-->
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Id="*"
|
||||
Name="FEnigma"
|
||||
Language="1033"
|
||||
Version="$(var.AppVersion)"
|
||||
Manufacturer="FEnigma"
|
||||
UpgradeCode="DAB672A3-9E27-4F3F-8251-0AACD6E57B94">
|
||||
|
||||
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
||||
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of FEnigma is already installed." />
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="FEnigma" />
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="FEnigma" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<!-- AppFiles (all of dist\mingw64 + dist\src, harvested by heat.exe
|
||||
into files.wxs) is referenced by id only: its actual file list
|
||||
lives in that generated fragment, not here. -->
|
||||
<Feature Id="MainFeature" Title="FEnigma" Level="1">
|
||||
<ComponentGroupRef Id="AppFiles" />
|
||||
<ComponentRef Id="ApplicationShortcutComponent" />
|
||||
</Feature>
|
||||
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="ApplicationShortcutComponent" Guid="*">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
Name="FEnigma"
|
||||
Description="Screen-reading helper for IRON NEST: Heavy Turret Simulator"
|
||||
Target="[INSTALLFOLDER]mingw64\bin\pythonw.exe"
|
||||
Arguments="-m fenigma.app"
|
||||
WorkingDirectory="INSTALLFOLDER" />
|
||||
<RemoveFolder Id="CleanUpShortcut" On="uninstall" />
|
||||
<!-- Machine-wide PYTHONPATH so the bundled mingw64\bin\pythonw.exe
|
||||
(which knows nothing about this app on its own) can find the
|
||||
fenigma package: same role run.sh's env var plays on Linux.
|
||||
Permanent="no": removed again on uninstall. -->
|
||||
<Environment Id="PythonPathEnv" Name="PYTHONPATH" Value="[INSTALLFOLDER]src"
|
||||
Permanent="no" Action="set" System="yes" Part="last" />
|
||||
<RegistryValue Root="HKCU" Key="Software\FEnigma" Name="installed" Type="integer" Value="1" KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- WixUI_Minimal: no EULA screen, so no WixUILicenseRtf override needed. -->
|
||||
<UIRef Id="WixUI_Minimal" />
|
||||
</Product>
|
||||
</Wix>
|
||||
@@ -0,0 +1,31 @@
|
||||
@echo off
|
||||
REM Runs persistently from system boot (see install.bat's scheduled task).
|
||||
REM Polls the host-shared Z:\ drive for a build request and, when one
|
||||
REM shows up, runs build.bat against it. This is what lets build_windows.sh
|
||||
REM on the Linux host trigger a build without any RDP/remote-exec: it's
|
||||
REM all just files dropped on the shared folder in both directions.
|
||||
REM
|
||||
REM UNTESTED (see install.bat's note).
|
||||
|
||||
:wait_for_share
|
||||
if not exist Z:\ (
|
||||
timeout /t 5 /nobreak >nul
|
||||
goto wait_for_share
|
||||
)
|
||||
|
||||
:loop
|
||||
if exist Z:\BUILD_REQUEST (
|
||||
REM Claim the request before acting on it -- if watch_build.bat somehow
|
||||
REM ended up running twice this boot (install.bat starts it once
|
||||
REM immediately, the ONSTART task could also fire the same boot), only
|
||||
REM one of them wins this move and actually builds.
|
||||
move /y Z:\BUILD_REQUEST Z:\BUILD_REQUEST.claimed >nul 2>&1
|
||||
if exist Z:\BUILD_REQUEST.claimed (
|
||||
del Z:\BUILD_REQUEST.claimed
|
||||
del /q Z:\BUILD_DONE 2>nul
|
||||
del /q Z:\BUILD_FAILED 2>nul
|
||||
call C:\OEM\build.bat
|
||||
)
|
||||
)
|
||||
timeout /t 5 /nobreak >nul
|
||||
goto loop
|
||||
Reference in New Issue
Block a user