Skip to content

Commit

Permalink
Refactor: Replaced chunk events with direct calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Martomate committed Dec 30, 2023
1 parent 7b3d1c4 commit 6aef36e
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 72 deletions.
3 changes: 2 additions & 1 deletion game/src/main/scala/hexacraft/game/GameScene.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class GameScene(
private val otherPlayer: ControlledPlayerEntity =
new ControlledPlayerEntity(PlayerEntityModel.create("player"), new EntityBaseData(CylCoords(player.position)))

private val worldRenderer: WorldRenderer = new WorldRenderer(world, blockSpecRegistry, initialWindowSize.physicalSize)
private val worldRenderer: WorldRenderer =
new WorldRenderer(world, world.requestRenderUpdate, blockSpecRegistry, initialWindowSize.physicalSize)
world.trackEvents(worldRenderer.onWorldEvent _)

// worldRenderer.addPlayer(otherPlayer)
Expand Down
12 changes: 6 additions & 6 deletions game/src/main/scala/hexacraft/world/LightPropagator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package hexacraft.world
import hexacraft.math.MathUtils.oppositeSide
import hexacraft.world.block.BlockState
import hexacraft.world.chunk.{Chunk, LocalBlockState}
import hexacraft.world.coord.{BlockRelChunk, BlockRelWorld, NeighborOffsets}
import hexacraft.world.coord.{BlockRelChunk, BlockRelWorld, ChunkRelWorld, NeighborOffsets}

import scala.collection.mutable

class LightPropagator(world: BlocksInWorld)(using CylinderSize) {
class LightPropagator(world: BlocksInWorld, requestRenderUpdate: ChunkRelWorld => Unit)(using CylinderSize) {
private val chunkCache: ChunkCache = new ChunkCache(world)

def initBrightnesses(chunk: Chunk): Unit = {
Expand Down Expand Up @@ -133,7 +133,7 @@ class LightPropagator(world: BlocksInWorld)(using CylinderSize) {
}
}

chunksNeedingRenderUpdate.foreach(_.requestRenderUpdate())
chunksNeedingRenderUpdate.foreach(c => requestRenderUpdate(c.coords))

propagateTorchlight(lightQueue)
}
Expand Down Expand Up @@ -167,7 +167,7 @@ class LightPropagator(world: BlocksInWorld)(using CylinderSize) {
}
}

chunksNeedingRenderUpdate.foreach(_.requestRenderUpdate())
chunksNeedingRenderUpdate.foreach(c => requestRenderUpdate(c.coords))
propagateSunlight(lightQueue)
}

Expand Down Expand Up @@ -201,7 +201,7 @@ class LightPropagator(world: BlocksInWorld)(using CylinderSize) {
}
}

chunksNeedingRenderUpdate.foreach(_.requestRenderUpdate())
chunksNeedingRenderUpdate.foreach(c => requestRenderUpdate(c.coords))
}

private def propagateSunlight(queue: mutable.Queue[(BlockRelChunk, Chunk)]): Unit = {
Expand All @@ -225,7 +225,7 @@ class LightPropagator(world: BlocksInWorld)(using CylinderSize) {
}
}

chunksNeedingRenderUpdate.foreach(_.requestRenderUpdate())
chunksNeedingRenderUpdate.foreach(c => requestRenderUpdate(c.coords))
}

