Compare commits

...
Author SHA1 Message Date
admin a2dff8f911 fix the action result type mapping 2026-01-07 06:46:29 -08:00
adminandClaude Opus 4.5 fa5cc43672 Fix new game creation crashes: RoundPhase and BattalionTypes
Two issues prevented CreateGame from succeeding:

1. UNKNOWN_PHASE error: GameStateProto was created without currentPhase,
   defaulting to UNKNOWN_PHASE (0) which caused ProtoConversionException

2. None.get in BattalionSuitability: newBattalionTypes was in the proto
   but commented out in the Scala model. When creating games, battalion
   types were set in ActionResult but lost during proto-to-Scala conversion,
   so they never got applied to GameState

Fixed by:
- Setting currentPhase = NEW_ROUND in PersistedHistory initial states
- Adding newBattalionTypes field to ActionResultT, ActionResultC
- Adding conversion in ActionResultProtoConverter
- Adding applyNewBattalionTypes extension method
- Calling extension in ActionResultApplierImpl

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 21:19:58 -08:00
11 changed files with 30 additions and 6 deletions
@@ -26,8 +26,8 @@ package net.eagle0.eagle.model.action_result.types
import net.eagle0.eagle.model.action_result.types.base.ActionResultType
object %sResultType extends ActionResultType(%s)
`, actionResultTypeName, actionResultTypeValue)
object %sResultType extends ActionResultType(%s) { override def name: String = "%s" }
`, actionResultTypeName, actionResultTypeValue, actionResultTypeName)
fmt.Print(contents)
}
@@ -127,6 +127,7 @@ class ActionResultApplierImpl(validator: Option[ScalaValidator]) extends ActionR
.applyNewSeed(result.newRandomSeed)
.applyChronicleEntry(result.newChronicleEntry)
.applyCommandCountUpdate(result.actingFactionId)
.applyNewBattalionTypes(result.newBattalionTypes)
// Validate final game state
validate(finalState)
@@ -4,6 +4,7 @@ import net.eagle0.eagle.*
import net.eagle0.eagle.model.action_result.NotificationT
import net.eagle0.eagle.model.state.chronicle_entry.ChronicleEntry
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.BattalionType
object GameStateMiscExtensions {
@@ -50,5 +51,9 @@ object GameStateMiscExtensions {
)
}
.getOrElse(gameState)
def applyNewBattalionTypes(newTypes: Vector[BattalionType]): GameState =
if newTypes.isEmpty then gameState
else gameState.copy(battalionTypes = gameState.battalionTypes ++ newTypes)
}
}
@@ -12,6 +12,7 @@ import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.province.ProvinceT
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
import net.eagle0.eagle.model.state.BattalionType
import net.eagle0.eagle.model.state.RoundPhase
object ActionResultT {
@@ -45,7 +46,7 @@ trait ActionResultT {
def newFactions: Vector[FactionT]
def newHeroes: Vector[HeroT]
def newProvinces: Vector[ProvinceT]
// def newBattalionTypes: Vector[BattalionType]
def newBattalionTypes: Vector[BattalionType]
def destroyedBattalionIds: Vector[BattalionId]
def removedHeroIds: Vector[HeroId]
@@ -87,6 +88,7 @@ trait ActionResultT {
newFactions: Vector[FactionT] = newFactions,
newHeroes: Vector[HeroT] = newHeroes,
newProvinces: Vector[ProvinceT] = newProvinces,
newBattalionTypes: Vector[BattalionType] = newBattalionTypes,
destroyedBattalionIds: Vector[BattalionId] = destroyedBattalionIds,
removedHeroes: Vector[HeroId] = removedHeroIds,
removedFactionIds: Vector[FactionId] = removedFactionIds,
@@ -36,6 +36,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/chronicle_entry",
@@ -57,6 +58,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/chronicle_entry",
@@ -20,6 +20,7 @@ import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.province.ProvinceT
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
import net.eagle0.eagle.model.state.BattalionType
import net.eagle0.eagle.model.state.RoundPhase
object ActionResultC {
@@ -47,6 +48,7 @@ case class ActionResultC(
newFactions: Vector[FactionT] = Vector.empty,
newHeroes: Vector[HeroT] = Vector.empty,
newProvinces: Vector[ProvinceT] = Vector.empty,
newBattalionTypes: Vector[BattalionType] = Vector.empty,
destroyedBattalionIds: Vector[BattalionId] = Vector.empty,
removedHeroIds: Vector[HeroId] = Vector.empty,
removedFactionIds: Vector[FactionId] = Vector.empty,
@@ -83,6 +85,7 @@ case class ActionResultC(
newFactions: Vector[FactionT] = newFactions,
newHeroes: Vector[HeroT] = newHeroes,
newProvinces: Vector[ProvinceT] = newProvinces,
newBattalionTypes: Vector[BattalionType] = newBattalionTypes,
destroyedBattalionIds: Vector[BattalionId] = destroyedBattalionIds,
removedHeroes: Vector[HeroId] = removedHeroIds,
removedFactionIds: Vector[FactionId] = removedFactionIds,
@@ -116,6 +119,7 @@ case class ActionResultC(
newFactions = newFactions,
newHeroes = newHeroes,
newProvinces = newProvinces,
newBattalionTypes = newBattalionTypes,
destroyedBattalionIds = destroyedBattalionIds,
removedHeroIds = removedHeroes,
clientTextVisibilityExtensions = clientTextVisibilityExtensions,
@@ -27,6 +27,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/chronicle_entry",
@@ -1,3 +1,5 @@
package net.eagle0.eagle.model.action_result.types.base
class ActionResultType(val value: Int) {}
abstract class ActionResultType(val value: Int) {
def name: String
}
@@ -40,6 +40,7 @@ import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.province.ProvinceT
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
import net.eagle0.eagle.model.state.BattalionType
import net.eagle0.eagle.model.state.RoundPhase
object ActionResultProtoConverter {
@@ -66,6 +67,7 @@ object ActionResultProtoConverter {
newFactions: Vector[FactionT],
newHeroes: Vector[HeroT],
newProvinces: Vector[ProvinceT],
newBattalionTypes: Vector[BattalionType],
removedBattalionIds: Vector[BattalionId],
removedHeroIds: Vector[HeroId],
removedFactionIds: Vector[FactionId],
@@ -112,6 +114,7 @@ object ActionResultProtoConverter {
newBattalions = newBattalions.map(BattalionConverter.toProto),
newFactions = newFactions.map(FactionConverter.toProto),
newHeroes = newHeroes.map(HeroConverter.toProto),
newBattalionTypes = newBattalionTypes.map(BattalionTypeConverter.toProto),
destroyedBattalionIds = removedBattalionIds,
removedHeroes = removedHeroIds,
removedFactionIds = removedFactionIds,
@@ -175,6 +178,7 @@ object ActionResultProtoConverter {
proto.notificationsToDeliver.map(n => NotificationConverter.fromProto(n, deferred = false)).toVector
ActionResultC(
// This doesn't actually map to the appropriate object
actionResultType = new ActionResultType(proto.`type`.value),
actingHeroId = proto.leader,
actingFactionId = proto.player,
@@ -197,6 +201,7 @@ object ActionResultProtoConverter {
newFactions = proto.newFactions.map(FactionConverter.fromProto).toVector,
newHeroes = proto.newHeroes.map(HeroConverter.fromProto).toVector,
newProvinces = proto.newProvinces.map(ProvinceConverter.fromProto).toVector,
newBattalionTypes = proto.newBattalionTypes.map(BattalionTypeConverter.fromProto).toVector,
destroyedBattalionIds = proto.destroyedBattalionIds.toVector,
removedHeroIds = proto.removedHeroes.toVector,
removedFactionIds = proto.removedFactionIds.toVector,
@@ -20,6 +20,7 @@ scala_library(
],
deps = [
":battalion_converter",
":battalion_type_converter",
":changed_faction_converter",
":changed_hero_converter",
":changed_province_converter",
@@ -95,11 +95,12 @@ object PersistedHistory {
startingState = GameStateProto(
runStatus = RunStatus.RUN_STATUS_RUNNING,
gameId = gameId,
randomSeed = gameId
randomSeed = gameId,
currentPhase = RoundPhaseProto.NEW_ROUND
),
directory = PartialGameDirectory(gameId = gameId, entries = Vector()),
recentHistory = formAwrs(
GameStateProto(runStatus = RunStatus.RUN_STATUS_RUNNING),
GameStateProto(runStatus = RunStatus.RUN_STATUS_RUNNING, currentPhase = RoundPhaseProto.NEW_ROUND),
startingResults
),
shardokHistory = startingShardokResults,