Fix memory requirements of previous commit :)

This commit is contained in:
DBotThePony 2024-04-10 09:55:28 +07:00
parent e134554879
commit 318b689d2d
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 139 additions and 142 deletions

View File

@ -16,7 +16,7 @@ import java.io.DataOutputStream
class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClientPacket { class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readChunkPos(), stream.readCollection { MutableCell().read(stream).immutable() }) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readChunkPos(), stream.readCollection { MutableCell().read(stream).immutable() })
constructor(chunk: Chunk<*, *, *>) : this(chunk.pos, ArrayList<ImmutableCell>(CHUNK_SIZE * CHUNK_SIZE).also { constructor(chunk: Chunk<*, *>) : this(chunk.pos, ArrayList<ImmutableCell>(CHUNK_SIZE * CHUNK_SIZE).also {
for (x in 0 until CHUNK_SIZE) { for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) { for (y in 0 until CHUNK_SIZE) {
it.add(chunk.getCell(x, y).immutable()) it.add(chunk.getCell(x, y).immutable())

View File

@ -1,17 +1,12 @@
package ru.dbotthepony.kstarbound.client.world package ru.dbotthepony.kstarbound.client.world
import ru.dbotthepony.kommons.arrays.Object2DArray
import ru.dbotthepony.kstarbound.world.Chunk import ru.dbotthepony.kstarbound.world.Chunk
import ru.dbotthepony.kstarbound.world.ChunkPos import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.ChunkState import ru.dbotthepony.kstarbound.world.ChunkState
import ru.dbotthepony.kstarbound.world.api.AbstractCell import ru.dbotthepony.kstarbound.world.api.AbstractCell
import ru.dbotthepony.kstarbound.world.api.ImmutableCell import ru.dbotthepony.kstarbound.world.api.ImmutableCell
class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk, ClientChunk.ChunkCell>(world, pos) { class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk>(world, pos) {
inner class ChunkCell(x: Int, y: Int) : Chunk<ClientWorld, ClientChunk, ClientChunk.ChunkCell>.ChunkCell(x, y)
override val cells: Object2DArray<ChunkCell> = Object2DArray(width, height, ::ChunkCell)
override val state: ChunkState override val state: ChunkState
get() = ChunkState.FULL get() = ChunkState.FULL

View File

@ -62,13 +62,7 @@ import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, ServerChunk, ServerChunk.ChunkCell>(world, pos) { class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, ServerChunk>(world, pos) {
inner class ChunkCell(x: Int, y: Int) : Chunk<ServerWorld, ServerChunk, ServerChunk.ChunkCell>.ChunkCell(x, y) {
}
override val cells: Object2DArray<ChunkCell> = Object2DArray(width, height, ::ChunkCell)
override var state: ChunkState = ChunkState.FRESH override var state: ChunkState = ChunkState.FRESH
private set private set
@ -156,9 +150,11 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
if (world.template.worldLayout == null || world.template.worldParameters is FloatingDungeonWorldParameters) { if (world.template.worldLayout == null || world.template.worldParameters is FloatingDungeonWorldParameters) {
// skip since no cells will be generated anyway // skip since no cells will be generated anyway
val cells = cells.value
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
cells[x, y].setStateQuiet(AbstractCell.EMPTY) cells[x, y] = AbstractCell.EMPTY
} }
} }
@ -191,6 +187,8 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
if (world.template.worldLayout != null && world.template.worldParameters !is FloatingDungeonWorldParameters) { if (world.template.worldLayout != null && world.template.worldParameters !is FloatingDungeonWorldParameters) {
placeGrass() placeGrass()
} }
signalChunkContentsUpdated()
} }
ChunkState.FRESH -> throw RuntimeException() ChunkState.FRESH -> throw RuntimeException()
@ -401,15 +399,10 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
temporary.forEach { if (it.targetState <= state) it.chunk.complete(this) } temporary.forEach { if (it.targetState <= state) it.chunk.complete(this) }
} }
fun copyCells(): Object2DArray<ImmutableCell> {
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) 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 { fun damageTile(pos: Vector2i, isBackground: Boolean, sourcePosition: Vector2d, damage: TileDamage, source: AbstractEntity? = null): DamageResult {
val cellState = cells[pos.x, pos.y] val cell = cells.value[pos.x, pos.y]
val cell = cellState.state
if (cell.isIndestructible || cell.tile(isBackground).material.value.isMeta) { if (cell.isIndestructible || cell.tile(isBackground).material.value.isMeta) {
return DamageResult(TileDamageResult.NONE) return DamageResult(TileDamageResult.NONE)
@ -423,7 +416,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
result = TileDamageResult.PROTECTED result = TileDamageResult.PROTECTED
} }
val health = if (isBackground) cellState.backgroundHealth else cellState.foregroundHealth val health = if (isBackground) backgroundHealth.computeIfAbsent(pos) { TileHealth.Tile() } else foregroundHealth.computeIfAbsent(pos) { TileHealth.Tile() }
val tile = cell.tile(isBackground) val tile = cell.tile(isBackground)
val params = if (!damage.type.isPenetrating && tile.modifier.value.breaksWithTile) { val params = if (!damage.type.isPenetrating && tile.modifier.value.breaksWithTile) {
@ -436,8 +429,6 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
onTileHealthUpdate(pos.x, pos.y, isBackground, health) onTileHealthUpdate(pos.x, pos.y, isBackground, health)
if (health.isDead) { if (health.isDead) {
damagedCells.remove(pos)
val drops = ArrayList<ItemDescriptor>() val drops = ArrayList<ItemDescriptor>()
val copyHealth = health.copy() val copyHealth = health.copy()
@ -475,32 +466,24 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
setCell(pos.x, pos.y, mCell.immutable()) setCell(pos.x, pos.y, mCell.immutable())
health.reset()
return DamageResult(result, copyHealth, cell) return DamageResult(result, copyHealth, cell)
} else { } else {
damagedCells.add(pos)
return DamageResult(result, health, cell) return DamageResult(result, health, cell)
} }
} }
private val damagedCells = ObjectArraySet<Vector2i>()
fun tileDamagePackets(): List<TileDamageUpdatePacket> { fun tileDamagePackets(): List<TileDamageUpdatePacket> {
val result = ArrayList<TileDamageUpdatePacket>() val result = ArrayList<TileDamageUpdatePacket>()
for (x in 0 until width) { for ((pos, health) in backgroundHealth) {
for (y in 0 until height) { if (!health.isHealthy) {
val health = cells[x, y].backgroundHealth result.add(TileDamageUpdatePacket(this.pos.tileX + pos.x, this.pos.tileY + pos.y, true, health))
}
}
if (!health.isHealthy) { for ((pos, health) in foregroundHealth) {
result.add(TileDamageUpdatePacket(pos.tileX + x, pos.tileY + y, true, health)) if (!health.isHealthy) {
} result.add(TileDamageUpdatePacket(this.pos.tileX + pos.x, this.pos.tileY + pos.y, false, health))
val health2 = cells[x, y].foregroundHealth
if (!health2.isHealthy) {
result.add(TileDamageUpdatePacket(pos.tileX + x, pos.tileY + y, false, health2))
}
} }
} }
@ -540,23 +523,18 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
super.tick() super.tick()
damagedCells.removeIf { (x, y) -> foregroundHealth.entries.removeIf { (pos, health) ->
val health = cells[x, y].foregroundHealth val (x, y) = pos
val health2 = cells[x, y].backgroundHealth val remove = !health.tick(cells.value[x, y].foreground.material.value.actualDamageTable)
onTileHealthUpdate(x, y, false, health)
remove
}
var any = false backgroundHealth.entries.removeIf { (pos, health) ->
val (x, y) = pos
if (health.isTicking) { val remove = !health.tick(cells.value[x, y].background.material.value.actualDamageTable)
any = health.tick(cells[x, y].state.foreground.material.value.actualDamageTable) || any onTileHealthUpdate(x, y, true, health)
onTileHealthUpdate(x, y, false, health) remove
}
if (health2.isTicking) {
any = health2.tick(cells[x, y].state.background.material.value.actualDamageTable) || any
onTileHealthUpdate(x, y, false, health2)
}
!any
} }
} }
@ -591,15 +569,22 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
fun legacyNetworkCells(): Object2DArray<LegacyNetworkCellState> { fun legacyNetworkCells(): Object2DArray<LegacyNetworkCellState> {
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() { private fun prepareCells() {
val cells = cells.value
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
val info = world.template.cellInfo(pos.tileX + x, pos.tileY + y) 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.blockBiome = info.blockBiomeIndex
state.envBiome = info.environmentBiomeIndex state.envBiome = info.environmentBiomeIndex
@ -643,15 +628,17 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
} }
cells[x, y].setStateQuiet(state.immutable()) cells[x, y] = state.immutable()
} }
} }
} }
private fun finalizeCells() { private fun finalizeCells() {
val cells = cells.value
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
val cell = cells[x, y].state.mutable() val cell = cells[x, y].mutable()
val info by lazy { world.template.cellInfo(pos.tileX + x, pos.tileY + y) } val info by lazy { world.template.cellInfo(pos.tileX + x, pos.tileY + y) }
if (cell.liquid.isInfinite) { if (cell.liquid.isInfinite) {
@ -676,7 +663,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
replaceBiomeBlocks(cell, info) replaceBiomeBlocks(cell, info)
cells[x, y].state = cell.immutable() cells[x, y] = cell.immutable()
} }
} }
} }
@ -709,11 +696,13 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
private fun replaceBiomeBlocks() { private fun replaceBiomeBlocks() {
val cells = cells.value
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
val cell = cells[x, y].state.mutable() val cell = cells[x, y].mutable()
replaceBiomeBlocks(cell, world.template.cellInfo(pos.tileX + x, pos.tileY + y)) replaceBiomeBlocks(cell, world.template.cellInfo(pos.tileX + x, pos.tileY + y))
cells[x, y].state = cell.immutable() cells[x, y] = cell.immutable()
} }
} }
} }
@ -789,10 +778,12 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
} }
private fun placeGrass() { private fun placeGrass() {
val cells = cells.value
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
val biome = world.template.cellInfo(pos.tileX + x, pos.tileY + y).blockBiome ?: continue val biome = world.template.cellInfo(pos.tileX + x, pos.tileY + y).blockBiome ?: continue
val cell = cells[x, y].state val cell = cells[x, y]
// determine layer for grass mod calculation // determine layer for grass mod calculation
val isBackground = cell.foreground.material.isEmptyTile val isBackground = cell.foreground.material.isEmptyTile
@ -853,7 +844,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
modify.background.modifierHueShift = biome.hueShift(modify.background.modifier) modify.background.modifierHueShift = biome.hueShift(modify.background.modifier)
modify.foreground.modifierHueShift = biome.hueShift(modify.foreground.modifier) modify.foreground.modifierHueShift = biome.hueShift(modify.foreground.modifier)
cells[x, y].state = modify.immutable() cells[x, y] = modify.immutable()
} }
} }
} }

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.kstarbound.world package ru.dbotthepony.kstarbound.world
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
import ru.dbotthepony.kommons.arrays.Boolean2DArray
import ru.dbotthepony.kommons.arrays.Object2DArray import ru.dbotthepony.kommons.arrays.Object2DArray
import ru.dbotthepony.kommons.util.AABB import ru.dbotthepony.kommons.util.AABB
import ru.dbotthepony.kommons.util.AABBi import ru.dbotthepony.kommons.util.AABBi
@ -17,6 +18,7 @@ import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
import ru.dbotthepony.kstarbound.world.physics.CollisionType import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.world.physics.getBlockPlatforms import ru.dbotthepony.kstarbound.world.physics.getBlockPlatforms
import ru.dbotthepony.kstarbound.world.physics.getBlocksMarchingSquares import ru.dbotthepony.kstarbound.world.physics.getBlocksMarchingSquares
import java.util.BitSet
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -31,7 +33,7 @@ import kotlin.math.min
* *
* Весь игровой мир будет измеряться в Starbound Unit'ах * Весь игровой мир будет измеряться в Starbound Unit'ах
*/ */
abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType, This, CellType>, CellType : Chunk<WorldType, This, CellType>.ChunkCell>( abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType, This>>(
val world: WorldType, val world: WorldType,
val pos: ChunkPos, val pos: ChunkPos,
) : ICellAccess { ) : ICellAccess {
@ -67,12 +69,26 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
val aabb = AABBi(pos.tile, pos.tile + Vector2i(width, height)) val aabb = AABBi(pos.tile, pos.tile + Vector2i(width, height))
val aabbd = aabb.toDoubleAABB() val aabbd = aabb.toDoubleAABB()
// TODO: maybe fit them into "width" and "height" variables added recently? protected val cells = lazy {
protected abstract val cells: Object2DArray<CellType> Object2DArray(width, height, AbstractCell.NULL)
}
protected val backgroundHealth = HashMap<Vector2i, TileHealth.Tile>()
protected val foregroundHealth = HashMap<Vector2i, TileHealth.Tile>()
protected val collisionCacheDirty = Boolean2DArray.allocate(width, height)
protected val collisionCache by lazy {
Object2DArray(width, height) { _, _ -> ObjectArrayList<CollisionPoly>(0) }
}
init {
for (x in 0 until width)
for (y in 0 until height)
collisionCacheDirty[x, y] = true
}
private var hasDirtyCollisions = false 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() { protected fun signalChunkContentsUpdated() {
val signalPositions = ArrayList<Vector2i>() val signalPositions = ArrayList<Vector2i>()
@ -93,7 +109,17 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
val chunk = world.chunkMap[world.geometry.chunkFromCell(actualCellPosition)] ?: continue val chunk = world.chunkMap[world.geometry.chunkFromCell(actualCellPosition)] ?: continue
chunk.hasDirtyCollisions = true chunk.hasDirtyCollisions = true
chunk.cells[actualCellPosition.x - chunk.pos.tileX, actualCellPosition.y - chunk.pos.tileY].collisionCacheDirty = true chunk.collisionCacheDirty[actualCellPosition.x - chunk.pos.tileX, actualCellPosition.y - chunk.pos.tileY] = true
}
hasDirtyCollisions = true
backgroundHealth.clear()
foregroundHealth.clear()
for (x in 0 until width) {
for (y in 0 until height) {
collisionCacheDirty[x, y] = true
}
} }
} }
@ -110,7 +136,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
for (x in 0 until width) { for (x in 0 until width) {
for (y in 0 until height) { for (y in 0 until height) {
if (cells[x, y].collisionCacheDirty) { if (collisionCacheDirty[x, y]) {
minX = min(minX, x) minX = min(minX, x)
minY = min(minY, y) minY = min(minY, y)
maxX = max(maxX, x) maxX = max(maxX, x)
@ -121,13 +147,11 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
for (x in minX .. maxX) { for (x in minX .. maxX) {
for (y in minY .. maxY) { for (y in minY .. maxY) {
val cell = cells[x, y] if (collisionCacheDirty[x, y]) {
collisionCache[x, y].clear()
if (cell.collisionCacheDirty) { getBlocksMarchingSquares(pos.tileX + x, pos.tileY + y, world.foreground, CollisionType.DYNAMIC, collisionCache[x, y])
cell.collisionCache.clear() getBlockPlatforms(pos.tileX + x, pos.tileY + y, world.foreground, CollisionType.PLATFORM, collisionCache[x, y])
getBlocksMarchingSquares(pos.tileX + x, pos.tileY + y, world.foreground, CollisionType.DYNAMIC, cell.collisionCache) collisionCacheDirty[x, y] = false
getBlockPlatforms(pos.tileX + x, pos.tileY + y, world.foreground, CollisionType.PLATFORM, cell.collisionCache)
cell.collisionCacheDirty = false
} }
} }
} }
@ -137,84 +161,71 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
hasDirtyCollisions = false hasDirtyCollisions = false
} }
target.addAll(cells[x, y].collisionCache) target.addAll(collisionCache[x, y])
}
abstract inner class ChunkCell(val x: Int, val y: Int) {
private var actualState: ImmutableCell = AbstractCell.NULL
var state: ImmutableCell
get() = actualState
set(value) {
if (actualState != value) {
foregroundHealth.reset()
backgroundHealth.reset()
hasDirtyCollisions = true
collisionCacheDirty = 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.cells[actualCellPosition.x - chunk.pos.tileX, actualCellPosition.y - chunk.pos.tileY].collisionCacheDirty = true
}
}
val old = actualState
actualState = value
if (old.foreground != value.foreground) {
foregroundChanges(x, y, value)
}
if (old.background != value.background) {
backgroundChanges(x, y, value)
}
if (old.liquid != value.liquid) {
liquidChanges(x, y, value)
}
cellChanges(x, y, value)
}
}
/**
* Does not trigger any change events
*/
fun setStateQuiet(state: ImmutableCell) {
foregroundHealth.reset()
backgroundHealth.reset()
hasDirtyCollisions = true
collisionCacheDirty = true
actualState = state
}
var collisionCacheDirty = true
val foregroundHealth = TileHealth.Tile()
val backgroundHealth = TileHealth.Tile()
val collisionCache = ObjectArrayList<CollisionPoly>(2) // no CME checks
} }
fun loadCells(source: Object2DArray<out AbstractCell>) { fun loadCells(source: Object2DArray<out AbstractCell>) {
val ours = cells val ours = cells.value
source.checkSizeEquals(ours) source.checkSizeEquals(ours)
for (x in 0 until CHUNK_SIZE) { for (x in 0 until CHUNK_SIZE) {
for (y 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<ImmutableCell> {
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 { 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 { 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 return true
} }

View File

@ -46,7 +46,7 @@ import java.util.random.RandomGenerator
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.math.roundToInt import kotlin.math.roundToInt
abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType, *>>(val template: WorldTemplate) : ICellAccess { abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType>>(val template: WorldTemplate) : ICellAccess {
val background = TileView.Background(this) val background = TileView.Background(this)
val foreground = TileView.Foreground(this) val foreground = TileView.Foreground(this)
val sky = Sky(template.skyParameters) val sky = Sky(template.skyParameters)