mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-12 17:07:55 -04:00
more sounds
This commit is contained in:
parent
52f873c5f8
commit
504302025f
@ -26,6 +26,7 @@ import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.ElementRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.shader.Shader
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.getWorldOffset
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.BitByte.isBit
|
||||
@ -165,7 +166,7 @@ class BlockOutlineRenderer(
|
||||
collisionMesh?.unload()
|
||||
outlineMesh = BlockOutlineMesh()
|
||||
|
||||
val blockOffset = raycastHit.blockPosition.getWorldOffset(raycastHit.blockState.block).plus(raycastHit.blockPosition)
|
||||
val blockOffset = raycastHit.blockPosition.toVec3 + raycastHit.blockPosition.getWorldOffset(raycastHit.blockState.block)
|
||||
|
||||
drawVoxelShape(raycastHit.blockState.outlineShape, blockOffset, outlineMesh)
|
||||
outlineMesh.load()
|
||||
|
@ -29,6 +29,7 @@ import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.rotate
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
|
||||
import glm_.vec3.Vec3
|
||||
|
||||
class ElementRenderer(
|
||||
@ -83,7 +84,7 @@ class ElementRenderer(
|
||||
|
||||
for ((drawPositionIndex, texturePositionIndex) in DRAW_ODER) {
|
||||
val input = drawPositions[drawPositionIndex]
|
||||
val output = context.blockPosition plus context.offset + input + DRAW_OFFSET
|
||||
val output = context.blockPosition.toVec3 + input + DRAW_OFFSET + context.offset
|
||||
mesh.addVertex(
|
||||
position = output,
|
||||
textureCoordinates = texturePositions[texturePositionIndex]!!,
|
||||
|
@ -29,6 +29,7 @@ import de.bixilon.minosoft.modding.event.events.BlockBreakAckEvent
|
||||
import de.bixilon.minosoft.protocol.packets.c2s.play.ArmSwingC2SP
|
||||
import de.bixilon.minosoft.protocol.packets.c2s.play.BlockBreakC2SP
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.KUtil.asResourceLocation
|
||||
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
|
||||
import glm_.pow
|
||||
import glm_.vec3.Vec3i
|
||||
@ -131,6 +132,8 @@ class LeftClickHandler(
|
||||
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.FINISHED_DIGGING, raycastHit.blockPosition, raycastHit.hitDirection))
|
||||
clearDigging()
|
||||
connection.world.setBlockState(raycastHit.blockPosition, null)
|
||||
|
||||
renderWindow.rendering.audioPlayer.playSoundEvent(connection.registries.soundEventRegistry[BLOCK_BREAK_SOUND]!!) // ToDO
|
||||
}
|
||||
|
||||
val canStartBreaking = currentTime - breakSent >= ProtocolDefinition.TICK_TIME
|
||||
@ -268,4 +271,8 @@ class LeftClickHandler(
|
||||
}
|
||||
swingArm()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val BLOCK_BREAK_SOUND = "minecraft:block.metal.break".asResourceLocation()
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,20 @@ import de.bixilon.minosoft.gui.rendering.Rendering
|
||||
import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound
|
||||
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.CountUpAndDownLatch
|
||||
import de.bixilon.minosoft.util.KUtil.asResourceLocation
|
||||
import de.bixilon.minosoft.util.KUtil.synchronizedListOf
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedList
|
||||
import de.bixilon.minosoft.util.Queue
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import org.lwjgl.BufferUtils.createShortBuffer
|
||||
import org.lwjgl.openal.AL
|
||||
import org.lwjgl.openal.AL10.*
|
||||
import org.lwjgl.openal.ALC
|
||||
import org.lwjgl.openal.ALC10.*
|
||||
import org.lwjgl.openal.EXTThreadLocalContext.alcSetThreadContext
|
||||
import org.lwjgl.stb.STBVorbis.stb_vorbis_get_samples_short_interleaved
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.IntBuffer
|
||||
@ -53,16 +54,35 @@ class AudioPlayer(
|
||||
private var device = 0L
|
||||
private var context = 0L
|
||||
|
||||
private var source = 0
|
||||
private val queue = Queue()
|
||||
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..." }
|
||||
if (SoundConstants.DISABLE_PRELOADING) {
|
||||
return
|
||||
}
|
||||
|
||||
for (soundList in sounds.values) {
|
||||
for (sound in soundList.sounds) {
|
||||
if (SoundConstants.PRELOAD_ALL_SOUNDS || sound.preload) {
|
||||
sound.load(connection.assetsManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun init(latch: CountUpAndDownLatch) {
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "Loading OpenAL..." }
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
|
||||
loadSounds()
|
||||
preloadSounds()
|
||||
|
||||
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Initializing OpenAL..." }
|
||||
@ -77,59 +97,84 @@ class AudioPlayer(
|
||||
val deviceCaps = ALC.createCapabilities(device)
|
||||
AL.createCapabilities(deviceCaps)
|
||||
|
||||
val listener = SoundListener()
|
||||
|
||||
val source = SoundSource(false)
|
||||
|
||||
|
||||
// Testing, ToDo
|
||||
val sound = sounds[connection.registries.soundEventRegistry[0]]!!.sounds.iterator().next()
|
||||
|
||||
sound.load(connection.assetsManager)
|
||||
listener = SoundListener()
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "OpenAL loaded!" }
|
||||
|
||||
|
||||
val pcm = createShortBuffer(sound.samplesLength)
|
||||
|
||||
pcm.limit(stb_vorbis_get_samples_short_interleaved(sound.handle, sound.channels, pcm) * sound.channels)
|
||||
|
||||
val buffer = alGenBuffers()
|
||||
|
||||
alBufferData(buffer, sound.format, pcm, sound.sampleRate)
|
||||
|
||||
source.buffer = buffer
|
||||
source.play()
|
||||
|
||||
while (source.isPlaying) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "Sound played!" }
|
||||
|
||||
|
||||
initialized = true
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
fun playSoundEvent(soundEvent: SoundEvent) {
|
||||
playSound(sounds[soundEvent]!!.getRandom())
|
||||
}
|
||||
|
||||
|
||||
private fun getAvailableSource(): SoundSource? {
|
||||
for (source in sources.toSynchronizedList()) {
|
||||
if (source.available) {
|
||||
return source
|
||||
}
|
||||
}
|
||||
// no source available
|
||||
if (sources.size > SoundConstants.MAX_SOURCES_AMOUNT) {
|
||||
return null
|
||||
}
|
||||
val source = SoundSource(false)
|
||||
sources += source
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
|
||||
fun playSound(sound: Sound) {
|
||||
queue += add@{
|
||||
sound.load(connection.assetsManager)
|
||||
if (sound.loadFailed) {
|
||||
return@add
|
||||
}
|
||||
val source = getAvailableSource() ?: let {
|
||||
Log.log(LogMessageType.RENDERING_GENERAL, LogLevels.WARN) { "Can not play sound: No source available!" }
|
||||
return@add
|
||||
}
|
||||
source.sound = sound
|
||||
source.play()
|
||||
}
|
||||
}
|
||||
|
||||
fun startLoop() {
|
||||
while (connection.isConnected) {
|
||||
while (connection.connectionState != ConnectionStates.DISCONNECTING) {
|
||||
queue.work()
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
}
|
||||
|
||||
fun exit() {
|
||||
// alDeleteBuffers(buffers)
|
||||
alDeleteSources(source)
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "Unloading OpenAL..." }
|
||||
|
||||
//MemoryUtil.memFree(buffers)
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Unloading sounds..." }
|
||||
for (soundList in sounds.values) {
|
||||
for (sound in soundList.sounds) {
|
||||
sound.unload()
|
||||
}
|
||||
}
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Unloading sources..." }
|
||||
for (source in sources.toSynchronizedList()) {
|
||||
source.unload()
|
||||
}
|
||||
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Destroying OpenAL context..." }
|
||||
MemoryUtil.memFree(pcm)
|
||||
|
||||
alcSetThreadContext(MemoryUtil.NULL)
|
||||
alcDestroyContext(context)
|
||||
alcCloseDevice(device)
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "Unloaded OpenAL!" }
|
||||
}
|
||||
|
||||
private fun loadSounds() {
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
|
||||
val data = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE)
|
||||
|
||||
for ((soundEventResourceLocation, json) in data.entrySet()) {
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.gui.rendering.sound
|
||||
|
||||
object SoundConstants {
|
||||
const val PRELOAD_ALL_SOUNDS = false
|
||||
const val DISABLE_PRELOADING = false
|
||||
|
||||
const val MAX_SOURCES_AMOUNT = 32
|
||||
}
|
@ -24,6 +24,7 @@ class SoundListener(position: Vec3 = Vec3.EMPTY) {
|
||||
alListener3f(AL_POSITION, value.x, value.y, value.z)
|
||||
field = value
|
||||
}
|
||||
|
||||
var velocity: Vec3 = Vec3.EMPTY
|
||||
set(value) {
|
||||
alListener3f(AL_VELOCITY, value.x, value.y, value.z)
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.sound
|
||||
|
||||
import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
|
||||
import glm_.vec3.Vec3
|
||||
import org.lwjgl.openal.AL10.*
|
||||
@ -52,15 +53,23 @@ class SoundSource(loop: Boolean = false) {
|
||||
field = value
|
||||
}
|
||||
|
||||
var buffer: Int = -1
|
||||
var sound: Sound? = null
|
||||
set(value) {
|
||||
alSourcei(source, AL_BUFFER, value)
|
||||
stop()
|
||||
if (value?.loaded != true || value.loadFailed) {
|
||||
field = null
|
||||
return
|
||||
}
|
||||
alSourcei(source, AL_BUFFER, value.buffer)
|
||||
field = value
|
||||
}
|
||||
|
||||
val isPlaying: Boolean
|
||||
get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING
|
||||
|
||||
val available: Boolean
|
||||
get() = isPlaying
|
||||
|
||||
fun play() {
|
||||
alSourcePlay(source)
|
||||
}
|
||||
@ -73,7 +82,7 @@ class SoundSource(loop: Boolean = false) {
|
||||
alSourceStop(source)
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
fun unload() {
|
||||
stop()
|
||||
alDeleteSources(source)
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.openal.AL10.AL_FORMAT_MONO16
|
||||
import org.lwjgl.openal.AL10.AL_FORMAT_STEREO16
|
||||
import org.lwjgl.openal.AL10.*
|
||||
import org.lwjgl.stb.STBVorbis.*
|
||||
import org.lwjgl.stb.STBVorbisInfo
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
@ -41,47 +40,74 @@ data class Sound(
|
||||
private set
|
||||
var loadFailed: Boolean = false
|
||||
private set
|
||||
var buffer: ByteBuffer? = null
|
||||
var handle: Long = -1L
|
||||
var channels: Int = -1
|
||||
private set
|
||||
var sampleRate: Int = -1
|
||||
private set
|
||||
var samplesLength: Int = -1
|
||||
private set
|
||||
var sampleSeconds: Float = -1.0f
|
||||
private set
|
||||
var buffer = -1
|
||||
private set
|
||||
|
||||
var format: Int = -1
|
||||
private var vorbisBuffer: ByteBuffer? = null
|
||||
|
||||
@Synchronized
|
||||
fun load(assetsManager: AssetsManager) {
|
||||
if (loaded || loadFailed) {
|
||||
return
|
||||
}
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loading audio file: $path" }
|
||||
try {
|
||||
|
||||
val buffer = assetsManager.readByteAsset(path)
|
||||
this.buffer = buffer
|
||||
|
||||
val vorbisBuffer = assetsManager.readByteAsset(path)
|
||||
this.vorbisBuffer = vorbisBuffer
|
||||
|
||||
val error = BufferUtils.createIntBuffer(1)
|
||||
handle = stb_vorbis_open_memory(buffer, error, null)
|
||||
if (handle == MemoryUtil.NULL) {
|
||||
val vorbis = stb_vorbis_open_memory(vorbisBuffer, error, null)
|
||||
if (vorbis == MemoryUtil.NULL) {
|
||||
throw IllegalStateException("Can not load vorbis: ${path}: ${error[0]}")
|
||||
}
|
||||
val info = stb_vorbis_get_info(handle, STBVorbisInfo.malloc())
|
||||
val info = stb_vorbis_get_info(vorbis, STBVorbisInfo.malloc())
|
||||
channels = info.channels()
|
||||
format = when (channels) {
|
||||
val format = when (channels) {
|
||||
1 -> AL_FORMAT_MONO16
|
||||
2 -> AL_FORMAT_STEREO16
|
||||
else -> TODO("Channels: $channels")
|
||||
}
|
||||
sampleRate = info.sample_rate()
|
||||
|
||||
samplesLength = stb_vorbis_stream_length_in_samples(handle)
|
||||
sampleSeconds = stb_vorbis_stream_length_in_seconds(handle)
|
||||
samplesLength = stb_vorbis_stream_length_in_samples(vorbis)
|
||||
sampleSeconds = stb_vorbis_stream_length_in_seconds(vorbis)
|
||||
|
||||
|
||||
val pcm = BufferUtils.createShortBuffer(samplesLength)
|
||||
|
||||
pcm.limit(stb_vorbis_get_samples_short_interleaved(vorbis, channels, pcm) * channels)
|
||||
//ToDo: Somehow crashed?: MemoryUtil.memFree(vorbisBuffer)
|
||||
|
||||
this.buffer = alGenBuffers()
|
||||
|
||||
alBufferData(buffer, format, pcm, sampleRate)
|
||||
loaded = true
|
||||
} catch (exception: FileNotFoundException) {
|
||||
loadFailed = true
|
||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.WARN) { "Can not load sound: $path: $exception" }
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun unload() {
|
||||
if (!loaded) {
|
||||
return
|
||||
}
|
||||
alDeleteBuffers(buffer)
|
||||
vorbisBuffer?.let { MemoryUtil.memFree(it) }
|
||||
buffer = -1
|
||||
channels = -1
|
||||
sampleRate = -1
|
||||
samplesLength = -1
|
||||
sampleSeconds = -1.0f
|
||||
loaded = false
|
||||
}
|
||||
}
|
||||
|
@ -148,14 +148,10 @@ object VecUtil {
|
||||
}
|
||||
|
||||
val Vec3i.entityPosition: Vec3
|
||||
get() {
|
||||
return Vec3(x + 0.5f, y, z + 0.5f) // ToDo
|
||||
}
|
||||
get() = Vec3(x + 0.5f, y, z + 0.5f) // ToDo
|
||||
|
||||
val Vec3.blockPosition: Vec3i
|
||||
get() {
|
||||
return Vec3i((x - 0.5f).toInt(), y.toInt(), (z - 0.5f).toInt()) // ToDo
|
||||
}
|
||||
get() = Vec3i((x - 0.5f).toInt(), y.toInt(), (z - 0.5f).toInt()) // ToDo
|
||||
|
||||
fun Vec3i.Companion.of(chunkPosition: Vec2i, sectionHeight: Int, inChunkSectionPosition: Vec3i): Vec3i {
|
||||
return Vec3i(
|
||||
@ -198,8 +194,8 @@ object VecUtil {
|
||||
fun Vec3i.getWorldOffset(block: Block): Vec3 {
|
||||
if (block.randomOffsetType == null || !Minosoft.config.config.game.other.flowerRandomOffset) {
|
||||
return EMPTY_VEC3
|
||||
|
||||
}
|
||||
|
||||
val positionHash = generatePositionHash(x, 0, z)
|
||||
val maxModelOffset = 0.25f // ToDo: use block.model.max_model_offset
|
||||
|
||||
@ -239,18 +235,22 @@ object VecUtil {
|
||||
position[axis].ceilInt - 1
|
||||
}
|
||||
}
|
||||
|
||||
fun getLengthMultiplier(direction: Vec3, position: Vec3, axis: Axes): Float {
|
||||
return (getTarget(direction, position, axis) - position[axis]) / direction[axis]
|
||||
}
|
||||
|
||||
val directionXDistance = getLengthMultiplier(direction, position, Axes.X)
|
||||
val directionYDistance = getLengthMultiplier(direction, position, Axes.Y)
|
||||
val directionZDistance = getLengthMultiplier(direction, position, Axes.Z)
|
||||
return glm.min(directionXDistance, directionYDistance, directionZDistance)
|
||||
}
|
||||
|
||||
val Vec3.min: Float get() = glm.min(this.x, this.y, this.z)
|
||||
val Vec3.min: Float
|
||||
get() = glm.min(this.x, this.y, this.z)
|
||||
|
||||
val Vec3.max: Float get() = glm.max(this.x, this.y, this.z)
|
||||
val Vec3.max: Float
|
||||
get() = glm.max(this.x, this.y, this.z)
|
||||
|
||||
val Vec3.signs: Vec3
|
||||
get() {
|
||||
@ -283,8 +283,11 @@ object VecUtil {
|
||||
return minDistanceDirection
|
||||
}
|
||||
|
||||
val Vec3i.toVec3: Vec3
|
||||
get() = Vec3(this)
|
||||
|
||||
operator fun Vec3.get(axis: Axes): Float {
|
||||
return when(axis) {
|
||||
return when (axis) {
|
||||
Axes.X -> this.x
|
||||
Axes.Y -> this.y
|
||||
Axes.Z -> this.z
|
||||
|
@ -26,7 +26,7 @@ import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
|
||||
class ContainerOpenS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
val containerId = if (buffer.versionId <= V_1_16) { // ToDo: This is completely guessed
|
||||
val containerId = if (buffer.versionId <= V_1_14) { // ToDo: This is completely guessed, it has changed between 1.13 and 1.14, same as #L38
|
||||
buffer.readUnsignedByte()
|
||||
} else {
|
||||
buffer.readVarInt()
|
||||
@ -35,7 +35,7 @@ class ContainerOpenS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
buffer.versionId < V_14W03B -> {
|
||||
buffer.connection.registries.containerTypeRegistry[buffer.readUnsignedByte()]
|
||||
}
|
||||
buffer.versionId >= V_1_16 -> { // ToDo: This is completely guessed
|
||||
buffer.versionId >= V_1_14 -> { // ToDo: This is completely guessed
|
||||
buffer.connection.registries.containerTypeRegistry[buffer.readVarInt()]
|
||||
}
|
||||
else -> {
|
||||
|
@ -27,7 +27,7 @@ class Queue {
|
||||
add(runnable)
|
||||
}
|
||||
|
||||
fun work(maxJobs: Int) {
|
||||
fun work(maxJobs: Int = Int.MAX_VALUE) {
|
||||
var jobsDone = 0
|
||||
for (runnable in queue.toSynchronizedList()) {
|
||||
this.queue.remove(runnable)
|
||||
|
Loading…
x
Reference in New Issue
Block a user