From 318b689d2de81d6de247fdc78344c025d318736c Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 10 Apr 2024 09:55:28 +0700 Subject: [PATCH] Fix memory requirements of previous commit :) --- .../network/packets/ChunkCellsPacket.kt | 2 +- .../kstarbound/client/world/ClientChunk.kt | 7 +- .../kstarbound/server/world/ServerChunk.kt | 107 ++++++------ .../ru/dbotthepony/kstarbound/world/Chunk.kt | 163 ++++++++++-------- .../ru/dbotthepony/kstarbound/world/World.kt | 2 +- 5 files changed, 139 insertions(+), 142 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/network/packets/ChunkCellsPacket.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/network/packets/ChunkCellsPacket.kt index 0b473997..43f5c84b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/network/packets/ChunkCellsPacket.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/network/packets/ChunkCellsPacket.kt @@ -16,7 +16,7 @@ import java.io.DataOutputStream class ChunkCellsPacket(val pos: ChunkPos, val data: List) : IClientPacket { constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readChunkPos(), stream.readCollection { MutableCell().read(stream).immutable() }) - constructor(chunk: Chunk<*, *, *>) : this(chunk.pos, ArrayList(CHUNK_SIZE * CHUNK_SIZE).also { + constructor(chunk: Chunk<*, *>) : this(chunk.pos, ArrayList(CHUNK_SIZE * CHUNK_SIZE).also { for (x in 0 until CHUNK_SIZE) { for (y in 0 until CHUNK_SIZE) { it.add(chunk.getCell(x, y).immutable()) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt index 7a42f3de..a1f0f74e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientChunk.kt @@ -1,17 +1,12 @@ package ru.dbotthepony.kstarbound.client.world -import ru.dbotthepony.kommons.arrays.Object2DArray import ru.dbotthepony.kstarbound.world.Chunk import ru.dbotthepony.kstarbound.world.ChunkPos import ru.dbotthepony.kstarbound.world.ChunkState import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kstarbound.world.api.ImmutableCell -class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk(world, pos) { - inner class ChunkCell(x: Int, y: Int) : Chunk.ChunkCell(x, y) - - override val cells: Object2DArray = Object2DArray(width, height, ::ChunkCell) - +class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk(world, pos) { override val state: ChunkState get() = ChunkState.FULL diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerChunk.kt index 014b6d4c..6fde06ee 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerChunk.kt @@ -62,13 +62,7 @@ import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk(world, pos) { - inner class ChunkCell(x: Int, y: Int) : Chunk.ChunkCell(x, y) { - - } - - override val cells: Object2DArray = Object2DArray(width, height, ::ChunkCell) - +class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk(world, pos) { override var state: ChunkState = ChunkState.FRESH private set @@ -156,9 +150,11 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk throw RuntimeException() @@ -401,15 +399,10 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk { - return Object2DArray(CHUNK_SIZE, CHUNK_SIZE) { x, y -> cells[x, y].state } - } - data class DamageResult(val result: TileDamageResult, val health: TileHealth? = null, val stateBefore: AbstractCell? = null) fun damageTile(pos: Vector2i, isBackground: Boolean, sourcePosition: Vector2d, damage: TileDamage, source: AbstractEntity? = null): DamageResult { - val cellState = cells[pos.x, pos.y] - val cell = cellState.state + val cell = cells.value[pos.x, pos.y] if (cell.isIndestructible || cell.tile(isBackground).material.value.isMeta) { return DamageResult(TileDamageResult.NONE) @@ -423,7 +416,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk() val copyHealth = health.copy() @@ -475,32 +466,24 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk() - fun tileDamagePackets(): List { val result = ArrayList() - for (x in 0 until width) { - for (y in 0 until height) { - val health = cells[x, y].backgroundHealth + for ((pos, health) in backgroundHealth) { + if (!health.isHealthy) { + result.add(TileDamageUpdatePacket(this.pos.tileX + pos.x, this.pos.tileY + pos.y, true, health)) + } + } - if (!health.isHealthy) { - result.add(TileDamageUpdatePacket(pos.tileX + x, pos.tileY + y, true, health)) - } - - val health2 = cells[x, y].foregroundHealth - - if (!health2.isHealthy) { - result.add(TileDamageUpdatePacket(pos.tileX + x, pos.tileY + y, false, health2)) - } + for ((pos, health) in foregroundHealth) { + if (!health.isHealthy) { + result.add(TileDamageUpdatePacket(this.pos.tileX + pos.x, this.pos.tileY + pos.y, false, health)) } } @@ -540,23 +523,18 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk - val health = cells[x, y].foregroundHealth - val health2 = cells[x, y].backgroundHealth + foregroundHealth.entries.removeIf { (pos, health) -> + val (x, y) = pos + val remove = !health.tick(cells.value[x, y].foreground.material.value.actualDamageTable) + onTileHealthUpdate(x, y, false, health) + remove + } - var any = false - - if (health.isTicking) { - any = health.tick(cells[x, y].state.foreground.material.value.actualDamageTable) || any - onTileHealthUpdate(x, y, false, health) - } - - if (health2.isTicking) { - any = health2.tick(cells[x, y].state.background.material.value.actualDamageTable) || any - onTileHealthUpdate(x, y, false, health2) - } - - !any + backgroundHealth.entries.removeIf { (pos, health) -> + val (x, y) = pos + val remove = !health.tick(cells.value[x, y].background.material.value.actualDamageTable) + onTileHealthUpdate(x, y, true, health) + remove } } @@ -591,15 +569,22 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk { - return Object2DArray(width, height) { a, b -> cells[a, b].state.toLegacyNet() } + if (cells.isInitialized()) { + val cells = cells.value + return Object2DArray(width, height) { a, b -> cells[a, b].toLegacyNet() } + } else { + return Object2DArray(width, height, AbstractCell.NULL.toLegacyNet()) + } } private fun prepareCells() { + val cells = cells.value + for (x in 0 until width) { for (y in 0 until height) { val info = world.template.cellInfo(pos.tileX + x, pos.tileY + y) - val state = cells[x, y].state.mutable() + val state = cells[x, y].mutable() state.blockBiome = info.blockBiomeIndex state.envBiome = info.environmentBiomeIndex @@ -643,15 +628,17 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk, This : Chunk, CellType : Chunk.ChunkCell>( +abstract class Chunk, This : Chunk>( val world: WorldType, val pos: ChunkPos, ) : ICellAccess { @@ -67,12 +69,26 @@ abstract class Chunk, This : Chunk + protected val cells = lazy { + Object2DArray(width, height, AbstractCell.NULL) + } + + protected val backgroundHealth = HashMap() + protected val foregroundHealth = HashMap() + protected val collisionCacheDirty = Boolean2DArray.allocate(width, height) + protected val collisionCache by lazy { + Object2DArray(width, height) { _, _ -> ObjectArrayList(0) } + } + + init { + for (x in 0 until width) + for (y in 0 until height) + collisionCacheDirty[x, y] = true + } private var hasDirtyCollisions = false - // bulk mark collision dirty of neighbour chunks + // bulk mark collision dirty of neighbour chunks as well as ours protected fun signalChunkContentsUpdated() { val signalPositions = ArrayList() @@ -93,7 +109,17 @@ abstract class Chunk, This : Chunk, This : Chunk, This : Chunk, This : Chunk(2) // no CME checks + target.addAll(collisionCache[x, y]) } fun loadCells(source: Object2DArray) { - val ours = cells + val ours = cells.value source.checkSizeEquals(ours) for (x in 0 until CHUNK_SIZE) { for (y in 0 until CHUNK_SIZE) { - ours[x, y].state = source[x, y].immutable() + ours[x, y] = source[x, y].immutable() } } + + signalChunkContentsUpdated() + } + + fun copyCells(): Object2DArray { + if (cells.isInitialized()) { + val cells = cells.value + return Object2DArray(CHUNK_SIZE, CHUNK_SIZE) { x, y -> cells[x, y] } + } else { + return Object2DArray(CHUNK_SIZE, CHUNK_SIZE, AbstractCell.NULL) + } } override fun getCell(x: Int, y: Int): AbstractCell { - return cells[x, y].state + return if (cells.isInitialized()) cells.value[x, y] else AbstractCell.NULL } final override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean { - cells[x, y].state = cell.immutable() + val old = if (cells.isInitialized()) cells.value[x, y] else AbstractCell.NULL + val new = cell.immutable() + + if (old != new) { + cells.value[x, y] = new + hasDirtyCollisions = true + collisionCacheDirty[x, y] = true + + for (xoff in -2 .. 2) { + for (yoff in -2 .. 2) { + val actualCellPosition = world.geometry.wrap(pos.tile + Vector2i(x + xoff, y + yoff)) + val chunk = world.chunkMap[world.geometry.chunkFromCell(actualCellPosition)] ?: continue + + chunk.hasDirtyCollisions = true + chunk.collisionCacheDirty[actualCellPosition.x - chunk.pos.tileX, actualCellPosition.y - chunk.pos.tileY] = true + } + } + + if (old.foreground != new.foreground) { + foregroundHealth.remove(Vector2i(x, y)) + foregroundChanges(x, y, new) + } + + if (old.background != new.background) { + backgroundHealth.remove(Vector2i(x, y)) + backgroundChanges(x, y, new) + } + + if (old.liquid != new.liquid) { + liquidChanges(x, y, new) + } + + cellChanges(x, y, new) + } + return true } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 29ac42ed..c2cb92dc 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -46,7 +46,7 @@ import java.util.random.RandomGenerator import java.util.stream.Stream import kotlin.math.roundToInt -abstract class World, ChunkType : Chunk>(val template: WorldTemplate) : ICellAccess { +abstract class World, ChunkType : Chunk>(val template: WorldTemplate) : ICellAccess { val background = TileView.Background(this) val foreground = TileView.Foreground(this) val sky = Sky(template.skyParameters)