diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt index 7f3e97c2ea9..ef7e2c7972a 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt @@ -25,10 +25,7 @@ import net.ccbluex.liquidbounce.event.events.KeyboardKeyEvent import net.ccbluex.liquidbounce.event.events.MouseButtonEvent import net.ccbluex.liquidbounce.event.events.WorldChangeEvent import net.ccbluex.liquidbounce.event.handler -import net.ccbluex.liquidbounce.features.module.modules.client.ModuleAutoConfig -import net.ccbluex.liquidbounce.features.module.modules.client.ModuleLiquidChat -import net.ccbluex.liquidbounce.features.module.modules.client.ModuleRichPresence -import net.ccbluex.liquidbounce.features.module.modules.client.ModuleTargets +import net.ccbluex.liquidbounce.features.module.modules.client.* import net.ccbluex.liquidbounce.features.module.modules.combat.* import net.ccbluex.liquidbounce.features.module.modules.combat.autoarmor.ModuleAutoArmor import net.ccbluex.liquidbounce.features.module.modules.combat.crystalaura.ModuleCrystalAura diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/integration/theme/component/types/minimap/MinimapTextureAtlasManager.kt b/src/main/kotlin/net/ccbluex/liquidbounce/integration/theme/component/types/minimap/MinimapTextureAtlasManager.kt index 908180b781a..7469e244ffa 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/integration/theme/component/types/minimap/MinimapTextureAtlasManager.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/integration/theme/component/types/minimap/MinimapTextureAtlasManager.kt @@ -20,13 +20,14 @@ */ package net.ccbluex.liquidbounce.integration.theme.component.types.minimap +import kotlinx.atomicfu.locks.ReentrantLock +import kotlinx.atomicfu.locks.withLock import net.ccbluex.liquidbounce.render.engine.Color4b import net.ccbluex.liquidbounce.render.engine.font.BoundingBox2f import net.ccbluex.liquidbounce.utils.math.Vec2i import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImageBackedTexture import net.minecraft.util.math.ChunkPos -import java.util.concurrent.ConcurrentSkipListSet /** * Size of the texture atlas in chunks (size x size) @@ -43,9 +44,11 @@ private val NOT_LOADED_ATLAS_POSITION = MinimapTextureAtlasManager.AtlasPosition class MinimapTextureAtlasManager { private val texture = NativeImageBackedTexture(ATLAS_SIZE * 16, ATLAS_SIZE * 16, false) private val availableAtlasPositions = ArrayList(ATLAS_SIZE * ATLAS_SIZE - 1) - private val dirtyAtlasPositions = ConcurrentSkipListSet() + private val dirtyAtlasPositions = hashSetOf() private val chunkPosAtlasPosMap = hashMapOf() + private val lock = ReentrantLock() + private var allocated = false init { @@ -79,9 +82,11 @@ class MinimapTextureAtlasManager { } fun deallocate(chunkPos: ChunkPos) { - val atlasPosition = chunkPosAtlasPosMap.remove(chunkPos) ?: return + lock.withLock { + val atlasPosition = chunkPosAtlasPosMap.remove(chunkPos) ?: return - availableAtlasPositions.add(atlasPosition) + availableAtlasPositions.add(atlasPosition) + } } fun deallocateAll() { @@ -106,9 +111,9 @@ class MinimapTextureAtlasManager { chunkPos: ChunkPos, editor: (NativeImageBackedTexture, AtlasPosition) -> Unit, ) { - val atlasPosition = getOrAllocate(chunkPos) - - dirtyAtlasPositions.add(atlasPosition) + val atlasPosition = lock.withLock { + getOrAllocate(chunkPos).apply(dirtyAtlasPositions::add) + } editor(texture, atlasPosition) } @@ -119,22 +124,24 @@ class MinimapTextureAtlasManager { * @return the GLid of the texture */ fun prepareRendering(): Int { - if (this.dirtyAtlasPositions.isEmpty()) { - return this.texture.glId - } + lock.withLock { + if (this.dirtyAtlasPositions.isEmpty()) { + return this.texture.glId + } - this.texture.bindTexture() + this.texture.bindTexture() - val dirtyChunks = this.dirtyAtlasPositions.size + val dirtyChunks = this.dirtyAtlasPositions.size - when { - !this.allocated || dirtyChunks >= FULL_UPLOAD_THRESHOLD -> uploadFullTexture() - else -> uploadOnlyDirtyPositions() - } + when { + !this.allocated || dirtyChunks >= FULL_UPLOAD_THRESHOLD -> uploadFullTexture() + else -> uploadOnlyDirtyPositions() + } - this.dirtyAtlasPositions.clear() + this.dirtyAtlasPositions.clear() - return this.texture.glId + return this.texture.glId + } } private fun uploadFullTexture() { @@ -169,7 +176,7 @@ class MinimapTextureAtlasManager { } } - data class AtlasPosition(private val x: Int, private val y: Int) : Comparable { + data class AtlasPosition(private val x: Int, private val y: Int) { val baseXOnAtlas: Int = x shl 4 val baseYOnAtlas: Int = y shl 4 @@ -189,14 +196,8 @@ class MinimapTextureAtlasManager { * @param chunkX x coordinate in the chunk (0-15) * @param chunkY y coordinate in the chunk (0-15) */ - fun getPosOnAtlas( - chunkX: Int, - chunkY: Int, - ): Vec2i { - return Vec2i(baseXOnAtlas + chunkX, baseYOnAtlas + chunkY) + fun getPosOnAtlas(chunkX: Int, chunkY: Int): Vec2i { + return Vec2i(baseXOnAtlas or chunkX, baseYOnAtlas or chunkY) } - - override fun compareTo(other: AtlasPosition): Int = - compareValuesBy(this, other, AtlasPosition::x, AtlasPosition::y) } } diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/utils/block/ChunkScanner.kt b/src/main/kotlin/net/ccbluex/liquidbounce/utils/block/ChunkScanner.kt index 5653303d8ee..28ea1f17b60 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/utils/block/ChunkScanner.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/utils/block/ChunkScanner.kt @@ -30,12 +30,14 @@ import net.ccbluex.liquidbounce.utils.client.logger import net.ccbluex.liquidbounce.utils.client.mc import net.ccbluex.liquidbounce.utils.kotlin.getValue import net.minecraft.block.BlockState +import net.minecraft.util.Util import net.minecraft.util.math.BlockPos import net.minecraft.world.chunk.WorldChunk import java.util.concurrent.CopyOnWriteArrayList import kotlin.coroutines.cancellation.CancellationException object ChunkScanner : Listenable { + private val subscribers = CopyOnWriteArrayList() private val loadedChunks = hashSetOf() @@ -117,7 +119,16 @@ object ChunkScanner : Listenable { } object ChunkScannerThread { - private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) + + /** + * When the first request comes in, the dispatcher and the scope will be initialized, + * and its parallelism cannot be modified + */ + @OptIn(ExperimentalCoroutinesApi::class) + private val dispatcher = Util.getMainWorkerExecutor().asCoroutineDispatcher() + .limitedParallelism((Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(2)) + + private val scope = CoroutineScope(dispatcher + SupervisorJob()) /** * Shared cache for CoroutineScope @@ -153,16 +164,17 @@ object ChunkScanner : Listenable { retrying = 0 // process the update request - // TODO: may need to start a new job - when (chunkUpdate) { - is UpdateRequest.ChunkUpdateRequest -> scanChunk(chunkUpdate) + launch { + when (chunkUpdate) { + is UpdateRequest.ChunkUpdateRequest -> scanChunk(chunkUpdate) - is UpdateRequest.ChunkUnloadRequest -> subscribers.forEach { - it.clearChunk(chunkUpdate.x, chunkUpdate.z) - } + is UpdateRequest.ChunkUnloadRequest -> subscribers.forEach { + it.clearChunk(chunkUpdate.x, chunkUpdate.z) + } - is UpdateRequest.BlockUpdateEvent -> subscribers.forEach { - it.recordBlock(chunkUpdate.blockPos, chunkUpdate.newState, cleared = false) + is UpdateRequest.BlockUpdateEvent -> subscribers.forEach { + it.recordBlock(chunkUpdate.blockPos, chunkUpdate.newState, cleared = false) + } } } } catch (e: CancellationException) {