From e1651806efafdaa5edd8df87534648e09f491435 Mon Sep 17 00:00:00 2001 From: Moritz Ruth Date: Sat, 9 Jan 2021 16:20:05 +0100 Subject: [PATCH] Replace DataStorage with a better concept --- .../mdsp/generator/EntitiesGenerator.kt | 14 +---- .../kotlin/space/uranos/data/DataStorage.kt | 61 ------------------ .../data/DataStorageCombinableAction.kt | 12 ---- .../space/uranos/data/DataStorageKey.kt | 25 -------- .../kotlin/space/uranos/entity/CowEntity.kt | 18 ++---- .../main/kotlin/space/uranos/entity/Entity.kt | 20 +++++- .../kotlin/space/uranos/entity/ItemEntity.kt | 4 -- .../space/uranos/entity/LivingEntity.kt | 22 ++----- .../space/uranos/entity/PaintingEntity.kt | 4 -- .../space/uranos/entity/PlayerEntity.kt | 11 +--- .../util/TickSynchronizationContainer.kt | 63 +++++++++++++++++++ .../uranos/net/MinecraftProtocolDataTypes.kt | 1 - .../net/packet/play/JoinGamePacketCodec.kt | 2 +- .../play/SpawnLivingEntityPacketCodec.kt | 3 +- .../play/SpawnObjectEntityPacketCodec.kt | 3 +- .../packet/play/SpawnPaintingPacketCodec.kt | 3 +- .../uranos/net/packet/play/TagsPacketCodec.kt | 2 - .../main/kotlin/space/uranos/UranosServer.kt | 11 +++- .../kotlin/space/uranos/net/UranosSession.kt | 4 -- .../space/uranos/player/UranosPlayer.kt | 37 ++++------- 20 files changed, 118 insertions(+), 202 deletions(-) delete mode 100644 uranos-api/src/main/kotlin/space/uranos/data/DataStorage.kt delete mode 100644 uranos-api/src/main/kotlin/space/uranos/data/DataStorageCombinableAction.kt delete mode 100644 uranos-api/src/main/kotlin/space/uranos/data/DataStorageKey.kt create mode 100644 uranos-api/src/main/kotlin/space/uranos/util/TickSynchronizationContainer.kt diff --git a/buildSrc/src/main/kotlin/space/uranos/mdsp/generator/EntitiesGenerator.kt b/buildSrc/src/main/kotlin/space/uranos/mdsp/generator/EntitiesGenerator.kt index 71cb8dc..58816a4 100644 --- a/buildSrc/src/main/kotlin/space/uranos/mdsp/generator/EntitiesGenerator.kt +++ b/buildSrc/src/main/kotlin/space/uranos/mdsp/generator/EntitiesGenerator.kt @@ -70,8 +70,8 @@ class EntitiesGenerator( private fun generateEntityStubs(types: List) { for (entity in types) { - val name = - CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, entity.get("name").toString())!! + "Entity" + val name = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, entity.get("name").toString())!! + + "Entity" val filePathRelativeToSourceRoot = "./${ENTITY_PACKAGE.replace(".", "/")}/$name.kt" if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue @@ -84,16 +84,6 @@ class EntitiesGenerator( .initializer("Type") .build() ) - .addProperty( - PropertySpec.builder( - "dataStorage", - ClassName("$BASE_PACKAGE.data", "DataStorage").parameterizedBy(ClassName(ENTITY_PACKAGE, name)), - KModifier.OVERRIDE - ) - .addAnnotation(AnnotationSpec.builder(Suppress::class).addMember("\"LeakingThis\"").build()) - .initializer("%T(this)", ClassName("$BASE_PACKAGE.data", "DataStorage")) - .build() - ) .addType( TypeSpec.companionObjectBuilder("Type") .superclass(ClassName(ENTITY_PACKAGE, name + "Type")) diff --git a/uranos-api/src/main/kotlin/space/uranos/data/DataStorage.kt b/uranos-api/src/main/kotlin/space/uranos/data/DataStorage.kt deleted file mode 100644 index 577938e..0000000 --- a/uranos-api/src/main/kotlin/space/uranos/data/DataStorage.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020-2021 Moritz Ruth and Uranos contributors - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file - */ - -package space.uranos.data - -import java.lang.ref.WeakReference - -class DataStorage(val context: ContextT) { - private val map = HashMap, Entry<*>>() - - inner class Entry(private val key: DataStorageKey, value: V) { - private var oldValueRef = WeakReference(value) - private var changed = true - - var value: V = value - set(value) { - val oldValue = oldValueRef.get() - if (oldValue == value) return - field = value - changed = true - } - - suspend fun tick(): DataStorageCombinableAction? { - var action = key.tick(context, value, changed) - - if (changed) action = key.tickIfChanged(context, value) - - oldValueRef = WeakReference(value) - changed = false - - return action - } - } - - fun set(key: DataStorageKey, value: V) { - @Suppress("UNCHECKED_CAST") - val entry = map[key] as Entry? - - if (entry == null) map[key] = Entry(key, value) - else entry.value = value - } - - @Suppress("UNCHECKED_CAST") - fun get(key: DataStorageKey) = (map[key] as Entry?)!!.value - - suspend fun tick() { - val actions = map.values.mapNotNull { it.tick() } - - actions.groupBy { it.key }.forEach { (key, values) -> - @Suppress("UNCHECKED_CAST") - key as DataStorageCombinableActionKey - - key.tick(context, values) - } - } - - @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") - inline fun cast() = this as DataStorage -} diff --git a/uranos-api/src/main/kotlin/space/uranos/data/DataStorageCombinableAction.kt b/uranos-api/src/main/kotlin/space/uranos/data/DataStorageCombinableAction.kt deleted file mode 100644 index cbc7c64..0000000 --- a/uranos-api/src/main/kotlin/space/uranos/data/DataStorageCombinableAction.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2020-2021 Moritz Ruth and Uranos contributors - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file - */ - -package space.uranos.data - -data class DataStorageCombinableAction(val key: DataStorageCombinableActionKey, val value: V) - -abstract class DataStorageCombinableActionKey { - open suspend fun tick(context: ContextT, values: List) {} -} diff --git a/uranos-api/src/main/kotlin/space/uranos/data/DataStorageKey.kt b/uranos-api/src/main/kotlin/space/uranos/data/DataStorageKey.kt deleted file mode 100644 index 4098d40..0000000 --- a/uranos-api/src/main/kotlin/space/uranos/data/DataStorageKey.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020-2021 Moritz Ruth and Uranos contributors - * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file - */ - -package space.uranos.data - -abstract class DataStorageKey(val name: String) { - open suspend fun tick(context: ContextT, value: V, changed: Boolean): DataStorageCombinableAction? = - null - - open suspend fun tickIfChanged(context: ContextT, value: V): DataStorageCombinableAction? = null - - override fun toString(): String = "DataStorageKey:$name" -} - -fun createDataStorageKey( - name: String, - tick: suspend (context: ContextT, value: V, changed: Boolean) -> DataStorageCombinableAction? = { _, _, _ -> null }, - tickIfChanged: suspend (context: ContextT, value: V) -> DataStorageCombinableAction? = { _, _ -> null } -) = - object : DataStorageKey(name) { - override suspend fun tick(context: ContextT, value: V, changed: Boolean) = tick(context, value, changed) - override suspend fun tickIfChanged(context: ContextT, value: V) = tickIfChanged(context, value) - } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/CowEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/CowEntity.kt index 09cce2a..62a089e 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/CowEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/CowEntity.kt @@ -3,24 +3,14 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file */ -@file:Suppress("LeakingThis") - package space.uranos.entity import space.uranos.Position import space.uranos.Vector -import space.uranos.data.DataStorage -open class CowEntity(position: Position, override var headPitch: Float) : LivingEntity() { - final override val type: EntityType = Type - override var velocity: Vector = Vector.ZERO +open class CowEntity(position: Position, override var headPitch: Float) : LivingEntity(position) { + final override val type: EntityType = Type + override var velocity: Vector = Vector.ZERO - @Suppress("LeakingThis") - override val dataStorage: DataStorage = DataStorage(this) - - init { - this.position = position - } - - companion object Type : CowEntityType() + companion object Type : CowEntityType() } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/Entity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/Entity.kt index 533a28a..2f5ee7e 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/Entity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/Entity.kt @@ -8,11 +8,25 @@ package space.uranos.entity import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import space.uranos.Uranos -import space.uranos.data.DataStorage +import space.uranos.util.TickSynchronizationContainer import space.uranos.world.World import java.util.* abstract class Entity internal constructor() { + protected val container = TickSynchronizationContainer() + + @Deprecated( + "This function should only be called by the server.", + ReplaceWith(""), + DeprecationLevel.ERROR + ) + suspend fun tick() { + onTick() + container.tick() + } + + open fun onTick() {} + /** * The UUID of this entity. * @@ -24,7 +38,6 @@ abstract class Entity internal constructor() { open val uuid: UUID = UUID.randomUUID() abstract val type: EntityType - abstract val dataStorage: DataStorage private val worldMutex = Mutex() var world: World? = null; protected set @@ -51,5 +64,6 @@ abstract class Entity internal constructor() { * An integer unique to this entity which will not be persisted, for example when the entity is serialized. */ @Suppress("LeakingThis") - val uid: Int = Uranos.registerEntity(this) + @Deprecated("This is an internal value that you usually should not use.", ReplaceWith("uuid")) + val numericID: Int = Uranos.registerEntity(this) } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/ItemEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/ItemEntity.kt index 3be0df8..e8d5a50 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/ItemEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/ItemEntity.kt @@ -6,13 +6,9 @@ package space.uranos.entity import space.uranos.Position -import space.uranos.data.DataStorage open class ItemEntity(override var position: Position) : ObjectEntity() { final override val type: EntityType = Type - @Suppress("LeakingThis") - public override val dataStorage: DataStorage = DataStorage(this) - companion object Type : ItemEntityType() } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt index 70caa92..12d657c 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt @@ -3,28 +3,16 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file */ -@file:Suppress("LeakingThis") - package space.uranos.entity import space.uranos.Position import space.uranos.Vector -import space.uranos.data.DataStorage -import space.uranos.data.createDataStorageKey -abstract class LivingEntity : Entity(), Mobile { - abstract var headPitch: Float // TODO: This should probably be headYaw, but wiki.vg says headPitch - abstract override var velocity: Vector - abstract override val dataStorage: DataStorage +abstract class LivingEntity(position: Position) : Entity(), Mobile { + abstract var headPitch: Float // TODO: This should probably be headYaw, but wiki.vg says headPitch. And it is only used in the SpawnLivingEntity packet + abstract override var velocity: Vector // TODO: Move the entity every tick - override var position: Position - get() = dataStorage.cast().get(DataStorageKeys.position) - set(value) = dataStorage.cast().set(DataStorageKeys.position, value) - - object DataStorageKeys { - val position = createDataStorageKey("position") { entity: LivingEntity, value: Position -> - // TODO: Send the position to players - null - } + override var position: Position by container.ifChanged(position) { value -> + // TODO: Broadcast to players } } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/PaintingEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/PaintingEntity.kt index 289fb24..73afd17 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/PaintingEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/PaintingEntity.kt @@ -7,7 +7,6 @@ package space.uranos.entity import space.uranos.CardinalDirection import space.uranos.PaintingMotive -import space.uranos.data.DataStorage import space.uranos.world.VoxelLocation class PaintingEntity( @@ -17,8 +16,5 @@ class PaintingEntity( ) : Entity() { override val type: EntityType = Type - @Suppress("LeakingThis") - override val dataStorage: DataStorage = DataStorage(this) - companion object Type : PaintingEntityType() } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt index a043a71..a8b040c 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt @@ -3,13 +3,10 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file */ -@file:Suppress("LeakingThis") - package space.uranos.entity import space.uranos.Position import space.uranos.Vector -import space.uranos.data.DataStorage import space.uranos.player.Player import space.uranos.world.World @@ -22,16 +19,10 @@ open class PlayerEntity( */ open val player: Player? = null, override var headPitch: Float = 0f -) : LivingEntity() { +) : LivingEntity(position) { final override val type: EntityType = Type override var velocity: Vector = Vector.ZERO - override val dataStorage: DataStorage = DataStorage(this) - - init { - this.position = position - } - /** * Because [world] is never `null` for player entities, you can use this property instead of writing `world!!`. */ diff --git a/uranos-api/src/main/kotlin/space/uranos/util/TickSynchronizationContainer.kt b/uranos-api/src/main/kotlin/space/uranos/util/TickSynchronizationContainer.kt new file mode 100644 index 0000000..9a18bdc --- /dev/null +++ b/uranos-api/src/main/kotlin/space/uranos/util/TickSynchronizationContainer.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2020-2021 Moritz Ruth and Uranos contributors + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file + */ + +package space.uranos.util + +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import java.lang.ref.WeakReference +import kotlin.reflect.KProperty + +// If it is too resource-intensive to create a new object for every property, one instance could be used per TickSynchronizationContainer +class TickSynchronizationContainer { + private val delegates = mutableSetOf>() + + suspend fun tick() { + coroutineScope { + delegates.forEach { launch { it.tick() } } + } + } + + fun ifChanged( + initialValue: T, + onChange: ((value: T) -> Unit)? = null, + onTick: suspend (value: T) -> Unit + ): Delegate = + Delegate(initialValue, onChange, { value, changed -> if (changed) onTick(value) }).also { delegates.add(it) } + + operator fun invoke( + initialValue: T, + onChange: ((value: T) -> Unit)? = null, + onTick: suspend (value: T, changed: Boolean) -> Unit + ): Delegate = Delegate(initialValue, onChange, onTick).also { delegates.add(it) } + + inner class Delegate( + initialValue: T, + private val onChange: ((value: T) -> Unit)?, + private val onTick: suspend (value: T, changed: Boolean) -> Unit + ) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + val oldValue = oldValueRef.get() + if (oldValue == value) return + onChange?.invoke(value) + this.value = value + changed = true + } + + private var oldValueRef = WeakReference(initialValue) + private var changed = true + + var value: T = initialValue + + suspend fun tick() { + onTick(value, changed) + + oldValueRef = WeakReference(value) + changed = false + } + } +} diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/MinecraftProtocolDataTypes.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/MinecraftProtocolDataTypes.kt index a20bad5..bfb707c 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/MinecraftProtocolDataTypes.kt +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/MinecraftProtocolDataTypes.kt @@ -147,7 +147,6 @@ object MinecraftProtocolDataTypes { /** * Writes a [VoxelLocation] encoded as long. */ - @OptIn(ExperimentalUnsignedTypes::class) fun ByteBuf.writeVoxelLocation(value: VoxelLocation): ByteBuf { writeLong( value.x.toLong() and 0x3FFFFFF shl 38 or diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/JoinGamePacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/JoinGamePacketCodec.kt index 4674da5..72831bc 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/JoinGamePacketCodec.kt +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/JoinGamePacketCodec.kt @@ -63,7 +63,7 @@ object JoinGamePacketCodec : OutgoingPacketCodec(0x24, JoinGameP "minecraft:worldgen/biome" { "type" set "minecraft:worldgen/biome" - "value" set Uranos.biomeRegistry.items.values.mapIndexed { index, biome -> + "value" set Uranos.biomeRegistry.items.values.map { biome -> buildNBT { "name" set biome.id "id" set biome.numericID!! diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/SpawnLivingEntityPacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/SpawnLivingEntityPacketCodec.kt index 742e82a..59a18c9 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/SpawnLivingEntityPacketCodec.kt +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/SpawnLivingEntityPacketCodec.kt @@ -29,7 +29,8 @@ object SpawnLivingEntityPacketCodec : OutgoingPacketCodec(0x03, } fun getPacketFromEntity(entity: PaintingEntity) = SpawnPaintingPacket( - entity.uid, + @Suppress("DEPRECATION") + entity.numericID, entity.uuid, entity.motive, getCenterLocation(entity.topLeftLocation, entity.motive), diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/TagsPacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/TagsPacketCodec.kt index 5a3305d..4097545 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/TagsPacketCodec.kt +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/TagsPacketCodec.kt @@ -11,10 +11,8 @@ import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt import space.uranos.net.packet.OutgoingPacketCodec import space.uranos.tag.Tag import kotlin.time.Duration -import kotlin.time.ExperimentalTime import kotlin.time.toJavaDuration -@OptIn(ExperimentalTime::class) object TagsPacketCodec : OutgoingPacketCodec(0x5B, TagsPacket::class, CacheOptions(3, Duration.INFINITE.toJavaDuration())) { private val ORDER = listOf( diff --git a/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt b/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt index 528949f..2ce2b6d 100644 --- a/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt +++ b/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt @@ -124,9 +124,14 @@ class UranosServer internal constructor() : Server() { private fun startTicking() { scheduler.executeRepeating(1, 0) { - players.forEach { it.dataStorage.tick() } - entities.forEach { it.dataStorage.tick() } - sessions.forEach { it.tick() } + players.forEach { it.container.tick() } + + entities.forEach { + @Suppress("DEPRECATION_ERROR") + it.tick() + } + + sessions.forEach { it.packetsAdapter.tick() } } } diff --git a/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt b/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt index e4817f3..2ca4630 100644 --- a/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt +++ b/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt @@ -79,10 +79,6 @@ class UranosSession(val channel: io.netty.channel.Channel, val server: UranosSer scope.launch { packetsAdapter.sendNextTick(packet) } } - suspend fun tick() { - packetsAdapter.tick() - } - override suspend fun sendPluginMessage(channel: String, data: ByteBuf) { if (this.currentProtocol != PlayProtocol) throw IllegalStateException("The session is not using the PLAY protocol") send(OutgoingPluginMessagePacket(channel, data)) diff --git a/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt b/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt index 0aeaa6d..ec8ffb2 100644 --- a/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt +++ b/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt @@ -7,14 +7,13 @@ package space.uranos.player import space.uranos.Position import space.uranos.chat.TextComponent -import space.uranos.data.DataStorage -import space.uranos.data.createDataStorageKey import space.uranos.entity.PlayerEntity import space.uranos.net.UranosSession import space.uranos.net.packet.play.ChunkDataPacket import space.uranos.net.packet.play.ChunkLightDataPacket import space.uranos.net.packet.play.PlayerInfoPacket import space.uranos.net.packet.play.SelectedHotbarSlotPacket +import space.uranos.util.TickSynchronizationContainer import space.uranos.util.clampArgument import space.uranos.world.Chunk import space.uranos.world.VoxelLocation @@ -39,33 +38,19 @@ class UranosPlayer( override var compassTarget: VoxelLocation, selectedHotbarSlot: Int ) : Player { - val dataStorage = DataStorage(this) + val container = TickSynchronizationContainer() - object DataStorageKeys { - val selectedHotbarSlot = createDataStorageKey("selectedHotbarSlot") { player: UranosPlayer, value: Int -> - player.session.send(SelectedHotbarSlotPacket(value)) - null - } - - val playerListName = createDataStorageKey("playerListName") { player: UranosPlayer, value: TextComponent? -> - player.session.server.players.forEach { - it.session.sendNextTick(PlayerInfoPacket(PlayerInfoPacket.Action.UpdateDisplayName(mapOf(player.uuid to value)))) - } - - null - } + override var selectedHotbarSlot by container.ifChanged( + selectedHotbarSlot, + { clampArgument("selectedHotbarSlot", 0..8, it) }) { + session.send(SelectedHotbarSlotPacket(it)) } - override var selectedHotbarSlot - get() = dataStorage.get(DataStorageKeys.selectedHotbarSlot) - set(value) { - clampArgument("selectedHotbarSlot", 0..8, value) - dataStorage.set(DataStorageKeys.selectedHotbarSlot, value) + override var playerListName by container.ifChanged(TextComponent of name) { value -> + session.server.players.forEach { + it.session.sendNextTick(PlayerInfoPacket(PlayerInfoPacket.Action.UpdateDisplayName(mapOf(uuid to value)))) } - - override var playerListName - get() = dataStorage.get(DataStorageKeys.playerListName) - set(value) = dataStorage.set(DataStorageKeys.playerListName, value) + } init { this.selectedHotbarSlot = selectedHotbarSlot @@ -99,7 +84,7 @@ class UranosPlayer( } } - suspend fun sendChunksAndLight() { + private suspend fun sendChunksAndLight() { val chunks = currentlyViewedChunks.sortedBy { abs(it.key.x) + abs(it.key.z) } chunks.forEach { session.send(ChunkLightDataPacket(it.key, it.getLightData(this))) } chunks.forEach { session.send(ChunkDataPacket(it.key, it.getData(this))) }