* Clean up C++ test warnings
* Keep player setup expected map size readable
* Declare direct Bazel module dependencies (#7069)
* Check Shardok map proto parse results
* Prepare Apple and protobuf module resolution
* Declare direct Bazel module dependencies
* Decouple non-Mac builds from Xcode via BAZEL_NO_APPLE_CPP_TOOLCHAIN
Set BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 in .bazelrc so apple_support skips
Xcode detection (xcode-locator) entirely for Scala/C++/Go builds.
Only mactools builds (Mac/Sparkle) re-enable the Apple CC toolchain.
This means Xcode version changes no longer affect Eagle, Shardok, test,
auth, docker, or Unity Windows builds — no expunge, no cache invalidation,
no rebuild. The sync script and its expunge/cache-key logic are now scoped
to mactools only and removed from all non-Mac CI workflows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert BAZEL_NO_APPLE_CPP_TOOLCHAIN; keep Apple CC with stable environ
BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 broke builds because apple_support
registers platforms (e.g. macos_x86_64) that require Apple CC toolchains
for resolution — even in non-Mac builds.
Instead, keep Apple CC enabled but ensure its repo rule never
re-evaluates on non-Mac builds by keeping DEVELOPER_DIR pinned in
common:macos. The sync script only runs for mactools builds and
overrides DEVELOPER_DIR there.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The grpc 1.78.0 upgrade eliminated the rules_swift version conflict
that required SparklePlugin to live in a separate Bazel workspace.
Move it back into the main workspace under src/main/objc/.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
grpc 1.78.0 dropped its rules_swift dependency, resolving the version
conflict (rules_swift 3.x vs 2.x) that forced SparklePlugin into a
separate workspace. This removes the rules_swift single_version_override.
Includes a patch for a known BCR bug (grpc/grpc#41438) where grpc 1.78.0
requests Python 3.14.0b2 but rules_python >= 1.6.0 replaced it with 3.14.
The patch backports the upstream fix (commit 459279b).
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Aligns pinned versions with what the dependency graph was already resolving,
eliminating the --check_direct_dependencies warnings.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Replace dotnet proto build with Bazel-hermetic C# proto generation
Use rules_proto_grpc_csharp to generate .cs source files via Bazel
instead of requiring a locally-installed dotnet SDK to build protos.dll.
This eliminates the dotnet dependency for CI runners and makes the
proto build fully hermetic.
- Add bazel_dep for rules_proto_grpc_csharp 5.8.0
- Bump grpc 1.74.0 -> 1.74.1 (required by rules_proto_grpc_csharp)
- Add csharp_proto_compile/csharp_grpc_compile targets across 8 proto
BUILD.bazel files (81 .cs files from 78 protos)
- Rewrite build_protos.sh to use bazel build + copy instead of dotnet
- Output goes to Assets/GeneratedProtos/ with package-based subdirs
to avoid filename collisions (e.g. ActionResultView.cs in both
shardok/api/ and eagle/views/)
- Add Assets/GeneratedProtos/ to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove stale Eagle0Protos DLL references
The old build_protos.sh generated protos.dll via dotnet; the new one
generates .cs source files instead. Remove the tracked .meta and
.deps.json files that reference the no-longer-generated DLL, and
gitignore the directory to prevent re-adding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add design doc for programmatic map image generation
Documents the plan to:
- Create a Python tool for regenerating map images with more equalized province sizes
- Implement dynamic province name rendering that scales with zoom
- Preserve topology and neighbor relationships while enlarging small provinces
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Python map generator tool for province size equalization
Implements Part 1 of the programmatic map generation plan:
- Python script using weighted dilation to grow small provinces
- Preserves jagged/natural edges using noise fields
- Allows small provinces to claim limited ocean pixels (20% max)
- Maintains neighbor relationships and realistic coastlines
Results:
- Province size ratio reduced from 110x to 22x
- Smallest province (Kojaria) grew 5x (3,262 → 16,590 pixels)
- All 43 province masks and centroids.json generated
Usage:
cd tools/map_generator
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python generate_map.py --generate
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add ProvinceLabelsController for dynamic map labels
Part 2 of programmatic map generation - dynamic province name rendering:
- Creates TextMeshPro labels at province centroids
- Labels scale inversely with zoom level for readability
- Only visible when zoom >= 1.5x (configurable)
- Font size scales with province area for visual hierarchy
- Includes centroids.json with province positions
To use:
1. Add ProvinceLabelsController component to a GameObject in the scene
2. Assign zoomHandler, mapContent (RectTransform), and centroidsJson TextAsset
3. Optionally configure font, colors, and zoom threshold
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add topology documentation and tendril removal to map generator
- Document new province adjacencies caused by equalization
- Add priority_provinces parameter for boosting specific province growth
- Add remove_tendrils() post-processing to eliminate thin extensions
- Note: Will be restructured to use Bazel in next commit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Bazel Python support and parallelized map generator
- Add rules_python to MODULE.bazel with Python 3.13
- Move map generator to src/main/python/net/eagle0/eagle/
- Add parallel processing using ProcessPoolExecutor
- Provinces are now grown in parallel across multiple CPU cores
Usage:
bazel run //src/main/python/net/eagle0/eagle:map_generator -- --generate
bazel run //src/main/python/net/eagle0/eagle:map_generator -- --workers 8
The old tools/map_generator/ script remains for reference but the Bazel
version is now the primary implementation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Rust map generator and equalized province maps
- Add Rust map generator (src/main/rust/net/eagle0/eagle/map_generator/)
- Uses parallel graph coloring for non-adjacent province processing
- Generates equalized province sizes (smallest ~7x larger than before)
- Outputs: rawGray.gz.bytes, map_bw.png, province masks, centroids.json
- Run with: bazel run //src/main/rust/net/eagle0/eagle/map_generator -- --generate
- Update province_map.tsv with neighbor changes from equalization:
- Usvol gains: Laufarvia, Hella, Grytrand
- Chapellia loses Nikemi, gains Kojaria
- Kojaria gains Motcia, Chapellia
- Motcia gains Kojaria
- Update Unity assets with equalized map:
- New rawGray.gz.bytes with equalized province boundaries
- New map_bw.png with textured water, transparent land, dark borders
- Updated province mask images (1-43.png)
- Updated centroids.json with orientation data for label rotation
- Update ProvinceLabelsController.cs to support label rotation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix coordinate system for rawGray and province labels
- Fix save_raw_gray to not flip Y coordinates (was causing click detection
to be inverted)
- Fix ProvinceLabelsController to not flip Y coordinates (was causing
labels to appear in wrong positions)
- Update rawGray.gz.bytes with correct orientation
- Update Gameplay.unity with ProvinceLabelsController setup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve province label rendering
- Reduce max font size scale from 2.0 to 1.3
- Disable word wrapping, allow text overflow
- Fix orientation sign (remove negation)
- Allow near-vertical labels by expanding angle range to [-90, 90]
- Recalculate province orientations with new range
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Scale province label font size based on province dimensions
- Add principal_length and perpendicular_width to centroids.json
- Update ProvinceLabelsController to scale font based on principal_length
- Update Rust map generator to calculate province dimensions via PCA
eigenvalues (4 * sqrt(eigenvalue) for ±2 std dev coverage)
- Disable text wrapping on labels to prevent unwanted line breaks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Reduce province label base font size to 11
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update neighbor positions and hex map starting positions for new borders
TSV changes (neighborPositions for new neighbors):
- Laufarvia: Usvol attacks from dir 4 (E)
- Hella: Usvol attacks from dir 2 (NE)
- Chapellia: Kojaria attacks from dir 4 (E)
- Usvol: Laufarvia=3(W), Hella=5(SW), Grytrand=4(E)
- Kojaria: Chapellia=1(N), Motcia=3(W)
- Grytrand: Usvol attacks from dir 3 (W)
Hex map changes (new attackerStartingPositions):
- Motcia.e0mj: Added direction 0 (NW) for Kojaria
- Chapellia.e0mj: Added direction 4 (SE) for Kojaria
- Kojaria.e0mj: Added directions 1 (N) and 3 (E) for Chapellia/Motcia
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Revert outline width change (made text smaller)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add TMP underlay effect for better label visibility on dark backgrounds
The underlay expands outward (unlike outline which shrinks text inward),
providing a white glow around labels without reducing text size.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace Python map generator with Rust implementation
- Add Rust map generator at src/main/rust/net/eagle0/eagle/map_generator/
- Parallel province equalization using graph coloring
- Generates equalized province sizes with ~7x improvement
- Run with: bazel run //src/main/rust/net/eagle0/eagle/map_generator -- --generate
- Remove Python map generator (src/main/python/)
- Rust version is faster and more maintainable
- Removes rules_python dependency from MODULE.bazel
- Add build test to verify Rust code compiles
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove Python from CI test workflow
Python map generator was replaced with Rust in this branch.
Rust tests are already covered by //src/test/...
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Upgrade rules_java to 9.3.0 (enables JDK 25 toolchain support)
This adds an explicit dependency on rules_java 9.3.0, which includes
support for JDK 25 remote toolchains. The build continues to use
Bazel's embedded JDK 24 by default, but JDK 25 toolchains are now
available for future use.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Mark MCTS basic test as flaky
The shardok_mcts_ai_basic_test uses Monte Carlo Tree Search which
has inherent non-determinism. Tests like PrefersArcheryOverEndTurn
and DoesNotPreferStartFireWhenNotBeneficial assert on AI decisions
that may vary between runs depending on random exploration paths.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Move SparklePlugin to separate Bazel workspace to resolve rules_swift conflict
The problem: grpc 1.76.0.bcr.1 requires rules_swift 3.x, but rules_apple 4.x
requires rules_swift 2.x. These have different compatibility levels, causing
bzlmod resolution to fail when both are in the same workspace.
The solution: Move SparklePlugin (the only thing using rules_apple) to a
separate Bazel workspace at `sparkle_workspace/`. This workspace has its own
MODULE.bazel with only rules_apple and Sparkle dependencies, completely
isolated from the grpc/flatbuffers dependency tree.
Changes:
- Create sparkle_workspace/ with isolated MODULE.bazel
- Move SparklePlugin source files to sparkle_workspace/
- Update build_sparkle_plugin.sh to build from subworkspace
- Remove rules_apple from main MODULE.bazel
- Add single_version_override for rules_swift 3.1.2 (for grpc/flatbuffers)
All 315 tests pass.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add WORKSPACE.bazel to sparkle_workspace to prevent parent workspace detection
Without this file, Bazel may walk up the directory tree and find the parent
workspace's MODULE.bazel, causing dependency conflicts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Upgrade Eagle server and JFR sidecar from Java 21 to Java 25.
Java 25 is the latest LTS release (September 2025). Scala 3.7.2
fully supports JDK 25.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Upgrade Eagle server and JFR sidecar from Java 17 to Java 21.
Benefits:
- Better garbage collection (Generational ZGC available)
- General JVM performance improvements
- Foundation for future virtual threads adoption
- Supported until 2031
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The Eagle server only needs to run Java, not compile it. Using the JRE
base image instead of JDK reduces the image size from ~273MB to ~120MB.
The JFR sidecar still uses JDK because it needs jcmd for JFR dumps.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This is a stepping stone toward enabling -Werror for exhaustiveness checking
on sealed traits. Changes include:
- Enable -Wall in Scala toolchain configuration
- Add -Wconf suppressions for generated code and external dependencies
- Fix discarded value warnings by explicitly discarding with `val _ =`
- Fix non-local return warnings in JwtService using nested if/else
- Fix deprecated `= _` syntax using `= uninitialized` in OAuthHttpHandler
- Fix implicit parameter syntax: `(ec)` -> `(using ec)` in GrpcRetrier
- Remove unused imports across multiple files
- Add @unused annotation for intentionally unused parameters
- Fix wrong imports in SettingUpdater (was using wrong proto package)
- Fix BUILD.bazel exports to make GrpcRetrier available transitively
- BUG FIX: StringConstructionParser was silently ignoring parse errors
when closing character wasn't found (added missing return)
Note: -Werror is not yet enabled (TODO in tools/BUILD.bazel) because
there are still unused import warnings in some files. Once those are
fixed, -Werror can be uncommented to get compile-time exhaustiveness
checking.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add WebView GUI to Windows installer
Adds a native GUI window to the Windows installer using the webview
library which embeds Microsoft Edge WebView2. The installer now shows
a modern HTML/CSS/JS interface with progress bar and status updates.
Key changes:
- Add llvm-mingw toolchain for macOS → Windows CGO cross-compilation
- Add webview_go dependency for WebView2 integration
- Create UIInterface abstraction with ConsoleUI and WebViewUI impls
- Build tags select stub (non-Windows/non-CGO) vs real WebView impl
- Genrule handles CGO cross-compilation within Bazel sandbox
Build targets:
- eagle_installer_windows_amd64: Pure Go console version (5.8 MB)
- eagle_installer_windows_amd64_webview: CGO WebView GUI (13.7 MB)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix WebView genrule to use Bazel Go SDK
The CI doesn't have Go in PATH, so the genrule needs to use Bazel's
Go SDK explicitly. This change:
- Exposes @go_default_sdk via use_repo in MODULE.bazel
- Adds @go_default_sdk//:files as a srcs dependency to the genrule
- Uses absolute paths for GO and GOROOT so they work after cd
- Filters out Go SDK files when copying source files
- Updates go.mod version to 1.23 to match the Bazel SDK
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add native Sparkle plugin to enable Mac auto-updates
The Sparkle framework was being injected into the app bundle, but
nothing was initializing it. This adds:
- Native Objective-C plugin (SparklePlugin.m) that initializes
SPUStandardUpdaterController at runtime
- C# wrapper (SparkleUpdater.cs) for Unity to call the native plugin
- SparkleInitializer.cs uses RuntimeInitializeOnLoadMethod to
automatically initialize Sparkle at app startup
- Build script to compile the plugin as a universal binary
The plugin is weak-linked against Sparkle.framework, which is injected
separately by inject_sparkle.sh during the build process.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Convert Sparkle plugin build from clang to Bazel
- Add Sparkle framework as http_archive dependency in MODULE.bazel
- Add BUILD.sparkle to import the framework
- Add BUILD.bazel for SparklePlugin using macos_bundle rule
- Update build_sparkle_plugin.sh to use Bazel instead of direct clang
- Register Apple CC toolchain extension
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix symbol exports for SparklePlugin native library
The C functions need to be exported with visibility("default") and
explicit linker flags for Unity P/Invoke to find them. Without this,
the bundle binary had no exported symbols.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Re-enable Sparkle auto-update integration for Mac builds
Restores Sparkle integration that was temporarily removed in #5317:
- Restore inject_sparkle.sh script
- Add Sparkle injection step to mac_build.yml
- Re-enable Sparkle signing and appcast updates in deploy step
Combined with native SparklePlugin that initializes Sparkle at runtime.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Build SparklePlugin.bundle before Unity build
The SparklePlugin.bundle.meta file tells Unity to include the plugin,
but the actual bundle needs to be built by Bazel first.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Convert SparklePlugin Info.plist to XML format
Unity's build system requires Info.plist files in XML format,
but Bazel outputs them in binary plist format.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
FlatBuffers 25.9.23 changed GetMutableObject() on vectors of structs
to return const T* instead of T*. This is a const-correctness
improvement in the library.
Updated all C++ code to handle this change:
- Added const_cast<T*>() wrappers where mutation is needed on owned buffers
- Added helper function GetMutableTerrain() in HexMapUtils for terrain access
- All const_casts are safe because the code owns the underlying mutable buffers
All 112 C++ tests and 209 Scala tests pass.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sparkle.framework contains symlinks like Headers -> Versions/Current/Headers.
The previous code used filepath.Walk which follows symlinks, causing it to
try to read a directory as a file.
Now uses filepath.WalkDir with os.Lstat to detect symlinks and store them
properly in the zip archive.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The Mac history editor that used these targets was previously removed.
Cleaning up the unused Swift proto infrastructure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add ARM64 Linux build infrastructure for Shardok
Infrastructure to support cross-compiling Shardok for ARM64 Linux,
enabling deployment to Hetzner ARM instances (CAX41) for on-demand
compute.
Changes:
- Add linux_arm64 platform definition
- Add LLVM toolchain for ARM64 cross-compilation
- Add ARM64 sysroot placeholder (needs workflow run to populate)
- Add ARM64 busybox for container health checks
- Add Ubuntu 24.04 ARM64 base image
- Add Shardok ARM64 container image targets
- Update sysroot build workflow to support ARM64
Next steps:
1. Run "Build Linux Sysroot" workflow with architecture=arm64
2. Update MODULE.bazel with generated sysroot SHA
3. Build and push ARM64 container
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ARM64 sysroot SHA from workflow build
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix ARM64 container build issues
- Use linux/arm64/v8 platform string (rules_oci requires variant suffix)
- Remove busybox_layer_arm64 due to busybox.net SSL certificate issues
- Health checks can be added later using an alternative busybox source
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add GitHub Actions workflow for ARM64 Shardok build
Builds and pushes ARM64 container image to GitHub Container Registry
for deployment on Hetzner ARM instances (CAX41).
Triggered on:
- Push to main (when Shardok-related files change)
- Manual workflow_dispatch
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Switch Eagle server base image from JRE to JDK for JFR support
The previous eclipse-temurin image was a JRE which doesn't include jcmd.
This prevented dumping JFR recordings from a running server. Switch to
the 17-jdk tag which includes the full JDK with jcmd.
To dump JFR recording from running server:
docker exec eagle-server jcmd 1 JFR.dump filename=/app/jfr/profile.jfr
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update MODULE.bazel.lock for JDK image
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>