diff --git a/gradle.properties b/gradle.properties
index 2a092ddf..b18eb6dc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,7 +2,7 @@ kotlin.code.style=official
 org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
 
 kotlinVersion=1.9.0
-kommonsVersion=1.7.4
+kommonsVersion=1.7.5
 
 ffiVersion=2.2.13
 lwjglVersion=3.3.0
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt
index f35e4d02..3cad58dc 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt
@@ -18,6 +18,7 @@ import org.lwjgl.stb.STBImage
 import org.lwjgl.system.MemoryStack
 import org.lwjgl.system.MemoryUtil
 import org.lwjgl.system.MemoryUtil.memAddressSafe
+import ru.dbotthepony.kommons.collect.forValidRefs
 import ru.dbotthepony.kommons.core.IStruct4f
 import ru.dbotthepony.kommons.math.RGBAColor
 import ru.dbotthepony.kommons.matrix.Matrix3f
@@ -63,7 +64,6 @@ import ru.dbotthepony.kstarbound.client.world.ClientWorld
 import ru.dbotthepony.kstarbound.defs.image.Image
 import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
 import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
-import ru.dbotthepony.kstarbound.util.forEachValid
 import ru.dbotthepony.kstarbound.util.formatBytesShort
 import ru.dbotthepony.kstarbound.world.Direction
 import ru.dbotthepony.kstarbound.world.LightCalculator
@@ -809,8 +809,8 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
 		val program = programs.positionColor
 		val builder = program.builder
 
-		uberShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
-		fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
+		uberShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }
+		fontShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }
 
 		stack.clear(Matrix3f.identity())
 
@@ -884,8 +884,8 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
 			.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
 			.translate(-camera.pos.x.toFloat(), -camera.pos.y.toFloat()) // перемещаем вид к камере
 
-		uberShaderPrograms.forEachValid { it.viewMatrix = viewMatrix }
-		fontShaderPrograms.forEachValid { it.viewMatrix = viewMatrix }
+		uberShaderPrograms.forValidRefs { it.viewMatrix = viewMatrix }
+		fontShaderPrograms.forValidRefs { it.viewMatrix = viewMatrix }
 
 		viewportLighting.clear()
 		val viewportLightingMem = viewportLightingMem
@@ -930,7 +930,7 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
 			(1f - (viewportCellX + viewportCellWidth - viewportTopRight.x) / viewportLighting.width).toFloat(),
 			(1f - (viewportCellY + viewportCellHeight - viewportTopRight.y) / viewportLighting.height).toFloat())
 
-		uberShaderPrograms.forEachValid {
+		uberShaderPrograms.forValidRefs {
 			it.lightmapTexture = lightMapLocation
 			it.lightmapUV = lightmapUV
 		}
@@ -984,7 +984,7 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
 
 			layers.clear()
 
-			uberShaderPrograms.forEachValid {
+			uberShaderPrograms.forValidRefs {
 				if (it.flags.contains(UberShader.Flag.NEEDS_SCREEN_SIZE)) {
 					it.screenSize = Vector2f(viewportWidth.toFloat(), viewportHeight.toFloat())
 				}
@@ -1005,8 +1005,8 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
 				activeConnection.send(TrackedPositionPacket(camera.pos))
 			}
 
-			uberShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
-			fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
+			uberShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }
+			fontShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }
 
 			stack.clear(Matrix3f.identity())
 
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt
index 7e525fd7..52a7996d 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt
@@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive
 import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
 import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
 import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
+import ru.dbotthepony.kommons.collect.immutableMap
 import ru.dbotthepony.kstarbound.Registries
 import ru.dbotthepony.kstarbound.Registry
 import ru.dbotthepony.kstarbound.Starbound
@@ -17,7 +18,6 @@ import ru.dbotthepony.kstarbound.lua.luaFunctionN
 import ru.dbotthepony.kstarbound.lua.luaStub
 import ru.dbotthepony.kstarbound.lua.set
 import ru.dbotthepony.kstarbound.util.ItemStack
-import ru.dbotthepony.kstarbound.util.immutableMap
 import java.util.*
 import kotlin.collections.ArrayList
 
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt
index 4e84b954..9904836d 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt
@@ -4,15 +4,14 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
 import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
 import it.unimi.dsi.fastutil.objects.ObjectArraySet
+import ru.dbotthepony.kommons.collect.chainOptionalFutures
 import ru.dbotthepony.kommons.core.KOptional
 import ru.dbotthepony.kstarbound.Starbound
 import ru.dbotthepony.kstarbound.server.StarboundServer
 import ru.dbotthepony.kstarbound.server.network.ServerConnection
