Immediate dungeon placement
This commit is contained in:
parent
161d19f263
commit
566751b77b
@ -259,7 +259,7 @@ public class DirectCallExecutor {
|
||||
*/
|
||||
public Object[] resume(Continuation continuation)
|
||||
throws CallException, CallPausedException, InterruptedException {
|
||||
return execute(continuation, schedulingContextFactory.newInstance(), performJavaConversions);
|
||||
return execute(continuation, schedulingContextFactory.newInstance(), false);
|
||||
}
|
||||
|
||||
private static class Result implements CallEventHandler {
|
||||
|
@ -29,6 +29,8 @@ import ru.dbotthepony.kstarbound.json.builder.JsonIgnore
|
||||
import ru.dbotthepony.kstarbound.util.random.random
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.ImmutableTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.MutableLiquidState
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
|
||||
@ -90,13 +92,7 @@ abstract class DungeonBrush {
|
||||
return
|
||||
|
||||
// TODO: delete objects too?
|
||||
world.setLiquid(x, y, AbstractLiquidState.EMPTY)
|
||||
world.setForeground(x, y, BuiltinMetaMaterials.EMPTY)
|
||||
world.setForeground(x, y, BuiltinMetaMaterials.EMPTY_MOD)
|
||||
world.setBackground(x, y, BuiltinMetaMaterials.EMPTY)
|
||||
world.setBackground(x, y, BuiltinMetaMaterials.EMPTY_MOD)
|
||||
|
||||
world.setDungeonID(x, y)
|
||||
world.clearTile(x, y)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@ -145,24 +141,19 @@ abstract class DungeonBrush {
|
||||
val colorVariant: String = "0", // HOLY FUCKING SHIT
|
||||
)
|
||||
|
||||
private val state by lazy {
|
||||
ImmutableTileState(material.orEmptyTile, modifier.orEmptyModifier, color, hueShift, modHueShift)
|
||||
}
|
||||
|
||||
override fun execute(x: Int, y: Int, phase: Phase, world: DungeonWorld) {
|
||||
if (phase !== Phase.PLACE_WALLS)
|
||||
return
|
||||
|
||||
if (isBackground)
|
||||
world.setBackground(x, y, material.orEmptyTile, hueShift, color)
|
||||
else
|
||||
world.setForeground(x, y, material.orEmptyTile, hueShift, color)
|
||||
|
||||
if (!isBackground && material.orEmptyTile.value.collisionKind.isSolidCollision) {
|
||||
world.setLiquid(x, y, AbstractLiquidState.EMPTY)
|
||||
}
|
||||
|
||||
if (modifier.isRealModifier) {
|
||||
if (isBackground)
|
||||
world.setBackground(x, y, modifier.orEmptyModifier, modHueShift)
|
||||
else
|
||||
world.setForeground(x, y, modifier.orEmptyModifier, modHueShift)
|
||||
if (isBackground) {
|
||||
world.setCombined(x, y, background = state, foreground = null, liquid = null, setBackgroundModifier = modifier.isRealModifier)
|
||||
} else {
|
||||
val liquid = if (material.orEmptyTile.value.collisionKind.isSolidCollision) AbstractLiquidState.EMPTY else null
|
||||
world.setCombined(x, y, background = null, foreground = state, liquid = liquid, setForegroundModifier = modifier.isRealModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,10 +220,11 @@ data class DungeonDefinition(
|
||||
dungeonID: Int = 0,
|
||||
terrainSurfaceSpaceExtends: Int = 0,
|
||||
commit: Boolean = true,
|
||||
scope: CoroutineScope = Starbound.GLOBAL_SCOPE
|
||||
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)
|
||||
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize, immediate = immediate)
|
||||
|
||||
val validAnchors = anchorParts.filter { world.template.threatLevel in it.minimumThreatLevel .. it.maximumThreatLevel }
|
||||
|
||||
@ -247,9 +248,21 @@ data class DungeonDefinition(
|
||||
}.asCompletableFuture()
|
||||
}
|
||||
|
||||
fun build(anchor: DungeonPart, world: ServerWorld, random: RandomGenerator, x: Int, y: Int, dungeonID: Int = NO_DUNGEON_ID, markSurfaceAndTerrain: Boolean = false, forcePlacement: Boolean = false, terrainSurfaceSpaceExtends: Int = 0, commit: Boolean = true, scope: CoroutineScope = Starbound.GLOBAL_SCOPE): CompletableFuture<DungeonWorld> {
|
||||
fun build(
|
||||
anchor: DungeonPart,
|
||||
world: ServerWorld,
|
||||
random: RandomGenerator,
|
||||
x: Int, y: Int,
|
||||
dungeonID: Int = NO_DUNGEON_ID,
|
||||
markSurfaceAndTerrain: Boolean = false,
|
||||
forcePlacement: Boolean = false,
|
||||
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)
|
||||
val dungeonWorld = DungeonWorld(world, random, if (markSurfaceAndTerrain) y else null, terrainSurfaceSpaceExtends, expectedSize = expectedSize, immediate = immediate)
|
||||
|
||||
return scope.async {
|
||||
generate0(anchor, dungeonWorld, x, y, forcePlacement, dungeonID)
|
||||
|
@ -27,7 +27,9 @@ import ru.dbotthepony.kstarbound.util.random.random
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.ChunkState
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.MutableTileState
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.TileEntity
|
||||
@ -45,7 +47,20 @@ import kotlin.math.max
|
||||
// Facade world for generating dungeons, so generation can be performed without affecting world state,
|
||||
// and if error occurs, won't require world's rollback, as well allowing dungeon to be generated mostly
|
||||
// off world's thread.
|
||||
class DungeonWorld(val parent: ServerWorld, val random: RandomGenerator, val markSurfaceLevel: Int? = null, val terrainSurfaceSpaceExtends: Int = 0, expectedSize: Int = 0) {
|
||||
class DungeonWorld(
|
||||
val parent: ServerWorld,
|
||||
val random: RandomGenerator,
|
||||
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
|
||||
|
||||
data class Material(
|
||||
@ -81,11 +96,11 @@ class DungeonWorld(val parent: ServerWorld, val random: RandomGenerator, val mar
|
||||
|
||||
val targetChunkState = if (parent.template.worldParameters is FloatingDungeonWorldParameters) ChunkState.FULL else ChunkState.TERRAIN
|
||||
|
||||
private val liquid = HashMap<Vector2i, AbstractLiquidState>(max(8192, expectedSize), 0.5f)
|
||||
private val foregroundMaterial = HashMap<Vector2i, Material>(max(8192, expectedSize), 0.5f)
|
||||
private val foregroundModifier = HashMap<Vector2i, Modifier>(max(8192, expectedSize), 0.5f)
|
||||
private val backgroundMaterial = HashMap<Vector2i, Material>(max(8192, expectedSize), 0.5f)
|
||||
private val backgroundModifier = HashMap<Vector2i, Modifier>(max(8192, expectedSize), 0.5f)
|
||||
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)
|
||||
|
||||
// for entity spaces which should be considered empty if they
|
||||
// are occupied by tile entity
|
||||
@ -117,8 +132,6 @@ class DungeonWorld(val parent: ServerWorld, val random: RandomGenerator, val mar
|
||||
|
||||
private var currentBoundingBox: AABBi? = null
|
||||
|
||||
fun touched(): Set<Vector2i> = Collections.unmodifiableSet(touchedTiles)
|
||||
|
||||
fun touch(x: Int, y: Int) {
|
||||
val wrapped = geometry.wrap(Vector2i(x, y))
|
||||
touchedTiles.add(wrapped)
|
||||
@ -260,6 +273,131 @@ class DungeonWorld(val parent: ServerWorld, val random: RandomGenerator, val mar
|
||||
setBackground(x, y, Modifier(modifier, hueShift))
|
||||
}
|
||||
|
||||
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(
|
||||
x: Int, y: Int,
|
||||
foreground: AbstractTileState?,
|
||||
background: AbstractTileState?,
|
||||
liquid: AbstractLiquidState?,
|
||||
setForegroundMaterial: Boolean,
|
||||
setForegroundModifier: Boolean,
|
||||
setBackgroundMaterial: Boolean,
|
||||
setBackgroundModifier: Boolean,
|
||||
) {
|
||||
val pos = geometry.wrap(Vector2i(x, y))
|
||||
|
||||
if (foreground != null) {
|
||||
if (setForegroundMaterial)
|
||||
this.foregroundMaterial[pos] = Material(foreground.material, foreground.hueShift, foreground.color)
|
||||
|
||||
if (setForegroundModifier)
|
||||
this.foregroundModifier[pos] = Modifier(foreground.modifier, foreground.modifierHueShift)
|
||||
}
|
||||
|
||||
if (background != null) {
|
||||
if (setBackgroundMaterial)
|
||||
this.backgroundMaterial[pos] = Material(background.material, background.hueShift, background.color)
|
||||
|
||||
if (setBackgroundModifier)
|
||||
this.backgroundModifier[pos] = Modifier(background.modifier, background.modifierHueShift)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
dungeonIDs.remove(pos)
|
||||
}
|
||||
|
||||
fun needsForegroundBiomeMod(x: Int, y: Int): Boolean {
|
||||
val pos = geometry.wrap(Vector2i(x, y))
|
||||
val material = foregroundMaterial[pos] ?: return false
|
||||
|
@ -142,6 +142,14 @@ data class AABBi(val mins: Vector2i, val maxs: Vector2i) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rectangle with given [width] * 2 and [height] * 2
|
||||
*/
|
||||
fun withSide(pos: IStruct2i, size: IStruct2i): AABBi {
|
||||
val (width, height) = size
|
||||
return withSide(pos, width, height)
|
||||
}
|
||||
|
||||
@JvmField val ZERO = AABBi(Vector2i.ZERO, Vector2i.ZERO)
|
||||
}
|
||||
}
|
||||
|
@ -565,13 +565,33 @@ class ServerWorld private constructor(
|
||||
|
||||
x = geometry.x.cell(x)
|
||||
|
||||
LOGGER.info("Trying to place dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}...")
|
||||
val tickets = ArrayList<ServerChunk.ITicket>()
|
||||
|
||||
val dungeonWorld = try {
|
||||
dungeon.dungeon.value.generate(this, random(random.nextLong()), x, dungeon.baseHeight, dungeon.blendWithTerrain, dungeon.forcePlacement, dungeonID = currentDungeonID).await()
|
||||
// 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}...")
|
||||
}
|
||||
|
||||
dungeon.dungeon.value.generate(
|
||||
this,
|
||||
random(random.nextLong()),
|
||||
x, dungeon.baseHeight,
|
||||
dungeon.blendWithTerrain,
|
||||
dungeon.forcePlacement,
|
||||
dungeonID = currentDungeonID,
|
||||
immediate = immediate).await()
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while placing dungeon ${dungeon.dungeon.key} at $x, ${dungeon.baseHeight}", err)
|
||||
// continue
|
||||
tickets.forEach { it.cancel() }
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,19 @@ data class MutableTileState(
|
||||
modifierHueShift = 0f
|
||||
}
|
||||
|
||||
fun from(other: AbstractTileState, setMaterial: Boolean = true, setModifer: Boolean = true) {
|
||||
if (setMaterial) {
|
||||
material = other.material
|
||||
modifier = other.modifier
|
||||
color = other.color
|
||||
}
|
||||
|
||||
if (setModifer) {
|
||||
hueShift = other.hueShift
|
||||
modifierHueShift = other.modifierHueShift
|
||||
}
|
||||
}
|
||||
|
||||
fun setHueShift(value: Int): MutableTileState {
|
||||
if (value < 0) {
|
||||
hueShift = 0f
|
||||
|
Loading…
Reference in New Issue
Block a user