Get rid of chunk tuples as we know them

because they are useless in this form
This commit is contained in:
DBotThePony 2022-07-29 13:59:31 +07:00
parent c1d19d951d
commit af97c80cdd
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 177 additions and 165 deletions

View File

@ -101,30 +101,40 @@ fun main() {
val ent = PlayerEntity(client.world!!)
Starbound.onInitialize {
var find = 0L
var set = 0L
var parse = 0L
for (chunkX in 0 .. 61) {
for (chunkY in 0 .. 61) {
var t = System.currentTimeMillis()
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
find += System.currentTimeMillis() - t
if (data != null) {
val chunk = client.world!!.computeIfAbsent(ChunkPos(chunkX, chunkY))
val inflater = Inflater()
inflater.setInput(data)
t = System.currentTimeMillis()
val output = ByteArray(64_000)
val actual = inflater.inflate(output)
val reader = DataInputStream(ByteArrayInputStream(output))
parse += System.currentTimeMillis() - t
reader.skipBytes(3)
var hitTile = false
t = System.currentTimeMillis()
for (y in 0 .. 31) {
for (x in 0 .. 31) {
val materialID = reader.readShort()
val getMat = Starbound.tilesAccessID[materialID.toInt()]
if (getMat != null) {
chunk.chunk.foreground[x, y] = getMat
chunk.foreground[x, y] = getMat
hitTile = true
}
@ -134,7 +144,7 @@ fun main() {
val getMat2 = Starbound.tilesAccessID[materialID2.toInt()]
if (getMat2 != null) {
chunk.chunk.background[x, y] = getMat2
chunk.background[x, y] = getMat2
hitTile = true
}
@ -142,6 +152,8 @@ fun main() {
}
}
set += System.currentTimeMillis() - t
if (hitTile) {
//println(chunk.chunk.posVector2d)
// ent.position = chunk.chunk.posVector2d + Vector2d(16.0, 34.0)
@ -149,6 +161,8 @@ fun main() {
}
}
}
println("$find $set $parse")
}
//ent.position += Vector2d(y = 14.0, x = -10.0)

View File

@ -119,9 +119,9 @@ class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWo
val determineRenderers = ArrayList<ClientChunk>()
for (chunk in collectInternal(size.encasingChunkPosAABB())) {
determineRenderers.add(chunk.chunk)
chunk.chunk.bake()
for (chunk in collect(size.encasingChunkPosAABB())) {
determineRenderers.add(chunk)
chunk.bake()
}
renderLayeredList(client.gl.matrixStack, determineRenderers)

View File

@ -163,26 +163,50 @@ const val CHUNK_SIZE_FF = CHUNK_SIZE - 1
const val CHUNK_SIZEf = CHUNK_SIZE.toFloat()
const val CHUNK_SIZEd = CHUNK_SIZE.toDouble()
/**
* Сетка чанков идёт как и сетка тайлов.
*
* * Вправо у нас положительный X
* * Влево у нас отрицательный X
* * Вверх у нас положительный Y
* * Вниз у нас отрицательный Y
*/
class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
constructor(pos: IStruct2i) : this(pos.component1(), pos.component2())
val firstBlock get() = Vector2i(x shl CHUNK_SHIFT, y shl CHUNK_SHIFT)
val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SHIFT) - 1, ((y + 1) shl CHUNK_SHIFT) - 1)
fun up(): ChunkPos {
val top: ChunkPos get() {
return ChunkPos(x, y + 1)
}
fun down(): ChunkPos {
val bottom: ChunkPos get() {
return ChunkPos(x, y - 1)
}
fun left(): ChunkPos {
return ChunkPos(x + 1, y)
val left: ChunkPos get() {
return ChunkPos(x - 1, y)
}
fun right(): ChunkPos {
return ChunkPos(x - 1, y)
val topLeft: ChunkPos get() {
return ChunkPos(x - 1, y + 1)
}
val topRight: ChunkPos get() {
return ChunkPos(x + 1, y + 1)
}
val bottomLeft: ChunkPos get() {
return ChunkPos(x - 1, y - 1)
}
val bottomRight: ChunkPos get() {
return ChunkPos(x + 1, y - 1)
}
val right: ChunkPos get() {
return ChunkPos(x + 1, y)
}
override fun equals(other: Any?): Boolean {
@ -258,7 +282,7 @@ class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
* и при этом координаты проверяются относительно чанка и могут спокойно выйти за его пределы,
* с желанием получить тайл из соседнего чанка
*/
open class TileChunkView(
open class TileView(
open val center: ITileChunk,
open val right: ITileChunk?,
@ -311,7 +335,7 @@ open class TileChunkView(
get() = center.pos
}
class MutableTileChunkView(
class MutableTileView(
override val center: IMutableTileChunk,
override val right: IMutableTileChunk?,
@ -323,7 +347,7 @@ class MutableTileChunkView(
override val bottom: IMutableTileChunk?,
override val bottomLeft: IMutableTileChunk?,
override val bottomRight: IMutableTileChunk?,
) : TileChunkView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? {
if (x in 0 .. CHUNK_SIZE_FF) {
if (y in 0 .. CHUNK_SIZE_FF) {
@ -765,6 +789,15 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
changeset++
}
val left get() = pos.left
val right get() = pos.right
val top get() = pos.top
val bottom get() = pos.bottom
val topLeft get() = pos.topLeft
val topRight get() = pos.topRight
val bottomLeft get() = pos.bottomLeft
val bottomRight get() = pos.bottomRight
val aabb = aabbBase + Vector2d(pos.x * CHUNK_SIZE.toDouble(), pos.y * CHUNK_SIZE.toDouble())
var isPhysicsDirty = false
@ -962,6 +995,15 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
override val pos: ChunkPos
get() = this@Chunk.pos
inline val left get() = pos.left
inline val right get() = pos.right
inline val top get() = pos.top
inline val bottom get() = pos.bottom
inline val topLeft get() = pos.topLeft
inline val topRight get() = pos.topRight
inline val bottomLeft get() = pos.bottomLeft
inline val bottomRight get() = pos.bottomRight
/**
* Хранит тайлы как x + y * CHUNK_SIZE
*/

View File

@ -14,7 +14,6 @@ import ru.dbotthepony.kstarbound.defs.TileDefinition
import ru.dbotthepony.kstarbound.math.*
import ru.dbotthepony.kstarbound.world.entities.CollisionResolution
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.MovementController
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.util2d.AABBi
@ -32,69 +31,52 @@ interface IWorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType :
val left: IWorldChunkTuple<WorldType, ChunkType>?
val right: IWorldChunkTuple<WorldType, ChunkType>?
val bottom: IWorldChunkTuple<WorldType, ChunkType>?
val topLeft: IWorldChunkTuple<WorldType, ChunkType>?
val topRight: IWorldChunkTuple<WorldType, ChunkType>?
val bottomLeft: IWorldChunkTuple<WorldType, ChunkType>?
val bottomRight: IWorldChunkTuple<WorldType, ChunkType>?
}
interface IMutableWorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType : Chunk<WorldType, ChunkType>> : IWorldChunkTuple<WorldType, ChunkType> {
override var top: IMutableWorldChunkTuple<WorldType, ChunkType>?
override var left: IMutableWorldChunkTuple<WorldType, ChunkType>?
override var right: IMutableWorldChunkTuple<WorldType, ChunkType>?
override var bottom: IMutableWorldChunkTuple<WorldType, ChunkType>?
}
class WorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType : Chunk<WorldType, ChunkType>>(
class ProxiedWorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType : Chunk<WorldType, ChunkType>>(
private val parent: IWorldChunkTuple<WorldType, ChunkType>
) : IWorldChunkTuple<WorldType, ChunkType> {
override val world get() = parent.world
override val chunk get() = parent.chunk
override val top: IWorldChunkTuple<WorldType, ChunkType>? get() {
val getValue = parent.top
if (getValue != null) {
return WorldChunkTuple(getValue)
}
return null
}
override val left: IWorldChunkTuple<WorldType, ChunkType>? get() {
val getValue = parent.left
if (getValue != null) {
return WorldChunkTuple(getValue)
}
return null
}
override val right: IWorldChunkTuple<WorldType, ChunkType>? get() {
val getValue = parent.right
if (getValue != null) {
return WorldChunkTuple(getValue)
}
return null
}
override val bottom: IWorldChunkTuple<WorldType, ChunkType>? get() {
val getValue = parent.bottom
if (getValue != null) {
return WorldChunkTuple(getValue)
}
return null
}
override val top: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.top?.let(::ProxiedWorldChunkTuple)
override val left: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.left?.let(::ProxiedWorldChunkTuple)
override val right: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.right?.let(::ProxiedWorldChunkTuple)
override val bottom: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.bottom?.let(::ProxiedWorldChunkTuple)
override val topLeft: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.topLeft?.let(::ProxiedWorldChunkTuple)
override val topRight: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.topRight?.let(::ProxiedWorldChunkTuple)
override val bottomLeft: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.bottomLeft?.let(::ProxiedWorldChunkTuple)
override val bottomRight: IWorldChunkTuple<WorldType, ChunkType>? get() = parent.bottomRight?.let(::ProxiedWorldChunkTuple)
}
open class MutableWorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType : Chunk<WorldType, ChunkType>>(
class InstantWorldChunkTuple<WorldType : World<WorldType, ChunkType>, ChunkType : Chunk<WorldType, ChunkType>>(
override val world: WorldType,
override val chunk: ChunkType,
override var top: IMutableWorldChunkTuple<WorldType, ChunkType>?,
override var left: IMutableWorldChunkTuple<WorldType, ChunkType>?,
override var right: IMutableWorldChunkTuple<WorldType, ChunkType>?,
override var bottom: IMutableWorldChunkTuple<WorldType, ChunkType>?,
) : IMutableWorldChunkTuple<WorldType, ChunkType>
override val chunk: ChunkType
) : IWorldChunkTuple<WorldType, ChunkType> {
private val _top = world[chunk.top]
private val _left = world[chunk.left]
private val _right = world[chunk.right]
private val _bottom = world[chunk.bottom]
private val _topLeft = world[chunk.topLeft]
private val _topRight = world[chunk.topRight]
private val _bottomLeft = world[chunk.bottomLeft]
private val _bottomRight = world[chunk.bottomRight]
override val top: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _top?.let { InstantWorldChunkTuple(world, it) } }
override val left: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _left?.let { InstantWorldChunkTuple(world, it) } }
override val right: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _right?.let { InstantWorldChunkTuple(world, it) } }
override val bottom: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _bottom?.let { InstantWorldChunkTuple(world, it) } }
override val topLeft: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _topLeft?.let { InstantWorldChunkTuple(world, it) } }
override val topRight: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _topRight?.let { InstantWorldChunkTuple(world, it) } }
override val bottomLeft: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _bottomLeft?.let { InstantWorldChunkTuple(world, it) } }
override val bottomRight: IWorldChunkTuple<WorldType, ChunkType>? by lazy { _bottomRight?.let { InstantWorldChunkTuple(world, it) } }
}
const val EARTH_FREEFALL_ACCELERATION = 9.8312 / METRES_IN_STARBOUND_UNIT
@ -142,7 +124,7 @@ class Timer(val period: Double, val executionTimes: Int, val func: (Timer) -> Un
}
abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType>>(val seed: Long = 0L) {
protected val chunkMap = Object2ObjectAVLTreeMap<ChunkPos, IMutableWorldChunkTuple<This, ChunkType>> cmp@{ a, b ->
protected val chunkMap = Object2ObjectAVLTreeMap<ChunkPos, ChunkType> cmp@{ a, b ->
return@cmp a.compareTo(b)
}
@ -151,7 +133,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
*/
val dirtyPhysicsChunks = HashSet<ChunkType>()
protected var lastAccessedChunk: IMutableWorldChunkTuple<This, ChunkType>? = null
protected var lastAccessedChunk: ChunkType? = null
val physics = B2World(Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION))
@ -317,51 +299,37 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
pos: ChunkPos,
): ChunkType
protected fun getChunkInternal(pos: ChunkPos): IMutableWorldChunkTuple<This, ChunkType>? {
if (lastAccessedChunk?.chunk?.pos == pos) {
/**
* Возвращает чанк на указанной позиции если он существует
*/
open operator fun get(pos: ChunkPos): ChunkType? {
val lastAccessedChunk = lastAccessedChunk
if (lastAccessedChunk?.pos == pos) {
return lastAccessedChunk
}
return chunkMap[pos]
}
open fun getChunk(pos: ChunkPos): IWorldChunkTuple<This, ChunkType>? {
val getTuple = getChunkInternal(pos)
if (getTuple != null)
return WorldChunkTuple(getTuple)
return null
open fun getInstantTuple(pos: ChunkPos): IWorldChunkTuple<This, ChunkType>? {
return this[pos]?.let { InstantWorldChunkTuple(this as This, it) }
}
protected open fun computeIfAbsentInternal(pos: ChunkPos): IWorldChunkTuple<This, ChunkType> {
if (lastAccessedChunk?.chunk?.pos == pos) {
return lastAccessedChunk!!
/**
* Возвращает чанк на заданной позиции, создаёт его если он не существует
*/
open fun computeIfAbsent(pos: ChunkPos): ChunkType {
val _lastAccessedChunk = lastAccessedChunk
if (_lastAccessedChunk?.pos == pos) {
return _lastAccessedChunk
}
return chunkMap.computeIfAbsent(pos, Object2ObjectFunction {
val chunk = chunkFactory(pos)
val top = getChunkInternal(pos.up())
val left = getChunkInternal(pos.left())
val right = getChunkInternal(pos.right())
val bottom = getChunkInternal(pos.down())
val tuple = MutableWorldChunkTuple(
world = this as This,
chunk = chunk,
top = top,
left = left,
right = right,
bottom = bottom,
)
top?.bottom = tuple
left?.right = tuple
right?.left = tuple
bottom?.top = tuple
lastAccessedChunk = tuple
lastAccessedChunk = chunk
val orphanedInThisChunk = ArrayList<Entity>()
@ -377,88 +345,76 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
ent.chunk = chunk
}
return@Object2ObjectFunction tuple
return@Object2ObjectFunction chunk
})
}
open fun computeIfAbsent(pos: ChunkPos): IWorldChunkTuple<This, ChunkType> {
return WorldChunkTuple(computeIfAbsentInternal(pos))
}
open fun getForegroundView(pos: ChunkPos): TileView? {
val get = get(pos) ?: return null
val tuple = InstantWorldChunkTuple(this as This, get)
open fun getForegroundView(pos: ChunkPos): TileChunkView? {
val get = getChunkInternal(pos) ?: return null
return TileChunkView(
center = get.chunk.foreground,
left = get.left?.chunk?.foreground,
top = get.top?.chunk?.foreground,
topLeft = getChunkInternal(pos.up().left())?.chunk?.foreground,
topRight = getChunkInternal(pos.up().right())?.chunk?.foreground,
right = get.right?.chunk?.foreground,
bottom = get.bottom?.chunk?.foreground,
bottomLeft = getChunkInternal(pos.down().left())?.chunk?.foreground,
bottomRight = getChunkInternal(pos.down().right())?.chunk?.foreground,
return TileView(
center = tuple.chunk.foreground,
left = tuple.left?.chunk?.foreground,
top = tuple.top?.chunk?.foreground,
topLeft = tuple.topLeft?.chunk?.foreground,
topRight = tuple.topRight?.chunk?.foreground,
right = tuple.right?.chunk?.foreground,
bottom = tuple.bottom?.chunk?.foreground,
bottomLeft = tuple.bottomLeft?.chunk?.foreground,
bottomRight = tuple.bottomRight?.chunk?.foreground,
)
}
open fun getBackgroundView(pos: ChunkPos): TileChunkView? {
val get = getChunkInternal(pos) ?: return null
open fun getBackgroundView(pos: ChunkPos): TileView? {
val get = get(pos) ?: return null
val tuple = InstantWorldChunkTuple(this as This, get)
return TileChunkView(
center = get.chunk.background,
left = get.left?.chunk?.background,
top = get.top?.chunk?.background,
topLeft = getChunkInternal(pos.up().left())?.chunk?.background,
topRight = getChunkInternal(pos.up().right())?.chunk?.background,
right = get.right?.chunk?.background,
bottom = get.bottom?.chunk?.background,
bottomLeft = getChunkInternal(pos.down().left())?.chunk?.background,
bottomRight = getChunkInternal(pos.down().right())?.chunk?.background,
return TileView(
center = tuple.chunk.background,
left = tuple.left?.chunk?.background,
top = tuple.top?.chunk?.background,
topLeft = tuple.topLeft?.chunk?.background,
topRight = tuple.topRight?.chunk?.background,
right = tuple.right?.chunk?.background,
bottom = tuple.bottom?.chunk?.background,
bottomLeft = tuple.bottomLeft?.chunk?.background,
bottomRight = tuple.bottomRight?.chunk?.background,
)
}
fun getTile(pos: Vector2i): ChunkTile? {
return getChunkInternal(ChunkPos.fromTilePosition(pos))?.chunk?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
}
fun setTile(pos: Vector2i, tile: TileDefinition?): IWorldChunkTuple<This, ChunkType> {
val chunk = computeIfAbsentInternal(ChunkPos.fromTilePosition(pos))
chunk.chunk.foreground[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
fun setTile(pos: Vector2i, tile: TileDefinition?): ChunkType {
val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos))
chunk.foreground[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
return chunk
}
fun getBackgroundTile(pos: Vector2i): ChunkTile? {
return getChunkInternal(ChunkPos.fromTilePosition(pos))?.chunk?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
}
fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): IWorldChunkTuple<This, ChunkType> {
val chunk = computeIfAbsentInternal(ChunkPos.fromTilePosition(pos))
chunk.chunk.background[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): ChunkType {
val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos))
chunk.background[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
return chunk
}
protected open fun collectInternal(boundingBox: AABBi): List<IMutableWorldChunkTuple<This, ChunkType>> {
val output = ArrayList<IMutableWorldChunkTuple<This, ChunkType>>()
for (pos in boundingBox.chunkPositions) {
val chunk = getChunkInternal(pos)
if (chunk != null) {
output.add(chunk)
}
}
return output
}
/**
* Возвращает все чанки, которые пересекаются с заданным [boundingBox]
*/
open fun collect(boundingBox: AABBi): List<IWorldChunkTuple<This, ChunkType>> {
val output = ArrayList<IWorldChunkTuple<This, ChunkType>>()
open fun collect(boundingBox: AABBi): List<ChunkType> {
val output = ArrayList<ChunkType>()
for (chunk in collectInternal(boundingBox)) {
output.add(WorldChunkTuple(chunk))
for (pos in boundingBox.chunkPositions) {
val chunk = get(pos)
if (chunk != null) {
output.add(chunk)
}
}
return output
@ -475,11 +431,11 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
): WorldSweepResult {
var deltaMovement = _deltaMovement
var potentialAABB = worldaabb + deltaMovement
var combined = worldaabb.combine(potentialAABB)
val potentialAABB = worldaabb + deltaMovement
val combined = worldaabb.combine(potentialAABB)
val collected = collectInternal((combined).encasingChunkPosAABB())
.map { it.chunk.foreground.collisionLayers() }
val collected = collect((combined).encasingChunkPosAABB())
.map { it.foreground.collisionLayers() }
.flatten()
.sortedWith { o1, o2 ->
val a = o1.distance(worldaabb)
@ -630,8 +586,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
* Возвращает, застрянет ли сущность будет с коллизией [worldaabb]
*/
fun isSpaceEmptyFromTiles(worldaabb: AABB): Boolean {
val collected = collectInternal(worldaabb.encasingChunkPosAABB())
.map { it.chunk.foreground.collisionLayers() }
val collected = collect(worldaabb.encasingChunkPosAABB())
.map { it.foreground.collisionLayers() }
.flatten()
.sortedWith { o1, o2 ->
val a = o1.distance(worldaabb)

View File

@ -130,7 +130,7 @@ abstract class Entity(override val world: World<*, *>) : IEntity {
val newChunkPos = ChunkPos.fromTilePosition(value)
if (oldChunkPos != newChunkPos) {
chunk = world.getChunk(newChunkPos)?.chunk
chunk = world[newChunkPos]
}
}
}
@ -155,7 +155,7 @@ abstract class Entity(override val world: World<*, *>) : IEntity {
isSpawned = true
world.entities.add(this)
chunk = world.getChunk(ChunkPos.fromTilePosition(position))?.chunk
chunk = world[ChunkPos.fromTilePosition(position)]
if (chunk == null) {
world.orphanedEntities.add(this)