diff --git a/game/src/main/scala/hexacraft/world/World.scala b/game/src/main/scala/hexacraft/world/World.scala index 1fa03d79..ef8524df 100644 --- a/game/src/main/scala/hexacraft/world/World.scala +++ b/game/src/main/scala/hexacraft/world/World.scala @@ -8,6 +8,7 @@ import hexacraft.world.entity.Entity import com.martomate.nbt.Nbt +import scala.annotation.tailrec import scala.collection.mutable object World: @@ -85,7 +86,7 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep def removeBlock(coords: BlockRelWorld): Unit = getChunk(coords.getChunkRelWorld) match case Some(chunk) => - chunk.removeBlock(coords.getBlockRelChunk) + chunk.setBlock(coords.getBlockRelChunk, BlockState.Air) onSetBlock(coords, BlockState.Air) case None => @@ -163,7 +164,18 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep if blockUpdateTimer.tick() then performBlockUpdates() if relocateEntitiesTimer.tick() then performEntityRelocation() - for col <- columns.values do col.tick(this, collisionDetector) + for + col <- columns.values + ch <- col.allChunks + do + ch.optimizeStorage() + tickEntities(ch.entities) + + @tailrec // this is done for performance + def tickEntities(ents: Iterable[Entity]): Unit = + if ents.nonEmpty then + ents.head.tick(this, collisionDetector) + tickEntities(ents.tail) private val blockUpdateTimer: TickableTimer = TickableTimer(World.ticksBetweenBlockUpdates) diff --git a/game/src/main/scala/hexacraft/world/WorldGenerator.scala b/game/src/main/scala/hexacraft/world/WorldGenerator.scala index a5b90ba6..0f1aaea0 100644 --- a/game/src/main/scala/hexacraft/world/WorldGenerator.scala +++ b/game/src/main/scala/hexacraft/world/WorldGenerator.scala @@ -49,8 +49,7 @@ class WorldGenerator(worldGenSettings: WorldGenSettings)(using CylinderSize) { storage.setBlock(BlockRelChunk(i, j, k), new BlockState(getBlockAtDepth(yToGo))) } - val data = ChunkData.fromStorage(storage) - data + ChunkData.fromStorage(storage) private def getBlockAtDepth(yToGo: Int) = { if (yToGo < -5) Block.Stone diff --git a/game/src/main/scala/hexacraft/world/chunk/chunk.scala b/game/src/main/scala/hexacraft/world/chunk/chunk.scala index af5efaf1..645ed965 100644 --- a/game/src/main/scala/hexacraft/world/chunk/chunk.scala +++ b/game/src/main/scala/hexacraft/world/chunk/chunk.scala @@ -9,7 +9,6 @@ import hexacraft.world.entity.{Entity, EntityFactory} import com.martomate.nbt.Nbt -import scala.annotation.tailrec import scala.collection.mutable object Chunk: @@ -30,51 +29,41 @@ class Chunk private (val coords: ChunkRelWorld, chunkData: ChunkData)(using Cyli lighting.setInitialized() lightPropagator.initBrightnesses(this) - def entities: collection.Seq[Entity] = chunkData.allEntities + def entities: collection.Seq[Entity] = chunkData.entities def addEntity(entity: Entity): Unit = - chunkData.addEntity(entity) + chunkData.entities += entity _modCount += 1 def removeEntity(entity: Entity): Unit = - chunkData.removeEntity(entity) + chunkData.entities -= entity _modCount += 1 - def blocks: Array[LocalBlockState] = chunkData.allBlocks + def blocks: Array[LocalBlockState] = chunkData.storage.allBlocks - def getBlock(coords: BlockRelChunk): BlockState = chunkData.getBlock(coords) + def getBlock(coords: BlockRelChunk): BlockState = chunkData.storage.getBlock(coords) def setBlock(blockCoords: BlockRelChunk, block: BlockState): Unit = val before = getBlock(blockCoords) if before != block then - chunkData.setBlock(blockCoords, block) + chunkData.storage.setBlock(blockCoords, block) _modCount += 1 - def removeBlock(coords: BlockRelChunk): Unit = setBlock(coords, BlockState.Air) - - def tick(world: BlocksInWorld, collisionDetector: CollisionDetector): Unit = - chunkData.optimizeStorage() - - tickEntities(entities) - - @tailrec - def tickEntities(ents: Iterable[Entity]): Unit = - if ents.nonEmpty then - ents.head.tick(world, collisionDetector) - tickEntities(ents.tail) + def optimizeStorage(): Unit = chunkData.optimizeStorage() def toNbt: Nbt.MapTag = chunkData.toNBT def isDecorated: Boolean = chunkData.isDecorated def setDecorated(): Unit = if !chunkData.isDecorated then - chunkData.setDecorated() + chunkData.isDecorated = true _modCount += 1 -class ChunkData(private var storage: ChunkStorage, entities: mutable.ArrayBuffer[Entity])(using CylinderSize): - private var _isDecorated: Boolean = false - def isDecorated: Boolean = _isDecorated - +class ChunkData( + private[chunk] var storage: ChunkStorage, + private[chunk] val entities: mutable.ArrayBuffer[Entity], + private[chunk] var isDecorated: Boolean +): def optimizeStorage(): Unit = if storage.isDense then { if storage.numBlocks < 32 @@ -84,29 +73,19 @@ class ChunkData(private var storage: ChunkStorage, entities: mutable.ArrayBuffer then storage = DenseChunkStorage.fromStorage(storage) } - def getBlock(coords: BlockRelChunk): BlockState = storage.getBlock(coords) - def setBlock(coords: BlockRelChunk, block: BlockState): Unit = storage.setBlock(coords, block) - def allBlocks: Array[LocalBlockState] = storage.allBlocks - - def addEntity(entity: Entity): Unit = entities += entity - def removeEntity(entity: Entity): Unit = entities -= entity - def allEntities: collection.Seq[Entity] = entities - - def setDecorated(): Unit = _isDecorated = true - def toNBT: Nbt.MapTag = val storageNbt = storage.toNBT Nbt.makeMap( "blocks" -> Nbt.ByteArrayTag(storageNbt.blocks), "metadata" -> Nbt.ByteArrayTag(storageNbt.metadata), - "entities" -> Nbt.ListTag(allEntities.map(e => e.toNBT).toSeq), - "isDecorated" -> Nbt.ByteTag(_isDecorated) + "entities" -> Nbt.ListTag(entities.map(e => e.toNBT).toSeq), + "isDecorated" -> Nbt.ByteTag(isDecorated) ) object ChunkData: - def fromStorage(storage: ChunkStorage)(using CylinderSize): ChunkData = - new ChunkData(storage, mutable.ArrayBuffer.empty) + def fromStorage(storage: ChunkStorage): ChunkData = + new ChunkData(storage, mutable.ArrayBuffer.empty, false) def fromNBT(nbt: Nbt.MapTag)(using CylinderSize): ChunkData = val storage = nbt.getByteArray("blocks") match @@ -116,26 +95,18 @@ object ChunkData: case None => SparseChunkStorage.empty - val entities = - nbt.getList("entities") match - case Some(tags) => entitiesFromNbt(tags.map(_.asInstanceOf[Nbt.MapTag])) - case None => Nil - - val data = new ChunkData(storage, mutable.ArrayBuffer.from(entities)) - data._isDecorated = nbt.getBoolean("isDecorated", default = false) - data - - private def entitiesFromNbt(list: Seq[Nbt.MapTag])(using - CylinderSize - ): Iterable[Entity] = val entities = mutable.ArrayBuffer.empty[Entity] - - for tag <- list do + for + tags <- nbt.getList("entities") + tag <- tags.map(_.asInstanceOf[Nbt.MapTag]) + do EntityFactory.fromNbt(tag) match case Ok(entity) => entities += entity case Err(message) => println(message) - entities + val isDecorated = nbt.getBoolean("isDecorated", default = false) + + new ChunkData(storage, entities, isDecorated) class ChunkLighting: private val brightness: SmartArray[Byte] = SmartArray.withByteArray(16 * 16 * 16, 0) diff --git a/game/src/main/scala/hexacraft/world/chunk/column.scala b/game/src/main/scala/hexacraft/world/chunk/column.scala index e35f09a5..a8d22da6 100644 --- a/game/src/main/scala/hexacraft/world/chunk/column.scala +++ b/game/src/main/scala/hexacraft/world/chunk/column.scala @@ -122,7 +122,4 @@ class ChunkColumn private ( case Some(h) => heightMap(x)(z) = h.toShort case None => - def tick(world: BlocksInWorld, collisionDetector: CollisionDetector): Unit = - chunks.foreachValue(_.tick(world, collisionDetector)) - def toNBT: Nbt.MapTag = ChunkColumnData(Some(heightMap)).toNBT diff --git a/game/src/main/scala/hexacraft/world/chunk/storage.scala b/game/src/main/scala/hexacraft/world/chunk/storage.scala index 1ce832ad..46bb6b53 100644 --- a/game/src/main/scala/hexacraft/world/chunk/storage.scala +++ b/game/src/main/scala/hexacraft/world/chunk/storage.scala @@ -1,7 +1,6 @@ package hexacraft.world.chunk import hexacraft.util.SmartArray -import hexacraft.world.CylinderSize import hexacraft.world.block.{Block, BlockState} import hexacraft.world.coord.BlockRelChunk @@ -72,9 +71,7 @@ object DenseChunkStorage: for LocalBlockState(i, b) <- storage.allBlocks do result.setBlock(i, b) result - def fromNBT(blocks: Array[Byte], metadata: Option[Array[Byte]])(using - CylinderSize - ): DenseChunkStorage = + def fromNBT(blocks: Array[Byte], metadata: Option[Array[Byte]]): DenseChunkStorage = val storage = new DenseChunkStorage val meta = metadata.map(_.apply).getOrElse(_ => 0) @@ -114,7 +111,7 @@ class SparseChunkStorage extends ChunkStorage: ChunkStorage.NbtData(blocks = ids, metadata = meta) object SparseChunkStorage: - def empty(using CylinderSize): ChunkStorage = new SparseChunkStorage + def empty: ChunkStorage = new SparseChunkStorage def fromStorage(storage: ChunkStorage): SparseChunkStorage = val result = new SparseChunkStorage