mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
y u no call back :(
This commit is contained in:
@@ -112,7 +112,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
private val internalPlaylistName: String
|
||||
get() = ingame.musicStreamer.playlistName
|
||||
|
||||
fun registerPlaylist(path: String, fileToName: JsonValue?, shuffled: Boolean, diskJockeyingMode: String): TerrarumMusicPlaylist {
|
||||
private fun registerPlaylist(path: String, fileToName: JsonValue?, shuffled: Boolean, diskJockeyingMode: String): TerrarumMusicPlaylist {
|
||||
fun String.isNum(): Boolean {
|
||||
try {
|
||||
this.toInt()
|
||||
@@ -123,7 +123,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}
|
||||
}
|
||||
|
||||
val playlist = ingame.musicStreamer.queueDirectory(path, shuffled, diskJockeyingMode, false) { filename ->
|
||||
val playlist = TerrarumMusicPlaylist.fromDirectory(path, shuffled, diskJockeyingMode) { filename ->
|
||||
fileToName?.get(filename).let {
|
||||
if (it == null)
|
||||
filename.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.let {
|
||||
@@ -333,8 +333,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
if (mode < MODE_SHOW_LIST) {
|
||||
if (!transitionOngoing) {
|
||||
transitionRequest = MODE_SHOW_LIST
|
||||
currentListMode =
|
||||
0 // no list transition anim is needed this time, just set the variable
|
||||
currentListMode = 0 // no list transition anim is needed this time, just set the variable
|
||||
resetAlbumlistScroll()
|
||||
}
|
||||
}
|
||||
@@ -344,7 +343,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}
|
||||
else {
|
||||
if (!transitionOngoing)
|
||||
transitionRequest = App.audioMixer.musicTrack.isPlaying.toInt() * MODE_MOUSE_UP
|
||||
transitionRequest = MODE_MOUSE_UP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +351,6 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
// prev song
|
||||
if (mode < MODE_SHOW_LIST) {
|
||||
MusicService.playPrevSongInPlaylist {
|
||||
ingame.musicStreamer.startMusic(this) // required for "intermittent" mode
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false
|
||||
}
|
||||
@@ -393,10 +391,15 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false*/
|
||||
|
||||
MusicService.resumePlaylistPlayback {
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false
|
||||
}
|
||||
MusicService.resumePlaylistPlayback(
|
||||
/* onSuccess: () -> Unit */{
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false
|
||||
},
|
||||
/* onFailure: (Throwable) -> Unit */ {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,7 +408,6 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
// next song
|
||||
if (mode < MODE_SHOW_LIST) {
|
||||
MusicService.playNextSongInPlaylist {
|
||||
ingame.musicStreamer.startMusic(this) // required for "intermittent" mode
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false
|
||||
}
|
||||
@@ -427,8 +429,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
if (mode < MODE_SHOW_LIST) {
|
||||
if (!transitionOngoing) {
|
||||
transitionRequest = MODE_SHOW_LIST
|
||||
currentListMode =
|
||||
1 // no list transition anim is needed this time, just set the variable
|
||||
currentListMode = 1 // no list transition anim is needed this time, just set the variable
|
||||
resetPlaylistScroll()
|
||||
}
|
||||
}
|
||||
@@ -438,7 +439,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}
|
||||
else {
|
||||
if (!transitionOngoing)
|
||||
transitionRequest = App.audioMixer.musicTrack.isPlaying.toInt() * MODE_MOUSE_UP
|
||||
transitionRequest = MODE_MOUSE_UP
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +464,6 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}*/
|
||||
|
||||
MusicService.playNthSongInPlaylist(index) {
|
||||
ingame.musicStreamer.startMusic(this) // required for "intermittent" mode
|
||||
iHitTheStopButton = false
|
||||
stopRequested = false
|
||||
}
|
||||
@@ -509,23 +509,8 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
|
||||
// printdbg(this, "mode = $mode; req = $transitionRequest")
|
||||
|
||||
/*if (shouldPlayerBeDisabled || iHitTheStopButton) {
|
||||
if (!stopRequested) {
|
||||
stopRequested = true
|
||||
ingame.backgroundMusicPlayer.stopMusic(this)
|
||||
}
|
||||
}
|
||||
else*/ if (ingame.musicStreamer.playCaller is PlaysMusic && !jukeboxStopMonitorAlert && !App.audioMixer.musicTrack.isPlaying) {
|
||||
jukeboxStopMonitorAlert = true
|
||||
val interval = ingame.musicStreamer.getRandomMusicInterval()
|
||||
ingame.musicStreamer.stopMusic(this, false, interval)
|
||||
}
|
||||
else if (App.audioMixer.musicTrack.isPlaying) {
|
||||
jukeboxStopMonitorAlert = false
|
||||
}
|
||||
}
|
||||
|
||||
private var jukeboxStopMonitorAlert = true
|
||||
private var iHitTheStopButton = false
|
||||
private var stopRequested = false
|
||||
|
||||
@@ -726,6 +711,22 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
drawControls(frameDelta, batch, _posX, posYforControls)
|
||||
drawList(camera, frameDelta, batch, _posX, _posY)
|
||||
|
||||
|
||||
// debug codes
|
||||
if (MusicService.transactionLocked) {
|
||||
batch.color = Color.RED
|
||||
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "LOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5)
|
||||
}
|
||||
else {
|
||||
batch.color = Color.WHITE
|
||||
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "UNLOCKED", Toolkit.drawWidth, 0, _posY.toInt() + height + 5)
|
||||
}
|
||||
|
||||
batch.color = Color.WHITE
|
||||
Toolkit.drawTextCentered(batch, App.fontSmallNumbers, "State: ${MusicService.currentPlaybackState.get()}", Toolkit.drawWidth, 0, _posY.toInt() + height + 18)
|
||||
// end of debug codes
|
||||
|
||||
|
||||
batch.color = Color.WHITE
|
||||
|
||||
|
||||
@@ -1057,6 +1058,8 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
else
|
||||
0f
|
||||
|
||||
val baseCol = if (MusicService.transactionLocked) Color.RED else Color.WHITE
|
||||
|
||||
if (alpha > 0f) {
|
||||
val alpha0 = alpha.coerceIn(0f, 1f).organicOvershoot().coerceAtMost(1f)
|
||||
val internalWidth =minOf(widthForMouseUp.toFloat(), width - 20f)
|
||||
@@ -1078,10 +1081,10 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
// prev/next button
|
||||
if (i == 1 || i == 3) {
|
||||
// prev/next song button
|
||||
batch.color = Color(1f, 1f, 1f, alphaBase * (1f - buttonFadePerc))
|
||||
batch.color = baseCol * Color(1f, 1f, 1f, alphaBase * (1f - buttonFadePerc))
|
||||
batch.draw(controlButtons.get(i, 0), btnX, btnY)
|
||||
// prev/next page button
|
||||
batch.color = Color(1f, 1f, 1f, alphaBase * buttonFadePerc)
|
||||
batch.color = baseCol * Color(1f, 1f, 1f, alphaBase * buttonFadePerc)
|
||||
batch.draw(controlButtons.get(i, 1), btnX, btnY)
|
||||
}
|
||||
// stop button
|
||||
@@ -1089,13 +1092,13 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
// get correct stop/play button
|
||||
val iconY = if (!App.audioMixer.musicTrack.isPlaying) 1 else 0
|
||||
// fade if avaliable
|
||||
batch.color = Color(1f, 1f, 1f, alphaBase * (1f - buttonFadePerc))
|
||||
batch.color = baseCol * Color(1f, 1f, 1f, alphaBase * (1f - buttonFadePerc))
|
||||
batch.draw(controlButtons.get(i, iconY), btnX, btnY)
|
||||
// page number with fade
|
||||
|
||||
for (mode in 0..1) {
|
||||
val alphaNum = if (mode == 0) 1f - listViewPanelScroll else listViewPanelScroll
|
||||
batch.color = Color(1f, 1f, 1f, alphaBase2 * buttonFadePerc * alphaNum) // don't use mouse-up effect
|
||||
batch.color = baseCol * Color(1f, 1f, 1f, alphaBase2 * buttonFadePerc * alphaNum) // don't use mouse-up effect
|
||||
val (thisPage, totalPage) = if (mode == 0)
|
||||
albumlistScroll.div(PLAYLIST_LINES).plus(1) to albumsList.size.toFloat().div(PLAYLIST_LINES).ceilToInt()
|
||||
else
|
||||
@@ -1111,7 +1114,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}
|
||||
// else button
|
||||
else {
|
||||
batch.color = Color(1f, 1f, 1f, alphaBase)
|
||||
batch.color = baseCol * Color(1f, 1f, 1f, alphaBase)
|
||||
batch.draw(controlButtons.get(i, 0), btnX, btnY)
|
||||
}
|
||||
|
||||
@@ -1338,7 +1341,7 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
track.doGaplessPlayback = (albumProp.diskJockeyingMode == "continuous")
|
||||
if (track.doGaplessPlayback) {
|
||||
track.pullNextTrack = {
|
||||
track.currentTrack = ingame.musicStreamer.pullNextMusicTrack(true)
|
||||
track.currentTrack = MusicService.currentPlaylist!!.peekNext()
|
||||
setMusicName(track.currentTrack?.name ?: "")
|
||||
}
|
||||
}
|
||||
@@ -1360,4 +1363,13 @@ class MusicPlayerControl(private val ingame: TerrarumIngame) : UICanvas() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun Color.times(other: Color): Color {
|
||||
return Color(
|
||||
this.r * other.r,
|
||||
this.g * other.g,
|
||||
this.b * other.b,
|
||||
this.a * other.a,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1052,6 +1052,7 @@ public class App implements ApplicationListener {
|
||||
printdbg("AppLoader-Static", "Screen before change: " + currentScreen.getClass().getCanonicalName());
|
||||
|
||||
currentScreen.hide();
|
||||
MusicService.INSTANCE.leaveScene();
|
||||
currentScreen.dispose();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import net.torvald.terrarum.audio.AudioBank
|
||||
import net.torvald.terrarum.audio.AudioMixer.Companion.DEFAULT_FADEOUT_LEN
|
||||
import net.torvald.terrarum.transaction.Transaction
|
||||
import net.torvald.terrarum.transaction.TransactionListener
|
||||
import net.torvald.terrarum.transaction.TransactionState
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/**
|
||||
* To play the music, create a transaction then pass it to the `runTransaction(Transaction)`
|
||||
@@ -12,18 +15,147 @@ import net.torvald.terrarum.transaction.TransactionState
|
||||
*/
|
||||
object MusicService : TransactionListener() {
|
||||
|
||||
var currentPlaylist: TerrarumMusicPlaylist? = null; private set
|
||||
private val currentPlaylistReference = AtomicReference<TerrarumMusicPlaylist?>(null)
|
||||
val currentPlaylist: TerrarumMusicPlaylist?; get() = currentPlaylistReference.get()
|
||||
|
||||
override fun getCurrentStatusForTransaction(): TransactionState {
|
||||
return TransactionState(
|
||||
mutableMapOf(
|
||||
"currentPlaylist" to currentPlaylist
|
||||
hashMapOf(
|
||||
"currentPlaylist" to currentPlaylistReference.get()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun commitTransaction(state: TransactionState) {
|
||||
this.currentPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
|
||||
(state["currentPlaylist"] as TerrarumMusicPlaylist?).let {
|
||||
this.currentPlaylistReference.set(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private const val STATE_INTERMISSION = 0
|
||||
private const val STATE_FIREPLAY = 1
|
||||
private const val STATE_PLAYING = 2
|
||||
|
||||
val currentPlaybackState = AtomicInteger(STATE_INTERMISSION)
|
||||
private var waitAkku = 0f
|
||||
private var waitTime = 10f
|
||||
|
||||
private fun enterSTATE_INTERMISSION(waitFor: Float) {
|
||||
currentPlaybackState.set(STATE_INTERMISSION)
|
||||
waitTime = waitFor
|
||||
waitAkku = 0f
|
||||
}
|
||||
|
||||
private fun enterSTATE_FIREPLAY() {
|
||||
val state = currentPlaybackState.get()
|
||||
if (state == STATE_FIREPLAY) throw IllegalStateException("Cannot change state FIREPLAY -> FIREPLAY")
|
||||
if (state == STATE_PLAYING) throw IllegalStateException("Cannot change state PLAYING -> FIREPLAY")
|
||||
|
||||
waitAkku = 0f
|
||||
currentPlaybackState.set(STATE_FIREPLAY)
|
||||
}
|
||||
|
||||
private fun enterSTATE_PLAYING() {
|
||||
val state = currentPlaybackState.get()
|
||||
if (state == STATE_INTERMISSION) throw IllegalStateException("Cannot change state INTERMISSION -> PLAYING")
|
||||
if (state == STATE_PLAYING) throw IllegalStateException("Cannot change state PLAYING -> PLAYING")
|
||||
|
||||
currentPlaybackState.set(STATE_PLAYING)
|
||||
}
|
||||
|
||||
fun getRandomMusicInterval() = 20f + Math.random().toFloat() * 4f // longer gap (20s to 24s)
|
||||
|
||||
private fun enterIntermissionAndWaitForPlaylist() {
|
||||
val time = when (currentPlaylist?.diskJockeyingMode ?: "intermittent") {
|
||||
"intermittent" -> getRandomMusicInterval()
|
||||
"continuous" -> 0f
|
||||
else -> getRandomMusicInterval()
|
||||
}
|
||||
enterSTATE_INTERMISSION(time)
|
||||
}
|
||||
|
||||
fun enterIntermission() {
|
||||
enterSTATE_INTERMISSION(getRandomMusicInterval())
|
||||
}
|
||||
|
||||
fun onMusicFinishing(audio: AudioBank) {
|
||||
synchronized(this) {
|
||||
enterIntermissionAndWaitForPlaylist()
|
||||
}
|
||||
}
|
||||
|
||||
private var playTransactionOngoing = false
|
||||
|
||||
fun update(delta: Float) {
|
||||
when (currentPlaybackState.get()) {
|
||||
STATE_FIREPLAY -> {
|
||||
if (!playTransactionOngoing) {
|
||||
playTransactionOngoing = true
|
||||
MusicService.resumePlaylistPlayback(
|
||||
/* onSuccess: () -> Unit */
|
||||
{
|
||||
runTransaction(object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent())
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
enterSTATE_PLAYING()
|
||||
}
|
||||
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval
|
||||
}
|
||||
})
|
||||
},
|
||||
/* onFailure: (Throwable) -> Unit */
|
||||
{
|
||||
enterSTATE_INTERMISSION(getRandomMusicInterval()) // will try again after a random interval
|
||||
},
|
||||
// onFinally: () -> Unit
|
||||
{
|
||||
playTransactionOngoing = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
STATE_PLAYING -> {
|
||||
// onMusicFinishing() will be called when the music finishes; it's on the setOnCompletionListener
|
||||
}
|
||||
STATE_INTERMISSION -> {
|
||||
waitAkku += delta
|
||||
|
||||
if (waitAkku >= waitTime && currentPlaylist != null) {
|
||||
enterSTATE_FIREPLAY()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun enterScene(id: String) {
|
||||
synchronized(this) {
|
||||
/*val playlist = when (id) {
|
||||
"title" -> getTitlePlaylist()
|
||||
"ingame" -> getIngameDefaultPlaylist()
|
||||
else -> getIngameDefaultPlaylist()
|
||||
}
|
||||
|
||||
putNewPlaylist(playlist) {
|
||||
// after the fadeout, we'll...
|
||||
enterSTATE_FIREPLAY()
|
||||
}*/
|
||||
|
||||
stopPlaylistPlayback { }
|
||||
}
|
||||
}
|
||||
|
||||
fun leaveScene() {
|
||||
synchronized(this) {
|
||||
stopPlaylistPlayback {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,6 +164,9 @@ object MusicService : TransactionListener() {
|
||||
* be reset as soon as the transaction starts. Note that the resetting behaviour is NOT atomic. (the given
|
||||
* playlist will stay in reset state even if the transaction fails)
|
||||
*
|
||||
* When the transaction was successful, the old playlist gets disposed of, then the songFinishedHook of
|
||||
* the songs in the new playlist will be overwritten, before `onSuccess` is called.
|
||||
*
|
||||
* The old playlist will be disposed of if and only if the transaction was successful.
|
||||
*
|
||||
* @param playlist An instance of a [TerrarumMusicPlaylist] to be changed into
|
||||
@@ -45,7 +180,6 @@ object MusicService : TransactionListener() {
|
||||
|
||||
override fun start(state: TransactionState) {
|
||||
oldPlaylist = state["currentPlaylist"] as TerrarumMusicPlaylist?
|
||||
|
||||
if (oldPlaylist == playlist) return
|
||||
|
||||
playlist.reset()
|
||||
@@ -64,15 +198,27 @@ object MusicService : TransactionListener() {
|
||||
waitUntil { fadedOut }
|
||||
}
|
||||
else {
|
||||
/* do nothing */
|
||||
// put new playlist
|
||||
state["currentPlaylist"] = playlist
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
oldPlaylist?.dispose()
|
||||
|
||||
(state["currentPlaylist"] as TerrarumMusicPlaylist?)?.let {
|
||||
it.musicList.forEach {
|
||||
it.songFinishedHook = {
|
||||
onMusicFinishing(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,20 +226,30 @@ object MusicService : TransactionListener() {
|
||||
return object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
var fadedOut = false
|
||||
var err: Throwable? = null
|
||||
// request fadeout
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
|
||||
// callback: play next song in the playlist
|
||||
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getNext())
|
||||
fadedOut = true
|
||||
try {
|
||||
// callback: play next song in the playlist
|
||||
// TODO queue the next song on the playlist, the actual playback will be done by the state machine update
|
||||
|
||||
fadedOut = true
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
|
||||
waitUntil { fadedOut }
|
||||
waitUntil { fadedOut || err != null }
|
||||
if (err != null) throw err!!
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
onSuccess()
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,20 +257,30 @@ object MusicService : TransactionListener() {
|
||||
return object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
var fadedOut = false
|
||||
var err: Throwable? = null
|
||||
// request fadeout
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
|
||||
// callback: play prev song in the playlist
|
||||
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getPrev())
|
||||
fadedOut = true
|
||||
try {
|
||||
// callback: play prev song in the playlist
|
||||
// TODO queue the prev song on the playlist, the actual playback will be done by the state machine update
|
||||
|
||||
fadedOut = true
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
|
||||
waitUntil { fadedOut }
|
||||
waitUntil { fadedOut || err != null }
|
||||
if (err != null) throw err!!
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
onSuccess()
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,18 +288,29 @@ object MusicService : TransactionListener() {
|
||||
return object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
var fadedOut = false
|
||||
var err: Throwable? = null
|
||||
// request fadeout
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack) {
|
||||
// callback: play prev song in the playlist
|
||||
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getNthSong(index))
|
||||
fadedOut = true
|
||||
try {
|
||||
// callback: play prev song in the playlist
|
||||
// TODO queue the nth song on the playlist, the actual playback will be done by the state machine update
|
||||
|
||||
|
||||
fadedOut = true
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
|
||||
waitUntil { fadedOut }
|
||||
waitUntil { fadedOut || err != null }
|
||||
if (err != null) throw err!!
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,21 +324,32 @@ object MusicService : TransactionListener() {
|
||||
}
|
||||
|
||||
waitUntil { fadedOut }
|
||||
|
||||
enterSTATE_INTERMISSION(Float.POSITIVE_INFINITY)
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
onSuccess()
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTransactionForPlaylistResume(onSuccess: () -> Unit): Transaction {
|
||||
private fun createTransactionForPlaylistResume(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit): Transaction {
|
||||
return object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
App.audioMixer.startMusic((state["currentPlaylist"] as TerrarumMusicPlaylist).getCurrent())
|
||||
enterSTATE_FIREPLAY()
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {}
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
onSuccess()
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
onFailure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,22 +362,44 @@ object MusicService : TransactionListener() {
|
||||
return object : Transaction {
|
||||
override fun start(state: TransactionState) {
|
||||
var fadedOut = false
|
||||
var err: Throwable? = null
|
||||
println("createTransactionPausePlaylistForMusicalFixture start")
|
||||
// request fadeout
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.musicTrack, DEFAULT_FADEOUT_LEN / 2.0) {
|
||||
// callback: let the caller actually take care of playing the audio
|
||||
action()
|
||||
println("createTransactionPausePlaylistForMusicalFixture fadeout end")
|
||||
try {
|
||||
// callback: let the caller actually take care of playing the audio
|
||||
action()
|
||||
|
||||
fadedOut = true
|
||||
fadedOut = true
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
err = e
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
waitUntil { fadedOut }
|
||||
waitUntil { fadedOut || err != null }
|
||||
if (err != null) throw err!!
|
||||
|
||||
// enter intermission state
|
||||
println("createTransactionPausePlaylistForMusicalFixture fadeout waiting end, entering INTERMISSION state")
|
||||
enterSTATE_INTERMISSION(Float.POSITIVE_INFINITY)
|
||||
|
||||
// wait until the interjected music finishes
|
||||
println("createTransactionPausePlaylistForMusicalFixture waiting for musicFinished()")
|
||||
waitUntil { musicFinished() }
|
||||
}
|
||||
|
||||
override fun onSuccess(state: TransactionState) { onSuccess() }
|
||||
override fun onFailure(e: Throwable, state: TransactionState) { onFailure(e) }
|
||||
override fun onSuccess(state: TransactionState) {
|
||||
onSuccess()
|
||||
enterSTATE_INTERMISSION(getRandomMusicInterval())
|
||||
}
|
||||
override fun onFailure(e: Throwable, state: TransactionState) {
|
||||
e.printStackTrace()
|
||||
onFailure(e)
|
||||
enterSTATE_INTERMISSION(getRandomMusicInterval())
|
||||
}
|
||||
}
|
||||
|
||||
// note to self: wait() and notify() using a lock object is impractical as the Java thread can wake up
|
||||
@@ -215,9 +425,11 @@ object MusicService : TransactionListener() {
|
||||
runTransaction(createTransactionPlaylistChange(playlist, onSuccess), onFinally)
|
||||
}
|
||||
|
||||
/** Normal playlist playback will resume after the transaction, after the onSuccess/onFailure */
|
||||
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) {
|
||||
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure))
|
||||
}
|
||||
/** Normal playlist playback will resume after the transaction, after the onSuccess/onFailure but before the onFinally */
|
||||
fun playMusicalFixture(action: () -> Unit, musicFinished: () -> Boolean, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit, onFinally: () -> Unit = {}) {
|
||||
runTransaction(createTransactionPausePlaylistForMusicalFixture(action, musicFinished, onSuccess, onFailure), onFinally)
|
||||
}
|
||||
@@ -250,10 +462,10 @@ object MusicService : TransactionListener() {
|
||||
runTransaction(createTransactionForPlaylistStop(onSuccess), onFinally)
|
||||
}
|
||||
|
||||
fun resumePlaylistPlayback(onSuccess: () -> Unit) {
|
||||
runTransaction(createTransactionForPlaylistResume(onSuccess))
|
||||
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) {
|
||||
runTransaction(createTransactionForPlaylistResume(onSuccess, onFailure))
|
||||
}
|
||||
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFinally: () -> Unit) {
|
||||
runTransaction(createTransactionForPlaylistResume(onSuccess), onFinally)
|
||||
fun resumePlaylistPlayback(onSuccess: () -> Unit, onFailure: (Throwable) -> Unit, onFinally: () -> Unit) {
|
||||
runTransaction(createTransactionForPlaylistResume(onSuccess, onFailure), onFinally)
|
||||
}
|
||||
}
|
||||
@@ -47,14 +47,19 @@ class TerrarumMusicPlaylist(
|
||||
fun getCurrent(): MusicContainer {
|
||||
checkRefill()
|
||||
|
||||
return musicList[currentIndexCursor]
|
||||
return musicList[internalIndices[currentIndexCursor]]
|
||||
}
|
||||
|
||||
fun getNext(): MusicContainer {
|
||||
checkRefill()
|
||||
currentIndexCursor += 1
|
||||
|
||||
return musicList[currentIndexCursor]
|
||||
return musicList[internalIndices[currentIndexCursor]]
|
||||
}
|
||||
|
||||
fun peekNext(): MusicContainer {
|
||||
checkRefill()
|
||||
return musicList[internalIndices[currentIndexCursor + 1]]
|
||||
}
|
||||
|
||||
fun getPrev(): MusicContainer {
|
||||
@@ -75,7 +80,7 @@ class TerrarumMusicPlaylist(
|
||||
|
||||
currentIndexCursor -= 1
|
||||
|
||||
return musicList[currentIndexCursor]
|
||||
return musicList[internalIndices[currentIndexCursor]]
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +97,8 @@ class TerrarumMusicPlaylist(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val validMusicExtensions = hashSetOf("mp3", "wav", "ogg")
|
||||
|
||||
/**
|
||||
* Adding songFinishedHook to the songs is a responsibility of the caller.
|
||||
*/
|
||||
@@ -104,7 +111,7 @@ class TerrarumMusicPlaylist(
|
||||
|
||||
val playlistName = musicDir.substringAfterLast('/')
|
||||
|
||||
val playlist = File(musicDir).listFiles()?.sortedBy { it.name }?.mapNotNull {
|
||||
val playlist = File(musicDir).listFiles()?.sortedBy { it.name }?.filter { Companion.validMusicExtensions.contains(it.extension.lowercase()) }?.mapNotNull {
|
||||
printdbg(this, "Music: ${it.absolutePath}")
|
||||
try {
|
||||
MusicContainer(
|
||||
|
||||
@@ -321,6 +321,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
)
|
||||
|
||||
loadedTime_t = App.getTIME_T()
|
||||
|
||||
MusicService.enterScene("ingame")
|
||||
}
|
||||
|
||||
data class NewGameParams(
|
||||
@@ -1007,6 +1009,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
}
|
||||
|
||||
musicStreamer.update(this, delta)
|
||||
MusicService.update(delta)
|
||||
|
||||
////////////////////////
|
||||
// ui-related updates //
|
||||
|
||||
@@ -36,40 +36,6 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
musicBin = ArrayList(if (shuffled) playlist.shuffled() else playlist.slice(playlist.indices))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the playlist to the MusicService to be played at the other play commands.
|
||||
*
|
||||
* @param musicDir where the music files are. Absolute path.
|
||||
* @param shuffled if the tracks are to be shuffled
|
||||
* @param diskJockeyingMode `intermittent` to give random gap between tracks, `continuous` for continuous playback
|
||||
*/
|
||||
fun queueDirectory(musicDir: String, shuffled: Boolean, diskJockeyingMode: String, playImmediately: Boolean, fileToName: ((String) -> String) = { name: String ->
|
||||
name.substringBeforeLast('.').replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" ")
|
||||
}): TerrarumMusicPlaylist {
|
||||
// FIXME the olde way -- must be replaced with one that utilises MusicService
|
||||
/*if (musicState != STATE_INIT && musicState != STATE_INTERMISSION) {
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
|
||||
stopMusic0(App.audioMixer.musicTrack.currentTrack)
|
||||
}
|
||||
|
||||
playlist.forEach { it.tryDispose() }
|
||||
registerSongsFromDir(musicDir, fileToName)
|
||||
|
||||
this.shuffled = shuffled
|
||||
this.diskJockeyingMode = diskJockeyingMode
|
||||
|
||||
restockMusicBin()*/
|
||||
|
||||
val playlist = TerrarumMusicPlaylist.fromDirectory(musicDir, shuffled, diskJockeyingMode, fileToName)
|
||||
|
||||
if (!playImmediately)
|
||||
MusicService.putNewPlaylist(playlist) {}
|
||||
else
|
||||
MusicService.putNewPlaylist(playlist)
|
||||
|
||||
return playlist
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a song to the head of the internal playlist (`musicBin`)
|
||||
*/
|
||||
@@ -142,7 +108,8 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>()
|
||||
|
||||
init {
|
||||
queueDirectory(App.customMusicDir, true, "intermittent", true)
|
||||
// TODO queue and play the default playlist
|
||||
// TerrarumMusicPlaylist.fromDirectory(App.defaultMusicDir, true, "intermittent")
|
||||
}
|
||||
|
||||
|
||||
@@ -175,78 +142,19 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
protected var ambState = 0
|
||||
protected var ambFired = false
|
||||
|
||||
fun getRandomMusicInterval() = 20f + Math.random().toFloat() * 4f // longer gap (20s to 24s)
|
||||
fun getRandomMusicInterval() = MusicService.getRandomMusicInterval()
|
||||
|
||||
var stopCaller: Any? = null; private set
|
||||
var playCaller: Any? = null; private set
|
||||
var stopCallTime: Long? = null; private set
|
||||
// call MusicService to fade out
|
||||
// pauses playlist update
|
||||
// called by MusicPlayerControl
|
||||
fun stopMusicPlayback() {
|
||||
|
||||
private fun stopMusic0(song: AudioBank?, callStopMusicHook: Boolean = true, customPauseLen: Float? = null) {
|
||||
musicState = if (customPauseLen == Float.POSITIVE_INFINITY) STATE_INIT else STATE_INTERMISSION
|
||||
// printdbg(this, "stopMusic1 customLen=$customPauseLen, stateNow: $musicState, called by")
|
||||
// printStackTrace(this)
|
||||
intermissionAkku = 0f
|
||||
intermissionLength = customPauseLen ?: getRandomMusicInterval()
|
||||
musicFired = false
|
||||
if (callStopMusicHook && musicStopHooks.isNotEmpty() && song is MusicContainer) musicStopHooks.forEach {
|
||||
it(song)
|
||||
}
|
||||
// printdbg(this, "StopMusic Intermission: $intermissionLength seconds")
|
||||
}
|
||||
|
||||
fun stopMusic(caller: Any?, callStopMusicHook: Boolean = true, pauseLen: Float = Float.POSITIVE_INFINITY) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val trackThis = App.audioMixer.musicTrack.currentTrack
|
||||
// resumes playlist update
|
||||
// called by MusicPlayerControl
|
||||
fun resumeMusicPlayback() {
|
||||
|
||||
if (caller is TerrarumMusicStreamer) {
|
||||
if (stopCaller == null) {
|
||||
// printdbg(this, "Caller: this, prev caller: $stopCaller, len: $pauseLen, obliging stop request")
|
||||
stopMusic0(trackThis, callStopMusicHook, pauseLen)
|
||||
}
|
||||
else {
|
||||
// printdbg(this, "Caller: this, prev caller: $stopCaller, len: $pauseLen, ignoring stop request")
|
||||
}
|
||||
}
|
||||
else {
|
||||
// printdbg(this, "Caller: $caller, prev caller: <doesn't matter>, len: $pauseLen, obliging stop request")
|
||||
stopMusic0(trackThis, callStopMusicHook, pauseLen)
|
||||
}
|
||||
|
||||
stopCaller = caller?.javaClass?.canonicalName
|
||||
stopCallTime = System.currentTimeMillis()
|
||||
|
||||
// printStackTrace(this)
|
||||
}
|
||||
|
||||
fun startMusic(caller: Any?) {
|
||||
playCaller = caller
|
||||
startMusic0(pullNextMusicTrack())
|
||||
}
|
||||
|
||||
private fun startMusic0(song: MusicContainer) {
|
||||
stopCaller = null
|
||||
stopCallTime = null
|
||||
|
||||
App.audioMixer.startMusic(song)
|
||||
// printdbg(this, "startMusic Now playing: ${song.name}, called by:")
|
||||
// printStackTrace(this)
|
||||
// INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
|
||||
if (musicStartHooks.isNotEmpty()) musicStartHooks.forEach { it(song) }
|
||||
musicState = STATE_PLAYING
|
||||
intermissionLength = 42.42424f
|
||||
}
|
||||
|
||||
// MixerTrackProcessor will call this function externally to make gapless playback work
|
||||
fun pullNextMusicTrack(callNextMusicHook: Boolean = false): MusicContainer {
|
||||
// printStackTrace(this)
|
||||
|
||||
// prevent same song to play twice in row (for the most time)
|
||||
if (musicBin.isEmpty()) {
|
||||
restockMusicBin()
|
||||
}
|
||||
return musicBin.removeAt(0).also { mus ->
|
||||
if (callNextMusicHook && musicStartHooks.isNotEmpty()) musicStartHooks.forEach { it(mus) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopAmbient() {
|
||||
@@ -286,12 +194,6 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
|
||||
override fun update(ingame: IngameInstance, delta: Float) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val callerRecordExpired = (timeNow - (stopCallTime ?: 0L) > 1000)
|
||||
|
||||
if (callerRecordExpired && stopCaller != null) {
|
||||
stopCaller = null
|
||||
stopCallTime = null
|
||||
}
|
||||
|
||||
// start the song queueing if there is one to play
|
||||
if (firstTime) {
|
||||
@@ -300,12 +202,21 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
if (ambients.isNotEmpty()) ambState = STATE_INTERMISSION
|
||||
}
|
||||
|
||||
|
||||
when (musicState) {
|
||||
STATE_FIREPLAY -> {
|
||||
if (!musicFired) {
|
||||
musicFired = true
|
||||
startMusic0(pullNextMusicTrack())
|
||||
MusicService.resumePlaylistPlayback(
|
||||
// onSuccess: () -> Unit
|
||||
{
|
||||
musicFired = true
|
||||
musicState = STATE_PLAYING
|
||||
},
|
||||
// onFailure: (Throwable) -> Unit
|
||||
{
|
||||
musicFired = false
|
||||
musicState = STATE_INTERMISSION
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
STATE_PLAYING -> {
|
||||
@@ -428,8 +339,6 @@ class TerrarumMusicStreamer : MusicStreamer() {
|
||||
private val TRACK2_DAWN_ELEV_DN_MAX = -10.0
|
||||
|
||||
override fun dispose() {
|
||||
App.audioMixer.requestFadeOut(App.audioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
|
||||
stopMusic0(App.audioMixer.musicTrack.currentTrack)
|
||||
stopAmbient()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +304,8 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
|
||||
loadThingsWhileIntroIsVisible()
|
||||
printdbg(this, "show() exit")
|
||||
|
||||
MusicService.enterScene("title")
|
||||
}
|
||||
|
||||
|
||||
@@ -330,6 +332,9 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
|
||||
// update UIs //
|
||||
uiContainer.forEach { it?.update(delta) }
|
||||
|
||||
// update MusicService //
|
||||
MusicService.update(delta)
|
||||
}
|
||||
|
||||
private val particles = CircularArray<ParticleBase>(16, true)
|
||||
|
||||
@@ -103,11 +103,6 @@ class FixtureJukebox : Electric, PlaysMusic {
|
||||
|
||||
override fun updateImpl(delta: Float) {
|
||||
super.updateImpl(delta)
|
||||
|
||||
// supress the normal background music playback
|
||||
if (musicIsPlaying && !flagDespawn) {
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,10 +130,8 @@ class FixtureJukebox : Electric, PlaysMusic {
|
||||
}
|
||||
|
||||
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
||||
returnToInitialState()
|
||||
printdbg(this, "Stop music $title - $artist")
|
||||
|
||||
// can't call stopDiscPlayback() because of the recursion
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, pauseLen = (INGAME.musicStreamer as TerrarumMusicStreamer).getRandomMusicInterval())
|
||||
}
|
||||
|
||||
discCurrentlyPlaying = index
|
||||
@@ -149,24 +142,18 @@ class FixtureJukebox : Electric, PlaysMusic {
|
||||
}*/
|
||||
|
||||
MusicService.playMusicalFixture(
|
||||
// action: () -> Unit
|
||||
{
|
||||
/* action: () -> Unit */ {
|
||||
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||
},
|
||||
// musicFinished: () -> Boolean
|
||||
{
|
||||
/* musicFinished: () -> Boolean */ {
|
||||
!musicIsPlaying
|
||||
},
|
||||
// onSuccess: () -> Unit
|
||||
{
|
||||
/* onSuccess: () -> Unit */ {
|
||||
|
||||
},
|
||||
// onFailure: (Throwable) -> Unit
|
||||
{
|
||||
|
||||
},
|
||||
// onFinally: () -> Unit
|
||||
returnToInitialState
|
||||
/* onFailure: (Throwable) -> Unit */ {
|
||||
returnToInitialState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -185,8 +172,10 @@ class FixtureJukebox : Electric, PlaysMusic {
|
||||
*/
|
||||
fun stopGracefully() {
|
||||
stopDiscPlayback()
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, pauseLen = (INGAME.musicStreamer as TerrarumMusicStreamer).getRandomMusicInterval())
|
||||
|
||||
try {
|
||||
MusicService.enterIntermission()
|
||||
}
|
||||
catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
override fun drawBody(frameDelta: Float, batch: SpriteBatch) {
|
||||
|
||||
@@ -92,11 +92,6 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
||||
|
||||
override fun updateImpl(delta: Float) {
|
||||
super.updateImpl(delta)
|
||||
|
||||
// supress the normal background music playback
|
||||
if (musicIsPlaying && !flagDespawn) {
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun playDisc() {
|
||||
@@ -118,10 +113,8 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
||||
}
|
||||
|
||||
musicNowPlaying = MusicContainer(title, musicFile.file()) {
|
||||
returnToInitialState()
|
||||
App.printdbg(this, "Stop music $title - $artist")
|
||||
|
||||
// can't call stopDiscPlayback() because of the recursion
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, pauseLen = (INGAME.musicStreamer as TerrarumMusicStreamer).getRandomMusicInterval())
|
||||
}
|
||||
|
||||
|
||||
@@ -131,24 +124,19 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
||||
}*/
|
||||
|
||||
MusicService.playMusicalFixture(
|
||||
// action: () -> Unit
|
||||
{
|
||||
/* action: () -> Unit */ {
|
||||
App.printdbg(this, "call startAudio(${musicNowPlaying?.name})")
|
||||
startAudio(musicNowPlaying!!) { loadEffector(it) }
|
||||
},
|
||||
// musicFinished: () -> Boolean
|
||||
{
|
||||
/* musicFinished: () -> Boolean */ {
|
||||
!musicIsPlaying
|
||||
},
|
||||
// onSuccess: () -> Unit
|
||||
{
|
||||
/* onSuccess: () -> Unit */ {
|
||||
|
||||
},
|
||||
// onFailure: (Throwable) -> Unit
|
||||
{
|
||||
|
||||
},
|
||||
// onFinally: () -> Unit
|
||||
returnToInitialState
|
||||
/* onFailure: (Throwable) -> Unit */ {
|
||||
returnToInitialState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -162,8 +150,10 @@ class FixtureMusicalTurntable : Electric, PlaysMusic {
|
||||
*/
|
||||
fun stopGracefully() {
|
||||
stopDiscPlayback()
|
||||
(INGAME.musicStreamer as TerrarumMusicStreamer).stopMusic(this, pauseLen = (INGAME.musicStreamer as TerrarumMusicStreamer).getRandomMusicInterval())
|
||||
|
||||
try {
|
||||
MusicService.enterIntermission()
|
||||
}
|
||||
catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
private fun stopDiscPlayback() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package net.torvald.terrarum.transaction
|
||||
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2024-06-28.
|
||||
*/
|
||||
interface Transaction {
|
||||
|
||||
/**
|
||||
* Call this function to begin the transaction.
|
||||
*
|
||||
@@ -28,18 +30,23 @@ interface Transaction {
|
||||
abstract class TransactionListener {
|
||||
|
||||
/** `null` if not locked, a class that acquired the lock if locked */
|
||||
var transactionLockedBy: Any? = null; private set
|
||||
val transactionLocked: Boolean; get() = (transactionLockedBy != null)
|
||||
private val transactionLockingClass: AtomicReference<Transaction?> = AtomicReference(null)
|
||||
val transactionLocked: Boolean; get() = (transactionLockingClass.get() != null)
|
||||
|
||||
|
||||
/**
|
||||
* Transaction modifies a given state to a new state, then applies the new state to the object.
|
||||
* The given `transaction` may only modify values which is passed to it.
|
||||
*
|
||||
* Transaction is fully unlocked and the previous locker is unknowable by the time `onFinally` executes.
|
||||
* Note that `onFinally` runs on the same thread the actual transaction has run (GL context not available).
|
||||
*/
|
||||
fun runTransaction(transaction: Transaction, onFinally: () -> Unit = {}) {
|
||||
printdbg(this, "Accepting transaction $transaction")
|
||||
Thread { synchronized(this) {
|
||||
val state = getCurrentStatusForTransaction()
|
||||
if (!transactionLocked) {
|
||||
transactionLockingClass.set(transaction)
|
||||
try {
|
||||
transaction.start(state)
|
||||
// if successful:
|
||||
@@ -49,14 +56,17 @@ abstract class TransactionListener {
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
// if failed, notify the failure
|
||||
System.err.println("Transaction failure: generic")
|
||||
transaction.onFailure(e, state)
|
||||
}
|
||||
finally {
|
||||
transactionLockingClass.set(null)
|
||||
onFinally()
|
||||
}
|
||||
}
|
||||
else {
|
||||
transaction.onFailure(LockedException(this, transactionLockedBy), state)
|
||||
System.err.println("Transaction failure: locked")
|
||||
transaction.onFailure(LockedException(this, transactionLockingClass.get()), state)
|
||||
}
|
||||
} }.start()
|
||||
}
|
||||
@@ -65,10 +75,10 @@ abstract class TransactionListener {
|
||||
protected abstract fun commitTransaction(state: TransactionState)
|
||||
}
|
||||
|
||||
class LockedException(listener: TransactionListener, lockedBy: Any?) :
|
||||
class LockedException(listener: TransactionListener, lockedBy: Transaction?) :
|
||||
Exception("Transaction is rejected because the class '${listener.javaClass.canonicalName}' is locked by '${lockedBy?.javaClass?.canonicalName}'")
|
||||
|
||||
@JvmInline value class TransactionState(val valueTable: MutableMap<String, Any?>) {
|
||||
@JvmInline value class TransactionState(val valueTable: HashMap<String, Any?>) {
|
||||
operator fun get(key: String) = valueTable[key]
|
||||
operator fun set(key: String, value: Any?) {
|
||||
valueTable[key] = value
|
||||
|
||||
Reference in New Issue
Block a user