From bbae97042ef9d38d22e69c0e07c76393348a583e Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:50:44 +0200 Subject: [PATCH 1/9] First draft of automatic game end --- .../openbase/planetsudo/game/GameManager.kt | 37 ++++++- .../openbase/planetsudo/game/GameTimeout.kt | 25 +++++ .../org/openbase/planetsudo/view/MainGUI.kt | 4 +- .../view/configuration/ConfigurationPanel.kt | 98 ++++++++++++++++++- 4 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt index d7b93420..4751257d 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt @@ -50,6 +50,11 @@ class GameManager : Runnable { private var gameSpeedFactor: Double var gameOverSoon: Boolean + private var gameEndDur: Long = 0 + private var gameEndCalcDur: Long = 0 + private var gameEnd: GameTimeout? = null + private var gameEndCalc: GameTimeout? = null + init { LOGGER.info("Create $this.") this.gameThread = Thread(this, "GameThread") @@ -116,7 +121,15 @@ class GameManager : Runnable { LOGGER.info("Set $level as new level.") } - fun startGame() { + fun setGameEndTimer(duration: Long) { + gameEndDur = duration + } + + fun setGameEndCalcTimer(duration: Long) { + gameEndCalcDur = duration + } + + fun startGame(gameEndFunction: () -> Unit) { val gameStartThread: Thread = object : Thread("Gamestart Thread") { override fun run() { LOGGER.info("Init game start...") @@ -143,11 +156,21 @@ class GameManager : Runnable { level?.setTeamA(teamA) level?.setTeamB(teamB) level?.reset() + + if (gameEndDur > 0) { + gameEnd = GameTimeout(gameEndDur) { setGameOverSoon() } + if (gameEndCalcDur > 0) { + gameEndCalc = GameTimeout(gameEndDur + gameEndCalcDur) { gameEndFunction() } + } + } + setGameState(GameState.Running) Thread(level, "Levelrunner").start() lock.withLock { condition.signalAll() } + gameEnd?.start() + gameEndCalc?.start() LOGGER.info("Game is Running.") } } @@ -168,7 +191,17 @@ class GameManager : Runnable { private fun setGameState(state: GameState) { GUIController.setEvent(PropertyChangeEvent(this, GUIController.GAME_STATE_CHANGE, gameState, state)) this.gameState = state - isPause = state == GameState.Break + + if (state == GameState.Break) { + isPause = true + gameEndCalc?.pause() + gameEnd?.pause() + } else { + isPause = false + gameEndCalc?.tryUnpause() + gameEnd?.tryUnpause() + } + if (state == GameState.Running) { isGameOver = false } else if (state == GameState.Configuration) { diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt new file mode 100644 index 00000000..6bcb090c --- /dev/null +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -0,0 +1,25 @@ +package org.openbase.planetsudo.game + +import org.openbase.jul.schedule.Timeout + +// TODO this is currently not aware of game speed factor +class GameTimeout(duration: Long, val function: () -> Unit) : Timeout(duration) { + + private var pausedAt: Long? = null + + override fun expired() { + function() + } + + fun pause() { + pausedAt = timeLeftUntilTimeout + cancel() + } + + fun tryUnpause() { + if (isActive || isExpired) { + return + } + pausedAt?.let { restart(it) } + } +} diff --git a/src/main/kotlin/org/openbase/planetsudo/view/MainGUI.kt b/src/main/kotlin/org/openbase/planetsudo/view/MainGUI.kt index 320cc280..8a9dfe58 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/MainGUI.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/MainGUI.kt @@ -260,7 +260,7 @@ class MainGUI : JFrame, PropertyChangeListener { private var finished = false - private fun finalizeGame() { + fun finalizeGame() { finished = true gameManager.switchGameState(GameState.Break) @@ -460,7 +460,7 @@ class MainGUI : JFrame, PropertyChangeListener { private fun startPauseMenuItemActionPerformed(evt: ActionEvent) { // GEN-FIRST:event_startPauseMenuItemActionPerformed when (gameManager.gameState) { - GameState.Configuration -> gameManager.startGame() + GameState.Configuration -> gameManager.startGame { finalizeGame() } GameState.Running -> gameManager.switchGameState(GameState.Break) GameState.Break -> gameManager.switchGameState(GameState.Running) else -> {} diff --git a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt index a3de66ec..cac7b251 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt @@ -10,6 +10,7 @@ package org.openbase.planetsudo.view.configuration import org.apache.commons.io.FileUtils +import org.apache.commons.lang3.time.TimeZones import org.openbase.jul.exception.CouldNotPerformException import org.openbase.jul.exception.printer.ExceptionPrinter import org.openbase.jul.visual.swing.image.ImageLoader @@ -32,8 +33,10 @@ import java.awt.Color import java.io.File import java.io.FileInputStream import java.io.FileOutputStream +import java.text.SimpleDateFormat import java.util.* import javax.swing.* +import javax.swing.JSpinner.DateEditor import javax.swing.border.BevelBorder import javax.swing.border.SoftBevelBorder import javax.swing.event.ListSelectionEvent @@ -157,6 +160,11 @@ class ConfigurationPanel : JPanel() { levelChooserPanel = JPanel() levelChooserComboBox = JComboBox() startGameButton = JButton() + gameEndTimeSpinner = JSpinner() + gameEndCalcTimeSpinner = JSpinner() + gameEndTimeLabel = JLabel() + gameEndCalcTimeLabel = JLabel() + automaticEndPanel = JPanel() networkPanel = JPanel() networkTeamPanel = JPanel() defaultTeamComboBox = JComboBox() @@ -218,6 +226,8 @@ class ConfigurationPanel : JPanel() { gameSettingsPanel!!.border = BorderFactory.createTitledBorder("Spiel Einstellungen") + automaticEndPanel!!.border = BorderFactory.createTitledBorder("Automatisches Ende") + teamsPanel!!.border = BorderFactory.createTitledBorder("Teams") versusLabel!!.horizontalAlignment = SwingConstants.CENTER @@ -418,6 +428,65 @@ class ConfigurationPanel : JPanel() { startGameButton!!.text = "Spiel Starten" startGameButton!!.addActionListener { _ -> startGameButtonActionPerformed() } + configureDateSpinner(gameEndTimeSpinner!!) + configureDateSpinner(gameEndCalcTimeSpinner!!) + + gameEndTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndTimer(((e.source as JSpinner).value as Date).time) } + gameEndCalcTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndCalcTimer(((e.source as JSpinner).value as Date).time) } + + gameEndTimeLabel!!.horizontalAlignment = SwingConstants.LEADING + gameEndTimeLabel!!.text = "Automatisches Spielende einleiten" + + gameEndCalcTimeLabel!!.horizontalAlignment = SwingConstants.LEADING + gameEndCalcTimeLabel!!.text = "danach automatische Endabrechnung" + + val automaticEndPanelLayout = GroupLayout(automaticEndPanel) + automaticEndPanel!!.layout = automaticEndPanelLayout + automaticEndPanelLayout.setHorizontalGroup( + automaticEndPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup( + automaticEndPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(gameEndTimeLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(gameEndTimeSpinner) + .addContainerGap(), + ) + .addGroup( + automaticEndPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(gameEndCalcTimeLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(gameEndCalcTimeSpinner) + .addContainerGap(), + ), + ) + + automaticEndPanelLayout.setVerticalGroup( + automaticEndPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup( + automaticEndPanelLayout.createSequentialGroup() + .addGroup( + automaticEndPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent( + gameEndTimeSpinner, + ) + .addComponent( + gameEndTimeLabel, + ), + ) + .addGroup( + automaticEndPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent( + gameEndCalcTimeSpinner, + ) + .addComponent( + gameEndCalcTimeLabel, + ), + ), + ), + ) + val gameSettingsPanelLayout = GroupLayout(gameSettingsPanel) gameSettingsPanel!!.layout = gameSettingsPanelLayout gameSettingsPanelLayout.setHorizontalGroup( @@ -439,6 +508,12 @@ class ConfigurationPanel : JPanel() { GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE.toInt(), ) + .addComponent( + automaticEndPanel, + GroupLayout.DEFAULT_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE.toInt(), + ) .addComponent( startGameButton, GroupLayout.Alignment.TRAILING, @@ -459,6 +534,8 @@ class ConfigurationPanel : JPanel() { .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(levelChooserPanel, GroupLayout.PREFERRED_SIZE, 62, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(automaticEndPanel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(startGameButton) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE.toInt()), ), @@ -668,6 +745,20 @@ class ConfigurationPanel : JPanel() { ) } // //GEN-END:initComponents + private fun configureDateSpinner(dateSpinner: JSpinner) { + val dateModel = SpinnerDateModel() + dateModel.calendarField = Calendar.MINUTE + dateSpinner.model = dateModel + + dateSpinner.editor = DateEditor(dateSpinner) + val dateFormat: SimpleDateFormat = (dateSpinner.editor as DateEditor).format + dateFormat.timeZone = TimeZones.GMT + dateFormat.applyLocalizedPattern("mm:ss") + + // Update value here because of https://stackoverflow.com/a/37444974 + dateModel.value = Date(60 * 1000) // TODO keep last value as default? + } + private fun levelChooserComboBoxActionPerformed() { // GEN-FIRST:event_levelChooserComboBoxActionPerformed object : SwingWorker() { @Throws(Exception::class) @@ -714,7 +805,7 @@ class ConfigurationPanel : JPanel() { private fun startGameButtonActionPerformed() { // GEN-FIRST:event_startGameButtonActionPerformed MainGUI.instance!!.showLoadingPanel() - gameManager.startGame() + gameManager.startGame { MainGUI.instance!!.finalizeGame() } } // GEN-LAST:event_startGameButtonActionPerformed private fun teamAComboBoxActionPerformed() { // GEN-FIRST:event_teamAComboBoxActionPerformed @@ -792,6 +883,11 @@ class ConfigurationPanel : JPanel() { private var connectionStateLabel: JLabel? = null private var defaultTeamComboBox: JComboBox? = null private var startGameButton: JButton? = null + private var gameEndTimeSpinner: JSpinner? = null + private var gameEndCalcTimeSpinner: JSpinner? = null + private var gameEndTimeLabel: JLabel? = null + private var gameEndCalcTimeLabel: JLabel? = null + private var automaticEndPanel: JPanel? = null private var teamALabel: JLabel? = null private var teamBLabel: JLabel? = null private var versusLabel: JLabel? = null From 508839e274d577d066028131de5c175ab77700f4 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:25:30 +0200 Subject: [PATCH 2/9] Copied in-game-time (`LevelMenuPanel`) to automatic end timer (`GameTimeout`). --- .../openbase/planetsudo/game/GameManager.kt | 20 ++++---- .../openbase/planetsudo/game/GameTimeout.kt | 48 ++++++++++++++----- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt index 4751257d..13ad41e7 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt @@ -158,9 +158,9 @@ class GameManager : Runnable { level?.reset() if (gameEndDur > 0) { - gameEnd = GameTimeout(gameEndDur) { setGameOverSoon() } + gameEnd = GameTimeout(level!!, gameEndDur) { setGameOverSoon() } if (gameEndCalcDur > 0) { - gameEndCalc = GameTimeout(gameEndDur + gameEndCalcDur) { gameEndFunction() } + gameEndCalc = GameTimeout(level!!, gameEndDur + gameEndCalcDur) { gameEndFunction() } } } @@ -169,8 +169,8 @@ class GameManager : Runnable { lock.withLock { condition.signalAll() } - gameEnd?.start() - gameEndCalc?.start() + gameEnd?.startTimer() + gameEndCalc?.startTimer() LOGGER.info("Game is Running.") } } @@ -194,18 +194,20 @@ class GameManager : Runnable { if (state == GameState.Break) { isPause = true - gameEndCalc?.pause() - gameEnd?.pause() - } else { + gameEndCalc?.stopTimer() + gameEnd?.stopTimer() + } else if (isPause && gameState == GameState.Running) { isPause = false - gameEndCalc?.tryUnpause() - gameEnd?.tryUnpause() + gameEndCalc?.startTimer() + gameEnd?.startTimer() } if (state == GameState.Running) { isGameOver = false } else if (state == GameState.Configuration) { isGameOver = true + gameEndCalc?.stopTimer() + gameEnd?.stopTimer() } } diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt index 6bcb090c..0878049a 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -1,25 +1,47 @@ package org.openbase.planetsudo.game -import org.openbase.jul.schedule.Timeout +import org.openbase.planetsudo.level.AbstractLevel +import java.awt.event.ActionEvent +import java.awt.event.ActionListener +import javax.swing.Timer -// TODO this is currently not aware of game speed factor -class GameTimeout(duration: Long, val function: () -> Unit) : Timeout(duration) { +// TODO this currently de-syncs slightly from the timer in LevelMenuPanel +class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit) : ActionListener { + private val timer: Timer = Timer(1000, this) + private var secondsRemaining = duration / 1000 + private var fired: Boolean = false - private var pausedAt: Long? = null + init { //This is a blatant copy of LevelMenuPanel, thus knowingly violating single source of truth + level.addPropertyChangeListener { + if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { + timer.isRunning.let { running -> + timer.delay = (1000.0 * (1 / (it.newValue as Double))).toInt() + if (running) { + timer.restart() + } + } + } + } + } - override fun expired() { - function() + fun startTimer() { + if (!timer.isRunning && !fired) { + timer.start() + } } - fun pause() { - pausedAt = timeLeftUntilTimeout - cancel() + fun stopTimer() { + if (timer.isRunning) { + timer.stop() + } } - fun tryUnpause() { - if (isActive || isExpired) { - return + override fun actionPerformed(ex: ActionEvent) { + secondsRemaining-- + if (secondsRemaining <= 0 && !fired) { + function() + fired = true + stopTimer() } - pausedAt?.let { restart(it) } } } From 20f371b7147a97aa7d9469678770042560f6128b Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:46:26 +0200 Subject: [PATCH 3/9] Reduced desync between in-game-clock and automatic game end. --- src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt | 3 ++- .../kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt index 0878049a..383fea21 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -7,7 +7,7 @@ import javax.swing.Timer // TODO this currently de-syncs slightly from the timer in LevelMenuPanel class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit) : ActionListener { - private val timer: Timer = Timer(1000, this) + private val timer: Timer = Timer((1000.0 * (1 / level.getGameSpeedFactor())).toInt(), this) private var secondsRemaining = duration / 1000 private var fired: Boolean = false @@ -16,6 +16,7 @@ class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> timer.delay = (1000.0 * (1 / (it.newValue as Double))).toInt() + timer.initialDelay = timer.delay if (running) { timer.restart() } diff --git a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt index 443cce77..36140ac3 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt @@ -26,10 +26,12 @@ class LevelMenuPanel : JPanel(), ActionListener { fun setLevel(level: AbstractLevel) { levelName = level.name + timer.delay = (1000.0 * (1 / level.getGameSpeedFactor())).toInt() level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> timer.delay = (1000.0 * (1 / (it.newValue as Double))).toInt() + timer.initialDelay = timer.delay if (running) timer.restart() } } From c7fdbab47f560cb714447fc204892770088be183 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:57:45 +0200 Subject: [PATCH 4/9] Slight refactor after fixing delay timers. --- .../openbase/planetsudo/game/GameTimeout.kt | 17 +++++++++---- .../planetsudo/view/menu/LevelMenuPanel.kt | 24 +++++++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt index 383fea21..2281e2a5 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -5,9 +5,9 @@ import java.awt.event.ActionEvent import java.awt.event.ActionListener import javax.swing.Timer -// TODO this currently de-syncs slightly from the timer in LevelMenuPanel +// this still de-syncs slightly from the timer in LevelMenuPanel class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit) : ActionListener { - private val timer: Timer = Timer((1000.0 * (1 / level.getGameSpeedFactor())).toInt(), this) + private val timer: Timer = Timer(calcTimerDelay(level.getGameSpeedFactor()), this) private var secondsRemaining = duration / 1000 private var fired: Boolean = false @@ -15,8 +15,7 @@ class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> - timer.delay = (1000.0 * (1 / (it.newValue as Double))).toInt() - timer.initialDelay = timer.delay + updateDelay(it.newValue as Double) if (running) { timer.restart() } @@ -25,6 +24,16 @@ class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit } } + private fun updateDelay(gameSpeed: Double) { + val delay = calcTimerDelay(gameSpeed) + timer.delay = delay + timer.initialDelay = delay + } + + private fun calcTimerDelay(gameSpeed: Double): Int { + return (1000.0 / gameSpeed).toInt() + } + fun startTimer() { if (!timer.isRunning && !fired) { timer.start() diff --git a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt index 36140ac3..adc17e47 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt @@ -26,19 +26,31 @@ class LevelMenuPanel : JPanel(), ActionListener { fun setLevel(level: AbstractLevel) { levelName = level.name - timer.delay = (1000.0 * (1 / level.getGameSpeedFactor())).toInt() + updateDelay (level.getGameSpeedFactor()) + timer.initialDelay = timer.delay level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> - timer.delay = (1000.0 * (1 / (it.newValue as Double))).toInt() - timer.initialDelay = timer.delay - if (running) timer.restart() + updateDelay(it.newValue as Double) + if (running) { + timer.restart() + } } } } updateTitle() } + private fun updateDelay(gameSpeed: Double) { + val delay = calcTimerDelay(gameSpeed) + timer.delay = delay + timer.initialDelay = delay + } + + private fun calcTimerDelay(gameSpeed: Double): Int { + return (1000.0 / gameSpeed).toInt() + } + private var text: String? = null private fun updateTitle() { text = "$levelName [" @@ -54,7 +66,9 @@ class LevelMenuPanel : JPanel(), ActionListener { } fun startTimer() { - if (!timer.isRunning) timer.start() + if (!timer.isRunning) { + timer.start() + } } fun stopTimer() { From 5a888e2dc4d6aec469f4381b8b34a3454e79b893 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:20:38 +0200 Subject: [PATCH 5/9] Reset game end timers before reinstating them. --- src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt index 13ad41e7..0b9f8d7b 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt @@ -157,6 +157,9 @@ class GameManager : Runnable { level?.setTeamB(teamB) level?.reset() + gameEnd = null + gameEndCalc = null + if (gameEndDur > 0) { gameEnd = GameTimeout(level!!, gameEndDur) { setGameOverSoon() } if (gameEndCalcDur > 0) { From 8fab4ec312b4141e7ecbd477ce4a29d9f287c270 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Tue, 22 Sep 2026 17:32:32 +0200 Subject: [PATCH 6/9] Min and max for game end date-spinners. --- .../planetsudo/view/configuration/ConfigurationPanel.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt index cac7b251..4c163366 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt @@ -749,14 +749,16 @@ class ConfigurationPanel : JPanel() { val dateModel = SpinnerDateModel() dateModel.calendarField = Calendar.MINUTE dateSpinner.model = dateModel + dateModel.start = Date(0) + dateModel.end = Date(60 * 60 * 1000 - 1) //59:59 in ms dateSpinner.editor = DateEditor(dateSpinner) val dateFormat: SimpleDateFormat = (dateSpinner.editor as DateEditor).format - dateFormat.timeZone = TimeZones.GMT dateFormat.applyLocalizedPattern("mm:ss") + dateFormat.timeZone = TimeZones.GMT //GMT because that's where a displayed value of "00:00" is 0 ms // Update value here because of https://stackoverflow.com/a/37444974 - dateModel.value = Date(60 * 1000) // TODO keep last value as default? + dateModel.value = Date(0) // TODO keep last value as default? } private fun levelChooserComboBoxActionPerformed() { // GEN-FIRST:event_levelChooserComboBoxActionPerformed From 84b8920a1753d5c944af06a7a39661115883e310 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Tue, 22 Sep 2026 17:42:52 +0200 Subject: [PATCH 7/9] Refactoring for descriptive names. --- .../openbase/planetsudo/game/GameManager.kt | 44 +++++++++---------- .../openbase/planetsudo/game/GameTimeout.kt | 4 +- .../view/configuration/ConfigurationPanel.kt | 4 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt index 0b9f8d7b..dc54c0b7 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameManager.kt @@ -50,10 +50,10 @@ class GameManager : Runnable { private var gameSpeedFactor: Double var gameOverSoon: Boolean - private var gameEndDur: Long = 0 - private var gameEndCalcDur: Long = 0 - private var gameEnd: GameTimeout? = null - private var gameEndCalc: GameTimeout? = null + private var gameEndDurationInMs: Long = 0 + private var gameEndCalcDurationInMs: Long = 0 + private var gameEndTimeout: GameTimeout? = null + private var gameEndCalcTimeout: GameTimeout? = null init { LOGGER.info("Create $this.") @@ -121,12 +121,12 @@ class GameManager : Runnable { LOGGER.info("Set $level as new level.") } - fun setGameEndTimer(duration: Long) { - gameEndDur = duration + fun setGameEndDuration(durationInMs: Long) { + gameEndDurationInMs = durationInMs } - fun setGameEndCalcTimer(duration: Long) { - gameEndCalcDur = duration + fun setGameEndCalcDuration(durationInMs: Long) { + gameEndCalcDurationInMs = durationInMs } fun startGame(gameEndFunction: () -> Unit) { @@ -157,13 +157,13 @@ class GameManager : Runnable { level?.setTeamB(teamB) level?.reset() - gameEnd = null - gameEndCalc = null + gameEndTimeout = null + gameEndCalcTimeout = null - if (gameEndDur > 0) { - gameEnd = GameTimeout(level!!, gameEndDur) { setGameOverSoon() } - if (gameEndCalcDur > 0) { - gameEndCalc = GameTimeout(level!!, gameEndDur + gameEndCalcDur) { gameEndFunction() } + if (gameEndDurationInMs > 0) { + gameEndTimeout = GameTimeout(level!!, gameEndDurationInMs) { setGameOverSoon() } + if (gameEndCalcDurationInMs > 0) { + gameEndCalcTimeout = GameTimeout(level!!, gameEndDurationInMs + gameEndCalcDurationInMs) { gameEndFunction() } } } @@ -172,8 +172,8 @@ class GameManager : Runnable { lock.withLock { condition.signalAll() } - gameEnd?.startTimer() - gameEndCalc?.startTimer() + gameEndTimeout?.startTimer() + gameEndCalcTimeout?.startTimer() LOGGER.info("Game is Running.") } } @@ -197,20 +197,20 @@ class GameManager : Runnable { if (state == GameState.Break) { isPause = true - gameEndCalc?.stopTimer() - gameEnd?.stopTimer() + gameEndCalcTimeout?.stopTimer() + gameEndTimeout?.stopTimer() } else if (isPause && gameState == GameState.Running) { isPause = false - gameEndCalc?.startTimer() - gameEnd?.startTimer() + gameEndCalcTimeout?.startTimer() + gameEndTimeout?.startTimer() } if (state == GameState.Running) { isGameOver = false } else if (state == GameState.Configuration) { isGameOver = true - gameEndCalc?.stopTimer() - gameEnd?.stopTimer() + gameEndCalcTimeout?.stopTimer() + gameEndTimeout?.stopTimer() } } diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt index 2281e2a5..c50dd527 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -6,9 +6,9 @@ import java.awt.event.ActionListener import javax.swing.Timer // this still de-syncs slightly from the timer in LevelMenuPanel -class GameTimeout(level: AbstractLevel, duration: Long, val function: () -> Unit) : ActionListener { +class GameTimeout(level: AbstractLevel, durationInMs: Long, val function: () -> Unit) : ActionListener { private val timer: Timer = Timer(calcTimerDelay(level.getGameSpeedFactor()), this) - private var secondsRemaining = duration / 1000 + private var secondsRemaining = durationInMs / 1000 private var fired: Boolean = false init { //This is a blatant copy of LevelMenuPanel, thus knowingly violating single source of truth diff --git a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt index 4c163366..2d241b07 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt @@ -431,8 +431,8 @@ class ConfigurationPanel : JPanel() { configureDateSpinner(gameEndTimeSpinner!!) configureDateSpinner(gameEndCalcTimeSpinner!!) - gameEndTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndTimer(((e.source as JSpinner).value as Date).time) } - gameEndCalcTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndCalcTimer(((e.source as JSpinner).value as Date).time) } + gameEndTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndDuration(((e.source as JSpinner).value as Date).time) } + gameEndCalcTimeSpinner!!.addChangeListener { e -> gameManager.setGameEndCalcDuration(((e.source as JSpinner).value as Date).time) } gameEndTimeLabel!!.horizontalAlignment = SwingConstants.LEADING gameEndTimeLabel!!.text = "Automatisches Spielende einleiten" From c31c2a9621a2ff17a15773e99e4bc9621fd24a3f Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Tue, 22 Sep 2026 18:32:02 +0200 Subject: [PATCH 8/9] Removed duplicated line in `LevelMenuPanel` --- .../kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt index adc17e47..87239892 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt @@ -27,7 +27,6 @@ class LevelMenuPanel : JPanel(), ActionListener { fun setLevel(level: AbstractLevel) { levelName = level.name updateDelay (level.getGameSpeedFactor()) - timer.initialDelay = timer.delay level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> From c4f94f44199ba9de358b68c0da026ca4454c5af8 Mon Sep 17 00:00:00 2001 From: tommy33790 <118300318+tommy33790@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:15:25 +0200 Subject: [PATCH 9/9] Fix formatting --- src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt | 4 ++-- .../planetsudo/view/configuration/ConfigurationPanel.kt | 4 ++-- .../org/openbase/planetsudo/view/menu/LevelMenuPanel.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt index c50dd527..5bc59a30 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/GameTimeout.kt @@ -5,13 +5,13 @@ import java.awt.event.ActionEvent import java.awt.event.ActionListener import javax.swing.Timer -// this still de-syncs slightly from the timer in LevelMenuPanel +// This still de-syncs slightly from the timer in LevelMenuPanel class GameTimeout(level: AbstractLevel, durationInMs: Long, val function: () -> Unit) : ActionListener { private val timer: Timer = Timer(calcTimerDelay(level.getGameSpeedFactor()), this) private var secondsRemaining = durationInMs / 1000 private var fired: Boolean = false - init { //This is a blatant copy of LevelMenuPanel, thus knowingly violating single source of truth + init { // This is a blatant copy of LevelMenuPanel, thus knowingly violating single source of truth level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running -> diff --git a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt index 2d241b07..8ad0242e 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/configuration/ConfigurationPanel.kt @@ -750,12 +750,12 @@ class ConfigurationPanel : JPanel() { dateModel.calendarField = Calendar.MINUTE dateSpinner.model = dateModel dateModel.start = Date(0) - dateModel.end = Date(60 * 60 * 1000 - 1) //59:59 in ms + dateModel.end = Date(60 * 60 * 1000 - 1) // 59:59 in ms dateSpinner.editor = DateEditor(dateSpinner) val dateFormat: SimpleDateFormat = (dateSpinner.editor as DateEditor).format dateFormat.applyLocalizedPattern("mm:ss") - dateFormat.timeZone = TimeZones.GMT //GMT because that's where a displayed value of "00:00" is 0 ms + dateFormat.timeZone = TimeZones.GMT // GMT because that's where a displayed value of "00:00" is 0 ms // Update value here because of https://stackoverflow.com/a/37444974 dateModel.value = Date(0) // TODO keep last value as default? diff --git a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt index 87239892..26986078 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/menu/LevelMenuPanel.kt @@ -26,7 +26,7 @@ class LevelMenuPanel : JPanel(), ActionListener { fun setLevel(level: AbstractLevel) { levelName = level.name - updateDelay (level.getGameSpeedFactor()) + updateDelay(level.getGameSpeedFactor()) level.addPropertyChangeListener { if (it.propertyName == AbstractLevel.GAME_SPEED_FACTOR_CHANGED) { timer.isRunning.let { running ->