-import ru.dbotthepony.kstarbound.util.composeFutures
 import ru.dbotthepony.kstarbound.world.ChunkPos
 import ru.dbotthepony.kstarbound.world.World
 import ru.dbotthepony.kstarbound.world.WorldGeometry
-import java.io.Closeable
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.locks.LockSupport
@@ -189,12 +188,12 @@ class ServerWorld(
 						ticketLists.add(this@TicketList)
 
 						if (chunkProviders.isNotEmpty()) {
-							composeFutures(chunkProviders)
+							chainOptionalFutures(chunkProviders)
 							{ if (!isValid) CompletableFuture.completedFuture(KOptional.empty()) else it.getTiles(pos) }
 								.thenAccept(Consumer { tiles ->
 									if (!isValid || !tiles.isPresent) return@Consumer
 
-									composeFutures(chunkProviders)
+									chainOptionalFutures(chunkProviders)
 									{ if (!isValid) CompletableFuture.completedFuture(KOptional.empty()) else it.getEntities(pos) }
 										.thenAcceptAsync(Consumer { ents ->
 											if (!isValid) return@Consumer
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt
index 4cf6ca67..b6ab28aa 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt
@@ -55,24 +55,6 @@ fun traverseJsonPath(path: String?, element: JsonElement?): JsonElement? {
 	}
 }
 
-inline fun <K : Any, V : Any> immutableMap(initializer: ImmutableMap.Builder<K, V>.() -> Unit): ImmutableMap<K, V> {
-	val builder = ImmutableMap.Builder<K, V>()
-	initializer.invoke(builder)
-	return builder.build()
-}
-
-inline fun <V : Any> immutableSet(initializer: Consumer<V>.() -> Unit): ImmutableSet<V> {
-	val builder = ImmutableSet.Builder<V>()
-	initializer.invoke(builder::add)
-	return builder.build()
-}
-
-inline fun <V : Any> immutableList(initializer: Consumer<V>.() -> Unit): ImmutableList<V> {
-	val builder = ImmutableList.Builder<V>()
-	initializer.invoke(builder::add)
-	return builder.build()
-}
-
 fun UUID.toStarboundString(): String {
 	val builder = StringBuilder(32)
 	val a = mostSignificantBits.toString(16)
@@ -90,37 +72,3 @@ fun UUID.toStarboundString(): String {
 
 	return builder.toString()
 }
-
-@Suppress("unchecked_cast")
-fun <T> Stream<T?>.filterNotNull(): Stream<T> {
-	return filter { it != null } as Stream<T>
-}
-
-inline fun <T> MutableIterable<Reference<T>>.forEachValid(block: (T) -> Unit) {
-	val i = iterator()
-
-	for (v in i) {
-		val get = v.get()
-
-		if (get == null) {
-			i.remove()
-		} else {
-			block.invoke(get)
-		}
-	}
-}
-
-fun <S, T> composeFutures(source: Iterable<S>, mapper: (S) -> CompletableFuture<KOptional<T>>): CompletableFuture<KOptional<T>> {
-	val itr = source.iterator()
-
-	if (itr.hasNext()) {
-		var future = mapper.invoke(itr.next())
-
-		for (v in itr)
-			future = future.thenCompose { if (it.isPresent) CompletableFuture.completedFuture(it) else mapper.invoke(v) }
-
-		return future
-	}
-
-	return CompletableFuture.completedFuture(KOptional.empty())
-}
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt
index cf551084..a43d8c98 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt
@@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArraySet
 import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
 import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
 import ru.dbotthepony.kommons.arrays.Object2DArray
+import ru.dbotthepony.kommons.collect.filterNotNull
 import ru.dbotthepony.kommons.core.IStruct2d
 import ru.dbotthepony.kommons.core.IStruct2i
 import ru.dbotthepony.kommons.util.AABB
@@ -13,7 +14,6 @@ import ru.dbotthepony.kommons.util.MailboxExecutorService
 import ru.dbotthepony.kommons.vector.Vector2d
 import ru.dbotthepony.kstarbound.math.*
 import ru.dbotthepony.kstarbound.util.ParallelPerform
-import ru.dbotthepony.kstarbound.util.filterNotNull
 import ru.dbotthepony.kstarbound.world.api.ICellAccess
 import ru.dbotthepony.kstarbound.world.api.AbstractCell
 import ru.dbotthepony.kstarbound.world.api.TileView