mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-18 20:05:02 -04:00
audio config, refactor CountUpAndDownLatch, allow parents and children
This commit is contained in:
parent
4534304d15
commit
ead9f6bfe3
@ -160,11 +160,7 @@ public final class Minosoft {
|
||||
taskWorker.addTask(new Task((progress) -> StartProgressWindow.show(START_STATUS_LATCH), "Progress Window", "Display progress window", Priorities.HIGH, TaskImportance.OPTIONAL, "JavaFX Toolkit", "Configuration"));
|
||||
}
|
||||
taskWorker.work(START_STATUS_LATCH);
|
||||
try {
|
||||
START_STATUS_LATCH.waitUntilZero();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
START_STATUS_LATCH.await();
|
||||
Log.info("Everything initialized!");
|
||||
if (StaticConfiguration.HEADLESS_MODE) {
|
||||
return;
|
||||
|
@ -16,6 +16,7 @@ package de.bixilon.minosoft.config.config.game
|
||||
import de.bixilon.minosoft.config.config.game.controls.ControlsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.elements.ElementsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.graphics.GraphicsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.sound.SoundConfig
|
||||
|
||||
data class GameConfig(
|
||||
var graphics: GraphicsGameConfig = GraphicsGameConfig(),
|
||||
@ -24,4 +25,5 @@ data class GameConfig(
|
||||
var controls: ControlsGameConfig = ControlsGameConfig(),
|
||||
var elements: ElementsGameConfig = ElementsGameConfig(),
|
||||
var camera: CameraGameConfig = CameraGameConfig(),
|
||||
var sound: SoundConfig = SoundConfig(),
|
||||
)
|
||||
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.config.config.game.sound
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
data class SoundConfig(
|
||||
var enabled: Boolean = true, // ToDo
|
||||
@Json(name = "master_volume") var masterVolume: Float = 1.0f,
|
||||
@Json(name = "enable_packet_sounds") var enablePacketSounds: Boolean = true,
|
||||
)
|
@ -140,10 +140,10 @@ class MinecraftAssetsManager(
|
||||
}
|
||||
|
||||
private fun verifyAssets(source: AssetsSource, latch: CountUpAndDownLatch?, assets: Map<ResourceLocation, String>): Map<ResourceLocation, String> {
|
||||
val assetsLatch = CountUpAndDownLatch(assets.size)
|
||||
latch?.addCount(assets.size)
|
||||
val assetsLatch = CountUpAndDownLatch(assets.size, latch)
|
||||
for (hash in assets.values) {
|
||||
Minosoft.THREAD_POOL.execute {
|
||||
// Log.log(LogMessageType.ASSETS, LogLevels.VERBOSE){"Assets, total=${assets.size}, latchTotal=${assetsLatch.total}, current=${assetsLatch.count}"}
|
||||
val compressed = source != AssetsSource.PIXLYZER
|
||||
if (StaticConfiguration.DEBUG_SLOW_LOADING) {
|
||||
Thread.sleep(100L)
|
||||
@ -151,11 +151,11 @@ class MinecraftAssetsManager(
|
||||
if (!verifyAssetHash(hash, compressed = compressed)) {
|
||||
downloadAsset(source, hash)
|
||||
}
|
||||
latch?.countDown()
|
||||
assetsLatch.countDown()
|
||||
assetsLatch.dec()
|
||||
}
|
||||
}
|
||||
assetsLatch.waitUntilZero()
|
||||
|
||||
assetsLatch.awaitWithChange()
|
||||
return assets
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ data class Version(
|
||||
val s2CPacketMapping: Map<ConnectionStates, HashBiMap<S2C, Int>>,
|
||||
) {
|
||||
var isLoaded = false
|
||||
var isGettingLoaded = false
|
||||
val registries: Registries = Registries()
|
||||
lateinit var assetsManager: MinecraftAssetsManager
|
||||
lateinit var localeManager: MinecraftLocaleManager
|
||||
@ -68,9 +67,9 @@ data class Version(
|
||||
localeManager.load(this, Minosoft.getConfig().config.general.language)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun load(latch: CountUpAndDownLatch) {
|
||||
if (isLoaded || isGettingLoaded) {
|
||||
// already loaded or is getting loaded
|
||||
if (isLoaded) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -84,8 +83,7 @@ data class Version(
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
latch.countUp()
|
||||
isGettingLoaded = true
|
||||
latch.inc()
|
||||
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.INFO) { "Loading mappings for $this..." }
|
||||
initializeAssetManger(latch)
|
||||
val startTime = System.currentTimeMillis()
|
||||
@ -109,17 +107,16 @@ data class Version(
|
||||
}
|
||||
JsonObject()
|
||||
}
|
||||
latch.addCount(1)
|
||||
latch.inc()
|
||||
registries.load(this, pixlyzerData)
|
||||
latch.countDown()
|
||||
latch.dec()
|
||||
if (pixlyzerData.size() > 0) {
|
||||
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.INFO) { "Loaded mappings for $this (${versionName} in ${System.currentTimeMillis() - startTime}ms" }
|
||||
} else {
|
||||
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.WARN) { "Could not load mappings for $this (${versionName}. Some features might not work." }
|
||||
}
|
||||
isLoaded = true
|
||||
isGettingLoaded = false
|
||||
latch.countDown()
|
||||
latch.dec()
|
||||
}
|
||||
|
||||
fun unload() {
|
||||
@ -128,7 +125,6 @@ data class Version(
|
||||
registries.parentRegistries = null
|
||||
}
|
||||
isLoaded = false
|
||||
isGettingLoaded = false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
@ -15,7 +15,6 @@ package de.bixilon.minosoft.gui.main;
|
||||
|
||||
import com.jfoenix.controls.JFXAlert;
|
||||
import com.jfoenix.controls.JFXDialogLayout;
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.ShutdownReasons;
|
||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
||||
@ -27,6 +26,7 @@ import de.bixilon.minosoft.util.logging.LogMessageType;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
@ -36,7 +36,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
public class StartProgressWindow extends Application {
|
||||
public static final CountDownLatch TOOLKIT_LATCH = new CountDownLatch(2); // 2 if not started, 1 if started, 0 if loaded
|
||||
public static JFXAlert<Boolean> progressDialog;
|
||||
private static JFXProgressBar progressBar;
|
||||
private static ProgressBar progressBar;
|
||||
private static Label progressLabel;
|
||||
private static boolean exit;
|
||||
|
||||
@ -56,7 +56,7 @@ public class StartProgressWindow extends Application {
|
||||
JFXDialogLayout layout = new JFXDialogLayout();
|
||||
layout.setHeading(new Label(LocaleManager.translate(Strings.MINOSOFT_STILL_STARTING_HEADER)));
|
||||
|
||||
progressBar = new JFXProgressBar();
|
||||
progressBar = new ProgressBar();
|
||||
progressBar.setPrefHeight(50);
|
||||
|
||||
progressLabel = new Label();
|
||||
@ -79,11 +79,7 @@ public class StartProgressWindow extends Application {
|
||||
stage.toFront();
|
||||
});
|
||||
while (progress.getCount() > 0) {
|
||||
try {
|
||||
progress.waitForChange();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
progress.waitForChange();
|
||||
Platform.runLater(() -> {
|
||||
progressBar.setProgress(1.0F - ((float) progress.getCount() / progress.getTotal()));
|
||||
progressLabel.setText(String.format("%d / %d", (progress.getTotal() - progress.getCount()), progress.getTotal()));
|
||||
|
@ -93,6 +93,8 @@ class RenderWindow(
|
||||
var tickCount = 0L
|
||||
var lastTickTimer = System.currentTimeMillis()
|
||||
|
||||
private var initalPositionReceived = false
|
||||
|
||||
init {
|
||||
connection.registerEvent(CallbackEventInvoker.of<ConnectionStateChangeEvent> {
|
||||
if (it.connection.isDisconnected) {
|
||||
@ -106,8 +108,9 @@ class RenderWindow(
|
||||
if (packet !is PositionAndRotationS2CP) {
|
||||
return@of
|
||||
}
|
||||
if (latch.count > 0) {
|
||||
latch.countDown()
|
||||
if (!initalPositionReceived) {
|
||||
latch.dec()
|
||||
initalPositionReceived = true
|
||||
}
|
||||
queue += {
|
||||
inputHandler.camera.setPosition(packet.position)
|
||||
@ -270,11 +273,11 @@ class RenderWindow(
|
||||
connection.fireEvent(ScreenResizeEvent(previousScreenDimensions = Vec2i(0, 0), screenDimensions = screenDimensions))
|
||||
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING) { "Rendering is fully prepared in ${stopwatch.totalTime()}" }
|
||||
Log.log(LogMessageType.RENDERING_LOADING) { "Rendering is fully prepared in ${stopwatch.totalTime()}" }
|
||||
initialized = true
|
||||
latch.countDown()
|
||||
latch.waitUntilZero()
|
||||
this.latch.waitUntilZero()
|
||||
latch.dec()
|
||||
latch.await()
|
||||
this.latch.await()
|
||||
glfwShowWindow(windowId)
|
||||
Log.log(LogMessageType.RENDERING_GENERAL) { "Showing window after ${stopwatch.totalTime()}" }
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering
|
||||
|
||||
import de.bixilon.minosoft.Minosoft
|
||||
import de.bixilon.minosoft.gui.rendering.sound.AudioPlayer
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
|
||||
@ -28,16 +29,19 @@ class Rendering(private val connection: PlayConnection) {
|
||||
|
||||
fun init(latch: CountUpAndDownLatch) {
|
||||
Log.log(LogMessageType.RENDERING_GENERAL, LogLevels.INFO) { "Hello LWJGL ${Version.getVersion()}!" }
|
||||
latch.countUp()
|
||||
latch.inc()
|
||||
startRenderThread(latch)
|
||||
latch.countUp()
|
||||
startAudioPlayerThread(latch)
|
||||
}
|
||||
|
||||
private fun startAudioPlayerThread(latch: CountUpAndDownLatch) {
|
||||
if (!Minosoft.config.config.game.sound.enabled) {
|
||||
return
|
||||
}
|
||||
val audioLatch = CountUpAndDownLatch(1, latch)
|
||||
Thread({
|
||||
try {
|
||||
audioPlayer.init(latch)
|
||||
audioPlayer.init(audioLatch)
|
||||
audioPlayer.startLoop()
|
||||
audioPlayer.exit()
|
||||
} catch (exception: Throwable) {
|
||||
@ -46,6 +50,7 @@ class Rendering(private val connection: PlayConnection) {
|
||||
audioPlayer.exit()
|
||||
} catch (ignored: Throwable) {
|
||||
}
|
||||
latch.minus(audioLatch.count)
|
||||
}
|
||||
}, "Audio#${connection.connectionId}").start()
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.sound
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import de.bixilon.minosoft.Minosoft
|
||||
import de.bixilon.minosoft.data.mappings.ResourceLocation
|
||||
import de.bixilon.minosoft.data.mappings.sounds.SoundEvent
|
||||
import de.bixilon.minosoft.gui.rendering.Rendering
|
||||
@ -23,6 +24,7 @@ import de.bixilon.minosoft.gui.rendering.input.camera.Camera
|
||||
import de.bixilon.minosoft.gui.rendering.modding.events.CameraPositionChangeEvent
|
||||
import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound
|
||||
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
|
||||
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
|
||||
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
|
||||
@ -45,7 +47,6 @@ import org.lwjgl.openal.EXTThreadLocalContext.alcSetThreadContext
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.IntBuffer
|
||||
import java.nio.ShortBuffer
|
||||
|
||||
|
||||
class AudioPlayer(
|
||||
@ -64,8 +65,6 @@ class AudioPlayer(
|
||||
private lateinit var listener: SoundListener
|
||||
private val sources: MutableList<SoundSource> = synchronizedListOf()
|
||||
|
||||
private var pcm: ShortBuffer? = null
|
||||
|
||||
|
||||
private fun preloadSounds() {
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Preloading sounds..." }
|
||||
@ -120,10 +119,13 @@ class AudioPlayer(
|
||||
|
||||
|
||||
initialized = true
|
||||
latch.countDown()
|
||||
latch.dec()
|
||||
}
|
||||
|
||||
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
|
||||
if (!initialized) {
|
||||
return
|
||||
}
|
||||
playSound(sounds[soundEvent]!!.getRandom(), position, volume, pitch)
|
||||
}
|
||||
|
||||
@ -144,7 +146,7 @@ class AudioPlayer(
|
||||
}
|
||||
|
||||
|
||||
fun playSound(sound: Sound, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
|
||||
private fun playSound(sound: Sound, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
|
||||
queue += add@{
|
||||
sound.load(connection.assetsManager)
|
||||
if (sound.loadFailed) {
|
||||
@ -159,12 +161,12 @@ class AudioPlayer(
|
||||
source.relative = false
|
||||
source.position = it
|
||||
} ?: let {
|
||||
source.position = Vec3(0, 0, 0)
|
||||
source.position = Vec3.EMPTY
|
||||
source.relative = true
|
||||
}
|
||||
source.sound = sound
|
||||
source.pitch = pitch * sound.pitch
|
||||
source.gain = volume * sound.volume
|
||||
source.gain = volume * sound.volume * Minosoft.config.config.game.sound.masterVolume
|
||||
source.play()
|
||||
}
|
||||
}
|
||||
@ -191,7 +193,6 @@ class AudioPlayer(
|
||||
}
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Destroying OpenAL context..." }
|
||||
MemoryUtil.memFree(pcm)
|
||||
|
||||
alcSetThreadContext(MemoryUtil.NULL)
|
||||
alcDestroyContext(context)
|
||||
|
@ -69,7 +69,7 @@ public class ModLoader {
|
||||
return;
|
||||
}
|
||||
|
||||
progress.addCount(MOD_MAP.size() * ModPhases.values().length); // count * mod phases
|
||||
progress.setCount(progress.getCount() + MOD_MAP.size() * ModPhases.values().length); // count * mod phases
|
||||
|
||||
// check if all dependencies are available
|
||||
modLoop:
|
||||
@ -128,7 +128,7 @@ public class ModLoader {
|
||||
Minosoft.THREAD_POOL.execute(() -> {
|
||||
if (!entry.getValue().isEnabled()) {
|
||||
modLatch.countDown();
|
||||
progress.countDown();
|
||||
progress.dec();
|
||||
return;
|
||||
}
|
||||
Log.log(LogMessageType.MOD_LOADING, LogLevels.VERBOSE, () -> "Loading mod " + entry.getValue().getInfo() + "in " + phase);
|
||||
@ -142,7 +142,7 @@ public class ModLoader {
|
||||
entry.getValue().setEnabled(false);
|
||||
}
|
||||
modLatch.countDown();
|
||||
progress.countDown();
|
||||
progress.dec();
|
||||
});
|
||||
}
|
||||
modLatch.await();
|
||||
@ -162,7 +162,7 @@ public class ModLoader {
|
||||
MinosoftMod instance;
|
||||
try {
|
||||
Log.log(LogMessageType.MOD_LOADING, LogLevels.VERBOSE, () -> "Trying to load " + file.getAbsolutePath());
|
||||
progress.countUp();
|
||||
progress.inc();
|
||||
ZipFile zipFile = new ZipFile(file);
|
||||
ModInfo modInfo = new ModInfo(Util.readJsonFromZip("mod.json", zipFile));
|
||||
if (isModLoaded(modInfo)) {
|
||||
@ -182,7 +182,7 @@ public class ModLoader {
|
||||
e.printStackTrace();
|
||||
Log.log(LogMessageType.MOD_LOADING, LogLevels.WARN, () -> "Could not load " + file.getAbsolutePath());
|
||||
}
|
||||
progress.countDown(); // failed
|
||||
progress.dec(); // failed
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -158,10 +158,9 @@ class PlayConnection(
|
||||
if (!RenderConstants.DISABLE_RENDERING && !StaticConfiguration.HEADLESS_MODE) {
|
||||
val renderer = Rendering(this)
|
||||
this.renderer = renderer
|
||||
renderer.init(latch)
|
||||
while (!renderer.renderWindow.initialized || !renderer.audioPlayer.initialized) {
|
||||
latch.waitForChange()
|
||||
}
|
||||
val renderLatch = CountUpAndDownLatch(0, latch)
|
||||
renderer.init(renderLatch)
|
||||
renderLatch.awaitWithChange()
|
||||
}
|
||||
Log.log(LogMessageType.NETWORK_STATUS, level = LogLevels.INFO) { "Connecting to server: $address" }
|
||||
network.connect(address)
|
||||
@ -172,7 +171,7 @@ class PlayConnection(
|
||||
lastException = MappingsLoadingException("Mappings could not be loaded", exception)
|
||||
connectionState = ConnectionStates.FAILED_NO_RETRY
|
||||
}
|
||||
latch.countDown()
|
||||
latch.dec()
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package de.bixilon.minosoft.protocol.packets.s2c.play
|
||||
|
||||
import de.bixilon.minosoft.Minosoft
|
||||
import de.bixilon.minosoft.data.SoundCategories
|
||||
import de.bixilon.minosoft.data.mappings.sounds.SoundEvent
|
||||
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
|
||||
@ -55,6 +56,9 @@ class SoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
}
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
if (!Minosoft.config.config.game.sound.enablePacketSounds) {
|
||||
return
|
||||
}
|
||||
connection.fireEvent(PlaySoundEvent(connection, this))
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ public class CLI {
|
||||
.build();
|
||||
|
||||
|
||||
latch.countDown();
|
||||
latch.dec();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
@ -118,6 +118,6 @@ public class CLI {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, "CLI").start();
|
||||
latch.waitUntilZero();
|
||||
latch.await();
|
||||
}
|
||||
}
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util;
|
||||
|
||||
// Thanks https://stackoverflow.com/questions/14255019/latch-that-can-be-incremented
|
||||
public class CountUpAndDownLatch {
|
||||
private final Object lock = new Object();
|
||||
private long count;
|
||||
private long total;
|
||||
|
||||
public CountUpAndDownLatch(int count) {
|
||||
this.total = count;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public void waitUntilZero() throws InterruptedException {
|
||||
synchronized (this.lock) {
|
||||
while (this.count > 0) {
|
||||
this.lock.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void waitUntilZero(long timeout) throws InterruptedException {
|
||||
synchronized (this.lock) {
|
||||
while (this.count > 0) {
|
||||
this.lock.wait(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void countUp() {
|
||||
synchronized (this.lock) {
|
||||
this.total++;
|
||||
this.count++;
|
||||
this.lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void countDown() {
|
||||
if (this.count == 0) {
|
||||
throw new IllegalStateException("Can not count down, counter is already 0");
|
||||
}
|
||||
synchronized (this.lock) {
|
||||
this.count--;
|
||||
this.lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
synchronized (this.lock) {
|
||||
return this.count;
|
||||
}
|
||||
}
|
||||
|
||||
public void setCount(int value) {
|
||||
synchronized (this.lock) {
|
||||
this.total += value;
|
||||
this.count = value;
|
||||
this.lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCount(int count) {
|
||||
synchronized (this.lock) {
|
||||
this.total += count;
|
||||
this.count += count;
|
||||
this.lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return this.total;
|
||||
}
|
||||
|
||||
public void waitForChange() throws InterruptedException {
|
||||
long latestCount = this.count;
|
||||
long latestTotal = this.total;
|
||||
synchronized (this.lock) {
|
||||
while (latestCount == this.count && latestTotal == this.total) {
|
||||
this.lock.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d / %d", this.count, this.total);
|
||||
}
|
||||
}
|
135
src/main/java/de/bixilon/minosoft/util/CountUpAndDownLatch.kt
Normal file
135
src/main/java/de/bixilon/minosoft/util/CountUpAndDownLatch.kt
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
package de.bixilon.minosoft.util
|
||||
|
||||
// Thanks https://stackoverflow.com/questions/14255019/latch-that-can-be-incremented
|
||||
class CountUpAndDownLatch @JvmOverloads constructor(count: Int, var parent: CountUpAndDownLatch? = null) {
|
||||
private val lock = Object()
|
||||
private val children: MutableSet<CountUpAndDownLatch> = mutableSetOf()
|
||||
var count: Int = 0
|
||||
get() {
|
||||
synchronized(lock) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
synchronized(lock) {
|
||||
val diff = value - field
|
||||
check(value >= 0) { "Can not set count (previous=$field, value=$value)" }
|
||||
if (diff > 0) {
|
||||
total += diff
|
||||
}
|
||||
field = value
|
||||
parent?.plus(diff) ?: notify()
|
||||
}
|
||||
}
|
||||
|
||||
var total: Int = count
|
||||
get() {
|
||||
synchronized(lock) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
private set(value) {
|
||||
check(value >= 0) { "Total can not be < 0: $value" }
|
||||
synchronized(lock) {
|
||||
check(value >= field) { "Total can not decrement! (current=$field, wanted=$value)" }
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
check(parent !== this)
|
||||
parent?.addChild(this)
|
||||
this.count += count
|
||||
}
|
||||
|
||||
fun addChild(latch: CountUpAndDownLatch) {
|
||||
synchronized(lock) {
|
||||
latch.parent = this
|
||||
children += latch
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun await(timeout: Long = 0L) {
|
||||
synchronized(lock) {
|
||||
while (count > 0) {
|
||||
lock.wait(timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName(name = "customNotify")
|
||||
private fun notify(`this`: CountUpAndDownLatch = this) {
|
||||
synchronized(lock) {
|
||||
lock.notifyAll()
|
||||
for (child in children) {
|
||||
if (child === `this`) {
|
||||
continue
|
||||
}
|
||||
child.notify(this)
|
||||
}
|
||||
}
|
||||
if (`this` === parent) {
|
||||
return
|
||||
}
|
||||
parent?.notify(this)
|
||||
}
|
||||
|
||||
operator fun inc(): CountUpAndDownLatch {
|
||||
plus(1)
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun dec(): CountUpAndDownLatch {
|
||||
minus(1)
|
||||
return this
|
||||
}
|
||||
|
||||
fun plus(value: Int): CountUpAndDownLatch {
|
||||
synchronized(lock) {
|
||||
count += value
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun minus(value: Int): CountUpAndDownLatch {
|
||||
return plus(-value)
|
||||
}
|
||||
|
||||
|
||||
fun waitForChange() {
|
||||
val lastCount = count
|
||||
val lastTotal = total
|
||||
synchronized(lock) {
|
||||
while (lastCount == count && lastTotal == total) {
|
||||
lock.wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun awaitWithChange() {
|
||||
synchronized(lock) {
|
||||
if (total == 0) {
|
||||
waitForChange()
|
||||
}
|
||||
await()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return String.format("%d / %d", count, total)
|
||||
}
|
||||
}
|
@ -90,18 +90,14 @@ public class AsyncTaskWorker {
|
||||
}
|
||||
}
|
||||
this.jobsDone.add(task.getTaskName());
|
||||
latch.countDown();
|
||||
latch.dec();
|
||||
});
|
||||
doing.remove(task);
|
||||
}
|
||||
}));
|
||||
try {
|
||||
latch.waitForChange();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
latch.waitForChange();
|
||||
}
|
||||
progress.countDown(); // remove initial value of 1
|
||||
progress.dec(); // remove initial value of 1
|
||||
}
|
||||
|
||||
public boolean isJobDone(String name) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user