Create docs/ folder at repo root and move documentation files: - CONNECTION_ARCHITECTURE.md (new comprehensive connection docs) - COMMAND_PROTO_USAGE_ANALYSIS.md - DEPROTO_PLAN.md - SCALA3_MODERNIZATION.md - actions-model-usage-analysis.md - occupants-optimization-report.md - scala3-reflection-issues.md CLAUDE.md remains at root (project instructions for Claude Code). Connection architecture documentation includes: - gRPC bidirectional streaming protocol details - Client-side connection management (PersistentClientConnection) - Server-side implementation (EagleServiceImpl) - nginx proxy configuration and timeouts - Timeout settings across all layers (client, nginx, server) - Eagle ↔ Shardok communication flow Critical findings: - 🔴 SECURITY: Shardok internal interface exposed without auth in nginx config - Mystery "2-minute timeout" doesn't exist in code (all timeouts are 5-20 minutes) - No state resync mechanism after connection drops during Shardok - Inefficient dual-layer heartbeat (HTTP/2 + application level) Hypotheses for remote player connection issues: - Most likely: NAT/firewall timeout at player's router/ISP (60-120s) - HTTP/2 keepalive (45s) may not be frequent enough to keep NAT alive - Shardok's bursty traffic pattern may appear "idle" at transport layer Recommendations: 1. Fix Shardok internal interface security vulnerability 2. Add precise connection drop logging with timestamps 3. Reduce HTTP/2 keepalive from 45s to 15s 4. Get network diagnostics from affected remote player 5. Implement state resync mechanism for Shardok 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
17 KiB
Actions and Commands Model Usage Analysis
This document analyzes all actions and commands in src/main/scala/net/eagle0/eagle/library/actions/impl to determine which use Scala models vs protobuf models, based on BUILD.bazel dependencies.
Legend:
- ✅ Scala Models Only - Uses only
//src/main/scala/net/eagle0/eagle/modeldependencies - ❌ Uses Protobuf - Has dependencies on
//src/main/protobuftargets - 🔄 Partial Conversion - Conversion attempted but blocked by dependencies
Summary
Based on BUILD.bazel dependency analysis (2025-09-16, updated 2025-09-17):
- Total Commands Analyzed: 41
- Commands Fully Migrated (No Protobuf): 41 (100%) ✅
- Commands Still Using Protobuf: 0 (0%) ✅
- Total Actions Analyzed: 48
- Actions Fully Migrated (No Protobuf): 5 (10.4%)
- Actions Partially Migrated: 19 (39.6%)
- Actions Still Using Protobuf: 24 (50%)
- Base Classes: 8 protoless variants available, 6 still use protobuf
- Shared Components:
ResolvedEagleUnitmigrated to useOption[BattalionT]for proper null handling
Conversion Insights
Based on conversion attempt of ResolveTruceOfferCommand (see PR #4379):
Key Challenges Discovered
-
LLM Integration Dependencies: Commands that use
DiplomacyResolutionLlmRequestGeneratorface challenges because the LLM system still expects protobuf enum types, not Scala model enums. -
Inconsistent Package Naming: Some files have inconsistent package declarations vs BUILD file locations (e.g.,
generated_text_request_generatorsin package vsllm_request_generatorsin BUILD). -
Model Constructor Differences: Scala model constructors (e.g.,
TruceOffer) have different required parameters than their protobuf counterparts, requiring more complex data mapping. -
Type System Complexity: Union types and type constraints become more complex when mixing protobuf and Scala model types during transition.
-
Cascading Dependency Issues: Converting to
ActionResultCrequires extensive trait dependencies (ChangedBattalionT,ChangedHeroT,GeneratedTextRequestT, etc.) that create complex BUILD dependency graphs, unlike simple protobufActionResult. -
BUILD Complexity: Each Scala model conversion requires significantly more BUILD dependencies than protobuf equivalents, making incremental conversion difficult.
-
Build Verification Critical: Any conversion must maintain working build state - even simple commands like
DefendCommandcan break main server build due to dependency cascades.
Successful Conversion Elements
- ✅ Base class conversion (
SimpleAction→ProtolessSimpleAction) - ✅ Import updates for most Scala model types
- ✅ BUILD.bazel dependency updates for core action result types
- ✅ Basic type conversions for simple cases
Recommended Conversion Strategy
- Architecture-First Approach: Convert base infrastructure (LLM generators, action result builders) before individual commands
- Wrapper Pattern: Use existing
Protoless*ActionWrapperclasses as templates for gradual transition - Dependency Analysis: Map full dependency trees before attempting conversions to avoid cascading build failures
- Batch Conversions: Convert related commands together to minimize dependency conflicts
- Build Verification: ALWAYS verify
//src/main/scala/net/eagle0/eagle:eagle_serverand test suite build before creating PRs
Conversion Requirements
Before creating any PR:
- ✅
bazel build //src/main/scala/net/eagle0/eagle:eagle_serversucceeds - ✅
bazel test //src/test/scala/... --keep_goingpasses (or doesn't introduce new failures) - ✅ All BUILD dependencies are correctly specified
- ✅ Scalafmt and other linters pass
Common Base Classes
| File | Type | Model Usage | Notes |
|---|---|---|---|
| Action.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto, game_state_scala_proto |
| ActionWithResultingState.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto, game_state_scala_proto |
| DeterministicSingleResultAction.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto, game_state_scala_proto |
| DeterministicSequentialResultsAction.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto, game_state_scala_proto |
| ProtolessRandomSequentialResultsAction.scala | Base Class | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model/action_result |
| ProtolessRandomSimpleAction.scala | Base Class | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model/action_result |
| ProtolessSequentialResultsAction.scala | Base Class | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model/action_result |
| ProtolessSimpleAction.scala | Base Class | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model/action_result |
| RandomSequentialResultsAction.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto, game_state_scala_proto |
| RandomSimpleAction.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto |
| RandomStateProtoSequencer.scala | Sequencer | ❌ Uses Protobuf | Bridge class, depends on both protobuf and Scala models |
| RandomStateTSequencer.scala | Sequencer | ❌ Uses Protobuf | Bridge class, depends on both protobuf and Scala models |
| SimpleAction.scala | Base Class | ❌ Uses Protobuf | Depends on action_result_scala_proto |
| VigorXPApplier.scala | Utility | ❌ Uses Protobuf | Depends on action_result_scala_proto |
Actions
✅ Fully Migrated Actions (No Protobuf Dependencies)
These actions have been successfully migrated to use Scala models only:
| File | Base Class | Notes |
|---|---|---|
| HeroBackstoryUpdateAction.scala | ProtolessSequentialResultsAction | Processes hero backstory updates with LLM integration |
| ProvinceConqueredAction.scala | ProtolessSimpleAction | Uses component-based design (gameId, currentRoundId, currentDate, Scala models) |
| ProvinceHeldAction.scala | ProtolessSimpleAction | Uses specific components (gameId, currentRoundId, defendingProvince, etc.) instead of full GameState |
| UnaffiliatedHeroAppearedAction.scala | ProtolessSimpleAction | Handles unaffiliated hero appearance with name generation |
| WithdrawnArmiesReturnHomeAction.scala | ProtolessSequentialResultsAction | Manages army withdrawal and return mechanics |
🔄 Actions Partially Migrated (Using Protoless Base Classes)
These actions use protoless base classes but still have some protobuf dependencies:
| File | Model Usage | Notes |
|---|---|---|
| CheckForFactionChangesAction.scala | ProtolessSequentialResultsAction | Still has some protobuf dependencies |
| CheckForFailedQuestsAction.scala | ProtolessSequentialResultsAction | Depends on unaffiliated_hero_quest_scala_proto |
| CheckForFulfilledQuestsAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndAttackDecisionPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndBattleAftermathPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndFreeForAllDecisionPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndPlayerCommandsPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndUnaffiliatedHeroActionsPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| EndVassalCommandsPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| FreeForAllDrawAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
| FriendlyMoveAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
| PerformUncontestedConquestAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| ProvinceConqueredAction.scala | ProtolessSimpleAction | CONVERTED - Uses specific components (gameId, currentRoundId, currentDate, Scala models) |
| SafePassageArmiesProceedAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| ShipmentArrivedAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
| TruceTurnBackPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
| UnaffiliatedHeroRejoinedAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
| WonFreeForAllAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
❌ Actions Still Using Protobuf (Not Yet Using Protoless Base Classes)
| File | Notes |
|---|---|
| ChronicleEventGenerator.scala | Depends on multiple protobuf targets |
| EndBattleRequestPhaseAction.scala | Depends on diplomacy_offer_status_scala_proto |
| EndBattleResolutionPhaseAction.scala | Depends on multiple protobuf targets |
| EndDefenseDecisionPhaseAction.scala | Depends on multiple protobuf targets |
| EndDiplomacyResolutionPhaseAction.scala | Depends on multiple protobuf targets |
| EndFreeForAllBattleRequestPhaseAction.scala | Depends on multiple protobuf targets |
| EndFreeForAllBattleResolutionPhaseAction.scala | Depends on multiple protobuf targets |
| EndHandleRiotsPhaseAction.scala | Depends on multiple protobuf targets |
| EndPleaseRecruitMePhaseAction.scala | Depends on multiple protobuf targets |
| EndProvinceMoveResolutionPhaseAction.scala | Depends on multiple protobuf targets |
| NewRoundAction.scala | Depends on multiple protobuf targets |
| NewYearAction.scala | Depends on multiple protobuf targets |
| PerformFoodConsumptionPhaseAction.scala | Depends on multiple protobuf targets |
| PerformForcedTurnBackAction.scala | Depends on multiple protobuf targets |
| PerformHeroDeparturesAction.scala | Depends on multiple protobuf targets |
| PerformHostileArmySetupAction.scala | Depends on multiple protobuf targets |
| PerformProvinceEventsAction.scala | Depends on province_event_scala_proto |
| PerformProvinceMoveResolutionAction.scala | Depends on multiple protobuf targets |
| PerformReconResolutionAction.scala | Depends on multiple protobuf targets |
| PerformUnaffiliatedHeroesAction.scala | Depends on unaffiliated_hero_quest_scala_proto |
| PerformVassalCommandsPhaseAction.scala | Depends on multiple protobuf targets |
| PerformVassalDefenseDecisionsAction.scala | Depends on multiple protobuf targets |
| PrisonerEscapeAction.scala | Depends on game_state_scala_proto |
| PrisonerExchangeAction.scala | Depends on multiple protobuf targets |
| RequestBattlesAction.scala | Depends on multiple protobuf targets |
| RequestFreeForAllBattlesAction.scala | Depends on multiple protobuf targets |
| ResolveBattleAction.scala | Depends on shardok_internal_interface_scala_grpc |
| UnaffiliatedHeroMovedAction.scala | Depends on multiple protobuf targets |
| UnaffiliatedHeroesChangedAction.scala | Depends on multiple protobuf targets |
Commands
✅ ALL COMMANDS FULLY MIGRATED (100% - 41/41 commands)
All 41 commands in the codebase have been successfully migrated to use Scala models only, with no protobuf dependencies. This includes:
- Simple Actions: Use
ProtolessSimpleActionbase class - Random Actions: Use
ProtolessRandomSimpleActionbase class - Complex Domain Models: Successfully integrated with LLM systems, diplomacy, quest fulfillment, and state management
- Complete Type Safety: All commands now use type-safe Scala domain models
Key Migration Achievements:
- ✅ All military commands (ArmTroops, Train, Organize, etc.)
- ✅ All diplomacy commands (Resolve Alliance/Truce/Ransom offers, etc.)
- ✅ All LLM-integrated commands (backstory generation, diplomacy resolution)
- ✅ All quest and event commands
- ✅ Final remaining command (FreeForAllDecisionCommand) migrated
Diplomacy Helpers
All diplomacy helpers use Scala models only:
| File | Model Usage | Notes |
|---|---|---|
| AllianceResolutionHelpers.scala | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model only |
| BreakAllianceResolutionHelpers.scala | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model only |
| InvitationResolutionHelpers.scala | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model only |
| RansomResolutionHelpers.scala | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model only |
| TruceResolutionHelpers.scala | ✅ Scala Models Only | Uses //src/main/scala/net/eagle0/eagle/model only |
Migration Priority Analysis
Based on the BUILD.bazel dependency analysis, here are the key findings and recommendations:
🎯 High Impact Migration Targets
Core Dependencies Blocking Multiple Commands:
-
action_result_scala_proto- Used by 12+ commands- Blocks:
DefendCommand,FreeForAllDecisionCommand, diplomacy resolvers - Impact: Would unlock many command migrations
- Blocks:
-
available_command_scala_proto/selected_command_scala_proto- Used by 10+ commands- Blocks: All UI-interactive commands
- Impact: Would enable client-server interaction model migration
-
game_state_scala_proto- Used by 8+ commands- Blocks: Complex state-dependent commands
- Impact: Core state representation migration
📊 Migration Tiers by Complexity
Tier 1 - Quick Wins (2 commands):
ArmTroopsCommand- Onlybattalion_typedependencyTrainCommand- Onlybattalion_typedependency- Effort: Low, Impact: Demonstrates battalion model usage
Tier 2 - API Layer (5 commands):
- Commands blocked by
available_command/selected_command - Effort: Medium, Impact: High (enables UI interaction models)
Tier 3 - Diplomacy Suite (6 commands):
- All
Resolve*Commanddiplomacy commands - Effort: High, Impact: High (complete diplomacy model migration)
- Strategy: Migrate as a group after diplomacy models are ready
🏆 Success Metrics
Current Status:
- ✅ 100% of commands fully migrated (41/41) 🎉
- ✅ All diplomacy helpers use Scala models
- ✅ All protoless base classes available
- ✅ ALL command migration completed
Completed Milestones:
- ✅ 70% target: Migrate Tier 1 + some Tier 2 commands COMPLETED
- ✅ 80% target: Continue with remaining non-diplomacy commands COMPLETED
- ✅ 85% target: Complete API layer migration COMPLETED
- ✅ 95% target: Complete diplomacy migration COMPLETED
- ✅ 100% target: Migrate final remaining command (FreeForAllDecisionCommand) COMPLETED
🎯 Action Migration Progress
Migration Statistics:
- 5/48 Actions fully migrated (10.4%)
- 20/48 Actions using protoless base classes but with protobuf dependencies (41.7%)
- 24/48 Actions still fully on protobuf (50%)
Successfully Migrated Actions:
- HeroBackstoryUpdateAction - LLM integration for hero backstories
- ProvinceConqueredAction - Component-based design with prisoner handling and province conquest
- ProvinceHeldAction - Component-based design pattern (gameId, currentRoundId, specific models)
- UnaffiliatedHeroAppearedAction - Hero appearance with name generation
- WithdrawnArmiesReturnHomeAction - Army withdrawal mechanics
Recent Migration Updates (2025-09-17):
- ResolvedEagleUnit - Changed
battalion: BattalionTtobattalion: Option[BattalionT]- Properly handles units without battalions (battalion ID -1)
- Updated
ShardokInterfaceGrpcClientto check fordefaultBattalionIdand useNone - Updated
ResolveBattleAction,ProvinceConqueredAction,RequestBattlesAction - All tests updated to handle optional battalions
Key Migration Patterns:
- ✅ Use specific components instead of full GameState (see ProvinceHeldAction, ProvinceConqueredAction)
- ✅ Convert protobuf models to Scala models at Action boundaries
- ✅ Update BUILD.bazel to remove protobuf dependencies
- ✅ Update all call sites and tests
- ✅ Use
Option[T]for optional fields instead of special sentinel values (e.g., battalion ID -1)
Next Migration Candidates (Simple Actions with Protoless Base):
- FreeForAllDrawAction - Already uses ProtolessSimpleAction
- FriendlyMoveAction - Already uses ProtolessSimpleAction
- ShipmentArrivedAction - Already uses ProtolessSimpleAction
- WonFreeForAllAction - Already uses ProtolessSimpleAction
- ProvinceConqueredAction - Already uses ProtolessSimpleAction, only needs
common_unitmigration
🔄 Conversion Strategy Updates
Revised Approach Based on Analysis:
-
Focus on Core Dependencies First
- Migrate
battalion_typemodel (unlocks 2 commands immediately) - Migrate
action_resultmodel (unlocks 12+ commands) - Migrate
available_command/selected_command(unlocks UI layer)
- Migrate
-
Leverage Existing Success
- 77.5% of commands already fully migrated
- Use migrated commands as reference implementations
- Diplomacy helpers prove complex business logic can work with Scala models
-
Group Related Migrations
- Military commands:
ArmTroopsCommand,TrainCommand,OrganizeTroopsCommand - UI commands: All using
available_command/selected_command - Diplomacy commands: All
Resolve*Commandvariants
- Military commands:
Updated on 2025-09-17 - Analysis based on BUILD.bazel dependencies and code review Latest update: ResolvedEagleUnit migrated to use Option[BattalionT] for proper battalion handling