Functional cell rootSource mechanics
This commit is contained in:
parent
74bbc58c60
commit
b50f356f7e
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.server.world
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.ints.IntArraySet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.future.asCompletableFuture
|
||||
@ -51,6 +52,7 @@ import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.RejectedExecutionException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Collectors
|
||||
|
||||
class ServerWorld private constructor(
|
||||
val server: StarboundServer,
|
||||
@ -167,22 +169,39 @@ class ServerWorld private constructor(
|
||||
if (damage.amount <= 0.0)
|
||||
return TileDamageResult.NONE
|
||||
|
||||
val actualPositions = positions.stream()
|
||||
.map { geometry.wrap(it) }
|
||||
.distinct()
|
||||
.map { it to chunkMap[geometry.chunkFromCell(it)] }
|
||||
.toList()
|
||||
val actualPositions = ObjectArraySet<Pair<Vector2i, ServerChunk?>>()
|
||||
|
||||
for (pos in positions) {
|
||||
val wrapped = geometry.wrap(pos)
|
||||
val chunk = chunkMap[geometry.chunkFromCell(wrapped)]
|
||||
|
||||
if (chunk != null) {
|
||||
val cell = chunk.getCell(wrapped - chunk.pos.tile)
|
||||
|
||||
if (cell.rootSource != null) {
|
||||
actualPositions.add(geometry.wrap(cell.rootSource!!) to chunk)
|
||||
} else {
|
||||
actualPositions.add(wrapped to chunk)
|
||||
}
|
||||
} else {
|
||||
actualPositions.add(wrapped to chunk)
|
||||
}
|
||||
}
|
||||
|
||||
var topMost = TileDamageResult.NONE
|
||||
|
||||
val damagedEntities = ObjectArraySet<TileEntity>()
|
||||
|
||||
for ((pos, chunk) in actualPositions) {
|
||||
var damage = damage
|
||||
var tileEntityResult = TileDamageResult.NONE
|
||||
|
||||
if (chunk?.getCell(pos - chunk.pos.tile)?.dungeonId in protectedDungeonIDs)
|
||||
damage = damage.copy(type = TileDamageType.PROTECTED)
|
||||
val getCell = chunk?.getCell(pos - chunk.pos.tile)
|
||||
|
||||
if (getCell != null) {
|
||||
if (getCell.dungeonId in protectedDungeonIDs) {
|
||||
damage = damage.copy(type = TileDamageType.PROTECTED)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isBackground) {
|
||||
for (entity in entitiesAtTile(pos)) {
|
||||
|
@ -33,6 +33,6 @@ data class ImmutableCell(
|
||||
}
|
||||
|
||||
override fun mutable(): MutableCell {
|
||||
return MutableCell(foreground.mutable(), background.mutable(), liquid.mutable(), dungeonId, blockBiome, envBiome, biomeTransition)
|
||||
return MutableCell(foreground.mutable(), background.mutable(), liquid.mutable(), dungeonId, blockBiome, envBiome, biomeTransition, rootSource)
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ data class MutableCell(
|
||||
}
|
||||
|
||||
override fun immutable(): ImmutableCell {
|
||||
return POOL.intern(ImmutableCell(foreground.immutable(), background.immutable(), liquid.immutable(), dungeonId, blockBiome, envBiome, biomeTransition))
|
||||
return POOL.intern(ImmutableCell(foreground.immutable(), background.immutable(), liquid.immutable(), dungeonId, blockBiome, envBiome, biomeTransition, rootSource))
|
||||
}
|
||||
|
||||
override fun mutable(): MutableCell {
|
||||
|
@ -654,9 +654,10 @@ class PlantEntity() : TileEntity() {
|
||||
if (world.getCell(root).foreground.material.isEmptyTile) {
|
||||
if (fallsWhenDead) {
|
||||
breakAtPosition(tilePosition, position)
|
||||
} else {
|
||||
remove(RemovalReason.DYING)
|
||||
}
|
||||
|
||||
remove(RemovalReason.DYING)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -672,7 +673,7 @@ class PlantEntity() : TileEntity() {
|
||||
this.metaBoundingBox = this.calculatedBoundingBox + this.position
|
||||
this.occupySpaces = this.calculatedOccupySpaces.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
||||
this.roots = this.calculatedRoots.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
||||
markSpacesDirty()
|
||||
markRootsDirty()
|
||||
updateSpatialPosition()
|
||||
}
|
||||
|
||||
|
@ -113,12 +113,14 @@ abstract class TileEntity : AbstractEntity() {
|
||||
protected open fun onPositionUpdated() {
|
||||
updateSpatialPosition()
|
||||
markSpacesDirty()
|
||||
markRootsDirty()
|
||||
}
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
super.onJoinWorld(world)
|
||||
updateSpatialPosition()
|
||||
markSpacesDirty()
|
||||
markRootsDirty()
|
||||
}
|
||||
|
||||
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
||||
@ -157,6 +159,41 @@ abstract class TileEntity : AbstractEntity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove roots
|
||||
if (!updateRoots(listOf())) {
|
||||
// we were unable to remove all roots...
|
||||
// create a task to try not to leave world in inconsistent state!
|
||||
val currentRoots = currentRoots
|
||||
val tilePosition = tilePosition
|
||||
|
||||
world.eventLoop.scope.launch {
|
||||
world as ServerWorld
|
||||
val tickets = ArrayList<ServerChunk.ITicket>()
|
||||
|
||||
try {
|
||||
currentRoots.forEach { p ->
|
||||
tickets.add(world.permanentChunkTicket(world.geometry.chunkFromCell(p.x, p.y), ChunkState.EMPTY).await() ?: return@forEach)
|
||||
}
|
||||
|
||||
tickets.forEach { it.chunk.await() }
|
||||
|
||||
for (space in currentRoots) {
|
||||
val cell = world.getCell(space).mutable()
|
||||
|
||||
if (cell.rootSource == tilePosition) {
|
||||
cell.rootSource = null
|
||||
}
|
||||
|
||||
if (!world.setCell(space, cell)) {
|
||||
LOGGER.warn("Unable to clear root source at ${space}, world left in inconsistent state.")
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tickets.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +259,62 @@ abstract class TileEntity : AbstractEntity() {
|
||||
return clear
|
||||
}
|
||||
|
||||
protected fun updateRoots(desired: Collection<Vector2i>): Boolean {
|
||||
val toRemove = ArrayList<Vector2i>()
|
||||
val toPlace = ArrayList<Vector2i>()
|
||||
|
||||
for (current in currentRoots) {
|
||||
if (current !in desired) {
|
||||
toRemove.add(current)
|
||||
}
|
||||
}
|
||||
|
||||
for (pos in desired) {
|
||||
if (pos !in currentRoots) {
|
||||
toPlace.add(pos)
|
||||
}
|
||||
}
|
||||
|
||||
var clear = true
|
||||
|
||||
for (pos in toRemove) {
|
||||
val cell = world.getCell(pos).mutable()
|
||||
|
||||
if (cell.rootSource == tilePosition) {
|
||||
cell.rootSource = null
|
||||
|
||||
if (world.setCell(pos, cell)) {
|
||||
currentRoots.remove(pos)
|
||||
} else {
|
||||
clear = false
|
||||
}
|
||||
} else {
|
||||
currentRoots.remove(pos)
|
||||
}
|
||||
}
|
||||
|
||||
for (pos in toPlace) {
|
||||
val cell = world.getCell(pos).mutable()
|
||||
|
||||
if (cell.rootSource == null) {
|
||||
cell.rootSource = tilePosition
|
||||
|
||||
if (world.setCell(pos, cell)) {
|
||||
currentRoots.add(pos)
|
||||
} else {
|
||||
clear = false
|
||||
}
|
||||
} else {
|
||||
// bugger, someone rooted onto our tile!
|
||||
// will try again...
|
||||
// TODO: however, shouldn't we just ignore this and move on?
|
||||
clear = false
|
||||
}
|
||||
}
|
||||
|
||||
return clear
|
||||
}
|
||||
|
||||
fun updateMaterialSpacesNow() {
|
||||
needToUpdateSpaces = false
|
||||
|
||||
@ -232,12 +325,26 @@ abstract class TileEntity : AbstractEntity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRootsNow() {
|
||||
needToUpdateRoots = false
|
||||
|
||||
// only server can update entity tiles
|
||||
// even if this tile entity is owned by client
|
||||
if (world.isServer) {
|
||||
needToUpdateRoots = !updateRoots(roots)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(delta: Double) {
|
||||
super.tick(delta)
|
||||
|
||||
if (world.isServer && needToUpdateSpaces) {
|
||||
updateMaterialSpacesNow()
|
||||
}
|
||||
|
||||
if (world.isServer && needToUpdateRoots) {
|
||||
updateRootsNow()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
Loading…
Reference in New Issue
Block a user