Rollback immediate placement, make custom terrain regions actually work

This commit is contained in:
DBotThePony 2024-05-06 12:45:38 +07:00
parent 566751b77b
commit 79e606f609
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 29 additions and 129 deletions

View File

@ -221,10 +221,9 @@ data class DungeonDefinition(
terrainSurfaceSpaceExtends: Int = 0,
commit: Boolean = true,
scope: CoroutineScope = Starbound.GLOBAL_SCOPE,
immediate: Boolean = false,
): CompletableFuture<DungeonWorld> {
require(dungeonID in 0 .. NO_DUNGEON_ID) { "Dungeon ID out of range: $dungeonID" }
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize, immediate = immediate)
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize)
val validAnchors = anchorParts.filter { world.template.threatLevel in it.minimumThreatLevel .. it.maximumThreatLevel }
@ -259,10 +258,9 @@ data class DungeonDefinition(
terrainSurfaceSpaceExtends: Int = 0,
commit: Boolean = true,
scope: CoroutineScope = Starbound.GLOBAL_SCOPE,
immediate: Boolean = false
): CompletableFuture<DungeonWorld> {
require(anchor in anchorParts) { "$anchor does not belong to $name" }
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize, immediate = immediate)
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize)
return scope.async {
generate0(anchor, dungeonWorld, x, y, forcePlacement, dungeonID)

View File

@ -53,13 +53,6 @@ class DungeonWorld(
val markSurfaceLevel: Int? = null,
val terrainSurfaceSpaceExtends: Int = 0,
expectedSize: Int = 0,
/**
* Whenever tile changes should be passthrough, if possible
* (improved generation speed and memory usage, but expects world chunks to be already
* loaded into memory AND no concurrent chunkmap operations are performed)
*/
val immediate: Boolean = false,
) {
val geometry = parent.geometry
@ -94,13 +87,13 @@ class DungeonWorld(
var hasGenerated = false
private set
val targetChunkState = if (parent.template.worldParameters is FloatingDungeonWorldParameters) ChunkState.FULL else ChunkState.TERRAIN
val targetChunkState = if (parent.template.worldParameters is FloatingDungeonWorldParameters) ChunkState.FULL else ChunkState.EMPTY
private val liquid = HashMap<Vector2i, AbstractLiquidState>(if (immediate) 2048 else max(8192, expectedSize), 0.5f)
private val foregroundMaterial = HashMap<Vector2i, Material>(if (immediate) 2048 else max(8192, expectedSize), 0.5f)
private val foregroundModifier = HashMap<Vector2i, Modifier>(if (immediate) 2048 else max(8192, expectedSize), 0.5f)
private val backgroundMaterial = HashMap<Vector2i, Material>(if (immediate) 2048 else max(8192, expectedSize), 0.5f)
private val backgroundModifier = HashMap<Vector2i, Modifier>(if (immediate) 2048 else max(8192, expectedSize), 0.5f)
private val liquid = HashMap<Vector2i, AbstractLiquidState>(max(8192, expectedSize * 2), 0.5f)
private val foregroundMaterial = HashMap<Vector2i, Material>(max(8192, expectedSize * 2), 0.5f)
private val foregroundModifier = HashMap<Vector2i, Modifier>(max(8192, expectedSize * 2), 0.5f)
private val backgroundMaterial = HashMap<Vector2i, Material>(max(8192, expectedSize * 2), 0.5f)
private val backgroundModifier = HashMap<Vector2i, Modifier>(max(8192, expectedSize * 2), 0.5f)
// for entity spaces which should be considered empty if they
// are occupied by tile entity
@ -109,8 +102,8 @@ class DungeonWorld(
// entities themselves to be removed
private val tileEntitiesToRemove = HashSet<TileEntity>(2048, 0.5f)
val touchedTiles = HashSet<Vector2i>(max(16384, expectedSize), 0.5f)
val protectTile = HashSet<Vector2i>(max(16384, expectedSize), 0.5f)
val touchedTiles = HashSet<Vector2i>(max(16384, expectedSize * 4), 0.5f)
val protectTile = HashSet<Vector2i>(max(16384, expectedSize * 4), 0.5f)
private val boundingBoxes = ArrayList<AABBi>()
@ -276,15 +269,16 @@ class DungeonWorld(
private val emptyMaterial = Material(AbstractTileState.EMPTY.material, AbstractTileState.EMPTY.hueShift, AbstractTileState.EMPTY.color)
private val emptyModifier = Modifier(AbstractTileState.EMPTY.modifier, AbstractTileState.EMPTY.modifierHueShift)
private fun setCombinedFallback(
fun setCombined(
x: Int, y: Int,
foreground: AbstractTileState?,
background: AbstractTileState?,
liquid: AbstractLiquidState?,
setForegroundMaterial: Boolean,
setForegroundModifier: Boolean,
setBackgroundMaterial: Boolean,
setBackgroundModifier: Boolean,
dungeonId: Int? = null,
setForegroundMaterial: Boolean = true,
setForegroundModifier: Boolean = true,
setBackgroundMaterial: Boolean = true,
setBackgroundModifier: Boolean = true,
) {
val pos = geometry.wrap(Vector2i(x, y))
@ -305,95 +299,17 @@ class DungeonWorld(
}
if (liquid != null) this.liquid[pos] = liquid
}
fun setCombined(
x: Int, y: Int,
foreground: AbstractTileState?,
background: AbstractTileState?,
liquid: AbstractLiquidState?,
dungeonId: Int? = null,
setForegroundMaterial: Boolean = true,
setForegroundModifier: Boolean = true,
setBackgroundMaterial: Boolean = true,
setBackgroundModifier: Boolean = true,
) {
if (immediate) {
val cell = parent.getCell(x, y).mutable()
if (foreground != null) cell.foreground.from(foreground, setForegroundMaterial, setForegroundModifier)
if (background != null) cell.background.from(background, setBackgroundMaterial, setBackgroundModifier)
if (liquid != null) cell.liquid.from(liquid)
cell.dungeonId = dungeonId ?: cell.dungeonId
if (!parent.setCell(x, y, cell.immutable())) {
LOGGER.debug("Cell at $x, $y is out of preloaded bounds for immediate dungeon generator... While this won't affect dungeon generation, it will degrade performance and signify there is a bug somewhere.")
setCombinedFallback(
x = x,
y = y,
foreground = foreground,
background = background,
liquid = liquid,
setForegroundMaterial = setForegroundMaterial,
setForegroundModifier = setForegroundModifier,
setBackgroundMaterial = setBackgroundMaterial,
setBackgroundModifier = setBackgroundModifier,
)
} else {
val pos = geometry.wrap(Vector2i(x, y))
if (foreground != null) {
if (setForegroundMaterial) this.foregroundMaterial.remove(pos)
if (setForegroundModifier) this.foregroundModifier.remove(pos)
}
if (background != null) {
if (setBackgroundMaterial) this.backgroundMaterial.remove(pos)
if (setBackgroundModifier) this.backgroundModifier.remove(pos)
}
if (liquid != null) this.liquid.remove(pos)
}
} else {
setCombinedFallback(
x = x,
y = y,
foreground = foreground,
background = background,
liquid = liquid,
setForegroundMaterial = setForegroundMaterial,
setForegroundModifier = setForegroundModifier,
setBackgroundMaterial = setBackgroundMaterial,
setBackgroundModifier = setBackgroundModifier,
)
}
if (dungeonId != null) dungeonIDs[pos] = dungeonId
}
fun clearTile(x: Int, y: Int) {
val pos = geometry.wrap(Vector2i(x, y))
if (immediate) {
if (parent.setCell(x, y, AbstractCell.EMPTY)) {
this.foregroundMaterial.remove(pos)
this.foregroundModifier.remove(pos)
this.backgroundMaterial.remove(pos)
this.backgroundModifier.remove(pos)
this.liquid.remove(pos)
} else {
this.foregroundMaterial[pos] = emptyMaterial
this.foregroundModifier[pos] = emptyModifier
this.backgroundMaterial[pos] = emptyMaterial
this.backgroundModifier[pos] = emptyModifier
this.liquid[pos] = AbstractLiquidState.EMPTY
}
} else {
this.foregroundMaterial[pos] = emptyMaterial
this.foregroundModifier[pos] = emptyModifier
this.backgroundMaterial[pos] = emptyMaterial
this.backgroundModifier[pos] = emptyModifier
this.liquid[pos] = AbstractLiquidState.EMPTY
}
this.foregroundMaterial[pos] = emptyMaterial
this.foregroundModifier[pos] = emptyModifier
this.backgroundMaterial[pos] = emptyMaterial
this.backgroundModifier[pos] = emptyModifier
this.liquid[pos] = AbstractLiquidState.EMPTY
dungeonIDs.remove(pos)
}
@ -623,7 +539,7 @@ class DungeonWorld(
}.await()
for (box in boundingBoxes) {
tickets.addAll(parent.permanentChunkTicket(box, targetChunkState).await())
tickets.addAll(parent.permanentChunkTicket(box, ChunkState.TERRAIN).await())
}
// apply tiles to world per-chunk

View File

@ -565,20 +565,8 @@ class ServerWorld private constructor(
x = geometry.x.cell(x)
val tickets = ArrayList<ServerChunk.ITicket>()
val dungeonWorld = try {
// if immediate placement will cause issues it can be safely toggled off
var immediate = false
if (dungeon.dungeon.value.anchorParts.size == 1 && dungeon.dungeon.value.partMap.size == 1) {
immediate = true
LOGGER.info("Trying to immediately place dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}...")
tickets.addAll(permanentChunkTicket(AABBi.withSide(Vector2i(x, dungeon.baseHeight), dungeon.dungeon.value.anchorParts.first().reader.size), ChunkState.TERRAIN).await())
tickets.forEach { it.chunk.await() }
} else {
LOGGER.info("Trying to place dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}...")
}
LOGGER.info("Trying to place dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}...")
dungeon.dungeon.value.generate(
this,
@ -586,12 +574,10 @@ class ServerWorld private constructor(
x, dungeon.baseHeight,
dungeon.blendWithTerrain,
dungeon.forcePlacement,
dungeonID = currentDungeonID,
immediate = immediate).await()
dungeonID = currentDungeonID).await()
} catch (err: Throwable) {
LOGGER.error("Exception while placing dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}", err)
// continue
tickets.forEach { it.cancel() }
break
}

View File

@ -247,11 +247,11 @@ abstract class CoordinateMapper {
private var cellsEdgeFloat = cellsF
override fun diff(a: Int, b: Int): Int {
return (a - b).coerceIn(0, cells)
return (a - b).coerceIn(-cells, cells)
}
override fun diff(a: Double, b: Double): Double {
return (a - b).coerceIn(0.0, cellsD)
return (a - b).coerceIn(-cellsD, cellsD)
}
init {
@ -304,11 +304,11 @@ abstract class CoordinateMapper {
}
override fun nearestTo(source: Int, target: Int): Int {
return diff(source, target)
return target
}
override fun nearestTo(source: Double, target: Double): Double {
return diff(source, target)
return target
}
}
}