Fixed concurrency problems when working on "next turn" by working on a clone of the game state

This commit is contained in:
Yair Morgenstern 2019-08-15 10:15:27 +03:00
parent 05d3aa9193
commit 202c1828a7
3 changed files with 49 additions and 46 deletions

View File

@ -29,19 +29,19 @@ enum class GameSpeed{
}
}
class GameParameters{
var difficulty="Prince"
class GameParameters { // Default values are the default new game
var difficulty = "Prince"
var gameSpeed = GameSpeed.Standard
var mapRadius=20
var mapRadius = 20
var players = ArrayList<Player>().apply {
add(Player().apply { playerType=PlayerType.Human })
for(i in 1..3) add(Player())
add(Player().apply { playerType = PlayerType.Human })
for (i in 1..3) add(Player())
}
var numberOfCityStates=0
var mapType= MapType.Perlin
var noBarbarians=false
var mapFileName :String?=null
var victoryTypes :ArrayList<VictoryType> = VictoryType.values().toCollection(ArrayList()) // By default, all victory types
var numberOfCityStates = 0
var mapType = MapType.Perlin
var noBarbarians = false
var mapFileName: String? = null
var victoryTypes: ArrayList<VictoryType> = VictoryType.values().toCollection(ArrayList()) // By default, all victory types
}
class GameStarter{

View File

@ -16,9 +16,7 @@ class PlayerReadyScreen(currentPlayerCiv: CivilizationInfo) : CameraStageBaseScr
.setFontColor(currentPlayerCiv.getNation().getSecondaryColor()))
table.onClick {
UnCivGame.Current.worldScreen = WorldScreen(currentPlayerCiv).apply {
shouldUpdate = true
}
UnCivGame.Current.worldScreen = WorldScreen(currentPlayerCiv)
UnCivGame.Current.setWorldScreen()
}
table.setFillParent(true)

View File

@ -165,16 +165,16 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
notificationsScroll.setPosition(stage.width - notificationsScroll.width - 5f,
nextTurnButton.y - notificationsScroll.height - 5f)
when {
!gameInfo.oneMoreTurnMode && gameInfo.civilizations.any { it.victoryManager.hasWon() } -> game.screen = VictoryScreen()
viewingCiv.policies.freePolicies>0 -> game.screen = PolicyPickerScreen(viewingCiv)
viewingCiv.greatPeople.freeGreatPeople>0 -> game.screen = GreatPersonPickerScreen()
viewingCiv.tradeRequests.isNotEmpty() ->{
TradePopup(this)
val isSomethingOpen = tutorials.isTutorialShowing || stage.actors.any { it is TradePopup }
|| AlertPopup.isOpen
if(!isSomethingOpen) {
when {
!gameInfo.oneMoreTurnMode && gameInfo.civilizations.any { it.victoryManager.hasWon() } -> game.screen = VictoryScreen()
viewingCiv.policies.freePolicies > 0 -> game.screen = PolicyPickerScreen(viewingCiv)
viewingCiv.greatPeople.freeGreatPeople > 0 -> game.screen = GreatPersonPickerScreen()
viewingCiv.tradeRequests.isNotEmpty() -> TradePopup(this)
viewingCiv.popupAlerts.any() -> AlertPopup(this, viewingCiv.popupAlerts.first())
}
!tutorials.isTutorialShowing
&& viewingCiv.popupAlerts.any() && !AlertPopup.isOpen ->
AlertPopup(this,viewingCiv.popupAlerts.first())
}
updateNextTurnButton()
}
@ -259,42 +259,53 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
return@onClick
}
nextTurn(nextTurnButton) // If none of the above
nextTurn() // If none of the above
}
return nextTurnButton
}
private fun nextTurn(nextTurnButton: TextButton) {
private fun nextTurn() {
isPlayersTurn = false
shouldUpdate = true
thread {
val gameInfoClone = gameInfo.clone()
gameInfoClone.setTransients()
try {
gameInfo.nextTurn()
gameInfoClone.nextTurn()
} catch (ex: Exception) {
game.settings.hasCrashedRecently = true
game.settings.save()
throw ex
}
if (gameInfo.turns % game.settings.turnsBetweenAutosaves == 0) {
GameSaver().autoSave(gameInfo) {
nextTurnButton.enable() // only enable the user to next turn once we've saved the current one
updateNextTurnButton()
}
} else nextTurnButton.enable() // Enable immediately
game.gameInfo = gameInfoClone
// If we put this BEFORE the save game, then we try to save the game...
// but the main thread does other stuff, including showing tutorials which guess what? Changes the game data
// BOOM! Exception!
// That's why this needs to be after the game is saved.
isPlayersTurn = true
shouldUpdate = true
val shouldAutoSave = gameInfoClone.turns % game.settings.turnsBetweenAutosaves == 0
// do this on main thread
// create a new worldscreen to show the new stuff we've changed, and switch out the current screen.
// do this on main thread - it's the only one that has a GL context to create images from
Gdx.app.postRunnable {
updateNextTurnButton()
if(gameInfoClone.currentPlayerCiv.civName == viewingCiv.civName) {
val newWorldScreen = WorldScreen(gameInfoClone.currentPlayerCiv)
newWorldScreen.tileMapHolder.scrollX = tileMapHolder.scrollX
newWorldScreen.tileMapHolder.scrollY = tileMapHolder.scrollY
newWorldScreen.tileMapHolder.scaleX = tileMapHolder.scaleX
newWorldScreen.tileMapHolder.scaleY = tileMapHolder.scaleY
newWorldScreen.tileMapHolder.updateVisualScroll()
game.worldScreen = newWorldScreen
game.setWorldScreen()
}
else UnCivGame.Current.screen = PlayerReadyScreen(gameInfoClone.getCurrentPlayerCivilization())
if(shouldAutoSave) {
game.worldScreen.nextTurnButton.disable()
GameSaver().autoSave(gameInfoClone) {
game.worldScreen.nextTurnButton.enable() // only enable the user to next turn once we've saved the current one
}
}
}
}
}
@ -324,7 +335,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
}
}
var shouldUpdate=true
var shouldUpdate=false
override fun render(delta: Float) {
@ -333,12 +344,6 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
if (shouldUpdate) {
shouldUpdate = false
if (viewingCiv != gameInfo.getCurrentPlayerCivilization()) {
UnCivGame.Current.worldScreen.dispose() // for memory saving
UnCivGame.Current.screen = PlayerReadyScreen(gameInfo.getCurrentPlayerCivilization())
return
}
update()
showTutorialsOnNextTurn()
}