mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Use active Shardok battles in text client (#8452)
This commit is contained in:
@@ -119,6 +119,12 @@ post-shardok <shardok-game-id> <index>
|
||||
post-shardok <shardok-game-id> <index> <integer-roll>
|
||||
```
|
||||
|
||||
The battle ID may be omitted only when exactly one outstanding battle currently
|
||||
has postable commands. If multiple battles have commands, the client refuses the
|
||||
ambiguous shorthand; use the battle ID printed by `shardok-commands`. Battles
|
||||
removed from Eagle's outstanding-battle list are no longer postable, and a
|
||||
Shardok update with no available commands clears the previous command list.
|
||||
|
||||
Use `shardok-commands-json` or `shardok-units-json` when the concise output omits a needed field.
|
||||
|
||||
## Reconnects And Shutdown
|
||||
|
||||
@@ -531,14 +531,21 @@ final class ClientState {
|
||||
println(s"post-command: ${value.status} game=${value.gameId} ${value.errorMessage}")
|
||||
case UpdateStreamResponse.ResponseDetails.GameUpdate(value) =>
|
||||
latestGameId = Some(value.gameId)
|
||||
value.startingState.foreach(state => latestState = Some(state))
|
||||
value.startingState.foreach { state =>
|
||||
latestState = Some(state)
|
||||
latestShardokBattles = Map.empty
|
||||
}
|
||||
if value.serverGameStatus.isDefined then latestStatus = value.serverGameStatus
|
||||
value.gameUpdateDetails match {
|
||||
case GameUpdate.GameUpdateDetails.ActionResultResponse(ar) =>
|
||||
latestUnfilteredResultCount = ar.unfilteredResultCountAfter
|
||||
latestState = latestState.map(state =>
|
||||
ar.actionResultViews.foldLeft(state) { (current, actionResult) =>
|
||||
actionResult.gameStateDiff.map(applyGameStateDiff(current, _)).getOrElse(current)
|
||||
actionResult.gameStateDiff.map { diff =>
|
||||
latestShardokBattles --= diff.removedBattleIds
|
||||
applyGameStateDiff(current, diff)
|
||||
}
|
||||
.getOrElse(current)
|
||||
}
|
||||
)
|
||||
if ar.availableCommands.isDefined then latestCommands = ar.availableCommands
|
||||
@@ -607,7 +614,7 @@ final class ClientState {
|
||||
val updated = ShardokBattleState(
|
||||
shardokGameId = response.shardokGameId,
|
||||
token = response.newResultViewCount,
|
||||
availableCommands = response.availableCommands.orElse(existing.flatMap(_.availableCommands)),
|
||||
availableCommands = response.availableCommands,
|
||||
status = response.shardokServerStatus.orElse(existing.flatMap(_.status)),
|
||||
units = snapshot.units,
|
||||
reserveUnits = snapshot.reserveUnits,
|
||||
@@ -914,9 +921,17 @@ final class ClientState {
|
||||
.flatMap(_.commands.collectFirst { case command: MarchAvailableCommand => command })
|
||||
|
||||
def shardokCommandPost(shardokGameId: Option[String], index: Int): Option[ShardokCommandPost] = synchronized {
|
||||
val candidates = shardokGameId match {
|
||||
case Some(id) => latestShardokBattles.get(id).toVector
|
||||
case None => latestShardokBattles.values.toVector
|
||||
val outstandingBattleIds = latestState.toVector
|
||||
.flatMap(_.outstandingBattles)
|
||||
.flatMap(_.shardokGameId)
|
||||
.toSet
|
||||
val activeCandidates = latestShardokBattles.values.toVector
|
||||
.filter(battle => outstandingBattleIds.contains(battle.shardokGameId))
|
||||
val candidates = shardokGameId match {
|
||||
case Some(id) => activeCandidates.filter(_.shardokGameId == id)
|
||||
case None =>
|
||||
val postable = activeCandidates.filter(_.availableCommands.exists(_.currentCommand.nonEmpty))
|
||||
Option.when(postable.size == 1)(postable.head).toVector
|
||||
}
|
||||
|
||||
candidates
|
||||
|
||||
@@ -1,13 +1,57 @@
|
||||
package net.eagle0.eagle.text_client
|
||||
|
||||
import net.eagle0.eagle.api.eagle.{ActionResultResponse, GameUpdate, UpdateStreamResponse}
|
||||
import net.eagle0.eagle.api.eagle.{ActionResultResponse, GameUpdate, ShardokActionResultResponse, UpdateStreamResponse}
|
||||
import net.eagle0.eagle.views.action_result_view.ActionResultView
|
||||
import net.eagle0.eagle.views.game_state_view.{GameStateView, GameStateViewDiff}
|
||||
import net.eagle0.eagle.views.province_view.{ProvinceView, ProvinceViewDiff}
|
||||
import net.eagle0.eagle.views.shardok_battle_view.ShardokBattleView
|
||||
import net.eagle0.shardok.api.command_descriptor.{AvailableCommands, CommandDescriptor}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ClientStateTest extends AnyFlatSpec with Matchers {
|
||||
private def startingState(battleIds: String*): UpdateStreamResponse =
|
||||
UpdateStreamResponse(
|
||||
UpdateStreamResponse.ResponseDetails.GameUpdate(
|
||||
GameUpdate(
|
||||
gameId = 1L,
|
||||
startingState = Some(
|
||||
GameStateView(
|
||||
gameId = 1L,
|
||||
outstandingBattles = battleIds.map(id => ShardokBattleView(shardokGameId = Some(id))).toVector
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private def shardokUpdate(
|
||||
battleId: String,
|
||||
commands: Option[AvailableCommands]
|
||||
): UpdateStreamResponse =
|
||||
UpdateStreamResponse(
|
||||
UpdateStreamResponse.ResponseDetails.GameUpdate(
|
||||
GameUpdate(
|
||||
gameId = 1L,
|
||||
gameUpdateDetails = GameUpdate.GameUpdateDetails.ShardokActionResultResponse(
|
||||
ShardokActionResultResponse(
|
||||
shardokGameResponses = Vector(
|
||||
ShardokActionResultResponse.SingleShardokGameResultResponse(
|
||||
shardokGameId = battleId,
|
||||
newResultViewCount = 1,
|
||||
availableCommands = commands
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private val oneShardokCommand = AvailableCommands(
|
||||
currentCommand = Vector(CommandDescriptor(player = 3))
|
||||
)
|
||||
|
||||
"ClientTextStore" should "resume UTF-8 streaming text from byte offsets" in {
|
||||
val store = new ClientTextStore
|
||||
|
||||
@@ -75,4 +119,59 @@ class ClientStateTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
state.describeState should include("province 39: Kojaria ruler=3")
|
||||
}
|
||||
|
||||
it should "clear Shardok commands when a later update omits them" in {
|
||||
val state = new ClientState
|
||||
state.handle(startingState("battle-1"))
|
||||
state.handle(shardokUpdate("battle-1", Some(oneShardokCommand)))
|
||||
|
||||
state.shardokCommandPost(Some("battle-1"), index = 0).map(_.shardokGameId) shouldBe Some("battle-1")
|
||||
|
||||
state.handle(shardokUpdate("battle-1", commands = None))
|
||||
|
||||
state.shardokCommandPost(Some("battle-1"), index = 0) shouldBe None
|
||||
}
|
||||
|
||||
it should "post implicitly only when exactly one outstanding battle has commands" in {
|
||||
val state = new ClientState
|
||||
state.handle(startingState("battle-1", "battle-2"))
|
||||
state.handle(shardokUpdate("battle-1", Some(oneShardokCommand)))
|
||||
state.handle(shardokUpdate("battle-2", commands = None))
|
||||
|
||||
state.shardokCommandPost(shardokGameId = None, index = 0).map(_.shardokGameId) shouldBe Some("battle-1")
|
||||
|
||||
state.handle(shardokUpdate("battle-2", Some(oneShardokCommand)))
|
||||
|
||||
state.shardokCommandPost(shardokGameId = None, index = 0) shouldBe None
|
||||
state.shardokCommandPost(Some("battle-2"), index = 0).map(_.shardokGameId) shouldBe Some("battle-2")
|
||||
}
|
||||
|
||||
it should "remove cached Shardok state when Eagle removes the battle" in {
|
||||
val state = new ClientState
|
||||
state.handle(startingState("battle-1"))
|
||||
state.handle(shardokUpdate("battle-1", Some(oneShardokCommand)))
|
||||
|
||||
state.handle(
|
||||
UpdateStreamResponse(
|
||||
UpdateStreamResponse.ResponseDetails.GameUpdate(
|
||||
GameUpdate(
|
||||
gameId = 1L,
|
||||
gameUpdateDetails = GameUpdate.GameUpdateDetails.ActionResultResponse(
|
||||
ActionResultResponse(
|
||||
unfilteredResultCountAfter = 1,
|
||||
actionResultViews = Vector(
|
||||
ActionResultView(
|
||||
gameStateDiff = Some(GameStateViewDiff(removedBattleIds = Vector("battle-1")))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
state.describeShardokCommands(rawJson = false) shouldBe "No Shardok battles yet."
|
||||
state.shardokCommandPost(Some("battle-1"), index = 0) shouldBe None
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user