From 3767d6606506a24aab823f1ff03eecef5c75407f Mon Sep 17 00:00:00 2001 From: Moritz Ruth Date: Mon, 11 Jan 2021 21:55:03 +0100 Subject: [PATCH] Sync entity positions --- .../space/uranos/testplugin/TestPlugin.kt | 10 +++- .../space/uranos/entity/LivingEntity.kt | 6 +-- .../kotlin/space/uranos/util/WatchableSet.kt | 4 +- .../play/EntityOrientationPacketCodec.kt | 20 ++++++++ .../play/EntityRelativeMovePacketCodec.kt | 21 ++++++++ ...yRelativeMoveWithOrientationPacketCodec.kt | 23 +++++++++ .../packet/play/EntityTeleportPacketCodec.kt | 23 +++++++++ .../uranos/net/packet/play/PlayProtocol.kt | 4 ++ .../play/SpawnLivingEntityPacketCodec.kt | 2 +- .../net/packet/play/CompassTargetPacket.kt | 1 - .../packet/play/EntityOrientationPacket.kt | 15 ++++++ .../packet/play/EntityRelativeMovePacket.kt | 16 ++++++ ...EntityRelativeMoveWithOrientationPacket.kt | 24 +++++++++ .../net/packet/play/EntityTeleportPacket.kt | 15 ++++++ .../packet/play/SpawnLivingEntityPacket.kt | 1 - .../uranos/util/CreateEntityMovementPacket.kt | 49 +++++++++++++++++++ .../kotlin/space/uranos/util/SpawnEntity.kt | 1 - .../space/uranos/entity/UranosEntity.kt | 23 +++++++-- .../space/uranos/player/UranosPlayer.kt | 3 +- 19 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityOrientationPacketCodec.kt create mode 100644 uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMovePacketCodec.kt create mode 100644 uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMoveWithOrientationPacketCodec.kt create mode 100644 uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityTeleportPacketCodec.kt create mode 100644 uranos-packets/src/main/kotlin/space/uranos/net/packet/play/EntityOrientationPacket.kt create mode 100644 uranos-packets/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMovePacket.kt create mode 100644 uranos-packets/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMoveWithOrientationPacket.kt create mode 100644 uranos-packets/src/main/kotlin/space/uranos/net/packet/play/EntityTeleportPacket.kt create mode 100644 uranos-packets/src/main/kotlin/space/uranos/util/CreateEntityMovementPacket.kt rename {uranos-packet-codecs => uranos-packets}/src/main/kotlin/space/uranos/util/SpawnEntity.kt (98%) diff --git a/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt b/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt index 78aa0f7..013d96d 100644 --- a/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt +++ b/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt @@ -17,6 +17,7 @@ import space.uranos.player.GameMode import space.uranos.plugin.Plugin import space.uranos.testplugin.anvil.AnvilWorld import space.uranos.util.RGBColor +import space.uranos.util.runInServerThread import space.uranos.util.secondsToTicks import space.uranos.world.* import space.uranos.world.block.GreenWoolBlock @@ -61,7 +62,7 @@ class TestPlugin: Plugin("Test", "1.0.0") { } val entity = Uranos.create() - entity.position = Position(0.0, 10.0, 0.0, 0f, 0f) + entity.position = Position(0.5, 10.0, 0.5, 0f, 0f) entity.setWorld(world) Uranos.eventBus.on { event -> @@ -80,5 +81,12 @@ class TestPlugin: Plugin("Test", "1.0.0") { } } } + + var x = 1.0 + Uranos.scheduler.executeRepeating(1) { + x += 0.2 + runInServerThread { entity.position = Position(x + 0.5, x, 0.5, 0f, 0f) } + if (x >= 10.0) x = 0.0 + } } } 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 c952a6e..c9238f2 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt @@ -5,8 +5,4 @@ package space.uranos.entity -interface LivingEntity : Entity, Mobile { - // TODO: This should probably be headYaw, but wiki.vg says headPitch. - // And it is only used in the SpawnLivingEntity packet - var headPitch: Float -} +interface LivingEntity : Entity, Mobile diff --git a/uranos-api/src/main/kotlin/space/uranos/util/WatchableSet.kt b/uranos-api/src/main/kotlin/space/uranos/util/WatchableSet.kt index 62cad51..9dd1499 100644 --- a/uranos-api/src/main/kotlin/space/uranos/util/WatchableSet.kt +++ b/uranos-api/src/main/kotlin/space/uranos/util/WatchableSet.kt @@ -5,15 +5,17 @@ package space.uranos.util -import java.util.* +import java.util.Spliterator import java.util.function.Predicate import java.util.stream.Stream abstract class WatchableSet(private val backingSet: MutableSet) : MutableSet by backingSet { abstract fun onAdd(element: T) abstract fun onRemove(element: T) + abstract fun beforeAdd(element: T) override fun add(element: T): Boolean { + beforeAdd(element) val added = backingSet.add(element) if (added) onAdd(element) return added diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityOrientationPacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityOrientationPacketCodec.kt new file mode 100644 index 0000000..9f70490 --- /dev/null +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityOrientationPacketCodec.kt @@ -0,0 +1,20 @@ +/* + * 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.net.packet.play + +import io.netty.buffer.ByteBuf +import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt +import space.uranos.net.packet.OutgoingPacketCodec + +object EntityOrientationPacketCodec : + OutgoingPacketCodec(0x29, EntityOrientationPacket::class) { + override fun EntityOrientationPacket.encode(dst: ByteBuf) { + dst.writeVarInt(entityID) + dst.writeByte(yaw.toInt()) + dst.writeByte(pitch.toInt()) + dst.writeBoolean(onGround) + } +} diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMovePacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMovePacketCodec.kt new file mode 100644 index 0000000..1fb54b2 --- /dev/null +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMovePacketCodec.kt @@ -0,0 +1,21 @@ +/* + * 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.net.packet.play + +import io.netty.buffer.ByteBuf +import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt +import space.uranos.net.packet.OutgoingPacketCodec + +object EntityRelativeMovePacketCodec : + OutgoingPacketCodec(0x27, EntityRelativeMovePacket::class) { + override fun EntityRelativeMovePacket.encode(dst: ByteBuf) { + dst.writeVarInt(entityID) + dst.writeShort(deltaX.toInt()) + dst.writeShort(deltaY.toInt()) + dst.writeShort(deltaZ.toInt()) + dst.writeBoolean(onGround) + } +} diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMoveWithOrientationPacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMoveWithOrientationPacketCodec.kt new file mode 100644 index 0000000..d6fc32a --- /dev/null +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityRelativeMoveWithOrientationPacketCodec.kt @@ -0,0 +1,23 @@ +/* + * 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.net.packet.play + +import io.netty.buffer.ByteBuf +import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt +import space.uranos.net.packet.OutgoingPacketCodec + +object EntityRelativeMoveWithOrientationPacketCodec : + OutgoingPacketCodec(0x28, EntityRelativeMoveWithOrientationPacket::class) { + override fun EntityRelativeMoveWithOrientationPacket.encode(dst: ByteBuf) { + dst.writeVarInt(entityID) + dst.writeShort(deltaX.toInt()) + dst.writeShort(deltaY.toInt()) + dst.writeShort(deltaZ.toInt()) + dst.writeByte(yaw.toInt()) + dst.writeByte(pitch.toInt()) + dst.writeBoolean(onGround) + } +} diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityTeleportPacketCodec.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityTeleportPacketCodec.kt new file mode 100644 index 0000000..f1bf5b9 --- /dev/null +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/EntityTeleportPacketCodec.kt @@ -0,0 +1,23 @@ +/* + * 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.net.packet.play + +import io.netty.buffer.ByteBuf +import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt +import space.uranos.net.packet.OutgoingPacketCodec + +object EntityTeleportPacketCodec : + OutgoingPacketCodec(0x56, EntityTeleportPacket::class) { + override fun EntityTeleportPacket.encode(dst: ByteBuf) { + dst.writeVarInt(entityID) + dst.writeDouble(position.x) + dst.writeDouble(position.y) + dst.writeDouble(position.z) + dst.writeByte(position.yawIn256Steps.toInt()) + dst.writeByte(position.pitchIn256Steps.toInt()) + dst.writeBoolean(onGround) + } +} diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/PlayProtocol.kt b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/PlayProtocol.kt index c6e23b1..f26f784 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/PlayProtocol.kt +++ b/uranos-packet-codecs/src/main/kotlin/space/uranos/net/packet/play/PlayProtocol.kt @@ -18,6 +18,10 @@ object PlayProtocol : Protocol( DeclareRecipesPacketCodec, DestroyEntitiesPacketCodec, DisconnectPacketCodec, + EntityOrientationPacketCodec, + EntityRelativeMovePacketCodec, + EntityRelativeMoveWithOrientationPacketCodec, + EntityTeleportPacketCodec, IncomingKeepAlivePacketCodec, IncomingPlayerPositionPacketCodec, IncomingPluginMessagePacketCodec, 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 09df0aa..4d8d169 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 @@ -21,7 +21,7 @@ object SpawnLivingEntityPacketCodec : OutgoingPacketCodec, val position: Position, - val headPitch: Float, /** * Velocity in blocks per tick */ diff --git a/uranos-packets/src/main/kotlin/space/uranos/util/CreateEntityMovementPacket.kt b/uranos-packets/src/main/kotlin/space/uranos/util/CreateEntityMovementPacket.kt new file mode 100644 index 0000000..96610b3 --- /dev/null +++ b/uranos-packets/src/main/kotlin/space/uranos/util/CreateEntityMovementPacket.kt @@ -0,0 +1,49 @@ +/* + * 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 space.uranos.Position +import space.uranos.abs +import space.uranos.net.packet.OutgoingPacket +import space.uranos.net.packet.play.EntityOrientationPacket +import space.uranos.net.packet.play.EntityRelativeMovePacket +import space.uranos.net.packet.play.EntityRelativeMoveWithOrientationPacket +import space.uranos.net.packet.play.EntityTeleportPacket + +fun createEntityMovementPacket(entityID: Int, oldPosition: Position, newPosition: Position): OutgoingPacket? { + val delta = abs(newPosition.toVector() - oldPosition.toVector()) + val orientationChanged = oldPosition.yaw != newPosition.yaw || oldPosition.pitch != newPosition.pitch + val onGround = true // TODO: Find out what onGround does + + return if (delta.x + delta.y + delta.z == 0.0) { + if (orientationChanged) EntityOrientationPacket( + entityID, + newPosition.yawIn256Steps, + newPosition.pitchIn256Steps, + onGround + ) else null + } else if (delta.x > 8 || delta.y > 8 || delta.z > 8) { + EntityTeleportPacket(entityID, newPosition, onGround) + } else { + if (orientationChanged) EntityRelativeMoveWithOrientationPacket( + entityID, + getDeltaShort(delta.x), + getDeltaShort(delta.y), + getDeltaShort(delta.z), + newPosition.yawIn256Steps, + newPosition.pitchIn256Steps, + onGround + ) else EntityRelativeMovePacket( + entityID, + getDeltaShort(delta.x), + getDeltaShort(delta.y), + getDeltaShort(delta.z), + onGround + ) + } +} + +private fun getDeltaShort(delta: Double): Short = (delta * 32 * 128).toInt().toShort() diff --git a/uranos-packet-codecs/src/main/kotlin/space/uranos/util/SpawnEntity.kt b/uranos-packets/src/main/kotlin/space/uranos/util/SpawnEntity.kt similarity index 98% rename from uranos-packet-codecs/src/main/kotlin/space/uranos/util/SpawnEntity.kt rename to uranos-packets/src/main/kotlin/space/uranos/util/SpawnEntity.kt index f54e72a..8fcfcb8 100644 --- a/uranos-packet-codecs/src/main/kotlin/space/uranos/util/SpawnEntity.kt +++ b/uranos-packets/src/main/kotlin/space/uranos/util/SpawnEntity.kt @@ -17,7 +17,6 @@ fun Entity.createSpawnPacket(): OutgoingPacket = when (this) { uuid, type, position, - headPitch, velocity ) is ObjectEntity -> SpawnObjectEntityPacket( diff --git a/uranos-server/src/main/kotlin/space/uranos/entity/UranosEntity.kt b/uranos-server/src/main/kotlin/space/uranos/entity/UranosEntity.kt index 03d3a6a..b4f2021 100644 --- a/uranos-server/src/main/kotlin/space/uranos/entity/UranosEntity.kt +++ b/uranos-server/src/main/kotlin/space/uranos/entity/UranosEntity.kt @@ -13,6 +13,7 @@ import space.uranos.entity.event.ViewingChangedEvent import space.uranos.player.Player import space.uranos.util.TickSynchronizationContainer import space.uranos.util.WatchableSet +import space.uranos.util.createEntityMovementPacket import space.uranos.util.memoized import space.uranos.world.Chunk import space.uranos.world.VoxelLocation @@ -31,6 +32,11 @@ sealed class UranosEntity(server: UranosServer) : Entity { override val uuid: UUID = UUID.randomUUID() override val viewers: MutableSet = object : WatchableSet(Collections.newSetFromMap(WeakHashMap())) { + override fun beforeAdd(element: Player) { + if ((this@UranosEntity as? UranosPlayerEntity)?.let { it.player == element } == true) + throw IllegalArgumentException("A player cannot be a viewer of it's own entity") + } + override fun onAdd(element: Player) { Uranos.scope.launch { Uranos.eventBus.emit(ViewingChangedEvent(this@UranosEntity, element, true)) } } @@ -70,10 +76,16 @@ sealed class UranosEntity(server: UranosServer) : Entity { abstract class UranosLivingEntity(server: UranosServer) : UranosEntity(server), LivingEntity { override var velocity: Vector = Vector.ZERO - override var headPitch: Float = 0f + private var lastSentPosition: Position = Position.ZERO override var position: Position by container.ifChanged(Position.ZERO) { value -> - // TODO: Broadcast to players + if (viewers.isNotEmpty()) createEntityMovementPacket(numericID, lastSentPosition, value)?.let { + for (viewer in viewers) { + viewer.session.sendNextTick(it) + } + } + + lastSentPosition = value } override val chunkKey: Chunk.Key by memoized({ position }) { Chunk.Key.from(position) } @@ -82,8 +94,13 @@ abstract class UranosLivingEntity(server: UranosServer) : UranosEntity(server), abstract class UranosObjectEntity(server: UranosServer) : UranosEntity(server), ObjectEntity { override var velocity: Vector = Vector.ZERO + private var lastSentPosition: Position = Position.ZERO override var position: Position by container.ifChanged(Position.ZERO) { value -> - // TODO: Broadcast to players + createEntityMovementPacket(numericID, lastSentPosition, value)?.let { + for (viewer in viewers) { + viewer.session.sendNextTick(it) + } + } } override val chunkKey: Chunk.Key by memoized({ position }) { Chunk.Key.from(position) } 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 732c6d1..5b8c4a6 100644 --- a/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt +++ b/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt @@ -62,11 +62,10 @@ class UranosPlayer( override val entity: PlayerEntity = session.server.createPlayerEntity(this).also { it.position = position - it.headPitch = headPitch } suspend fun spawnInitially(world: World) { - session.server.entities.forEach { if (it.visibleToNewPlayers) it.viewers.add(this) } + session.server.entities.forEach { if (it.visibleToNewPlayers && it != entity) it.viewers.add(this) } entity.setWorld(world) updateCurrentlyViewedChunks() sendChunksAndLight()