* Update ASSET_AUDIT.md: mark Terrain Hexes and Discord logo as verified
- Terrain Hexes: confirmed Unity Asset Store purchase
- Discord logo: complies with Discord brand guidelines
- Updated action items and recommendations to reflect remaining work
Remaining items:
- 6 music tracks (1 unknown source, 5 Audius non-CC license)
- 56 Shardok sound effects (unknown origin)
- 138 StrategyGameIcons (unknown source)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: Medieval Victory Theme is CC0 Public Domain
Found source: RandomMind on Chosic, CC0 Public Domain license.
No attribution required, free for commercial use.
Remaining items:
- 5 Dima Koltsov/Audius tracks (non-CC license)
- 56 Shardok sound effects (unknown origin)
- 138 StrategyGameIcons (unknown source)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: Dima Koltsov tracks mostly verified as CC BY 4.0
- No Time for Greatness: CC BY 4.0 (YouTube)
- Warriors of Demacia: CC BY 4.0 (YouTube)
- Valor: CC BY 4.0 (YouTube)
- Forest Queen Tale: Presumed CC BY 4.0 (not found on YouTube)
- Clouds: Presumed CC BY 4.0 (not found on YouTube)
Remaining items:
- 56 Shardok sound effects (unknown origin)
- 138 StrategyGameIcons (unknown source)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: StrategyGameIcons verified as Asset Store purchase
Confirmed as "Strategy Game Icons" by REXARD from Unity Asset Store.
Remaining items:
- 56 Shardok sound effects (unknown origin)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: identify 3 sounds from Zombie Monster Undead Collection
Verified from Unity Asset Store purchase:
- raise_undead.mp3
- undead_break_control.mp3
- undead_grew.wav
Also corrected count: 37 audio files (not 56 - was counting .meta files)
34 sound effects still unverified.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: flag anybody.mp3 and runaway.mp3 for replacement
These two sound effects have licensing issues and must be replaced.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: also flag burnination.mp3 for replacement
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md: mark remaining sound effects as presumed Asset Store
Owner believes 31 remaining sounds are from purchased Asset Store packs:
- Fantasy Interface Sounds
- Medieval Combat Sounds
- Magic Spells Sound Effects LITE
- Medieval Battle Sound Pack
Only 3 files need replacement: anybody.mp3, burnination.mp3, runaway.mp3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Replace anybody.mp3 and burnination.mp3 with licensed alternatives
- anybody.mp3: replaced with Positive Effect 6.wav (Magic Spells Sound Effects LITE)
- burnination.mp3: replaced with Magic Element Fire 04.wav (Medieval Combat Sounds)
Still need replacements for:
- failure_horn.mp3
- runaway.mp3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add bridge rotation based on builder position or adjacent land
Bridges now rotate to face meaningful directions:
- For bridges built by engineers: one end faces the builder's location
- For bridges present at battle start: one end faces adjacent land
Implementation:
- Add rotationDegrees parameter to HexGrid.SetCellModifier3D()
- Track bridge builder locations in _bridgeBuilderLocations dictionary
- Add GetHexNeighbors() to find adjacent hex cells
- Add CalculateBridgeRotation() to compute angle between cells
- Add CalculateBridgeRotationFromLand() to find land for initial bridges
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Simplify bridge rotation to 3-orientation lookup table
Replace complex trigonometry with simple lookup based on hex grid
direction. Only 3 possible orientations exist:
- Horizontal (0°): left ↔ right
- Diagonal up (124°): down-left ↔ up-right
- Diagonal down (56°): up-left ↔ down-right
Add BridgeRotationCalculator with unit tests for all 6 hex directions
from both even and odd rows.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add iOS addressables build workflow
Creates a separate workflow to build and upload iOS addressables to CDN.
This is needed for iOS clients to load remote assets.
- Adds build_ios_addressables.sh script
- Adds ios_addressables_build.yml workflow
- Uploads to https://assets.eagle0.net/addressables/iOS/
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix iOS addressables build: add proto build step
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Document that bridge.png was replaced with AI-generated wooden rope
bridge icon created with ChatGPT/DALL-E 3.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
New 512x512 PNG icon featuring an isometric wooden rope bridge,
better suited for the fantasy game aesthetic. Generated with
ChatGPT/DALL-E, has transparent background.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace 2D bridge texture overlay with 3D bridge prefabs for more
visually appealing bridges on water tiles. The system now supports
an array of bridge prefabs for variety, selected deterministically
based on cell index.
- Add TerrainModifier3D field to HexGrid Cell struct
- Add SetCellModifier3D() and ClearCellModifier3Ds() methods
- Change ShardokGameController.bridgeImage to bridgePrefabs array
- Bridge positioned at true cell center using Geometry.AnchoredPosition
- Assign 6 bridge prefabs from TileableBridgePack in Gameplay scene
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
During blue-green deployments, warmup games may be invalidated while
LLM requests are in flight. The responses are correctly ignored, but
the log messages were noisy. Remove them since this is expected behavior.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The mobile shaders in TextMesh Pro/Shaders/ reference these include
files with relative paths but they only exist in Resources/Shaders/.
Copy them here to fix shader compilation errors.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Usage:
eagle-logs # Tail logs (follow mode)
eagle-logs -n 100 # Show last 100 lines and follow
eagle-logs --no-follow -n 50 # Last 50 lines without following
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace fire icon with UXWing flame and consolidate duplicates
Replaced the startFire.png icon with a clean flat-style flame from UXWing
(free for commercial use, no attribution required). Consolidated from two
duplicate copies to a single canonical copy in Assets/Images/, updating all
Unity scene references.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update ASSET_AUDIT.md with UXWing flame icon source
Document that startFire.png was replaced with the flame icon from UXWing
(https://uxwing.com/flame-icon/) which is free for commercial use with no
attribution required. Also note the consolidation from two duplicate copies
to one canonical copy.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Remove sudo symlink creation (deploy user lacks passwordless sudo)
- Simplify eagle-exec to read from /opt/eagle0/.active-instance file
- Update deploy-blue-green.sh to write active instance to file
- Keep fallback to checking running containers if file doesn't exist
Users can add their own alias:
alias eagle-exec='/opt/eagle0/scripts/eagle-exec.sh'
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add iOS/mobile support with touch-aware tooltips
- Add melee button to Shardok action bar (button 4 with [SHFT] label)
- Changed MeleeAttackGroup from -1 to 4 to show melee commands as button
- Updated Gameplay.unity to assign melee/charge commands to group 4
- Add touch-aware tooltip system using long-press pattern
- Updated HoveringTooltipTextProvider to detect touch vs mouse input
- On desktop: tooltips appear immediately on hover
- On mobile: tooltips appear after 0.4s long-press
- Created TouchAwareTooltip.cs as reusable component
- Add iOS build method to BuildScript.cs
- Generates Xcode project for building/signing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add dedicated melee button (button9) for mobile/tablet support
- Add button9 and buttonText9 fields to ShardokGameController
- Change MeleeAttackGroup from 4 to 9 for dedicated button
- Change EndTurnCommandGroup from 9 to 12 (handled separately)
- Update melee/charge commands in Gameplay.unity to group 9
- Update End Turn command to group 12
- Add null checks for unassigned buttons in UpdateActionButtons
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix melee/charge command groups in Gameplay.unity
Set commandGroup to 9 for melee and charge commands so they
appear on the dedicated melee button and indicators work correctly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a helper script that automatically runs docker exec against the
active Eagle instance (blue or green), determined by checking nginx
config.
Usage:
eagle-exec printenv GEMINI_API_KEY
eagle-exec jcmd 1 VM.flags
eagle-exec sh
The script is deployed to /opt/eagle0/scripts/ and symlinked to
/usr/local/bin/eagle-exec for easy access.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When LlmProvider was set to "gemini", the validation code was missing a
case for extracting the GeminiModelName, causing it to default to an
empty string. This resulted in malformed API URLs like:
https://generativelanguage.googleapis.com/v1beta/models/:streamGenerateContent
which returned 404 errors.
Added the "gemini" case to properly extract GeminiModelName from
settings.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When switching LLM providers in the admin console, if the API key for
the target provider isn't configured, the validation would crash with
ExceptionInInitializerError. This fix:
- Wraps service creation in Try to catch initialization errors
- Logs the error with context (likely missing API key)
- Reports to Sentry for monitoring
- Returns a user-friendly error message
- Falls back to keeping the old setting unchanged
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The LLM settings validation in GameAdminServiceImpl was missing the
"gemini" provider case, causing "Unknown provider: gemini" errors when
trying to switch to Gemini from the admin console.
Changes:
- Add GeminiServiceImpl import
- Add "gemini" case to validateLlmSettings()
- Add "GeminiModelName" to llmSettingKeys set
- Add gemini_service_impl dep to BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace 13 heroes that referenced real people or copyrighted characters
with original alternatives that preserve gameplay stats and general archetypes:
Real people replaced:
- Jesse "The Body" Ventura → Magnus "The Boulder" Varnok
- Jeb! → Aldric the Overlooked
- Ringo → Tomlin the Tuneful
- Flea → Zander the Restless
Copyrighted characters replaced:
- Darth Plagueis → Voros the Undying
- Deckard Cain → Old Marek the Learned
- Skeletor → Karvax the Fleshless
- Trap Jaw → Ironmaw
- Shu Lien → Mei Shen
- Roger the Shrubber → Hedrick the Hedge-Merchant
- Kriss Kross → The Tumbler
- Daddy Mac → Silvio the Smooth
- Ul (removed Warcraft "Far Seer" reference)
Image files in eagle0-headshots bucket have been renamed to match.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Refactor LLM update interface to use Scala domain types instead of protos
Replace proto types (GeneratedTextRequest, LLMResponse) with Scala domain
type (GeneratedTextRequestT) throughout the LLM streaming interface. This
eliminates unnecessary proto conversions and simplifies the code.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove unused llm_response_scala_proto dependency from llm_resolver
The LLMResponse proto is no longer used since we now pass Scala domain
types through the LLM update interface.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Filter LLM requests for deleted games before sending
Add a game validity check to LlmResolver to prevent sending LLM requests
for games that have been deleted (e.g., warmup games during blue-green
deployments). This avoids wasted API calls and eliminates the "Ignoring
LLM response for deleted game" log messages at startup.
Changes:
- Add LlmResolverGameDeleted result type to LlmResolver
- Add isGameValid callback parameter to LlmResolver constructor
- Check game validity before sending each request
- Handle LlmResolverGameDeleted in UnrequestedTextHandler
- Pass gameControllerInfos.contains check from GamesManager
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move proto conversion inside TextGenerationSuccess case
The proto conversion is only needed for the success case where we
actually send the request. Moving it avoids unnecessary conversion
for dependency-blocked requests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Move claude-3-5-haiku to the top of the dropdown since it's the
fastest model for streaming responses (~1.9s TTFT vs ~5.0s for sonnet).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add ANTHROPIC_API_KEY to docker-compose environment
Required for Claude LLM provider to work in production.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add ANTHROPIC_API_KEY to deployment workflow
Pass the secret to the deploy job and export it for docker-compose.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Only send reasoning_effort for reasoning models (o1, o3)
The reasoning_effort parameter is only valid for OpenAI reasoning models.
Sending it to non-reasoning models like gpt-5-mini causes a 400 Bad Request.
Now only includes reasoning_effort when the model name starts with "o1" or "o3".
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Treat models without -mini/-nano as reasoning models, add gpt-5.1 option
- Change reasoning model detection: models with -mini or -nano suffix are
non-reasoning; all others (gpt-5.1, gpt-5.2, o1, o3, etc.) get reasoning_effort
- Add gpt-5.1 to admin console dropdown options
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The previous change incorrectly set both total players and human players
to 1. This restores the intended behavior: 7 total players (1 human + 6 AI)
with 1 human player selected by default.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
OkHttp can call onFailure with a null Throwable when there's an HTTP
error response but no actual exception. Handle this case by:
- Null-checking before calling getMessage()
- Creating a synthetic RuntimeException when t is null
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
JFR profiling revealed that the first client connection after deploy was
experiencing 5+ second delays due to lazy initialization of the AWS S3 SDK
(profile loading, TLS handshake to DigitalOcean Spaces, etc.).
This change adds a warmup() method to S3Utils that:
- Forces initialization of the lazy transferManager
- Makes a lightweight headBucket call to complete TLS handshake
The warmup is called early in Main.scala before the server starts accepting
connections, moving the initialization cost to startup time rather than
first-request time.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The --gpt-model-name CLI argument was removed in the LLM model switching
PR (97b313f6), but docker-compose.prod.yml still passed it, causing the
server to fail with "Unknown option --gpt-model-name".
LLM provider and model are now configured via settings (admin console)
instead of CLI arguments.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add LLM model switching from admin console
Implement the ability to switch LLM provider and model dynamically from the
admin console with validation before applying changes.
Changes:
- Add String setting type support (StringSetting.scala, generator updates)
- Add LlmProvider, OpenAiModelName, ClaudeModelName settings
- Modify LlmResolver to read provider/model from settings dynamically
- Add recreateLlmCallers() to allow hot-swapping LLM configuration
- Add validation in GameAdminServiceImpl that tests API before applying
- Remove gptModelName command line flag (now configured via settings)
- Update AddSettingsResponse proto with success/errorMessage fields
The admin console now shows these settings and validates them by making
a test API call before applying. If validation fails, the error is returned
and settings remain unchanged.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add dropdown UI for LLM settings in admin console
Instead of showing LLM provider and model names as text inputs in the
general settings list, display them as dropdowns with common options:
- LlmProvider: openai, claude
- OpenAiModelName: gpt-5-mini, gpt-5.2, gpt-4.1, gpt-4.1-mini, etc.
- ClaudeModelName: claude-sonnet-4-20250514, claude-opus-4-20250514, etc.
This provides better UX by making it easy to switch between known models
without having to remember exact model name strings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When loading a game, if a hero's name text (hn_X format) is missing from
the text store, the server would throw an exception requiring the game
to be deleted. This occasionally happened when a hero was created but
the name request was somehow lost before persistence.
Changes:
- New MissingHeroNameRecovery utility: Provides a reusable method to
check heroes for missing name texts and recreate GeneratedHeroName
requests. Can be called from game load or other contexts as needed.
- GamesManager: Uses the new utility during game loading to recover
missing hero names instead of throwing an exception.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add explicit length instructions to prompt (IMPORTANT prefix)
- Add reminder at end of prompt to be selective
- Increase default word count from 200 to 400 (appropriate for 1.5 pages)
- Instruct LLM to focus on 2-3 most significant events
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Clarifies that users can right-click a province on the map or use the
dropdown menu to select a destination.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Default to 1-player game in create game dropdown
- Auto-create 1-player game on first launch (skip lobby)
- Add sign-in tutorial for users without stored accounts
- Add lobby tutorial for users who reach the lobby
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The installer is built by installer_build.yml, not the Docker workflow.
Adding exclusion prevents unnecessary Docker builds when only installer
files change.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Generate 64-bit .syso resource file for the WebView installer build.
The previous resource file was 32-bit and caused link errors with the
64-bit CGO cross-compilation.
Changes:
- Add resource_windows_amd64.syso (64-bit COFF object with icon/version)
- Update genrule to copy the .syso file during build
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Fix genrule variable syntax for manifest public key
Genrules use Make variable syntax $(VAR), not curly brace syntax {VAR}.
The curly brace syntax only works in x_defs for go_binary rules.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use --action_env to pass MANIFEST_PUBLIC_KEY to genrule
Bazel genrules don't support workspace status variable substitution
in cmd strings. Instead, use --action_env to pass the environment
variable into the sandbox, where it can be referenced as a regular
shell variable ($$MANIFEST_PUBLIC_KEY in the genrule cmd).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Handle missing MANIFEST_PUBLIC_KEY env var in genrule
When building via 'bazel test //src/main/go/...', the genrule runs
without --action_env=MANIFEST_PUBLIC_KEY set. Use bash default value
syntax ${VAR:-} to default to empty string when the env var isn't set,
allowing the build to succeed with an empty public key.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Simplify public key handling - let empty var expand naturally
Bazel doesn't support ${VAR:-} syntax in genrule cmd. Instead, just
use $MANIFEST_PUBLIC_KEY directly - if not set, it expands to empty
string which the installer handles gracefully.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add manual tag to exclude webview installer from wildcard builds
The webview installer genrule requires --action_env=MANIFEST_PUBLIC_KEY
to be set. Adding tags = ["manual"] excludes it from wildcard patterns
like "bazel test //src/main/go/..." so it won't fail in the test workflow.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
PR #5459 accidentally broke Shardok update streaming when refactoring
postBattleUpdate to make Engine protoless. The original code called
withHandledEngineAndResults() which invoked humanClientsAfterPostingResults
to push updates to connected clients. The refactored code updated the
history but skipped client notification.
This caused clients to only receive Shardok updates on initial subscription
or reconnect, not during ongoing gameplay. The heartbeat would detect sync
mismatches (client=X server=Y) but updates weren't pushed.
Fix: Call humanClientsAfterPostingResults in postBattleUpdate to push
Shardok results to connected human clients, restoring the behavior that
was lost during the refactor.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add stamp = 1 to the genrule so that {STABLE_MANIFEST_PUBLIC_KEY}
gets substituted with the actual value from workspace status.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When the client subscribes with ShardokViewStatuses empty (because
ShardokGameModels hasn't been populated yet), the server sends an
initial ShardokActionResultResponse as catch-up but doesn't know to
keep sending updates for that game.
The model is created when processing ShardokActionResultResponse, but
by then the subscription was already sent without it. The server
won't send further updates, causing sync mismatches detected by
heartbeat until the client reconnects.
Fix: When creating a new ShardokGameModel from ShardokActionResultResponse,
re-subscribe to tell the server to include this game in future updates.
This issue was exposed by commit fb2b0beb85 which added
ShardokGameModels.Clear() in HandleStartingState, ensuring models are
always empty when subscription is initially sent after reconnect.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Units are auto-placed in starting positions by default, so the
tutorial should explain how to move them rather than implying
you need to place them from the Unplaced Units panel first.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The manifest public key wasn't being injected properly because:
1. Workspace status variables need STABLE_ prefix for x_defs stamping
2. BUILD.bazel was using {MANIFEST_PUBLIC_KEY} but the variable
wasn't being output with the STABLE_ prefix
This caused the installer to try to base64 decode the literal string
"{MANIFEST_PUBLIC_KEY}" instead of the actual public key value.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a ShardokGameModel is first created via MakeGameModel, its
GameStatus property is null. It's only set later when history
entries are processed with a non-null gsvDiff.GameStatus.
The ShardokGameModelIsRunning check accessed sgm.GameStatus.State
without a null check, causing a NullReferenceException when
filtering ShardokGameModels. This caused RunningShardokGameModels
to fail and return empty, making the Observe Battle button not appear.
Fix: Add null check before accessing GameStatus.State, matching the
pattern already used in ShardokGameModel.InSetUp property.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When CurrentEagleToken or CurrentShardokToken is null (e.g., during
battles), TryPendingCommands was calling PostRequest which:
1. Adds the command back to _pendingCommands
2. Sends it to the server
3. Server returns BAD_TOKEN
4. Next game update triggers TryPendingCommands again
5. Command is still pending with null token → retry loop
This caused the same command to be posted multiple times, flooding the
server with BAD_TOKEN errors and potentially causing reconnect storms.
Fix: When token is null, add the command back to _pendingCommands
directly without calling PostRequest. The command stays pending until
we have a valid token to compare against.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Rename WebView installer output from EagleInstallerWebView.exe to Eagle0.exe
- Update CI workflow to build and deploy Eagle0.exe
- Update all references in authservice (download URLs, batch scripts, HTML)
- Update installer_build_handler default key and version content
- Update help text and versioninfo.json metadata
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>
Remove continue-on-error from "Run tests" step so GitHub UI expands
the actual failing step instead of "Fail if tests failed".
The log collection and artifact upload steps use `if: always()` so
they still run after test failures.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change hero selection in several commands to use HeroUtils.fatigue
(constitution - vigor) instead of absolute vigor. This is more
consistent with how other commands select heroes and properly accounts
for heroes having different max vigor (constitution).
Commands updated:
- HandleRiotCrackDown in CommandChoiceHelpers
- TruceOfferCommandSelector
- AllianceOfferCommandSelector
- Recon in MidGameAIClient
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add relationship context and personality guidance to prevent repetitive
"If you mean to kill me, do it swiftly" patterns:
- Detect if hero was previously in captor's faction (defection context)
- Note previous captures by the same faction
- Reference prior battles between the parties
- Use hero's personality words to guide tone
- Explicitly suggest varied emotional approaches and discourage clichés
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Match Unity client layout: left column (Discord, GitHub, Steam),
right column (Google, Apple, Twitch). Includes responsive fallback
to single column on narrow screens.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The library/ code is now fully protoless (zero Scala proto dependencies).
Transitive deps through C++/Go build tools (map generation) are expected.
Changes:
- Delete docs/DEPROTO_PLAN.md - migration complete
- Delete scripts/build_deps_baseline.txt - no longer needed
- Update check_build_deps.sh to enforce zero Scala proto deps in library/
(C++/Go proto deps are allowed as they're build-time tools)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The Windows installer has been rewritten in Go. Remove the old C#/.NET
installer code and the associated CI build script.
Deleted:
- src/main/csharp/net/eagle0/clients/win/ - .NET installer source
- ci/build_eagle_installer_win.sh - old manual build script
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
After the installer updates itself, the old version is left behind as
EagleInstaller.exe.backup. Now on startup, the installer checks for and
removes this backup file.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The perform_province_events_action_test and perform_vassal_commands_phase_action_test
had BUILD.bazel dependencies on proto_matchers but the Scala files don't actually
use ProtoMatchers or equalProto. Remove these dead dependencies.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove .NET installer build and old manifest support. The Go installer is
now the only version built and deployed. Both the installer and Unity
builds now update only the v2 manifest at installer/v2/eagle0_manifest.txt.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Two issues fixed:
1. Unity build workflow now updates BOTH manifests (old and v2)
- The v2 manifest was missing all game file hashes because only the
installer workflow was updating it
- Now unity_build.yml calls manifest_manager with both "unity3d" and
"unity3d-v2" to update both manifests
2. Go installer now verifies game exists before claiming "up to date"
- Previously, if manifests matched but files didn't exist, it would
claim success and try to launch a non-existent game
- Now checks if eagle0.exe exists; if not, forces redownload
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove all proto imports and proto_converters from ResolveBattleActionTest.
Use native Scala model types (ProvinceC, HeroC, FactionC, BattalionC,
GameState, etc.) and standard ScalaTest matchers instead of ProtoMatchers.
Key changes:
- Replace proto imports with Scala model imports
- Use inside() pattern for type-safe assertions
- Add resultsOfExecute() extension method using ActionResultApplierImpl
- Update BUILD.bazel to remove proto dependencies
- Add test visibility for battalion_view package
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This file was a proto-using duplicate of the test in battalion_suitability/
but was never built (no BUILD target referenced it).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add console UI with progress bar to Go installer
Adds a polished console-based UI to the Go installer with:
- Box-drawn banner header
- Status messages with ► prefix
- Success messages with ✓ prefix
- Error messages with ✗ prefix
- Warning messages with ⚠ prefix
- Progress bar with █ (filled) and ░ (empty) characters
- File count display during downloads
- Elapsed time on completion
The UI uses pure Go (no external dependencies) so the binary
size remains at 5.4MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add application icon to Go installer
- Copy eagle0.ico from .NET installer
- Add versioninfo.json for goversioninfo configuration
- Generate resource_windows.syso with embedded icon and version info
- Update BUILD.bazel to include the .syso resource in Windows build
Binary size increased from 5.4MB to 5.5MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Zone.Identifier removal in .NET installer
File.Exists() doesn't work with Windows Alternate Data Streams (ADS) -
it always returns false. Changed to unconditionally attempt deletion
and ignore errors if the ADS doesn't exist.
This fixes the self-update mechanism where SmartScreen was blocking
the new installer because Zone.Identifier was never actually removed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Two-stage manifest for clean installer upgrade path
Old .NET installers have a bug where Zone.Identifier removal fails,
blocking SmartScreen from allowing the Go installer to run. This change
creates a two-stage upgrade path:
1. OLD manifest (installer/eagle0_manifest.txt) -> points to .NET installer
- Old .NET installers download new .NET installer (same format, works)
2. NEW manifest (installer/v2/eagle0_manifest.txt) -> points to Go installer
- New .NET installer checks v2 manifest, downloads Go installer
- Go installer also checks v2 manifest for future updates
Changes:
- .NET installer now checks installer/v2/ manifest path
- Go installer now checks installer/v2/ manifest path
- manifest_manager supports "installer-v2" section for v2 manifest
- CI workflow updates both manifests appropriately
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Delete unused AvailableCommandMatcher and SelectedCommandMatcher
These proto-based test matchers were defined but never used by any test.
Removing them eliminates direct proto imports from library test code.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove stale selected_command_matcher dependency
perform_vassal_commands_phase_action_test had a dep on selected_command_matcher
but didn't actually use it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add console UI with progress bar to Go installer
Adds a polished console-based UI to the Go installer with:
- Box-drawn banner header
- Status messages with ► prefix
- Success messages with ✓ prefix
- Error messages with ✗ prefix
- Warning messages with ⚠ prefix
- Progress bar with █ (filled) and ░ (empty) characters
- File count display during downloads
- Elapsed time on completion
The UI uses pure Go (no external dependencies) so the binary
size remains at 5.4MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add application icon to Go installer
- Copy eagle0.ico from .NET installer
- Add versioninfo.json for goversioninfo configuration
- Generate resource_windows.syso with embedded icon and version info
- Update BUILD.bazel to include the .syso resource in Windows build
Binary size increased from 5.4MB to 5.5MB.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Zone.Identifier removal in .NET installer
File.Exists() doesn't work with Windows Alternate Data Streams (ADS) -
it always returns false. Changed to unconditionally attempt deletion
and ignore errors if the ADS doesn't exist.
This fixes the self-update mechanism where SmartScreen was blocking
the new installer because Zone.Identifier was never actually removed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The CI runner doesn't have s3cmd installed. Modified installer_build_handler
to accept an optional second argument for the S3 key, allowing it to upload
the Go installer to installer/EagleInstallerMini.exe.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Migrate deferred test files to Scala types, delete unused utilities
- Migrate BattalionTypesTestData.scala to use Scala BattalionType
- Migrate NewRoundActionTest.scala to Scala types (FactionRelationship, MovingArmy, etc.)
- Migrate EndBattleAftermathPhaseActionTest.scala with complete rewrite
- Migrate ResolveBattleActionTest.scala (remove IDable dependency)
- Delete unused impl/package.scala and availability/package.scala
- Remove 48 dangling action_impl_pkg references from BUILD files
- Update DEPROTO_PLAN.md (24 migrated, 1 deferred, 3 deleted)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete unused IDable.scala and remove all :idable BUILD references
IDable.scala was listed as a BUILD dependency in 22 targets, but no code
actually imported or used it. The BUILD dependencies were stale.
This completes the library test deproto migration with zero deferred files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove Zone.Identifier to fix Windows SmartScreen blocking
When the installer downloads a new version and launches it programmatically,
Windows SmartScreen blocks the execution because the file has a "Mark of the
Web" (Zone.Identifier alternate data stream) indicating it was downloaded
from the internet.
Remove the Zone.Identifier after download to allow the new installer to
launch without being blocked.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zone.Identifier fix to .NET installer, deploy both versions
- Add Zone.Identifier removal to .NET installer's download logic
- Update CI to build both .NET and Go installers
- Deploy .NET installer to installer/EagleInstaller.exe
- Deploy Go installer to installer/EagleInstallerMini.exe
- Manifest points to EagleInstallerMini.exe (Go version)
Flow for existing users:
1. Old .NET installer → auto-updates to fixed .NET installer
2. Fixed .NET installer → downloads EagleInstallerMini.exe, strips Zone.Identifier, updates to Go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Rewrite Windows installer in Go (73MB -> 5.4MB)
Rewrites the Eagle0 Windows installer from C#/.NET to Go, reducing the
binary size from 73MB to 5.4MB (93% reduction).
The Go implementation provides the same functionality:
- Downloads and verifies game files via manifest
- Ed25519 signature verification for manifest
- SHA256 verification for all downloaded files
- Parallel downloads (8 concurrent slots)
- Self-update mechanism for installer updates
- Console-based progress output
- Invitation code support
Changes:
- Add Go installer source code (main.go, updater.go, config.go)
- Add Bazel BUILD file with Windows cross-compilation target
- Update CI workflow to build Go binary instead of .NET
- Add MANIFEST_PUBLIC_KEY to workspace status for build-time injection
The installer is cross-compiled for Windows from Mac using Bazel.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI permission error when copying installer output
Remove any existing installer-output directory before copying
to avoid permission denied errors from previous runs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Replace proto ActionResult with Scala ActionResultC
- Replace proto Date with Scala Date
- Replace proto RoundPhase with Scala RoundPhase
- Add InMemoryHistory.fromScalaResults() method for building history
from Scala ActionResultT objects without proto conversion
- Update visibility for action_result_concrete to allow library tests
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Corrects error from #5512 - the LLM prompt generators are actually
fully protoless. My grep was matching Scala utility imports like
`net.eagle0.common.JsonUtils`, not proto imports.
Verified with: grep -r "import net.eagle0.eagle.(internal|common|api|views)."
which returns zero matches in src/main/scala/net/eagle0/eagle/library/
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Enable remote catalog building in AddressableAssetSettings
- Switch Default Local Group to use remote build/load paths
- Set CDN URL to https://assets.eagle0.net/addressables/[BuildTarget]
- Update BuildScript to log bundle output location for upload
- Add graceful error handling in SoundManager when music fails to load
(e.g., offline and not cached) - game continues without music
After building, bundles in ServerData/[BuildTarget] are uploaded to the
CDN by CI (see PR #5509).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Mark Hostility proto migration as complete (all 4 files now use Scala enum)
- Correct status: LLM prompt generators still have 4 files with proto imports
- List specific LLM files and their proto dependencies
- Move LLM generators from "Fully Protoless" to "Remaining Proto Usage"
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add upload_addressables.sh script for Addressables bundles
- Add upload step to Mac and Windows build workflows
- Update mac_build_handler to use eagle0-assets bucket
- Update unity3d_windows_build_handler to use eagle0-assets bucket
- Update installer_build_handler to use eagle0-assets bucket
- Update manifest_manager to use eagle0-assets bucket
All uploads now go to eagle0-assets bucket, accessible via assets.eagle0.net.
Old eagle0-windows bucket can be deleted after DNS TTL expires (~1 week).
Addressables uploaded to:
- https://assets.eagle0.net/addressables/StandaloneOSX/
- https://assets.eagle0.net/addressables/StandaloneWindows64/
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit migrates the GameStateViewFilterTest to use native Scala
model types instead of protobuf types:
- Replace proto GameState with Scala GameState
- Use Scala concrete types: FactionC, HeroC, ProvinceC, UnaffiliatedHeroC
- Use Scala enums: Profession.NoProfession, RoundPhase, etc.
- Update ShardokBattle and ShardokPlayer with all required fields
- Replace proto assertions with field-specific assertions on view types
- Add visibility for shardok_battle to test package
- Add required view dependencies to BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This test file was left behind when RansomCommand was refactored into
the DiplomacyCommand system. The file:
- Had no BUILD target
- Used types that no longer exist (RansomAvailableCommand, RansomSelectedCommand)
- Called APIs that no longer exist (RansomCommand.make)
The ransom functionality is now tested in DiplomacyCommandTest.scala
using the current API.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Reapply "Convert SoundManager music loading to Addressables (#5475)" (#5494)
This reverts commit bda787c07e.
* Add BuildScript to build Addressables before player build
The Addressables change (PR #5475) was reverted because the Mac build
failed. The issue was that Addressables content must be built before the
player build, which the CI scripts weren't doing.
This commit:
- Reapplies the Addressables changes (revert of the revert)
- Adds Editor/BuildScript.cs with methods to build Addressables content
before building the player
- Updates CI build scripts to use -executeMethod BuildScript.Build*Player
instead of -buildWindows64Player/-buildOSXUniversalPlayer
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Documents the completion of Phase 10 library test migration:
- 18 test files successfully migrated to Scala types
- 8 files deferred with documented rationale (shared utilities, API
boundary tests, complex proto integration)
- 1 orphaned test file deleted
- Added migration patterns and key conversions reference
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Convert test assertions from using proto ActionResult types to native
Scala types (ActionResultT, ChangedProvinceC, ChangedHeroC, ChangedFactionC,
UnaffiliatedHeroC, etc.). Keep proto types for input data construction
with GameStateConverter.fromProto() conversion.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Convert test to use native Scala model types instead of ScalaPB protos:
- Replace proto GameState, Hero, Province, Faction, Battalion with Scala types
- Use Scala Gender, Profession, UnaffiliatedHeroType, RecruitmentInfo enums
- Use FactionRelationship.RelationshipLevel instead of proto enum
- Use native Scala quest types (WealthQuest, TruceCountQuest)
- Use BackstoryVersion from Scala model
- Remove all proto converters (DateConverter, FactionConverter, etc.)
- Remove 16 proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Convert test to use native Scala model types instead of ScalaPB protos:
- Replace proto Date, Gender, Profession with Scala equivalents
- Use DeferredChange.* for BlizzardStarted, DroughtStarted, etc.
- Use ProvinceOrderType Scala enum
- Use RecruitmentInfo and UnaffiliatedHeroType enums
- Use GameState, ProvinceC, HeroC, FactionC, UnaffiliatedHeroC directly
- Remove 15 proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Convert from proto types with ScalaPB lens syntax to native Scala
models with .copy():
- Replace proto GameState/Province/Faction/Hero with Scala types
- Replace proto FactionRelationship/PrestigeModifier with Scala types
- Replace proto DiplomacyOffer with Scala TruceOffer
- Replace proto UnaffiliatedHero with UnaffiliatedHeroC
- Replace proto RecruitmentInfo/Status with Scala RecruitmentInfo
- Remove GameStateConverter.fromProto() calls
- Remove IDable.mapifyFactions/mapifyHeroes/mapifyProvinces helpers
- Remove 13 proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Tutorial improvements: queue-based system, skip Turn Cycle for AI
Refactored tutorial system to use a queue-based approach:
- Tutorials that can't be shown immediately are queued
- Queue is processed when current tutorial completes or is skipped
- Removes complex interruption logic and pending profession tracking
- Prerequisites checked when dequeuing (may have changed since enqueue)
Turn Cycle tutorial only shows for multiplayer games (>1 human player):
- Added TutorialManager.IsMultiplayerGame static property
- Set from ConnectionHandler when creating/joining games
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix profession tutorial timing after onboarding
- Remove ArePrerequisitesMet check from OnGameEvent so tutorials
can be queued during onboarding even when prerequisites aren't
met yet. ProcessQueue checks prerequisites when showing them.
- Process the tutorial queue when entering a hidden step, allowing
queued tutorials to show while onboarding waits for async events.
- Add re-entrancy guard to ProcessQueue to prevent nested calls
from StartSequence -> StopCurrentSequence -> ProcessQueue causing
tutorials to be started and immediately overwritten.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace proto imports with native Scala types:
- Use Date, ProvinceC, HeroC, BattalionC
- Use Army, MovingArmy, CombatUnit, Supplies
- Use BattalionType, BattalionTypeId
- Use GameState directly instead of via converter
Remove 8 proto dependencies from BUILD.bazel.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace proto imports with native Scala types:
- Use Date, ProvinceC, FactionC, HeroC, BattalionC
- Use Army, MovingArmy, Supplies for army types
- Use ProvinceEvent subtypes for events
- Use ProvinceEventRolls from action package
- Use GameState directly instead of via converter
Remove 9 proto dependencies from BUILD.bazel.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Replace proto imports with native Scala model types
- Use GameState, ProvinceC, FactionC, Army, MovingArmy, HostileArmyGroup
- Update FactionRelationship and RelationshipLevel imports
- Remove proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace proto types with native Scala equivalents:
- GameState, Province, Hero, Faction, Date
- UnaffiliatedHero, RecruitmentInfo, UnaffiliatedHeroType
- Remove IDable helpers, GameStateConverter.fromProto
- Remove 6 proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace proto types with native Scala equivalents:
- GameState, Province, Hero, Faction, Battalion
- Army, MovingArmy, HostileArmyGroup, CombatUnit
- Remove 10 proto dependencies from BUILD.bazel
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Migrate PerformHostileArmySetupActionTest to pure Scala types
Replace proto types (GameState, Province, Army, MovingArmy, RoundPhase)
with their Scala equivalents. Construct GameState directly instead of
using GameStateConverter.fromProto. Remove dependency on IDable.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Refactor PerformHostileArmySetupActionTest for clarity
* Update PerformHostileArmySetupActionTest.scala
* Add Inside matcher import for tests
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Migrate FeastCommandTest from proto to Scala ActionResultType
Replace proto ActionResultType.FEAST import with Scala ActionResultType.Feast
and remove proto dependency from BUILD.bazel.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix assertion for actionResultType in FeastCommandTest
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Full migration of test data from proto types to native Scala model types.
Key changes:
- Replace proto Army, CombatUnit, Battalion with Scala equivalents
- Remove ProvinceConverter.fromProto - construct ProvinceC directly
- Remove IDable.mapifyFactions usage
- Remove 10 proto deps, 3 converter deps, and idable from BUILD.bazel
- Add Scala state type dependencies
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Full migration of test data from proto types (Province, Hero, Faction,
Battalion, GameState) to native Scala model types (ProvinceC, HeroC,
FactionC, BattalionC, GameState).
Key changes:
- Replace ScalaPB .update() lens syntax with Scala .copy() methods
- Remove GameStateConverter.fromProto usage
- Remove 6 proto deps from BUILD.bazel
- Add equivalent Scala type dependencies
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
BattalionTypesTestData, impl/package.scala, and IDable.scala are shared
test utilities that use proto types and have cascading dependencies.
These need to be migrated together with their dependent tests.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The questDetailsProto, questProto, and euh variables using proto types
were defined but never used in any test. Removed the dead code and
proto dependencies.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The residentHeroes variable using proto Hero was dead code (created but
never used, alongside commented-out test code). Removed the variable,
the commented code, and the proto dependency.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove unused proto imports and dead code (questDetailsProto, questProto).
The test already uses Scala model types; these proto variables were never used.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Remove deleted stock images section (already removed files)
- Document Market Day (RandomMind) and Shopping List (Komiku) as free-to-use
- Flag Medieval: Victory Theme as needing verification
- Flag Dima Koltsov Audius tracks for eventual replacement (Open Music License, not CC)
- Update action items and recommendations
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Mark production library/ code as complete
- Remove detailed history of completed phases
- Add Phase 10: Library Test Migration with 27 files to migrate
- Categorize files by proto import count (light/medium/heavy)
- Include key type conversions and recommended migration order
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Convert SoundManager music loading to Addressables
Replace synchronous Resources.LoadAll calls with async Addressables loading.
This enables music files to be updated independently via Addressables catalog,
reducing download sizes for game updates.
- Add LoadMusicAsync coroutine using Addressables.LoadAssetsAsync
- Load music by label (music-spring, music-summer, etc.) instead of path
- Add MusicLoaded property to check when music is ready
- Guard PlayMusic() against empty clips during async loading
Note: Requires Unity Editor setup to mark music files as Addressable assets
and assign the corresponding labels.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Auto-play music after async loading completes
If a music type was requested before loading finished, start playing
once the music is ready. This ensures music plays even if
CurrentMusicType was set during the async load window.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Prioritize Summer/Autumn music loading, parallelize rest
- Load Summer first (most likely starting season)
- Load Autumn second (next season after Summer)
- Load remaining music (Spring, Winter, Travel, Battle, Victory) in parallel
- Try to start playing after each priority load completes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix: Don't interrupt playing music after async load
TryStartMusicIfNotPlaying() only starts music if:
- Music type was requested (not MusicNone)
- Music isn't already playing
- The requested type's clips are loaded
This prevents interrupting Summer playback when Autumn finishes loading.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix missing Clipboard Button texture in Chronicle canvas
Replace broken Unity built-in sprite reference with btn_icon_copy.png
from GUI Pro Kit. Also change Image Type to Simple with Preserve Aspect
for proper icon rendering.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Revert "Fix missing Clipboard Button texture in Chronicle canvas"
This reverts commit c747e78ea2dbe03ed5215566c6f1a6f2ac8dbdb2.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Verify code signature before attempting to staple (catches transfer corruption)
- Increase retry attempts from 5 to 10
- Increase wait between retries from 10s to 30s (total wait up to 5 minutes)
- Capture and display stapler error output for better debugging
- Show signature details if verification fails
This addresses intermittent stapling failures due to Apple CloudKit
propagation delays after notarization completes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove stale proto exports from province:province
The province target was exporting province_event_scala_proto,
army_scala_proto, and unaffiliated_hero_scala_proto but no downstream
targets actually needed these through that path. Removing these stale
exports reduces library/ proto deps from 41 to 26.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix unused import and stale test dependency
Remove unused LlmRequestT import and stale unaffiliated_hero_scala_proto
dependency that were exposed by removing the province proto exports.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix test build failures from stale proto dependencies
- Remove unused ProtoMatchers from tests that don't use it
- Add missing unaffiliated_hero_quest_scala_proto dep to quest_fulfillment_utils_test
- Remove stale unaffiliated_hero_quest_scala_proto dep from check_for_fulfilled_quests_action_test
- Keep ProtoMatchers in ResolveBattleActionTest (uses equalProto matcher)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove unused eagle_unit deps from resolved_eagle_unit and
province_conquered_action. These files use ResolvedEagleUnit (which
uses Scala types), not EagleUnit (which uses proto types).
This breaks the transitive proto dependency chain, reducing library/
proto deps from 74 to 41.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Shardok battle tutorials
Adds three introductory tutorials for tactical combat:
1. Victory conditions - explains attacker/defender win conditions
2. Unit placement - how to position units before battle
3. Battle controls - click to select, click to move/attack, ctrl+click for specials
Tutorials chain with prerequisites and trigger at appropriate game states.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Split victory conditions into multi-step tutorial
Expands the victory conditions tutorial into three steps:
1. "Battle!" - introduces hex-grid combat, shows if player is attacker/defender
2. "Attacker Victory" - capture all castles + hold, or eliminate defenders, by Day 31
3. "Defender Victory" - eliminate attackers, or hold out past Day 31 (scattering works)
Adds IsDefender property to ShardokGameModel for role detection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix profession tutorial bugs
- Fix duplicate hero name appearing multiple times by storing original
descriptions in a dictionary
- Fix Champion description: Duel, not Fear
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Migrate ResolvedShardokPlayer to use native Scala EndGameCondition
instead of the proto type. This removes 1 transitive proto dependency
from library/ (75 → 74).
Changes:
- Add Scala EndGameCondition sealed trait with Victory, AllyVictory,
Draw, and Loss cases
- Add DrawType enum for draw conditions
- Add EndGameConditionConverter in ShardokBattleConverter
- Update ResolvedShardokPlayer to use Scala EndGameCondition
- Move proto conversion to ShardokInterfaceGrpcClient boundary
- Update ResolveBattleAction to use Scala EndGameCondition API
- Update ResolveBattleActionTest to construct Scala EndGameCondition
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove stale proto_converter deps from library/ BUILD files
Library code should not depend on proto_converters - those belong at
the service layer boundary. Removed 9 stale proto_converter deps that
were no longer used by any Scala code.
The only remaining proto_converter dep is shardok_battle_converter,
which is actually used by ResolveBattleAction.scala (to be fixed in
a follow-up PR).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move ShardokBattle boundary conversion to ShardokInterfaceGrpcClient
Previously, BattleResolution contained a proto ShardokBattle, requiring
ResolveBattleAction in library/ to depend on ShardokBattleConverter.
This violated the boundary principle where proto conversions should
happen at the service layer, not in library code.
This change:
- Updates BattleResolution.battle to use Scala ShardokBattle
- Moves the proto-to-Scala conversion into ShardokInterfaceGrpcClient
- Removes ShardokBattleConverter dependency from library/ code
- Updates tests to use the scalaBattle() helper for conversion
This completely removes proto_converters dependencies from library/
(the last one was shardok_battle_converter).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add linter check for library/ proto_converters boundary
Adds Rule 3 to check_build_deps.sh that verifies library/ code does not
depend on proto_converters. Proto conversions should happen at service
boundaries (ShardokInterfaceGrpcClient, EagleServiceImpl, etc.), not in
library code.
Also updates baseline from 167 to 75 proto deps reflecting recent cleanup.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Documents the design for implementing delta patches in the Mac auto-update
system to reduce download sizes from ~200MB to ~10-30MB per update.
Covers:
- Appcast XML structure with sparkle:deltas
- S3 storage layout for app bundles and deltas
- BinaryDelta tool usage
- Migration strategy and error handling
- Storage impact analysis (~3.25GB total for 85% bandwidth savings)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Use run-specific artifact names (signed-mac-app-{run_id}, notarized-mac-app-{run_id})
to prevent race conditions where a previous run's cleanup job might delete
artifacts from a concurrent run.
Root cause: When PR #5465 merged, the previous run (which used the old workflow
that deleted ALL artifacts by name) ran its cleanup job between when the new
run uploaded its artifact (06:13:20) and when it tried to download it (06:18:27).
Fix: Include the GitHub run ID in artifact names so each run's artifacts are
uniquely named and cleanup jobs can only delete their own artifacts.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Library code should not depend on proto_converters - those belong at
the service layer boundary. Removed 9 stale proto_converter deps that
were no longer used by any Scala code.
The only remaining proto_converter dep is shardok_battle_converter,
which is actually used by ResolveBattleAction.scala (to be fixed in
a follow-up PR).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Eagle server build to CI
Add eagle_build.yml workflow that builds the Eagle server when Scala
or Eagle proto files change. This ensures build failures in the Eagle
server are caught during PR review, similar to how shardok_build.yml
works for the C++ Shardok server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix unused dependencies and simplify GameAdminServiceImpl
- Remove unused json4s and settings_loader deps from :service target
- Simplify GameAdminServiceImpl since GamesManager now returns
game_admin proto types directly (no conversion needed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Read version from the built app's Info.plist instead of calculating
from git commit count at deploy time. This ensures the appcast version
always matches what's actually in the app bundle.
Previously, the deploy step used `git rev-list --count HEAD` which could
differ from the version baked into the Unity build if commits were added
between the build and deploy steps.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add first-encounter profession tutorials
Shows tutorial popup explaining each profession's abilities when player
first encounters a hero with that profession. Covers both strategic
(Eagle) and tactical (Shardok) abilities in natural language.
Triggers when viewing provinces with employed or free heroes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update mage and paladin profession tutorial text
- Mage: Can create *or end* blizzards and droughts
- Paladin: Giving alms provides much larger support boost
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Now that the admin console uses the GameAdmin service (PR #5460),
remove the duplicate admin endpoints from eagle.proto and their
implementations from EagleServiceImpl.
Changes:
- Remove 14 admin RPC endpoints from Eagle service in eagle.proto
- Remove corresponding message types (AddSettings*, Get*, Convert*,
Reassign*, Rewind*, Import*, Delete*, CheckGame*, DownloadGame*)
- Remove admin method implementations from EagleServiceImpl.scala
- Update GamesManager to import admin response types from game_admin
proto instead of eagle proto
This completes the admin service separation:
- PR #5458: Create GameAdmin service
- PR #5460: Migrate admin console to GameAdmin
- This PR: Remove admin endpoints from Eagle
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
After the GameHistory split, EagleServiceImpl needs to access
shardokPlayerCount from controller.fullHistory instead of
controller.engine.history. Also export full_game_history from
game_controller so downstream targets can see the FullGameHistory type.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
With all proto imports removed from library/ Scala code, these proto
dependencies in BUILD.bazel files are no longer needed. This completes
the deproto milestone for the library/ layer.
- Update DEPROTO_PLAN.md to reflect 0 proto files in library/
- Remove unused proto deps from library/BUILD.bazel, util/, actions/impl/
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update admin_server.go to call the new GameAdmin gRPC service instead
of Eagle for all game management operations. This is step 2 of the
admin service separation:
- Replace grpcClient (EagleClient) with gameAdminClient (GameAdminClient)
- Update all game admin RPC calls to use gameadminpb types
- Add game_admin_go_proto dependency to BUILD.bazel
- Fix game_admin BUILD.bazel to use go_grpc instead of go_grpc_v2
(v2 only generates gRPC code, not message types)
Auth operations still use authpb (via Eagle's Auth service).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Create a separate GameAdmin service to handle admin-only game management
operations (RewindGame, DeleteGame, GetRunningGames, etc.) that were
previously exposed through the Eagle service alongside client endpoints.
This is the first step in separating admin and client APIs:
- Creates game_admin.proto with all 13 admin game endpoints
- Implements GameAdminServiceImpl calling the existing GamesManager
- Registers the new service in Main.scala on the same port as Eagle
The admin console will be migrated to use this service in a follow-up PR,
after which the admin endpoints can be removed from eagle.proto.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Go's XML unmarshaler doesn't preserve xmlns attributes correctly.
When the appcast was fetched and re-marshaled, the Sparkle namespace
was being set to empty string, which prevented Sparkle from parsing
version numbers and detecting updates.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change from com.Shardok-Games.eagle0 to net.eagle0.eagle0 to match
the project's domain (net.eagle0).
Updated in:
- Unity ProjectSettings (applicationIdentifier for Standalone)
- inject_sparkle.sh (CFBundleURLName for URL scheme)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Sparkle's default check interval is 24 hours. If a user launches the app
shortly after a new version is deployed but before their scheduled check,
they won't see the update prompt.
This adds an explicit background check on every startup to ensure users
get updates promptly when a new version is available.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Make Engine API protoless by introducing Scala ActionResultView
Created Scala ActionResultView type to replace proto ActionResultView
in the Engine's public API. Proto conversion now happens at the gRPC
boundary in HumanPlayerClientConnectionState.
Changes:
- Created model/view/action_result/ActionResultView.scala
- Created proto_converters/view/action_result/ActionResultViewConverter.scala
- Updated ActionResultFilter to return Scala ActionResultView
- Updated Engine and EngineImpl to use Scala type
- Updated HumanPlayerClientConnectionState to convert to proto at gRPC boundary
- Removed proto dependencies from library/
This follows the architecture principle that the Engine should vend a
pure Scala model API, with proto conversions happening at the outer
boundary layer (GameController/GamesManager).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Split GameHistory into protoless GameHistory and FullGameHistory
- Create FullGameHistory trait in service/ that extends GameHistory
and adds Shardok-specific methods (shardokPlayerResultsSince,
shardokPlayerAvailableCommands)
- Remove receiveBattleUpdate from Engine trait (moved to GamesManager)
- Update GameController to hold fullHistory: FullGameHistory field
- Update PersistedHistory and InMemoryHistory to extend FullGameHistory
- Add extractFullHistory helper methods for safe type extraction
- Update tests to use FullGameHistory mocks
This continues the deproto effort by isolating proto-dependent
Shardok methods from the library layer's GameHistory trait.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Regenerate ICO file with proper format:
- Use PNG compression for 256x256 size (Windows Vista+ requirement)
- Use BMP format for smaller sizes (16, 24, 32, 48)
- Proper BITMAPINFOHEADER structure for BMP entries
The previous ICO may not have displayed correctly due to format issues.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Reduce build size: optimize Twitch button, remove unused profession icons
1. glitch_flat_purple.png (Twitch OAuth button):
- Reduce maxTextureSize from 2048 to 512
- Enable crunched compression
- Expected savings: ~8 MB
2. Delete Resources/Professions folder:
- These icons were unused (profession textures are assigned via
EagleCommonTextures component, not loaded from Resources)
- Expected savings: ~7 MB (5.3 MB paladin + others)
Total expected savings: ~15 MB
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Move actually-used icons from Unused to InUse folder
22 icons from 4000_Fantasy_Icons/Unused were actually referenced by
Gameplay.unity (for flame effects, UI elements, etc). Moving them
to an InUse folder makes the naming accurate.
Unity tracks assets by GUID, so moving files with their .meta files
preserves all scene/prefab references.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Uses the same eagle head icon that was added to the Unity/macOS app.
Converted from PNG to ICO format with multiple sizes (16-256px).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Unity creates .meta files for the native plugins which should also
be ignored since the plugins are built at CI time.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Created Scala ActionResultView type to replace proto ActionResultView
in the Engine's public API. Proto conversion now happens at the gRPC
boundary in HumanPlayerClientConnectionState.
Changes:
- Created model/view/action_result/ActionResultView.scala
- Created proto_converters/view/action_result/ActionResultViewConverter.scala
- Updated ActionResultFilter to return Scala ActionResultView
- Updated Engine and EngineImpl to use Scala type
- Updated HumanPlayerClientConnectionState to convert to proto at gRPC boundary
- Removed proto dependencies from library/
This follows the architecture principle that the Engine should vend a
pure Scala model API, with proto conversions happening at the outer
boundary layer (GameController/GamesManager).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add styled DMG with arrow pointing to Applications
- Add create-dmg as a bazel dependency for creating styled DMG installers
- Add background image with arrow pointing from app to Applications
- Update mac_build_handler to use create-dmg for styled DMG creation
- Update CI workflow to use the new DMG creation process
The DMG now shows a visual arrow guiding users to drag the app
to the Applications folder.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add --skip-jenkins flag for headless CI environment
The AppleScript that positions icons times out in CI environments
without a GUI session. Using --skip-jenkins skips the Finder styling
while still including the background image and Applications link.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Switch from create-dmg to dmgbuild for CI compatibility
dmgbuild generates .DS_Store files programmatically without needing
AppleScript or Finder access, making it work in headless CI environments.
- Remove create-dmg bazel dependency (relied on AppleScript)
- Use dmgbuild Python tool instead (pip install dmgbuild)
- Generates proper icon positions and background without GUI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix DMG styling: lighter background, adjust icon positions
- Use light gray background so file/folder names are readable
- Move arrow up to align with icon centers
- Move Applications link further right (500 -> 520) to clear the arrow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix arrow: move left, connect head to shaft properly
- Arrow now starts at x=200 (closer to app icon)
- Arrow ends at x=460 (further from Applications at x=520)
- Head and shaft now overlap for proper connection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Created Scala ActionResultView type to replace proto ActionResultView
in the Engine's public API. Proto conversion now happens at the gRPC
boundary in HumanPlayerClientConnectionState.
Changes:
- Created model/view/action_result/ActionResultView.scala
- Created proto_converters/view/action_result/ActionResultViewConverter.scala
- Updated ActionResultFilter to return Scala ActionResultView
- Updated Engine and EngineImpl to use Scala type
- Updated HumanPlayerClientConnectionState to convert to proto at gRPC boundary
- Removed proto dependencies from library/
This follows the architecture principle that the Engine should vend a
pure Scala model API, with proto conversions happening at the outer
boundary layer (GameController/GamesManager).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove proto Date from GeneratorUtilities
All callers use Scala Date, so the proto Date overload is unused.
- Remove import of net.eagle0.eagle.common.date.Date
- Remove the proto Date overload of dateString()
- Keep only the Scala Date version
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove proto types from LLM prompt generators
Migrated 8 files to use Scala types instead of proto:
- BattalionDescriptions: uses BattalionTypeId, BattalionT, BattalionView
- ChronicleEventTextGenerator: uses ChronicleEvent sealed trait
- ChronicleUpdatePromptGenerator: removed ChronicleEventConverter
- HeroBackstoryUpdatePromptGenerator: removed BattalionViewConverter, QuestConverter
- QuestEndedGeneratorUtilities: uses QuestT/QuestC types
- QuestFailedPromptGenerator: removed QuestConverter
- QuestFulfilledPromptGenerator: removed QuestConverter
Updated DEPROTO_PLAN.md to accurately reflect completion of Phase 8
(LLM Prompt Generators) and Phase 8b (Hostility Migration).
All non-boundary library code is now protoless.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The IsRunningFromReadOnlyVolume and ShowDMGWarning functions were
not being exported from the native plugin, causing the DMG launch
check to fail silently.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Runs every 6 hours to check total artifact storage. Fails if storage
exceeds 500 MB and lists the largest artifacts for debugging.
Steady-state should be ~50 MB (logs and test results). This acts as
a safety net in case cleanup jobs fail to run properly.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The cleanup step was deleting ALL signed-mac-app artifacts across the
entire repo, not just the current run's. When two main builds ran
concurrently, one build's cleanup would delete the other's artifacts.
Fix: Change cleanup to only delete the current run's artifacts using
the runs/$run_id/artifacts API endpoint instead of deleting all
artifacts matching the name.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a step to the bazel_test workflow that runs check_build_deps.sh
in strict mode. This enforces architectural boundaries:
- src/main should not depend on src/test
- library/ proto dependencies should not exceed baseline
Runs before tests so failures are caught early.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The test tutorials ("You've issued your first command!") were
accidentally left enabled in the Gameplay scene.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace Hostility proto with Scala Hostility enum
This removes direct proto imports from library/ for the Hostility type:
- FactionUtils.hostilityStatus now returns Scala Hostility
- ArmyStats.hostility now uses Scala Hostility type
- AvailableAttackDecisionCommandFactory no longer imports proto
- AvailableFreeForAllDecisionCommandFactory no longer imports proto
- AttackDecisionCommandChooser uses Hostility.Enemy instead of proto
- BattleFilter no longer needs protoToScalaHostility converter
- AvailableCommandConverter uses HostilityConverter.toProto
This completes Phase 8b of the deproto migration, reducing direct
proto imports in library/ from 8 files to 4 (LLM prompt generators).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix AvailableCommandConverterTest to use Scala Hostility enum
Update test to use net.eagle0.eagle.model.state.Hostility.Self
instead of proto net.eagle0.common.hostility.Hostility.SELF_HOSTILITY.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add retention-days to all GitHub Actions artifacts
- Use retention-days: 1 for artifacts deployed to external storage
(sysroot, installer, mac app builds)
- Use retention-days: 3 for debug logs and test results
Prevents artifact storage quota from being exceeded.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete Mac build artifacts after successful deploy
Automatically deletes signed-mac-app and notarized-mac-app artifacts
after deployment completes. These ~250MB artifacts are only needed to
pass the app between workflow jobs; once deployed, they're redundant.
This prevents artifact storage from accumulating even if retention-days
doesn't expire them quickly enough.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete ALL old build artifacts after deploy (not just current run)
Both Mac and Windows installer workflows now delete ALL artifacts
with their respective names after successful deployment:
- Mac: signed-mac-app, notarized-mac-app
- Windows: eagle-installer
This ensures no artifact buildup even from failed/stale runs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Increase retention for test/build logs to 7 days
These are small files useful for debugging, so keep them longer:
- test.json (~100KB each)
- editor_win.log / editor_mac.log (~60KB each)
- failed-test-logs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use 5-day retention to match repository maximum
The repository has a 5-day maximum retention policy configured.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Delete old Mac builds from DigitalOcean when pruning appcast
The appcast keeps the last 10 versions, but the old DMG files were never
deleted from S3. Now when items are removed from the appcast, the
corresponding DMG files are also deleted from DigitalOcean Spaces.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add cleanup job that runs even when build fails
Previously, artifact cleanup only happened in the deploy job, which
doesn't run if earlier jobs fail. This left behind large artifacts
from failed builds, eventually hitting the storage quota.
The new cleanup job runs on ubuntu-latest with `if: always()` so it
executes regardless of whether build-and-sign, wait-notarization,
or deploy succeeded or failed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add BUILD.bazel dependency linting
Adds tooling to enforce architectural boundaries in the codebase:
1. Shell script `scripts/check_build_deps.sh` with multiple modes:
- Default: runs all checks and shows dependency counts
- --ci: fails only on hard violations (src/main depends on src/test)
- --strict: also fails if proto deps in library/ increase from baseline
- --count: just show current dependency counts
- --update-baseline: update the baseline file for tracking
2. Bazel test `//:build_deps_test` for integration with test suite
3. Baseline file `scripts/build_deps_baseline.txt` tracking proto dep count
Current enforced rules:
- src/main must not depend on src/test (enforced now)
- library/ proto dependencies tracked (167 currently, deproto in progress)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update DEPROTO_PLAN.md with accurate proto import inventory
The previous status was inaccurate. This update:
- Corrects summary table: Availability, Command Choice Helpers,
and other utilities still have Hostility proto imports
- Adds Phase 8b documenting Hostility proto migration (4 files)
- Updates detailed inventory showing actual 13 files with direct
proto imports (vs 167 transitive deps from bazel query)
- Corrects success criteria checkboxes to reflect actual status
- Documents the grep command used to verify imports
The 167 proto deps from bazel query are transitive dependencies.
Only 13 files have direct proto imports:
- 5 boundary files (expected to keep proto)
- 4 files using Hostility proto (Phase 8b)
- 4 LLM prompt generator files (Phase 8)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Mark Phase 8 (LLM Prompt Generators) as complete following PR #5429.
All 34 prompt generator files and all request generators now use Scala
LlmRequestT types instead of proto types.
Summary:
- Total proto imports: ~7 (down from 149)
- All remaining proto usage is at system boundaries (gRPC, persistence)
- All success criteria met
- All open questions resolved
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Unity auto-updated audio import settings:
- Serialized version 6 → 8
- Quality setting adjusted
- Preload audio data setting moved
Font SDF asset regenerated.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Convert all remaining LLM prompt generators to Scala types
This converts all remaining prompt generators from proto types to Scala
types (LlmRequestT). Updates include:
- All prompt generators now use LlmRequestT.* instead of proto types
- LlmResolver uses Scala type matches for all generators
- Proto fallback cases removed (now throw exceptions for non-LLM types)
- Updated BUILD files with correct dependencies
- Fixed test file to use Scala types
Note: ChronicleUpdatePromptGeneratorTest still needs to be updated to use
Scala types in a follow-up PR.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix ChronicleUpdatePromptGeneratorTest to use Scala types
Update the test to use LlmRequestT.ChronicleUpdateMessage and related
Scala types instead of proto types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The deferred flag logic was inverted - we were collecting notifications
where deferred=true (hold for later) instead of deferred=false (send now).
This bug was introduced in commit 8d3bc914ed when migrating from proto
types to Scala types. The original proto-based code used notificationsToDeliver
which already handled the deferred distinction. The new Scala code used
NotificationConverter.toProto() which returns (proto, deferred) tuple,
but the .collect pattern incorrectly filtered for deferred=true.
Affected features:
- Divine command notifications (learning about quests)
- Quest completion/failure notifications
- Diplomacy event notifications (alliance, truce, etc.)
- All other immediate notifications
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add in-app warning when running from Downloads folder
Sparkle auto-update framework cannot update apps running from the
Downloads folder or other transient locations. This adds a native
macOS alert that warns users when they launch the app from an
invalid location and instructs them to move it to Applications.
The check detects:
- /Downloads/ - where macOS downloads files
- /tmp/ or /private/tmp/ - temporary directories
- /.Trash/ - deleted files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix build: import AppKit for NSAlert
NSAlert is part of AppKit, not Foundation. Changed import to AppKit
which also includes Foundation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Offer to move app to /Applications and relaunch
Instead of just showing a warning, now offers to:
1. Copy the app to /Applications (removing existing if present)
2. Launch the new copy
3. Terminate the current instance
This provides a seamless experience - user just clicks "Move to
Applications" and the app handles everything automatically.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Switch Mac distribution from ZIP to DMG
Changes the Mac app distribution from a ZIP file to a DMG disk image.
This provides a better installation experience and enables enforcing
proper installation:
1. **DMG with Applications alias**: Users see the app and an
Applications folder alias, making drag-to-install intuitive.
2. **Read-only enforcement**: DMGs are mounted read-only, so if
users try to run the app directly from the DMG, we detect it
and show a blocking dialog telling them to install first.
3. **Sparkle works from any writable location**: Unlike the previous
Downloads-folder check, this approach allows the app to run from
anywhere writable (~/Desktop, ~/Games, /Applications, etc.).
Changes:
- SparklePlugin.m: Replace location warning with read-only volume check
- SparkleUpdater.cs: Update P/Invoke bindings for new functions
- SparkleInitializer.cs: Check for read-only volume instead of path
- mac_build_handler.go: Create DMG instead of ZIP using hdiutil
- invitation_handlers.go: Update download URL and instructions for DMG
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update BreakAllianceResolutionMessagePromptGenerator to accept
LlmRequestT.BreakAllianceResolutionMessage instead of the proto type.
Add Scala type matching in LlmResolver and update BUILD dependency
from proto to Scala types.
Also updates enum handling from proto DiplomacyOfferStatus to Scala Status.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update BreakAllianceMessagePromptGenerator to accept LlmRequestT.BreakAllianceMessage
instead of the proto type. Add Scala type matching in LlmResolver and update BUILD
dependency from proto to Scala types.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update AllianceResolutionMessagePromptGenerator to accept
LlmRequestT.AllianceOfferResolutionMessage instead of the proto type.
Add Scala type matching in LlmResolver and update BUILD dependency
from proto to Scala types.
Also updates enum handling from proto DiplomacyOfferStatus to Scala Status.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
docker_build.yml was missing TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET
in its env block and export statements. All other OAuth providers
(Discord, Google, GitHub, Apple) were present, but Twitch was omitted
when the provider was added.
This caused Twitch to not appear on the invitation landing page after
Eagle deployments, even though auth_build.yml had the secrets.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update AllianceOfferMessagePromptGenerator to accept LlmRequestT.AllianceOfferMessage
instead of the proto AllianceOfferMessage type. Add Scala type matching in LlmResolver
and update BUILD dependency from proto to Scala types.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update TruceResolutionMessagePromptGenerator to accept
LlmRequestT.TruceResolutionMessage and use the Scala Status enum
instead of proto DiplomacyOfferStatus. Add Scala type matching in
LlmResolver.promptGenerator before falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update TruceOfferMessagePromptGenerator to accept
LlmRequestT.TruceOfferMessage instead of the proto TruceOfferMessage
type. Add Scala type matching in LlmResolver.promptGenerator before
falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add --no-deps to nginx and admin docker compose commands to prevent
them from cascading to auth. The auth container has secrets (like
TWITCH_CLIENT_ID) that are only available in auth_build.yml, not in
docker_build.yml. Without --no-deps, docker compose would recreate
auth with blank env vars when it detected config changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update DivineMessagePromptGenerator to accept LlmRequestT.DivineMessage
instead of the proto DivineMessage type. Add Scala type matching in
LlmResolver.promptGenerator before falling back to proto matching.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add sworn brotherhood guidance tutorial
- Add guidance_sworn_brotherhood tutorial that triggers when:
- Player has 4+ provinces, OR
- SwearBrotherhood command is available with good candidates (server-determined)
- Explains that sworn siblings become faction leaders for direct province control
- Mentions succession benefit if Warlord falls
Also update guidance_expand to note that vassals manage provinces without
a Warlord or sworn sibling present.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Improve sworn brotherhood tutorial with candidate detection
- Use SwornBrotherChooser logic to identify good candidates:
charisma >= 65, constitution >= 65, loyalty >= 95, ranked by power
- Dynamically update tutorial text to name the specific candidate
- Add 95 loyalty requirement and permanence info to description
- Keep 4+ provinces as alternative trigger condition
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix hero stat access - use int directly, not StatView
Charisma, Constitution, Strength, Agility, Wisdom are int properties,
only Loyalty uses StatView?.Stat pattern.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix TextEntry access - use .Text property for string value
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use gender-neutral language: Brotherhood → Kinship
- Command header: "Swear Kinship with Hero"
- Tutorial text uses "sworn siblings" and "swear kinship"
- Internal IDs updated: guidance_sworn_brotherhood → guidance_sworn_kinship
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Native plugin (SparklePlugin.m):
- Log app version and build number from Info.plist
- Log feed URL configuration
- Log whether public key is set
- Log auto-check plist setting
- Log current auto-check state after initialization
- Log last update check date
Unity initializer (SparkleInitializer.cs):
- Log Application.version and Unity version
- Log platform and product name
- Log AutomaticallyChecksForUpdates setting
This helps debug why Sparkle updates may not be working.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This is the next step in the incremental migration from proto to Scala types
in the LLM processing pipeline.
Changes:
- LlmRequestWithGameState now takes GeneratedTextRequestT instead of
GeneratedTextRequest (proto)
- LlmResolver.resolveLlmRequests returns Scala types in the result
- Proto conversion happens at boundaries:
- Converting to proto for LLMResponse when streaming
- Converting to proto for receiveStreamingLlmFailure
- Converting to proto in promptGenerator for individual prompt generators
- UnrequestedTextHandler no longer needs to convert to proto when calling
LlmResolver
The prompt generators still expect proto types - they will be updated
incrementally in follow-up changes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The nginx config is already deployed by docker_build.yml (which has
nginx/** in its trigger paths). Adding it to auth_build.yml was
redundant and broken (the production server isn't a git repo).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a user tries to sign in with an OAuth provider but doesn't have an
existing account, the auth service returns InvitationRequired status.
Previously this fell through to the default case and threw a generic
"Unknown OAuth status" exception, providing a poor user experience.
Now displays a friendly message explaining that an invitation is required
to create a new account, and suggests using an invitation link or signing
in with the original provider if they already have an account.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 14:02:19 -08:00
4385 changed files with 34764 additions and 14416 deletions
"build_file_content": "\npackage(\n default_visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n name = \"buf\",\n srcs = [\n \"@com_github_bufbuild_buf//:bin/buf\",\n ],\n tags = [\"manual\"], # buf is downloaded as a linux binary; tagged manual to prevent build for non-linux users\n)\n"
| No Time for Greatness | Dima Koltsov | CC BY 4.0 | [YouTube](https://www.youtube.com/watch?v=cQh0OWIFdgM) |
| Warriors of Demacia | Dima Koltsov | CC BY 4.0 | [YouTube](https://www.youtube.com/watch?v=yktSUMJn9ao) |
| Forest Queen Tale | Dima Koltsov | Presumed CC BY 4.0 | Not found on YouTube; other Dima Koltsov tracks are CC BY 4.0 |
| Valor | Dima Koltsov | CC BY 4.0 | [YouTube](https://www.youtube.com/watch?v=uoHYJRPcS2Y) |
| Clouds | Dima Koltsov | Presumed CC BY 4.0 | Not found on YouTube; other Dima Koltsov tracks are CC BY 4.0 |
**Note on Dima Koltsov tracks:** 3 of 5 tracks confirmed CC BY 4.0 via YouTube. 2 remaining tracks (Forest Queen Tale, Clouds) presumed same license but not verified.
---
@@ -129,42 +138,55 @@ All 26 tracks have CC licenses with proper attribution:
| ~~`Assets/Shardok/commandImages/bridge.png`~~ | **REPLACED** (2026-01-23) with AI-generated wooden rope bridge icon (ChatGPT/DALL-E 3, 512x512 PNG). No licensing restrictions - AI-generated for this project. |
|~~`Assets/Images/startFire.png`~~ | **REPLACED** (2026-01-23) with "Flame Icon" from [UXWing](https://uxwing.com/flame-icon/) (free for commercial use, no attribution required). Consolidated duplicate removed. |
| ~~`Assets/Shardok/commandImages/startFire.png`~~ | **DELETED** (2026-01-23) - duplicate removed, all references updated to use `Assets/Images/startFire.png` |
### Shardok Sound Effects
- **Location:** `Assets/Shardok/soundEffects/`
- **Count:** 56 MP3 files
- **Count:** 37 audio files (was incorrectly counted as 56 including .meta files)
Remaining 31 files presumed from Asset Store purchases; 3 verified from Zombie Monster Undead Collection.
4.**Terrain Hexes** - 85 hex tiles of unknown source
- **TODO:** Confirm if this is a Unity Asset Store purchase (owner believes it is)
3.~~**Terrain Hexes**~~ - **VERIFIED** (2026-01-23): Confirmed Unity Asset Store purchase
5.**StrategyGameIcons** - 138 icons of unknown source
- **TODO:** Investigate origin - check Unity Asset Store purchase history
4.~~**StrategyGameIcons**~~ - **VERIFIED** (2026-01-23): Unity Asset Store purchase (REXARD)
6.**AUDIUS music tracks** - Verify Dima Koltsov tracks are licensed for commercial use
5.~~**Medieval: Victory Theme**~~ - **VERIFIED** (2026-01-23): CC0 Public Domain by RandomMind ([Chosic](https://www.chosic.com/download-audio/28492/))
7.**Discord logo** (`Eagle/Discord-Logo-Blurple.png`) - Likely fine for "Login with Discord" button per Discord brand guidelines, but verify usage complies with their terms
6.~~**Dima Koltsov tracks**~~ - **MOSTLY VERIFIED** (2026-01-23): 3 of 5 confirmed CC BY 4.0 via YouTube. 2 remaining (Forest Queen Tale, Clouds) presumed same license.
7.~~**Discord logo**~~ - **OK** (2026-01-23): Usage complies with Discord brand guidelines for "Login with Discord" button
### Already Safe:
@@ -229,12 +249,13 @@ NuGet packages in `Assets/Packages/` all include LICENSE.TXT files:
## Recommendation
Before removing HTTP basic auth:
Before public release:
1.~~Delete or replace the 4 suspicious JPG/JPEG files in `Assets/Eagle/` and `Assets/Images/`~~**DONE**(2025-01-04)
2.Replace clip art images (`bridge.png`, `startFire.png`) with properly licensed alternatives (e.g., from [game-icons.net](https://game-icons.net) CC BY 3.0)
3. Verify source of `Assets/Shardok/soundEffects/` MP3s
4. Verify source of `Assets/Terrain Hexes/` and `Assets/StrategyGameIcons/`
5.If any are from early development with unclear licensing, replace them
1.~~Replace clip art images (`bridge.png`, `startFire.png`) with properly licensed alternatives~~**DONE**- see Section 4
2.~~Verify source of `Assets/Shardok/soundEffects/` MP3s~~**MOSTLY DONE** - 3 files flagged for replacement, rest presumed Asset Store
3.~~Verify source of `Assets/Terrain Hexes/`~~**DONE** - confirmed Asset Store purchase
4.~~Verify source of `Assets/StrategyGameIcons/`~~**DONE** - Unity Asset Store (REXARD)
5.~~Replace Dima Koltsov Audius tracks~~**MOSTLY DONE** - 3/5 confirmed CC BY 4.0, 2 presumed same
6.~~Find source of Medieval: Victory Theme or replace~~**DONE** - CC0 Public Domain by RandomMind
The bulk of your assets (95%+) are properly licensed Asset Store purchases or CC content.
**Protocol buffers should only be used at the edges** — for network serialization (gRPC) and disk persistence. Inside the Eagle game engine, all logic should operate on native Scala models.
**Migration strategy**: These files generate text for LLM prompts. They can be migrated to accept Scala types (`HeroT`, `FactionT`, `ProvinceT`) with converters at the call sites if needed.
**Priority**: Medium - These don't block other migrations and are isolated.
- [x] Zero proto imports in `/library/actions/impl/command/`
- [x] Zero proto imports in `/library/actions/availability/`
- [x] Zero proto imports in `/library/util/command_choice_helpers/`
- [x] Zero proto imports in `/library/actions/impl/action/`
- [x] Zero proto imports in `/library/util/view_filters/` (except BattleFilter boundary code)
- [x] Zero proto imports in `/library/util/` (except view filters)
- [x] Zero proto imports in `/service/new_game_creation/` (except input GameParameters)
- [ ] Zero proto imports in `/library/actions/llm_prompt_generators/`
- [ ] Zero proto imports in `/library/actions/llm_request_generators/`
### Architecture
- [x] Clear separation: Scala models (internal) vs Proto (boundaries)
- [x] Converters as the only bridge between domains
- [x] CommandFactory accepts/returns Scala types
- [x] AI layer fully protoless
- [x] FactionViewFilter returns Scala types
- [x] HeroViewFilter returns Scala types
- [x] ProvinceViewFilter returns Scala types
- [x] All view filters return Scala types
- [x] NewGameCreation uses Scala types
- [x] PersistedHistory accepts Scala types for new games
- [ ] LLM layer uses Scala types
- [ ] No "proto creep" into business logic
---
## Open Questions
1.**LLM Prompt Generators**: Should these accept Scala types directly, or is it acceptable to have proto usage here since they're generating text (not core game logic)?
2.~~**GameStateViewDiffer**: This works with view protos for client updates. Should it remain proto-based since it's generating client-facing data?~~**Resolved**: GameStateViewDiffer now uses Scala types internally (`GameStateViewDiff`, `ProvinceViewDiff`, etc.) and converts to proto at the boundary in `ActionResultFilter`.
3.**History Serialization**: Keep proto for persistence (good for schema evolution) or consider alternatives?
---
## Proto Import Inventory (Detailed)
**Current: ~65 proto imports across ~35 files** (as of 2026-01-17)
These files generate LLM prompts and primarily use `internal.generated_text_request.*` types plus some common enums like `DiplomacyOfferStatus` and `BattalionTypeId`.
**Migration strategy**: Can be migrated to Scala types when convenient, but low priority as they're isolated from core game logic.
This document compares streaming latency (time-to-first-token) and pricing across OpenAI, Anthropic (Claude), and Google (Gemini) models for use in Eagle's narrative text generation.
## Test Methodology
All tests were performed locally using curl with streaming enabled. Each model was tested 3 times with the same prompt:
> "Write a short paragraph about a brave knight who discovers a hidden cave. Make it vivid and descriptive."
Time-to-first-token (TTFT) was measured from request initiation to the first text content appearing in the stream.
Be able to support a small (10-50 user) private alpha, including with strangers.
Larger set of goals in [The Big Eagle TODO](https://docs.google.com/document/d/17RTt3-4Wl2AAVMRLodaC3a84E4de6xuQTWBPvCRM484/edit?pli=1&tab=t.0), but most of that is not necessary for MVP.
## Required
### Gameplay Productionization
- [x]~~All functionality works on production eagle / shardok servers~~
- [x]~~Acceptable latency in all regions~~
- [x]~~Shardok performance similar to QA~~
- [x]~~Error logging & alerting~~
- [x]~~Fix long disconnects on deployments~~
- [ ] Fix the Mac installer
- [x]~~Still not reconnecting after deployments~~
- [ ] Notify about client updates, button to come directly back
- [ ] Generatedtext healing
- [ ] Kill outstanding shardok requests when game is deleted
### Other Productionization
- [x]~~Oauth sign-in~~
- [x]~~Add Google, others?~~
- [ ] User management
- [x]~~Invite codes~~
- [ ] Link accounts
- [x]~~Choose display name~~
- [x]~~Just do account setup from the landing page?~~
- [x]~~Download client directly from DO, avoid basic auth/my home network~~
This document outlines the implementation plan for adding delta update support to the Eagle0 macOS auto-update system using Sparkle's BinaryDelta feature.
### Current State
- Full DMG downloads (~200MB) for every update
-`mac_build_handler.go` creates DMG, signs it, uploads to S3, updates appcast.xml
- Keeps last 10 versions in appcast, deletes older DMGs
- Users must download full app even for small changes
### Goals
- Reduce update download size from ~200MB to ~10-30MB (85% reduction)
- Maintain backward compatibility with full DMG downloads
- Automatic fallback for users who are many versions behind
## Sparkle Delta Update Architecture
Sparkle supports binary delta updates through the `<sparkle:deltas>` element in the appcast. When a user updates, Sparkle:
1. Checks if a delta patch exists from their current version to the new version
2. If found, downloads the smaller delta patch instead of the full DMG
3. Applies the patch locally to create the new app version
4. Falls back to full DMG if no matching delta exists
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.