Functional cell rootSource mechanics
This commit is contained in:
parent
74bbc58c60
commit
b50f356f7e
src/main/kotlin/ru/dbotthepony/kstarbound
server/world
world
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.server.world
|
|||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.ints.IntArraySet
|
import it.unimi.dsi.fastutil.ints.IntArraySet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.future.asCompletableFuture
|
import kotlinx.coroutines.future.asCompletableFuture
|
||||||
@ -51,6 +52,7 @@ import java.util.concurrent.CopyOnWriteArrayList
|
|||||||
import java.util.concurrent.RejectedExecutionException
|
import java.util.concurrent.RejectedExecutionException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
class ServerWorld private constructor(
|
class ServerWorld private constructor(
|
||||||
val server: StarboundServer,
|
val server: StarboundServer,
|
||||||
@ -167,22 +169,39 @@ class ServerWorld private constructor(
|
|||||||
if (damage.amount <= 0.0)
|
if (damage.amount <= 0.0)
|
||||||
return TileDamageResult.NONE
|
return TileDamageResult.NONE
|
||||||
|
|
||||||
val actualPositions = positions.stream()
|
val actualPositions = ObjectArraySet<Pair<Vector2i, ServerChunk?>>()
|
||||||
.map { geometry.wrap(it) }
|
|
||||||
.distinct()
|
for (pos in positions) {
|
||||||
.map { it to chunkMap[geometry.chunkFromCell(it)] }
|
val wrapped = geometry.wrap(pos)
|
||||||
.toList()
|
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
|
var topMost = TileDamageResult.NONE
|
||||||
|
|
||||||
val damagedEntities = ObjectArraySet<TileEntity>()
|
val damagedEntities = ObjectArraySet<TileEntity>()
|
||||||
|
|
||||||
for ((pos, chunk) in actualPositions) {
|
for ((pos, chunk) in actualPositions) {
|
||||||
var damage = damage
|
var damage = damage
|
||||||
var tileEntityResult = TileDamageResult.NONE
|
var tileEntityResult = TileDamageResult.NONE
|
||||||
|
|
||||||
if (chunk?.getCell(pos - chunk.pos.tile)?.dungeonId in protectedDungeonIDs)
|
val getCell = chunk?.getCell(pos - chunk.pos.tile)
|
||||||
damage = damage.copy(type = TileDamageType.PROTECTED)
|
|
||||||
|
if (getCell != null) {
|
||||||
|
if (getCell.dungeonId in protectedDungeonIDs) {
|
||||||
|
damage = damage.copy(type = TileDamageType.PROTECTED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isBackground) {
|
if (!isBackground) {
|
||||||
for (entity in entitiesAtTile(pos)) {
|
for (entity in entitiesAtTile(pos)) {
|
||||||
|
@ -33,6 +33,6 @@ data class ImmutableCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mutable(): MutableCell {
|
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 {
|
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 {
|
override fun mutable(): MutableCell {
|
||||||
|
@ -654,9 +654,10 @@ class PlantEntity() : TileEntity() {
|
|||||||
if (world.getCell(root).foreground.material.isEmptyTile) {
|
if (world.getCell(root).foreground.material.isEmptyTile) {
|
||||||
if (fallsWhenDead) {
|
if (fallsWhenDead) {
|
||||||
breakAtPosition(tilePosition, position)
|
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.metaBoundingBox = this.calculatedBoundingBox + this.position
|
||||||
this.occupySpaces = this.calculatedOccupySpaces.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
this.occupySpaces = this.calculatedOccupySpaces.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
||||||
this.roots = this.calculatedRoots.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
this.roots = this.calculatedRoots.stream().map { it + tilePosition }.collect(ImmutableSet.toImmutableSet())
|
||||||
markSpacesDirty()
|
markRootsDirty()
|
||||||
updateSpatialPosition()
|
updateSpatialPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,12 +113,14 @@ abstract class TileEntity : AbstractEntity() {
|
|||||||
protected open fun onPositionUpdated() {
|
protected open fun onPositionUpdated() {
|
||||||
updateSpatialPosition()
|
updateSpatialPosition()
|
||||||
markSpacesDirty()
|
markSpacesDirty()
|
||||||
|
markRootsDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onJoinWorld(world: World<*, *>) {
|
override fun onJoinWorld(world: World<*, *>) {
|
||||||
super.onJoinWorld(world)
|
super.onJoinWorld(world)
|
||||||
updateSpatialPosition()
|
updateSpatialPosition()
|
||||||
markSpacesDirty()
|
markSpacesDirty()
|
||||||
|
markRootsDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
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
|
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() {
|
fun updateMaterialSpacesNow() {
|
||||||
needToUpdateSpaces = false
|
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) {
|
override fun tick(delta: Double) {
|
||||||
super.tick(delta)
|
super.tick(delta)
|
||||||
|
|
||||||
if (world.isServer && needToUpdateSpaces) {
|
if (world.isServer && needToUpdateSpaces) {
|
||||||
updateMaterialSpacesNow()
|
updateMaterialSpacesNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (world.isServer && needToUpdateRoots) {
|
||||||
|
updateRootsNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
Loading…
Reference in New Issue
Block a user