refactor audio system

This commit is contained in:
Bixilon 2021-11-27 23:39:35 +01:00
parent 8f010c6b84
commit 38993d28da
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
23 changed files with 487 additions and 280 deletions

View File

@ -18,7 +18,6 @@ import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties
import de.bixilon.minosoft.data.registries.blocks.types.Block import de.bixilon.minosoft.data.registries.blocks.types.Block
import de.bixilon.minosoft.data.registries.materials.Material import de.bixilon.minosoft.data.registries.materials.Material
import de.bixilon.minosoft.data.registries.registries.Registries import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import de.bixilon.minosoft.util.KUtil.toBoolean import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toInt import de.bixilon.minosoft.util.KUtil.toInt
@ -35,11 +34,11 @@ data class BlockState(
val outlineShape: VoxelShape, val outlineShape: VoxelShape,
val hardness: Float, val hardness: Float,
val requiresTool: Boolean, val requiresTool: Boolean,
val breakSoundEvent: SoundEvent?, val breakSoundEvent: ResourceLocation?,
val stepSoundEvent: SoundEvent?, val stepSoundEvent: ResourceLocation?,
val placeSoundEvent: SoundEvent?, val placeSoundEvent: ResourceLocation?,
val hitSoundEvent: SoundEvent?, val hitSoundEvent: ResourceLocation?,
val fallSoundEvent: SoundEvent?, val fallSoundEvent: ResourceLocation?,
val soundEventVolume: Float = 1.0f, val soundEventVolume: Float = 1.0f,
val soundEventPitch: Float = 1.0f, val soundEventPitch: Float = 1.0f,
) { ) {

View File

@ -40,7 +40,6 @@ import kotlin.random.Random
open class CampfireBlock(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>) : Block(resourceLocation, registries, data) { open class CampfireBlock(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>) : Block(resourceLocation, registries, data) {
val lavaParticles = data["lava_particles"]?.toBoolean() ?: true val lavaParticles = data["lava_particles"]?.toBoolean() ?: true
private val campfireCrackleSoundEvent = registries.soundEventRegistry[CAMPFIRE_CRACKLE_SOUND_RESOURCE_LOCATION]!!
private val cosySmokeParticle = registries.particleTypeRegistry[CampfireSmokeParticle.CosyFactory]!! private val cosySmokeParticle = registries.particleTypeRegistry[CampfireSmokeParticle.CosyFactory]!!
private val signalSmokeParticle = registries.particleTypeRegistry[CampfireSmokeParticle.SignalFactory]!! private val signalSmokeParticle = registries.particleTypeRegistry[CampfireSmokeParticle.SignalFactory]!!
private val lavaParticle = registries.particleTypeRegistry[LavaParticle]!! private val lavaParticle = registries.particleTypeRegistry[LavaParticle]!!
@ -82,7 +81,7 @@ open class CampfireBlock(resourceLocation: ResourceLocation, registries: Registr
return return
} }
if (random.chance(10)) { if (random.chance(10)) {
connection.world.playSoundEvent(campfireCrackleSoundEvent, blockPosition + Vec3(0.5f), 0.5f + random.nextFloat(), 0.6f + random.nextFloat() * 0.7f) connection.world.playSoundEvent(CAMPFIRE_CRACKLE_SOUND, blockPosition + Vec3(0.5f), 0.5f + random.nextFloat(), 0.6f + random.nextFloat() * 0.7f)
} }
if (lavaParticles && random.chance(20)) { if (lavaParticles && random.chance(20)) {
@ -104,7 +103,7 @@ open class CampfireBlock(resourceLocation: ResourceLocation, registries: Registr
} }
companion object : BlockFactory<CampfireBlock> { companion object : BlockFactory<CampfireBlock> {
private val CAMPFIRE_CRACKLE_SOUND_RESOURCE_LOCATION = "minecraft:block.campfire.crackle".toResourceLocation() private val CAMPFIRE_CRACKLE_SOUND = "minecraft:block.campfire.crackle".toResourceLocation()
override fun build(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>): CampfireBlock { override fun build(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>): CampfireBlock {
return CampfireBlock(resourceLocation, registries, data) return CampfireBlock(resourceLocation, registries, data)

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.data.registries.items
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.registries.Registries import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
import de.bixilon.minosoft.util.KUtil.nullCast import de.bixilon.minosoft.util.KUtil.nullCast
open class MusicDiscItem( open class MusicDiscItem(
@ -24,9 +23,5 @@ open class MusicDiscItem(
data: Map<String, Any>, data: Map<String, Any>,
) : Item(resourceLocation, registries, data) { ) : Item(resourceLocation, registries, data) {
val analogOutput = data["analog_output"].nullCast<Item>() ?: 0 val analogOutput = data["analog_output"].nullCast<Item>() ?: 0
val sound: SoundEvent? = null val sound: ResourceLocation? = registries.soundEventRegistry[data["sound"]]
init {
this::sound.inject(data["sound"])
}
} }

View File

@ -40,7 +40,6 @@ import de.bixilon.minosoft.data.registries.other.containers.ContainerType
import de.bixilon.minosoft.data.registries.other.game.event.GameEvent import de.bixilon.minosoft.data.registries.other.game.event.GameEvent
import de.bixilon.minosoft.data.registries.particle.ParticleType import de.bixilon.minosoft.data.registries.particle.ParticleType
import de.bixilon.minosoft.data.registries.registries.registry.* import de.bixilon.minosoft.data.registries.registries.registry.*
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
import de.bixilon.minosoft.data.registries.statistics.Statistic import de.bixilon.minosoft.data.registries.statistics.Statistic
import de.bixilon.minosoft.data.registries.versions.Version import de.bixilon.minosoft.data.registries.versions.Version
import de.bixilon.minosoft.protocol.packets.c2s.play.EntityActionC2SP import de.bixilon.minosoft.protocol.packets.c2s.play.EntityActionC2SP
@ -69,7 +68,7 @@ class Registries {
val dimensionRegistry: Registry<Dimension> = Registry() val dimensionRegistry: Registry<Dimension> = Registry()
val materialRegistry: Registry<Material> = Registry() val materialRegistry: Registry<Material> = Registry()
val fluidRegistry: Registry<Fluid> = Registry() val fluidRegistry: Registry<Fluid> = Registry()
val soundEventRegistry: Registry<SoundEvent> = Registry() val soundEventRegistry: ResourceLocationRegistry = ResourceLocationRegistry()
val villagerProfessionRegistry: Registry<VillagerProfession> = Registry() val villagerProfessionRegistry: Registry<VillagerProfession> = Registry()
@ -159,7 +158,7 @@ class Registries {
entityTypeRegistry.rawInitialize(pixlyzerData["entities"]?.compoundCast(), this, EntityType) entityTypeRegistry.rawInitialize(pixlyzerData["entities"]?.compoundCast(), this, EntityType)
motiveRegistry.rawInitialize(pixlyzerData["motives"]?.compoundCast(), this, Motive, version.isFlattened()) motiveRegistry.rawInitialize(pixlyzerData["motives"]?.compoundCast(), this, Motive, version.isFlattened())
soundEventRegistry.rawInitialize(pixlyzerData["sound_events"]?.compoundCast(), this, SoundEvent) soundEventRegistry.rawInitialize(pixlyzerData["sound_events"]?.compoundCast())
particleTypeRegistry.rawInitialize(pixlyzerData["particles"]?.compoundCast(), this, ParticleType) particleTypeRegistry.rawInitialize(pixlyzerData["particles"]?.compoundCast(), this, ParticleType)
materialRegistry.rawInitialize(pixlyzerData["materials"]?.compoundCast(), this, Material) materialRegistry.rawInitialize(pixlyzerData["materials"]?.compoundCast(), this, Material)
enchantmentRegistry.rawInitialize(pixlyzerData["enchantments"]?.compoundCast(), this, Enchantment) enchantmentRegistry.rawInitialize(pixlyzerData["enchantments"]?.compoundCast(), this, Enchantment)

View File

@ -195,6 +195,7 @@ open class Registry<T : RegistryItem>(
BITS_16, BITS_16,
} }
@Deprecated("TODO")
override fun iterator(): Iterator<T> { override fun iterator(): Iterator<T> {
return resourceLocationMap.values.iterator() return resourceLocationMap.values.iterator()
} }

View File

@ -0,0 +1,81 @@
package de.bixilon.minosoft.data.registries.registries.registry
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.json.ResourceLocationJsonMap.toResourceLocationMap
class ResourceLocationRegistry(
override var parent: AbstractRegistry<ResourceLocation>? = null,
) : AbstractRegistry<ResourceLocation> {
private var initialized = false
private val idValueMap: MutableMap<Int, ResourceLocation> = mutableMapOf()
private val valueIdMap: MutableMap<ResourceLocation, Int> = mutableMapOf()
override val size: Int
get() {
val value = idValueMap.size
parent?.let {
return value + it.size
}
return value
}
override fun clear() {
idValueMap.clear()
valueIdMap.clear()
}
override fun get(any: Any?): ResourceLocation? {
check(any is Int) { "Don't know how to get $any" }
return idValueMap[any]
}
override fun get(id: Int): ResourceLocation? {
return idValueMap[id]
}
override fun getId(value: ResourceLocation): Int {
return valueIdMap[value] ?: -1
}
fun initialize(data: Map<ResourceLocation, Any>?, alternative: ResourceLocationRegistry? = null): ResourceLocationRegistry {
check(!initialized) { "Already initialized" }
if (data == null) {
if (alternative != null) {
parent = alternative
}
return this
}
for ((resourceLocation, value) in data) {
val id: Int = when (value) {
is Number -> value.toInt()
is Map<*, *> -> value["id"].toInt()
else -> throw IllegalArgumentException("Don't know what $value is!")
}
idValueMap[id] = resourceLocation
valueIdMap[resourceLocation] = id
}
if (idValueMap.isEmpty()) {
parent = alternative
}
initialized = true
return this
}
fun rawInitialize(data: Map<String, Any>?, alternative: ResourceLocationRegistry? = null): ResourceLocationRegistry {
return initialize(data?.toResourceLocationMap(), alternative)
}
override fun toString(): String {
return super.toString() + ": ${idValueMap.size}x"
}
@Deprecated("TODO")
override fun iterator(): Iterator<ResourceLocation> {
return idValueMap.values.iterator()
}
}

View File

@ -1,35 +0,0 @@
/*
* 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.data.registries.sounds
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.registries.registry.RegistryItem
import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationDeserializer
data class SoundEvent(
override val resourceLocation: ResourceLocation,
) : RegistryItem() {
override fun toString(): String {
return resourceLocation.toString()
}
companion object : ResourceLocationDeserializer<SoundEvent> {
override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): SoundEvent {
return SoundEvent(
resourceLocation = resourceLocation,
)
}
}
}

View File

@ -0,0 +1,19 @@
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.util.VecUtil.centerf
import glm_.vec3.Vec3
import glm_.vec3.Vec3i
interface AbstractAudioPlayer {
fun playSoundEvent(sound: ResourceLocation, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
playSoundEvent(sound, position?.centerf, volume, pitch)
}
fun playSoundEvent(sound: ResourceLocation, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f)
fun stopSound(sound: ResourceLocation)
// ToDo: Stop category
}

View File

@ -20,12 +20,10 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.Particle import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.gui.rendering.sound.AudioPlayer
import de.bixilon.minosoft.gui.rendering.util.VecUtil.blockPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.blockPosition
import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition
@ -59,7 +57,7 @@ import kotlin.random.Random
*/ */
class World( class World(
val connection: PlayConnection, val connection: PlayConnection,
) : BiomeAccessor { ) : BiomeAccessor, AbstractAudioPlayer {
val lock = ReadWriteLock() val lock = ReadWriteLock()
var cacheBiomeAccessor: NoiseBiomeAccessor? = null var cacheBiomeAccessor: NoiseBiomeAccessor? = null
val chunks: LockMap<Vec2i, Chunk> = lockMapOf() val chunks: LockMap<Vec2i, Chunk> = lockMapOf()
@ -76,7 +74,7 @@ class World(
var thunderGradient = 0.0f var thunderGradient = 0.0f
private val random = Random private val random = Random
var audioPlayer: AudioPlayer? = null var audioPlayer: AbstractAudioPlayer? = null
var particleRenderer: ParticleRenderer? = null var particleRenderer: ParticleRenderer? = null
operator fun get(blockPosition: Vec3i): BlockState? { operator fun get(blockPosition: Vec3i): BlockState? {
@ -188,20 +186,13 @@ class World(
return ret.toMap() return ret.toMap()
} }
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
audioPlayer?.playSoundEvent(resourceLocation, position, volume, pitch) override fun playSoundEvent(sound: ResourceLocation, position: Vec3?, volume: Float, pitch: Float) {
audioPlayer?.playSoundEvent(sound, position, volume, pitch)
} }
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) { override fun stopSound(sound: ResourceLocation) {
audioPlayer?.playSoundEvent(resourceLocation, position, volume, pitch) audioPlayer?.stopSound(sound)
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
audioPlayer?.playSoundEvent(soundEvent, position, volume, pitch)
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
audioPlayer?.playSoundEvent(soundEvent, position, volume, pitch)
} }
fun addParticle(particle: Particle) { fun addParticle(particle: Particle) {

View File

@ -43,9 +43,7 @@ class WeightedBakedModel(
} }
private fun getModel(random: Random): BakedBlockModel { private fun getModel(random: Random): BakedBlockModel {
val totalWeight = abs(random.nextLong() % totalWeight) var weightLeft = abs(random.nextLong() % totalWeight)
var weightLeft = totalWeight
for ((model, weight) in models) { for ((model, weight) in models) {
weightLeft -= weight weightLeft -= weight

View File

@ -13,35 +13,24 @@
package de.bixilon.minosoft.gui.rendering.sound package de.bixilon.minosoft.gui.rendering.sound
import com.google.gson.JsonArray
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.data.world.AbstractAudioPlayer
import de.bixilon.minosoft.gui.rendering.Rendering import de.bixilon.minosoft.gui.rendering.Rendering
import de.bixilon.minosoft.gui.rendering.input.camera.Camera import de.bixilon.minosoft.gui.rendering.input.camera.Camera
import de.bixilon.minosoft.gui.rendering.modding.events.CameraPositionChangeEvent 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.Sound
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList
import de.bixilon.minosoft.gui.rendering.util.VecUtil.centerf
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.CountUpAndDownLatch import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil.nullCast
import de.bixilon.minosoft.util.KUtil.synchronizedListOf import de.bixilon.minosoft.util.KUtil.synchronizedListOf
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.toSynchronizedList import de.bixilon.minosoft.util.KUtil.toSynchronizedList
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.Queue import de.bixilon.minosoft.util.Queue
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
import glm_.vec3.Vec3 import glm_.vec3.Vec3
import glm_.vec3.Vec3i
import org.lwjgl.openal.AL import org.lwjgl.openal.AL
import org.lwjgl.openal.ALC import org.lwjgl.openal.ALC
import org.lwjgl.openal.ALC10.* import org.lwjgl.openal.ALC10.*
@ -54,14 +43,13 @@ import java.nio.IntBuffer
class AudioPlayer( class AudioPlayer(
val connection: PlayConnection, val connection: PlayConnection,
val rendering: Rendering, val rendering: Rendering,
) { ) : AbstractAudioPlayer {
private val sounds: MutableMap<SoundEvent, SoundList> = mutableMapOf() private val soundManager = SoundManager(connection)
var initialized = false var initialized = false
private set private set
private var device = 0L private var device = -1L
private var context = 0L private var context = -1L
private val queue = Queue() private val queue = Queue()
private lateinit var listener: SoundListener private lateinit var listener: SoundListener
@ -74,27 +62,12 @@ class AudioPlayer(
get() = sources.size get() = sources.size
private fun preloadSounds() {
Log.log(LogMessageType.AUDIO_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) { fun init(latch: CountUpAndDownLatch) {
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Loading OpenAL..." } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Loading OpenAL..." }
loadSounds() soundManager.load()
preloadSounds() Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Preloading sounds..." }
soundManager.preload()
@ -125,31 +98,33 @@ class AudioPlayer(
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "OpenAL loaded!" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "OpenAL loaded!" }
initialized = true initialized = true
connection.world.audioPlayer = this connection.world.audioPlayer = this
latch.dec() latch.dec()
} }
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) { override fun playSoundEvent(sound: ResourceLocation, position: Vec3?, volume: Float, pitch: Float) {
connection.registries.soundEventRegistry[resourceLocation]?.let { playSoundEvent(it, position?.centerf, volume, pitch) }
}
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
connection.registries.soundEventRegistry[resourceLocation]?.let { playSoundEvent(it, position, volume, pitch) }
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
playSoundEvent(soundEvent, position?.centerf, volume, pitch)
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
if (!initialized) { if (!initialized) {
return return
} }
playSound(sounds[soundEvent]!!.getRandom(), position, volume, pitch) playSound(soundManager[sound] ?: return, position, volume, pitch)
} }
override fun stopSound(sound: ResourceLocation) {
queue += {
for (source in sources) {
if (!source.isPlaying) {
continue
}
if (source.sound?.soundEvent != sound) {
continue
}
source.stop()
}
}
}
@Synchronized
private fun getAvailableSource(): SoundSource? { private fun getAvailableSource(): SoundSource? {
for (source in sources.toSynchronizedList()) { for (source in sources.toSynchronizedList()) {
if (source.available) { if (source.available) {
@ -169,11 +144,8 @@ class AudioPlayer(
private 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@{ queue += add@{
sound.load(connection.assetsManager) sound.load(connection.assetsManager)
if (sound.loadFailed) { val source = getAvailableSource()
return@add if (source == null) {
}
val source = getAvailableSource() ?: let {
// ToDo: Queue sound for later (and check a certain delay to not make the game feel laggy)
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.WARN) { "Can not play sound: No source available: $sound" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.WARN) { "Can not play sound: No source available: $sound" }
return@add return@add
} }
@ -191,20 +163,23 @@ class AudioPlayer(
} }
} }
private fun calculateAvailableSources() {
var availableSources = 0
for (source in sources) {
if (source.available) {
availableSources++
}
}
this.availableSources = availableSources
}
fun startLoop() { fun startLoop() {
while (true) { while (true) {
if (connection.wasConnected || connection.error != null) { if (connection.wasConnected || connection.error != null) {
break break
} }
queue.work() queue.work()
calculateAvailableSources()
var availableSources = 0
for (source in sources) {
if (source.available) {
availableSources++
}
}
this.availableSources = availableSources
Thread.sleep(1L) Thread.sleep(1L)
} }
@ -214,11 +189,8 @@ class AudioPlayer(
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloading OpenAL..." } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloading OpenAL..." }
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Unloading sounds..." } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Unloading sounds..." }
for (soundList in sounds.values) { soundManager.unload()
for (sound in soundList.sounds) {
sound.unload()
}
}
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Unloading sources..." } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Unloading sources..." }
for (source in sources.toSynchronizedList()) { for (source in sources.toSynchronizedList()) {
source.unload() source.unload()
@ -229,53 +201,7 @@ class AudioPlayer(
alcSetThreadContext(MemoryUtil.NULL) alcSetThreadContext(MemoryUtil.NULL)
alcDestroyContext(context) alcDestroyContext(context)
alcCloseDevice(device) alcCloseDevice(device)
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloaded OpenAL!" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloaded OpenAL!" }
} }
@Deprecated("Refactoring needed")
private fun loadSounds() {
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
val data = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE)
for ((soundEventResourceLocation, json) in data) {
check(json is Map<*, *>)
val soundEvent = connection.registries.soundEventRegistry[ResourceLocation(soundEventResourceLocation)]!!
val sounds: MutableSet<Sound> = mutableSetOf()
fun String.getSoundLocation(): ResourceLocation {
return ResourceLocation(ProtocolDefinition.DEFAULT_NAMESPACE, "sounds/${this}".replace('.', '/') + ".ogg") // ToDo: Resource Location
}
for (soundJson in json["sounds"]!!.listCast<Any>()!!) {
when (soundJson) {
is String -> {
sounds += Sound(soundJson.getSoundLocation())
}
is Map<*, *> -> {
sounds += Sound(
path = soundJson["name"].unsafeCast<String>().getSoundLocation(),
volume = soundJson["volume"]?.unsafeCast<Double>()?.toFloat() ?: 1.0f,
pitch = soundJson["pitch"]?.unsafeCast<Double>()?.toFloat() ?: 1.0f,
weight = soundJson["weight"]?.toInt() ?: 1,
stream = soundJson["stream"]?.toBoolean() ?: false,
attenuationDistance = soundJson["attenuation_distance"]?.toInt() ?: 16,
preload = soundJson["preload"]?.toBoolean() ?: false,
)
}
is JsonArray -> TODO()
}
}
this.sounds[soundEvent] = SoundList(
soundEvent = soundEvent,
sounds = sounds.toSet(),
subTitle = json["subtitle"].nullCast<String>()?.let { ResourceLocation(ProtocolDefinition.DEFAULT_NAMESPACE, it) },
)
}
}
companion object {
private val SOUNDS_INDEX_FILE = "minecraft:sounds.json".toResourceLocation()
}
} }

View File

@ -0,0 +1,55 @@
package de.bixilon.minosoft.gui.rendering.sound
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundType
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
import java.util.*
class SoundManager(
private val connection: PlayConnection,
) {
private val random = Random()
private val sounds: MutableMap<ResourceLocation, SoundType> = mutableMapOf()
fun load() {
val soundsIndex = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE)
for ((name, data) in soundsIndex) {
val resourceLocation = name.toResourceLocation()
sounds[resourceLocation] = SoundType(resourceLocation, data.asCompound())
}
}
@Synchronized
fun unload() {
for (soundType in sounds.values) {
for (sound in soundType.sounds) {
sound.unload()
}
}
}
@Synchronized
fun preload() {
for (soundType in sounds.values) {
for (sound in soundType.sounds) {
if (!sound.preload) {
continue
}
sound.load(connection.assetsManager)
}
}
}
operator fun get(sound: ResourceLocation): Sound? {
return sounds[sound]?.getSound(random)
}
companion object {
private val SOUNDS_INDEX_FILE = "minecraft:sounds.json".toResourceLocation()
}
}

View File

@ -61,11 +61,15 @@ class SoundSource {
var sound: Sound? = null var sound: Sound? = null
set(value) { set(value) {
stop() stop()
if (value?.loaded != true || value.loadFailed) { val buffer = value?.buffer
if (buffer == null) {
field = null field = null
return return
} }
alSourcei(source, AL_BUFFER, value.buffer) if (buffer.unloaded) {
throw IllegalArgumentException("OpenAL buffer is not loaded: ${value.soundEvent}")
}
alSourcei(source, AL_BUFFER, buffer.buffer)
field = value field = value
} }
@ -73,7 +77,7 @@ class SoundSource {
get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING
val available: Boolean val available: Boolean
get() = !isPlaying || System.currentTimeMillis() - playTime > (sound?.length ?: 0L) // ToDo: Allow pause get() = !isPlaying || System.currentTimeMillis() - playTime > (sound?.data?.length ?: 0L) // ToDo: Allow pause
fun play() { fun play() {
playTime = System.currentTimeMillis() playTime = System.currentTimeMillis()

View File

@ -11,18 +11,25 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.gui.rendering.sound.sounds package de.bixilon.minosoft.gui.rendering.sound
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.util.KUtil.toResourceLocation
data class SoundList( object SoundUtil {
val soundEvent: SoundEvent, fun ResourceLocation.sound(): ResourceLocation {
val sounds: Set<Sound>, var path = ""
val subTitle: ResourceLocation?,
) { if (!this.path.startsWith("sounds/")) {
fun getRandom(): Sound { path += "sounds/"
// ToDo: Support weight }
return sounds.random() path += this.path
if (!path.contains(".")) {
// ending
path += ".ogg"
}
return "$namespace:$path".toResourceLocation()
} }
} }

View File

@ -0,0 +1,38 @@
package de.bixilon.minosoft.gui.rendering.sound.sounds
import org.lwjgl.openal.AL10.*
import org.lwjgl.system.MemoryUtil.memFree
import java.nio.ShortBuffer
class OpenALBuffer(
val data: SoundData,
) {
val buffer: Int
private val pcm: ShortBuffer
var unloaded: Boolean = false
private set
init {
val pcm = data.createPCM()
this.pcm = pcm
this.buffer = alGenBuffers()
alBufferData(buffer, data.format, pcm, data.sampleRate)
}
@Synchronized
fun unload() {
if (unloaded) {
return
}
alDeleteBuffers(buffer)
memFree(pcm)
unloaded = true
}
protected fun finalize() {
unload()
}
}

View File

@ -15,102 +15,80 @@ package de.bixilon.minosoft.gui.rendering.sound.sounds
import de.bixilon.minosoft.data.assets.AssetsManager import de.bixilon.minosoft.data.assets.AssetsManager
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.sound.SoundUtil.sound
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toFloat
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL10.*
import org.lwjgl.stb.STBVorbis.*
import org.lwjgl.stb.STBVorbisInfo
import org.lwjgl.system.MemoryUtil
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.nio.ByteBuffer
data class Sound( data class Sound(
val soundEvent: ResourceLocation,
val path: ResourceLocation, val path: ResourceLocation,
val volume: Float = 1.0f, val volume: Float = 1.0f,
val pitch: Float = 1.0f, val pitch: Float = 1.0f,
val weight: Int = 1, val weight: Int = 1,
val stream: Boolean = false, // ToDo val stream: Boolean = false, // ToDo: Implement
val attenuationDistance: Int = 16, val attenuationDistance: Int = 16, // ToDo: Implement
val preload: Boolean = false, val preload: Boolean = false,
// ToDo: type
) { ) {
var length: Long = -1L var data: SoundData? = null
private set private set
var loaded: Boolean = false var buffer: OpenALBuffer? = null
private set private set
var loadFailed: Boolean = false
private set
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
private var vorbisBuffer: ByteBuffer? = null
@Synchronized @Synchronized
fun load(assetsManager: AssetsManager) { fun load(assetsManager: AssetsManager) {
if (loaded || loadFailed) { if (data != null) {
return return
} }
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading audio file: $path" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading audio file: $path" }
try { try {
val vorbisBuffer = assetsManager.readByteAsset(path) val data = SoundData(assetsManager, this)
this.vorbisBuffer = vorbisBuffer this.data = data
this.buffer = OpenALBuffer(data)
val error = BufferUtils.createIntBuffer(1)
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(vorbis, STBVorbisInfo.malloc())
channels = info.channels()
val format = when (channels) {
1 -> AL_FORMAT_MONO16
2 -> AL_FORMAT_STEREO16
else -> error("Don't know vorbis channels: $channels")
}
sampleRate = info.sample_rate()
samplesLength = stb_vorbis_stream_length_in_samples(vorbis)
sampleSeconds = stb_vorbis_stream_length_in_seconds(vorbis)
length = (sampleSeconds * 1000).toLong()
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) { } catch (exception: FileNotFoundException) {
loadFailed = true
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.WARN) { "Can not load sound: $path: $exception" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.WARN) { "Can not load sound: $path: $exception" }
} }
} }
@Synchronized @Synchronized
fun unload() { fun unload() {
if (!loaded) { data?.unload()
return buffer?.unload()
}
protected fun finalize() {
unload()
}
companion object {
operator fun invoke(soundEvent: ResourceLocation, data: Any): Sound {
if (data is String) {
return Sound(
soundEvent = soundEvent,
path = data.toResourceLocation().sound(),
)
}
check(data is Map<*, *>)
// ToDo: "type" attribute: event
return Sound(
soundEvent = soundEvent,
path = data["name"].toResourceLocation(),
volume = data["volume"]?.toFloat() ?: 1.0f,
pitch = data["pitch"]?.toFloat() ?: 1.0f,
weight = data["weight"]?.toInt() ?: 1,
stream = data["stream"]?.toBoolean() ?: false,
attenuationDistance = data["attenuation_distance"]?.toInt() ?: 16,
preload = data["preload"]?.toBoolean() ?: false,
)
} }
alDeleteBuffers(buffer)
vorbisBuffer?.let { MemoryUtil.memFree(it) }
buffer = -1
channels = -1
sampleRate = -1
samplesLength = -1
sampleSeconds = -1.0f
loaded = false
} }
} }

View File

@ -0,0 +1,80 @@
package de.bixilon.minosoft.gui.rendering.sound.sounds
import de.bixilon.minosoft.data.assets.AssetsManager
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL10.AL_FORMAT_MONO16
import org.lwjgl.openal.AL10.AL_FORMAT_STEREO16
import org.lwjgl.stb.STBVorbis.*
import org.lwjgl.stb.STBVorbisInfo
import org.lwjgl.system.MemoryUtil
import org.lwjgl.system.MemoryUtil.memFree
import java.nio.ByteBuffer
import java.nio.ShortBuffer
class SoundData(
val vorbis: Long,
val format: Int,
val buffer: ByteBuffer,
val length: Long,
val channels: Int,
val sampleRate: Int,
val samplesLength: Int,
val sampleSeconds: Float,
) {
private var unloaded = false
@Synchronized
fun unload() {
if (unloaded) {
return
}
memFree(buffer)
unloaded = true
}
protected fun finalize() {
unload()
}
fun createPCM(): ShortBuffer {
val pcm = BufferUtils.createShortBuffer(samplesLength)
pcm.limit(stb_vorbis_get_samples_short_interleaved(vorbis, channels, pcm) * channels)
return pcm
}
companion object {
operator fun invoke(assetsManager: AssetsManager, sound: Sound): SoundData {
val buffer = assetsManager.readByteAsset(sound.path)
val error = BufferUtils.createIntBuffer(1)
val vorbis = stb_vorbis_open_memory(buffer, error, null)
if (vorbis == MemoryUtil.NULL) {
throw IllegalStateException("Can not load vorbis: ${sound.path}: ${error[0]}")
}
val info = stb_vorbis_get_info(vorbis, STBVorbisInfo.malloc())
val channels = info.channels()
val format = when (channels) {
1 -> AL_FORMAT_MONO16
2 -> AL_FORMAT_STEREO16
else -> error("Don't know vorbis channels: $channels")
}
val sampleRate = info.sample_rate()
val samplesLength = stb_vorbis_stream_length_in_samples(vorbis)
val sampleSeconds = stb_vorbis_stream_length_in_seconds(vorbis)
val length = (sampleSeconds * 1000).toLong()
return SoundData(
vorbis = vorbis,
format = format,
buffer = buffer,
length = length,
channels = channels,
sampleRate = sampleRate,
samplesLength = samplesLength,
sampleSeconds = sampleSeconds
)
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.sounds
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.asList
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import java.util.*
import kotlin.math.abs
data class SoundType(
val soundEvent: ResourceLocation,
val sounds: Set<Sound>,
val subtitle: ResourceLocation?,
) {
val totalWeight: Int
init {
var totalWeight = 0
for (sound in sounds) {
totalWeight += sound.weight
}
this.totalWeight = totalWeight
}
fun getSound(random: Random): Sound {
var weightLeft = abs(random.nextLong() % totalWeight)
for (sound in sounds) {
weightLeft -= sound.weight
if (weightLeft < 0) {
return sound
}
}
throw IllegalStateException("Could not find sound: This should never happen!")
}
companion object {
operator fun invoke(soundEvent: ResourceLocation, data: Map<String, Any>): SoundType {
// ToDo: "replace" attribute
val subtitle = data["subtitle"]?.toResourceLocation()
val sounds: MutableSet<Sound> = mutableSetOf()
for (soundData in data["sounds"].asList()) {
sounds += Sound(soundEvent, soundData)
}
return SoundType(
soundEvent = soundEvent,
sounds = sounds,
subtitle = subtitle,
)
}
}
}

View File

@ -14,7 +14,7 @@
package de.bixilon.minosoft.modding.event.events package de.bixilon.minosoft.modding.event.events
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.modding.event.EventInitiators import de.bixilon.minosoft.modding.event.EventInitiators
import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -27,7 +27,7 @@ class PlaySoundEvent(
initiator: EventInitiators, initiator: EventInitiators,
val category: SoundCategories?, val category: SoundCategories?,
position: Vec3, position: Vec3,
val soundEvent: SoundEvent, val soundEvent: ResourceLocation,
val volume: Float, val volume: Float,
val pitch: Float, val pitch: Float,
) : PlayConnectionEvent(connection, initiator), CancelableEvent { ) : PlayConnectionEvent(connection, initiator), CancelableEvent {

View File

@ -13,7 +13,7 @@
package de.bixilon.minosoft.protocol.packets.s2c.play package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
@ -21,7 +21,7 @@ import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
class EntitySoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { class EntitySoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val soundEvent: SoundEvent = buffer.connection.registries.soundEventRegistry[buffer.readVarInt()] val soundEvent: ResourceLocation = buffer.connection.registries.soundEventRegistry[buffer.readVarInt()]!!
val category: SoundCategories = SoundCategories[buffer.readVarInt()] val category: SoundCategories = SoundCategories[buffer.readVarInt()]
val entityId: Int = buffer.readVarInt() val entityId: Int = buffer.readVarInt()
val volume: Float = buffer.readFloat() val volume: Float = buffer.readFloat()
@ -30,5 +30,4 @@ class EntitySoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
override fun log() { override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Entity sound effect (soundEvent=$soundEvent, category=$category, entityId$entityId, volume=$volume, pitch=$pitch)" } Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Entity sound effect (soundEvent=$soundEvent, category=$category, entityId$entityId, volume=$volume, pitch=$pitch)" }
} }
} }

View File

@ -14,7 +14,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
@ -27,7 +27,7 @@ import de.bixilon.minosoft.util.logging.LogMessageType
import glm_.vec3.Vec3 import glm_.vec3.Vec3
class NamedSoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { class NamedSoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val soundEvent: SoundEvent? val soundEvent: ResourceLocation?
val volume: Float val volume: Float
val pitch: Float val pitch: Float
lateinit var position: Vec3 lateinit var position: Vec3

View File

@ -14,7 +14,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.registries.sounds.SoundEvent import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
@ -30,7 +30,7 @@ class SoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
var category: SoundCategories? = null var category: SoundCategories? = null
private set private set
val position: Vec3i val position: Vec3i
val soundEvent: SoundEvent val soundEvent: ResourceLocation
val volume: Float val volume: Float
val pitch: Float val pitch: Float
@ -39,7 +39,7 @@ class SoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
// category was moved to the top // category was moved to the top
this.category = SoundCategories[buffer.readVarInt()] this.category = SoundCategories[buffer.readVarInt()]
} }
soundEvent = buffer.connection.registries.soundEventRegistry[buffer.readVarInt()] soundEvent = buffer.connection.registries.soundEventRegistry[buffer.readVarInt()]!!
if (buffer.versionId >= ProtocolVersions.V_17W15A && buffer.versionId < ProtocolVersions.V_17W18A) { if (buffer.versionId >= ProtocolVersions.V_17W15A && buffer.versionId < ProtocolVersions.V_17W18A) {
buffer.readString() // parrot entity type buffer.readString() // parrot entity type
} }

View File

@ -14,6 +14,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
@ -21,7 +22,6 @@ import de.bixilon.minosoft.util.BitByte.isBitMask
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import java.util.*
class StopSoundS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { class StopSoundS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val category: SoundCategories? val category: SoundCategories?
@ -31,7 +31,7 @@ class StopSoundS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
var category: SoundCategories? = null var category: SoundCategories? = null
var sound: ResourceLocation? = null var sound: ResourceLocation? = null
if (buffer.versionId < ProtocolVersions.V_17W45A) { // ToDo: these 2 values need to be switched in before 1.12.2 if (buffer.versionId < ProtocolVersions.V_17W45A) { // ToDo: these 2 values need to be switched in before 1.12.2
category = SoundCategories.valueOf(buffer.readString().uppercase(Locale.getDefault())) category = SoundCategories.valueOf(buffer.readString().uppercase())
sound = buffer.readResourceLocation() sound = buffer.readResourceLocation()
} else { } else {
val flags = buffer.readByte() val flags = buffer.readByte()
@ -46,6 +46,11 @@ class StopSoundS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
this.sound = sound this.sound = sound
} }
override fun handle(connection: PlayConnection) {
sound?.let { connection.world.stopSound(it) }
// ToDo: Category
}
override fun log() { override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Stop sound (category=$category, sound=$sound)" } Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Stop sound (category=$category, sound=$sound)" }
} }