Fix OutOfMemoryError when loading a game and another is already loaded (#7306)

* Fix OutOfMemoryError when loading a game and another is already loaded

* Fix merge error...
This commit is contained in:
Timo T 2022-07-01 08:34:33 +02:00 committed by GitHub
parent 119440ccec
commit 08cede4f5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 18 deletions

View File

@ -24,7 +24,7 @@ buildscript {
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${com.unciv.build.BuildConfig.kotlinVersion}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${com.unciv.build.BuildConfig.kotlinVersion}")
classpath("de.richsource.gradle.plugins:gwt-gradle-plugin:0.6") classpath("de.richsource.gradle.plugins:gwt-gradle-plugin:0.6")
classpath("com.android.tools.build:gradle:7.0.4") classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.mobidevelop.robovm:robovm-gradle-plugin:2.3.1") classpath("com.mobidevelop.robovm:robovm-gradle-plugin:2.3.1")
// This is for wrapping the .jar file into a standalone executable // This is for wrapping the .jar file into a standalone executable

View File

@ -37,6 +37,7 @@ import com.unciv.utils.concurrency.launchOnGLThread
import com.unciv.utils.concurrency.withGLContext import com.unciv.utils.concurrency.withGLContext
import com.unciv.utils.concurrency.withThreadPoolContext import com.unciv.utils.concurrency.withThreadPoolContext
import com.unciv.utils.debug import com.unciv.utils.debug
import kotlinx.coroutines.CancellationException
import java.io.PrintWriter import java.io.PrintWriter
import java.util.* import java.util.*
import kotlin.collections.ArrayDeque import kotlin.collections.ArrayDeque
@ -405,6 +406,9 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
/** Handles an uncaught exception or error. First attempts a platform-specific handler, and if that didn't handle the exception or error, brings the game to a [CrashScreen]. */ /** Handles an uncaught exception or error. First attempts a platform-specific handler, and if that didn't handle the exception or error, brings the game to a [CrashScreen]. */
fun handleUncaughtThrowable(ex: Throwable) { fun handleUncaughtThrowable(ex: Throwable) {
if (ex is CancellationException) {
return // kotlin coroutines use this for control flow... so we can just ignore them.
}
Log.error("Uncaught throwable", ex) Log.error("Uncaught throwable", ex)
try { try {
PrintWriter(files.fileWriter("lasterror.txt")).use { PrintWriter(files.fileWriter("lasterror.txt")).use {

View File

@ -24,6 +24,7 @@ class UncivStage(viewport: Viewport) : Stage(viewport) {
private set private set
private val events = EventBus.EventReceiver() private val events = EventBus.EventReceiver()
init { init {
lastKnownVisibleArea = Rectangle(0f, 0f, width, height) lastKnownVisibleArea = Rectangle(0f, 0f, width, height)
events.receive(VisibleAreaChanged::class) { events.receive(VisibleAreaChanged::class) {
@ -35,6 +36,11 @@ class UncivStage(viewport: Viewport) : Stage(viewport) {
override fun dispose() { override fun dispose() {
events.stopReceiving() events.stopReceiving()
super.dispose() super.dispose()
/** [Stage.dispose] is supposed to clear all references it holds. But it forgets the mouse over properties:
the [Stage.mouseOverActor] and [Stage.pointerOverActors]. [Stage.act] updates those properties,
and since there aren't any children left, sets all those properties to `null`. */
super.act()
} }
override fun draw() = override fun draw() =

View File

@ -212,8 +212,9 @@ class WorldScreen(
} }
override fun dispose() { override fun dispose() {
super.dispose()
events.stopReceiving() events.stopReceiving()
statusButtons.dispose()
super.dispose()
} }
private fun addKeyboardPresses() { private fun addKeyboardPresses() {

View File

@ -11,6 +11,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Stack import com.badlogic.gdx.scenes.scene2d.ui.Stack
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.badlogic.gdx.utils.Disposable
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.event.EventBus import com.unciv.logic.event.EventBus
import com.unciv.logic.multiplayer.HasMultiplayerGameName import com.unciv.logic.multiplayer.HasMultiplayerGameName
@ -26,14 +27,15 @@ import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.setSize import com.unciv.ui.utils.extensions.setSize
import com.unciv.utils.concurrency.Concurrency import com.unciv.utils.concurrency.Concurrency
import com.unciv.utils.concurrency.launchOnGLThread import com.unciv.utils.concurrency.launchOnGLThread
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
class MultiplayerStatusButton( class MultiplayerStatusButton(
/*val*/ screen: BaseScreen, screen: BaseScreen,
curGame: OnlineMultiplayerGame? curGame: OnlineMultiplayerGame?
) : Button(BaseScreen.skin) { ) : Button(BaseScreen.skin), Disposable {
private var curGameName = curGame?.name private var curGameName = curGame?.name
private val multiplayerImage = createMultiplayerImage() private val multiplayerImage = createMultiplayerImage()
private val loadingImage = createLoadingImage() private val loadingImage = createLoadingImage()
@ -43,6 +45,7 @@ class MultiplayerStatusButton(
private var loadingStarted: Instant? = null private var loadingStarted: Instant? = null
private val events = EventBus.EventReceiver() private val events = EventBus.EventReceiver()
private var loadStopJob: Job? = null
init { init {
turnIndicatorCell = add().padTop(10f).padBottom(10f) turnIndicatorCell = add().padTop(10f).padBottom(10f)
@ -96,7 +99,7 @@ class MultiplayerStatusButton(
} else { } else {
Duration.ZERO Duration.ZERO
} }
Concurrency.run("Hide loading indicator") { loadStopJob = Concurrency.run("Hide loading indicator") {
delay(waitFor.toMillis()) delay(waitFor.toMillis())
launchOnGLThread { launchOnGLThread {
loadingImage.clearActions() loadingImage.clearActions()
@ -147,11 +150,18 @@ class MultiplayerStatusButton(
turnIndicator.flash() turnIndicator.flash()
} }
} }
override fun dispose() {
events.stopReceiving()
turnIndicator.dispose()
loadStopJob?.cancel()
}
} }
private class TurnIndicator : HorizontalGroup() { private class TurnIndicator : HorizontalGroup(), Disposable {
val gameAmount = Label("2", BaseScreen.skin) val gameAmount = Label("2", BaseScreen.skin)
val image: Image val image: Image
private var job: Job? = null
init { init {
image = ImageGetter.getImage("OtherIcons/ExclamationMark") image = ImageGetter.getImage("OtherIcons/ExclamationMark")
image.setSize(30f) image.setSize(30f)
@ -175,11 +185,15 @@ private class TurnIndicator : HorizontalGroup() {
if (alternations == 0) return if (alternations == 0) return
gameAmount.color = nextColor gameAmount.color = nextColor
image.color = nextColor image.color = nextColor
Concurrency.run("StatusButton color flash") { job = Concurrency.run("StatusButton color flash") {
delay(500) delay(500)
launchOnGLThread { launchOnGLThread {
flash(alternations - 1, nextColor, curColor) flash(alternations - 1, nextColor, curColor)
} }
} }
} }
override fun dispose() {
job?.cancel()
}
} }

View File

@ -1,11 +1,12 @@
package com.unciv.ui.worldscreen.status package com.unciv.ui.worldscreen.status
import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup import com.badlogic.gdx.scenes.scene2d.ui.HorizontalGroup
import com.badlogic.gdx.utils.Disposable
class StatusButtons( class StatusButtons(
nextTurnButton: NextTurnButton, nextTurnButton: NextTurnButton,
multiplayerStatusButton: MultiplayerStatusButton? = null multiplayerStatusButton: MultiplayerStatusButton? = null
) : HorizontalGroup() { ) : HorizontalGroup(), Disposable {
var multiplayerStatusButton: MultiplayerStatusButton? = multiplayerStatusButton var multiplayerStatusButton: MultiplayerStatusButton? = multiplayerStatusButton
set(button) { set(button) {
multiplayerStatusButton?.remove() multiplayerStatusButton?.remove()
@ -23,4 +24,8 @@ class StatusButtons(
} }
addActor(nextTurnButton) addActor(nextTurnButton)
} }
override fun dispose() {
multiplayerStatusButton?.dispose()
}
} }