private def propagateSunlightInChunk(
Expand Down
60 changes: 28 additions & 32 deletions game/src/main/scala/hexacraft/world/World.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ object World:
var shouldChillChunkLoader = false

enum Event:
case ChunkAdded(chunk: Chunk)
case ChunkAdded(coords: ChunkRelWorld)
case ChunkRemoved(coords: ChunkRelWorld)
case ChunkNeedsRenderUpdate(coords: ChunkRelWorld)
case BlockReplaced(coords: BlockRelWorld, prev: BlockState, now: BlockState)

class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRepository with BlocksInWorld:
given size: CylinderSize = worldInfo.worldSize

private val worldGenerator = new WorldGenerator(worldInfo.gen)
private val worldPlanner: WorldPlanner = WorldPlanner(this, worldInfo.gen.seed)
private val lightPropagator: LightPropagator = new LightPropagator(this)
private val lightPropagator: LightPropagator = new LightPropagator(this, this.requestRenderUpdate)

val renderDistance: Double = 8 * CylinderSize.y60

Expand All @@ -40,7 +39,6 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
private val blocksToUpdate: UniqueQueue[BlockRelWorld] = new UniqueQueue

private val savedChunkModCounts = mutable.Map.empty[ChunkRelWorld, Long]
private val chunkEventTrackerRevokeFns = mutable.Map.empty[ChunkRelWorld, RevokeTrackerFn]

private val dispatcher = new EventDispatcher[World.Event]
def trackEvents(tracker: Tracker[World.Event]): Unit = dispatcher.track(tracker)
Expand Down Expand Up @@ -79,13 +77,17 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep

def setBlock(coords: BlockRelWorld, block: BlockState): Unit =
getChunk(coords.getChunkRelWorld) match
case Some(chunk) => chunk.setBlock(coords.getBlockRelChunk, block)
case None =>
case Some(chunk) =>
chunk.setBlock(coords.getBlockRelChunk, block)
onSetBlock(coords, block)
case None =>

def removeBlock(coords: BlockRelWorld): Unit =
getChunk(coords.getChunkRelWorld) match
case Some(chunk) => chunk.removeBlock(coords.getBlockRelChunk)
case None =>
case Some(chunk) =>
chunk.removeBlock(coords.getBlockRelChunk)
onSetBlock(coords, BlockState.Air)
case None =>

def addEntity(entity: Entity): Unit =
chunkOfEntity(entity) match
Expand All @@ -112,20 +114,19 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
ensureColumnExists(coords).terrainHeight(x & 15, z & 15)

def setChunk(ch: Chunk): Unit =
ensureColumnExists(ch.coords.getColumnRelWorld).setChunk(ch)
val col = ensureColumnExists(ch.coords.getColumnRelWorld)
col.setChunk(ch)

val revoke = ch.trackEvents(onChunkEvent _)
chunkEventTrackerRevokeFns += ch.coords -> revoke

dispatcher.notify(World.Event.ChunkAdded(ch))

ch.requestRenderUpdate()
requestRenderUpdateForNeighborChunks(ch.coords)
dispatcher.notify(World.Event.ChunkAdded(ch.coords))

worldPlanner.decorate(ch)
if ch.modCount != savedChunkModCounts.getOrElse(ch.coords, -1L) then
worldProvider.saveChunkData(ch.toNbt, ch.coords)
savedChunkModCounts(ch.coords) = ch.modCount
col.updateHeightmapAfterChunkUpdate(ch)

requestRenderUpdate(ch.coords)
requestRenderUpdateForNeighborChunks(ch.coords)

for block <- ch.blocks do
requestBlockUpdate(BlockRelWorld.fromChunk(block.coords, ch.coords))
Expand All @@ -139,10 +140,6 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
def removeChunk(ch: ChunkRelWorld): Unit =
for col <- columns.get(ch.getColumnRelWorld.value) do
for removedChunk <- col.removeChunk(ch.Y) do
chunkEventTrackerRevokeFns.remove(removedChunk.coords) match
case Some(revoke) => revoke()
case None =>

dispatcher.notify(World.Event.ChunkRemoved(removedChunk.coords))

if removedChunk.modCount != savedChunkModCounts.getOrElse(removedChunk.coords, -1L) then
Expand Down Expand Up @@ -198,7 +195,7 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
for
side <- 0 until 8
ch <- getChunk(coords.offset(NeighborOffsets(side)))
do ch.requestRenderUpdate()
do requestRenderUpdate(ch.coords)

private def ensureColumnExists(here: ColumnRelWorld): ChunkColumn =
columns.get(here.value) match
Expand All @@ -217,7 +214,14 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
case Some(c) => c.lighting.getBrightness(block.getBlockRelChunk)
case None => 1.0f

def onReloadedResources(): Unit = for col <- columns.values do col.onReloadedResources()
def onReloadedResources(): Unit =
for
col <- columns.values
ch <- col.allChunks
do requestRenderUpdate(ch.coords)

def requestRenderUpdate(chunkCoords: ChunkRelWorld): Unit =
dispatcher.notify(World.Event.ChunkNeedsRenderUpdate(chunkCoords))

def unload(): Unit =
blockUpdateTimer.enabled = false
Expand All @@ -229,14 +233,6 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
columns.clear()
chunkLoader.unload()

private def onChunkEvent(event: Chunk.Event): Unit =
event match
case Chunk.Event.ChunkNeedsRenderUpdate(coords) =>
dispatcher.notify(World.Event.ChunkNeedsRenderUpdate(coords))
case Chunk.Event.BlockReplaced(coords, prev, block) =>
onSetBlock(coords, block)
dispatcher.notify(World.Event.BlockReplaced(coords, prev, block))

private def requestBlockUpdate(coords: BlockRelWorld): Unit = blocksToUpdate.enqueue(coords)

private def onSetBlock(coords: BlockRelWorld, block: BlockState): Unit =
Expand All @@ -260,7 +256,7 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep
for c <- getChunk(cCoords) do
handleLightingOnSetBlock(c, bCoords, block)

c.requestRenderUpdate()
requestRenderUpdate(c.coords)
requestBlockUpdate(BlockRelWorld.fromChunk(bCoords, c.coords))

for i <- 0 until 8 do
Expand All @@ -269,7 +265,7 @@ class World(worldProvider: WorldProvider, worldInfo: WorldInfo) extends BlockRep

if isInNeighborChunk(off) then
for n <- getChunk(cCoords.offset(NeighborOffsets(i))) do
n.requestRenderUpdate()
requestRenderUpdate(n.coords)
requestBlockUpdate(BlockRelWorld.fromChunk(c2, n.coords))
else requestBlockUpdate(BlockRelWorld.fromChunk(c2, c.coords))

Expand Down
4 changes: 2 additions & 2 deletions game/src/main/scala/hexacraft/world/WorldPlanner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ class WorldPlanner(world: BlocksInWorld, mainSeed: Long)(using CylinderSize):

def onWorldEvent(event: World.Event): Unit =
event match
case World.Event.ChunkAdded(chunk) =>
case World.Event.ChunkAdded(coords) =>
for
ch <- chunk.coords.extendedNeighbors(1)
ch <- coords.extendedNeighbors(1)
p <- planners
do p.plan(ch)
case World.Event.ChunkRemoved(_) =>
Expand Down
18 changes: 3 additions & 15 deletions game/src/main/scala/hexacraft/world/chunk/chunk.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package hexacraft.world.chunk

import hexacraft.util.{EventDispatcher, RevokeTrackerFn, SmartArray, Tracker}
import hexacraft.util.Result.{Err, Ok}
import hexacraft.world.{BlocksInWorld, CollisionDetector, CylinderSize, LightPropagator, WorldGenerator}
import hexacraft.util.SmartArray
import hexacraft.world.*
import hexacraft.world.block.BlockState
import hexacraft.world.coord.{BlockRelChunk, BlockRelWorld, ChunkRelWorld}
import hexacraft.world.coord.{BlockRelChunk, ChunkRelWorld}
import hexacraft.world.entity.{Entity, EntityFactory}

import com.martomate.nbt.Nbt
Expand All @@ -13,10 +13,6 @@ import scala.annotation.tailrec
import scala.collection.mutable

object Chunk:
enum Event:
case ChunkNeedsRenderUpdate(coords: ChunkRelWorld)
case BlockReplaced(coords: BlockRelWorld, prev: BlockState, now: BlockState)

def fromNbt(coords: ChunkRelWorld, loadedTag: Nbt.MapTag)(using CylinderSize): Chunk =
new Chunk(coords, ChunkData.fromNBT(loadedTag))

Expand All @@ -28,9 +24,6 @@ class Chunk private (val coords: ChunkRelWorld, chunkData: ChunkData)(using Cyli
private var _modCount: Long = 0L
def modCount: Long = _modCount

private val dispatcher = new EventDispatcher[Chunk.Event]
def trackEvents(tracker: Tracker[Chunk.Event]): RevokeTrackerFn = dispatcher.track(tracker)

val lighting: ChunkLighting = new ChunkLighting
def initLightingIfNeeded(lightPropagator: LightPropagator): Unit =
if !lighting.initialized then
Expand All @@ -57,13 +50,8 @@ class Chunk private (val coords: ChunkRelWorld, chunkData: ChunkData)(using Cyli
chunkData.setBlock(blockCoords, block)
_modCount += 1

dispatcher.notify(Chunk.Event.BlockReplaced(BlockRelWorld.fromChunk(blockCoords, coords), before, block))

def removeBlock(coords: BlockRelChunk): Unit = setBlock(coords, BlockState.Air)

def requestRenderUpdate(): Unit =
dispatcher.notify(Chunk.Event.ChunkNeedsRenderUpdate(coords))

def tick(world: BlocksInWorld, collisionDetector: CollisionDetector): Unit =
chunkData.optimizeStorage()

Expand Down
24 changes: 13 additions & 11 deletions game/src/main/scala/hexacraft/world/chunk/column.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package hexacraft.world.chunk

import hexacraft.math.bits.Int12
import hexacraft.math.noise.Data2D
import hexacraft.world.{BlocksInWorld, CollisionDetector}
import hexacraft.world.{BlocksInWorld, CollisionDetector, CylinderSize}
import hexacraft.world.block.{Block, BlockState}
import hexacraft.world.coord.{BlockRelChunk, BlockRelWorld, ChunkRelWorld, ColumnRelWorld}

Expand Down Expand Up @@ -82,15 +82,21 @@ class ChunkColumn private (

def allChunks: Iterable[Chunk] = chunks.values

def updateHeightmapAfterChunkUpdate(chunk: Chunk)(using CylinderSize): Unit =
val chunkCoords = chunk.coords
for
cx <- 0 until 16
cz <- 0 until 16
do
val blockCoords = BlockRelChunk(cx, 15, cz)
updateHeightmapAfterBlockUpdate(BlockRelWorld.fromChunk(blockCoords, chunkCoords), chunk.getBlock(blockCoords))

def updateHeightmapAfterBlockUpdate(coords: BlockRelWorld, now: BlockState): Unit =
val height = terrainHeight(coords.cx, coords.cz)

if now.blockType != Block.Air then { // a block is being added
if coords.y > height then heightMap(coords.cx)(coords.cz) = coords.y.toShort
} else { // a block is being removed
if coords.y == height then
// remove and find the next highest

if coords.y >= height then
if now.blockType != Block.Air then heightMap(coords.cx)(coords.cz) = coords.y.toShort
else
heightMap(coords.cx)(coords.cz) = LazyList
.range((height - 1).toShort, Short.MinValue, -1.toShort)
.map(y =>
Expand All @@ -101,7 +107,6 @@ class ChunkColumn private (
.takeWhile(_ != null) // stop searching if the chunk is not loaded
.collectFirst({ case (y, block) if block.blockType != Block.Air => y })
.getOrElse(Short.MinValue)
}

private def onChunkLoaded(chunk: Chunk): Unit =
val yy = chunk.coords.Y.toInt * 16
Expand All @@ -120,7 +125,4 @@ class ChunkColumn private (
def tick(world: BlocksInWorld, collisionDetector: CollisionDetector): Unit =
chunks.foreachValue(_.tick(world, collisionDetector))

def onReloadedResources(): Unit =
chunks.foreachValue(_.requestRenderUpdate())

def toNBT: Nbt.MapTag = ChunkColumnData(Some(heightMap)).toNBT
2 changes: 1 addition & 1 deletion game/src/main/scala/hexacraft/world/loader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ChunkLoader(

def onWorldEvent(event: World.Event): Unit =
event match
case World.Event.ChunkAdded(chunk) => chunksLoading -= chunk.coords
case World.Event.ChunkAdded(coords) => chunksLoading -= coords
case World.Event.ChunkRemoved(coords) => chunksUnloading -= coords
case _ =>

Expand Down
13 changes: 9 additions & 4 deletions game/src/main/scala/hexacraft/world/render/WorldRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import org.joml.{Vector2ic, Vector3f}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

class WorldRenderer(world: BlocksInWorld, blockSpecs: BlockSpecRegistry, initialFramebufferSize: Vector2ic)(using
class WorldRenderer(
world: BlocksInWorld,
requestRenderUpdate: ChunkRelWorld => Unit,
blockSpecs: BlockSpecRegistry,
initialFramebufferSize: Vector2ic
)(using
CylinderSize
):
private val skyShader = new SkyShader()
Expand All @@ -24,7 +29,7 @@ class WorldRenderer(world: BlocksInWorld, blockSpecs: BlockSpecRegistry, initial

private val chunkHandler: ChunkRenderHandler = new ChunkRenderHandler

private val lightPropagator = new LightPropagator(world)
private val lightPropagator = new LightPropagator(world, requestRenderUpdate)

private val skyVao: VAO = SkyVao.create
private val skyRenderer =
Expand Down Expand Up @@ -145,8 +150,8 @@ class WorldRenderer(world: BlocksInWorld, blockSpecs: BlockSpecRegistry, initial

def onWorldEvent(event: World.Event): Unit =
event match
case World.Event.ChunkAdded(chunk) =>
chunksToRender.add(chunk.coords)
case World.Event.ChunkAdded(coords) =>
chunksToRender.add(coords)
case World.Event.ChunkRemoved(coords) =>
chunksToRender.remove(coords)
chunkHandler.clearChunkRenderData(coords)
Expand Down

0 comments on commit 6aef36e

Please sign in to comment.