Add entity metadata packet and object entity spawning
This commit is contained in:
parent
c959478978
commit
0b1ba947a1
26 changed files with 288 additions and 93 deletions
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
|
@ -1,5 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<list size="1">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="com.squareup.moshi.FromJson" />
|
||||||
|
</list>
|
||||||
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="true" project-jdk-name="14" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="true" project-jdk-name="14" project-jdk-type="JavaSDK" />
|
||||||
<component name="SuppressKotlinCodeStyleNotification">
|
<component name="SuppressKotlinCodeStyleNotification">
|
||||||
|
|
|
@ -3,12 +3,14 @@ package space.uranos.testplugin
|
||||||
import space.uranos.Uranos
|
import space.uranos.Uranos
|
||||||
import space.uranos.chat.ChatColor
|
import space.uranos.chat.ChatColor
|
||||||
import space.uranos.chat.TextComponent
|
import space.uranos.chat.TextComponent
|
||||||
import space.uranos.entity.MinecartEntity
|
|
||||||
import space.uranos.entity.Position
|
import space.uranos.entity.Position
|
||||||
|
import space.uranos.entity.RideableMinecartEntity
|
||||||
import space.uranos.net.ServerListInfo
|
import space.uranos.net.ServerListInfo
|
||||||
import space.uranos.net.event.ServerListInfoRequestEvent
|
import space.uranos.net.event.ServerListInfoRequestEvent
|
||||||
import space.uranos.net.event.SessionAfterLoginEvent
|
import space.uranos.net.event.SessionAfterLoginEvent
|
||||||
|
import space.uranos.net.packet.play.EntityMetadataPacket
|
||||||
import space.uranos.player.GameMode
|
import space.uranos.player.GameMode
|
||||||
|
import space.uranos.player.event.PlayerReadyEvent
|
||||||
import space.uranos.plugin.Plugin
|
import space.uranos.plugin.Plugin
|
||||||
import space.uranos.testplugin.anvil.AnvilWorld
|
import space.uranos.testplugin.anvil.AnvilWorld
|
||||||
import space.uranos.util.RGBColor
|
import space.uranos.util.RGBColor
|
||||||
|
@ -104,10 +106,22 @@ class TestPlugin : Plugin("Test", "1.0.0") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not showing up yet because no metadata is sent
|
val entity = Uranos.create<RideableMinecartEntity>()
|
||||||
val entity = Uranos.create<MinecartEntity>()
|
|
||||||
entity.position = Position(0.0, 4.0, 0.0)
|
entity.position = Position(0.0, 4.0, 0.0)
|
||||||
// entity.setWorld(world)
|
entity.setWorld(world)
|
||||||
|
|
||||||
|
Uranos.eventBus.on<PlayerReadyEvent> {
|
||||||
|
it.player.session.send(EntityMetadataPacket(entity.numericID, listOf(
|
||||||
|
EntityMetadataPacket.MetadataEntry.Byte(0u, 0x00),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Int(1u, 0x00),
|
||||||
|
EntityMetadataPacket.MetadataEntry.OptChat(2u, null),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Boolean(3u, false),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Boolean(4u, false),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Boolean(5u, false),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Int(6u, 0),
|
||||||
|
EntityMetadataPacket.MetadataEntry.Boolean(12u, true)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
var x = 0f
|
var x = 0f
|
||||||
var y = -90f
|
var y = -90f
|
||||||
|
|
|
@ -25,7 +25,7 @@ interface Entity {
|
||||||
val numericID: Int
|
val numericID: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Players that can see this entity.
|
* Players that can see this entity if it is in their view distance.
|
||||||
*/
|
*/
|
||||||
val viewers: MutableSet<Player>
|
val viewers: MutableSet<Player>
|
||||||
|
|
||||||
|
@ -38,6 +38,17 @@ interface Entity {
|
||||||
* If players should be added to [viewers] when they enter [world].
|
* If players should be added to [viewers] when they enter [world].
|
||||||
*/
|
*/
|
||||||
var visibleToNewPlayers: Boolean
|
var visibleToNewPlayers: Boolean
|
||||||
|
|
||||||
|
var glowing: Boolean // even experience orbs can glow
|
||||||
|
|
||||||
|
var invisible: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this entity does not produce any sounds.
|
||||||
|
*/
|
||||||
|
var silent: Boolean
|
||||||
|
|
||||||
|
var ignoreGravity: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package space.uranos.entity
|
||||||
|
|
||||||
|
import space.uranos.world.block.Block
|
||||||
|
|
||||||
|
interface MinecartEntity: ObjectEntity, YawRotatable, PitchRotatable {
|
||||||
|
/**
|
||||||
|
* Default is `0`.
|
||||||
|
*/
|
||||||
|
val shakingPower: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default is `1`.
|
||||||
|
*/
|
||||||
|
val shakingDirection: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default is `0.0`.
|
||||||
|
*/
|
||||||
|
val shakingMultiplier: Float
|
||||||
|
|
||||||
|
val customBlock: Block?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Y offset of the block inside the minecart, measured in 16ths of a block.
|
||||||
|
*/
|
||||||
|
val blockOffset: Int
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package space.uranos.entity
|
||||||
|
|
||||||
|
interface RideableMinecartEntity : MinecartEntity {
|
||||||
|
companion object Type : MinecartEntityType()
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf
|
||||||
|
import space.uranos.net.MinecraftProtocolDataTypes.writeString
|
||||||
|
import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt
|
||||||
|
import space.uranos.net.packet.OutgoingPacketCodec
|
||||||
|
|
||||||
|
object EntityMetadataPacketCodec : OutgoingPacketCodec<EntityMetadataPacket>(0x44, EntityMetadataPacket::class) {
|
||||||
|
override fun EntityMetadataPacket.encode(dst: ByteBuf) {
|
||||||
|
dst.writeVarInt(entityID)
|
||||||
|
|
||||||
|
for (entry in metadata) {
|
||||||
|
dst.writeByte(entry.index.toInt())
|
||||||
|
dst.writeVarInt(entry.typeID)
|
||||||
|
|
||||||
|
when(entry) {
|
||||||
|
is EntityMetadataPacket.MetadataEntry.Byte -> dst.writeByte(entry.value.toInt())
|
||||||
|
is EntityMetadataPacket.MetadataEntry.Int -> dst.writeVarInt(entry.value)
|
||||||
|
is EntityMetadataPacket.MetadataEntry.Float -> dst.writeFloat(entry.value)
|
||||||
|
is EntityMetadataPacket.MetadataEntry.String -> dst.writeString(entry.value)
|
||||||
|
is EntityMetadataPacket.MetadataEntry.OptChat -> {
|
||||||
|
if (entry.value == null) dst.writeBoolean(false)
|
||||||
|
else {
|
||||||
|
dst.writeBoolean(true)
|
||||||
|
dst.writeString(entry.value!!.toJson())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is EntityMetadataPacket.MetadataEntry.Boolean -> dst.writeBoolean(entry.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.writeByte(0xff)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf
|
||||||
|
import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt
|
||||||
|
import space.uranos.net.packet.OutgoingPacketCodec
|
||||||
|
|
||||||
|
object EntityVelocityPacketCodec : OutgoingPacketCodec<EntityVelocityPacket>(0x46, EntityVelocityPacket::class) {
|
||||||
|
override fun EntityVelocityPacket.encode(dst: ByteBuf) {
|
||||||
|
dst.writeVarInt(entityID)
|
||||||
|
dst.writeShort(x.toInt())
|
||||||
|
dst.writeShort(y.toInt())
|
||||||
|
dst.writeShort(z.toInt())
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,10 +14,12 @@ object PlayProtocol : Protocol(
|
||||||
DestroyEntitiesPacketCodec,
|
DestroyEntitiesPacketCodec,
|
||||||
DisconnectPacketCodec,
|
DisconnectPacketCodec,
|
||||||
EntityHeadYawPacketCodec,
|
EntityHeadYawPacketCodec,
|
||||||
|
EntityMetadataPacketCodec,
|
||||||
EntityOrientationPacketCodec,
|
EntityOrientationPacketCodec,
|
||||||
EntityRelativeMovePacketCodec,
|
EntityRelativeMovePacketCodec,
|
||||||
EntityRelativeMoveWithOrientationPacketCodec,
|
EntityRelativeMoveWithOrientationPacketCodec,
|
||||||
EntityTeleportPacketCodec,
|
EntityTeleportPacketCodec,
|
||||||
|
EntityVelocityPacketCodec,
|
||||||
IncomingKeepAlivePacketCodec,
|
IncomingKeepAlivePacketCodec,
|
||||||
IncomingPlayerPositionPacketCodec,
|
IncomingPlayerPositionPacketCodec,
|
||||||
IncomingPluginMessagePacketCodec,
|
IncomingPluginMessagePacketCodec,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import space.uranos.util.numbers.floorMod
|
||||||
object PlayerOrientationPacketCodec :
|
object PlayerOrientationPacketCodec :
|
||||||
IncomingPacketCodec<PlayerOrientationPacket>(0x14, PlayerOrientationPacket::class) {
|
IncomingPacketCodec<PlayerOrientationPacket>(0x14, PlayerOrientationPacket::class) {
|
||||||
override fun decode(msg: ByteBuf): PlayerOrientationPacket = PlayerOrientationPacket(
|
override fun decode(msg: ByteBuf): PlayerOrientationPacket = PlayerOrientationPacket(
|
||||||
floorMod(msg.readFloat(), 360f),
|
floorMod(msg.readFloat(), 360f), // TODO: Ensure it is never 360 (should be 0 then)
|
||||||
msg.readFloat(),
|
msg.readFloat(),
|
||||||
msg.readBoolean()
|
msg.readBoolean()
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,8 +20,8 @@ object SpawnLivingEntityPacketCodec : OutgoingPacketCodec<SpawnLivingEntityPacke
|
||||||
// This is named "head pitch" on wiki.vg, but it is actually head yaw.
|
// This is named "head pitch" on wiki.vg, but it is actually head yaw.
|
||||||
dst.writeByte(headYaw.mapToUByte(360f).toInt())
|
dst.writeByte(headYaw.mapToUByte(360f).toInt())
|
||||||
|
|
||||||
dst.writeShort((velocity.x * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityX.toInt())
|
||||||
dst.writeShort((velocity.y * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityY.toInt())
|
||||||
dst.writeShort((velocity.z * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityZ.toInt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ object SpawnObjectEntityPacketCodec : OutgoingPacketCodec<SpawnObjectEntityPacke
|
||||||
dst.writeByte(pitch.toInt())
|
dst.writeByte(pitch.toInt())
|
||||||
dst.writeByte(yaw.toInt())
|
dst.writeByte(yaw.toInt())
|
||||||
dst.writeInt(data)
|
dst.writeInt(data)
|
||||||
dst.writeShort((velocity.x * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityX.toInt())
|
||||||
dst.writeShort((velocity.y * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityY.toInt())
|
||||||
dst.writeShort((velocity.z * 8000).toInt().toShort().toInt())
|
dst.writeShort(velocityZ.toInt())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
|
import space.uranos.chat.ChatComponent
|
||||||
|
import space.uranos.net.packet.OutgoingPacket
|
||||||
|
|
||||||
|
data class EntityMetadataPacket(
|
||||||
|
val entityID: Int,
|
||||||
|
val metadata: Iterable<MetadataEntry<*>>
|
||||||
|
) : OutgoingPacket() {
|
||||||
|
sealed class MetadataEntry<T>(val typeID: kotlin.Int) {
|
||||||
|
abstract val index: UByte
|
||||||
|
abstract val value: T
|
||||||
|
|
||||||
|
data class Byte(override val index: UByte, override val value: kotlin.Byte) : MetadataEntry<kotlin.Byte>(0)
|
||||||
|
data class Int(override val index: UByte, override val value: kotlin.Int) : MetadataEntry<kotlin.Int>(1)
|
||||||
|
data class Float(override val index: UByte, override val value: kotlin.Float) : MetadataEntry<kotlin.Float>(2)
|
||||||
|
data class String(override val index: UByte, override val value: kotlin.String) : MetadataEntry<kotlin.String>(3)
|
||||||
|
data class OptChat(override val index: UByte, override val value: ChatComponent?) : MetadataEntry<ChatComponent?>(5)
|
||||||
|
data class Boolean(override val index: UByte, override val value: kotlin.Boolean) : MetadataEntry<kotlin.Boolean>(7)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
|
import space.uranos.net.packet.OutgoingPacket
|
||||||
|
|
||||||
|
data class EntityVelocityPacket(
|
||||||
|
val entityID: Int,
|
||||||
|
val x: Short,
|
||||||
|
val y: Short,
|
||||||
|
val z: Short
|
||||||
|
) : OutgoingPacket()
|
|
@ -1,6 +1,5 @@
|
||||||
package space.uranos.net.packet.play
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
import space.uranos.Vector
|
|
||||||
import space.uranos.entity.EntityType
|
import space.uranos.entity.EntityType
|
||||||
import space.uranos.entity.Position
|
import space.uranos.entity.Position
|
||||||
import space.uranos.net.packet.OutgoingPacket
|
import space.uranos.net.packet.OutgoingPacket
|
||||||
|
@ -8,6 +7,8 @@ import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent to spawn **living** entities.
|
* Sent to spawn **living** entities.
|
||||||
|
*
|
||||||
|
* Velocity is measured in 1/8000 blocks per tick.
|
||||||
*/
|
*/
|
||||||
data class SpawnLivingEntityPacket(
|
data class SpawnLivingEntityPacket(
|
||||||
val entityID: Int,
|
val entityID: Int,
|
||||||
|
@ -17,8 +18,7 @@ data class SpawnLivingEntityPacket(
|
||||||
val yaw: Float,
|
val yaw: Float,
|
||||||
val pitch: Float,
|
val pitch: Float,
|
||||||
val headYaw: Float,
|
val headYaw: Float,
|
||||||
/**
|
val velocityX: Short,
|
||||||
* Velocity in blocks per tick
|
val velocityY: Short,
|
||||||
*/
|
val velocityZ: Short
|
||||||
val velocity: Vector
|
|
||||||
) : OutgoingPacket()
|
) : OutgoingPacket()
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package space.uranos.net.packet.play
|
package space.uranos.net.packet.play
|
||||||
|
|
||||||
import space.uranos.Vector
|
|
||||||
import space.uranos.net.packet.OutgoingPacket
|
import space.uranos.net.packet.OutgoingPacket
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent to spawn object entities.
|
* Sent to spawn object entities.
|
||||||
|
*
|
||||||
|
* Velocity is measured in 1/8000 blocks per tick.
|
||||||
*/
|
*/
|
||||||
data class SpawnObjectEntityPacket(
|
data class SpawnObjectEntityPacket(
|
||||||
val entityID: Int,
|
val entityID: Int,
|
||||||
|
@ -17,5 +18,7 @@ data class SpawnObjectEntityPacket(
|
||||||
val yaw: UByte,
|
val yaw: UByte,
|
||||||
val pitch: UByte,
|
val pitch: UByte,
|
||||||
val data: Int,
|
val data: Int,
|
||||||
val velocity: Vector
|
val velocityX: Short,
|
||||||
|
val velocityY: Short,
|
||||||
|
val velocityZ: Short
|
||||||
) : OutgoingPacket()
|
) : OutgoingPacket()
|
||||||
|
|
|
@ -8,37 +8,45 @@ import space.uranos.net.packet.play.SpawnPaintingPacket
|
||||||
import space.uranos.util.numbers.mapToUByte
|
import space.uranos.util.numbers.mapToUByte
|
||||||
|
|
||||||
fun Entity.createSpawnPacket(): OutgoingPacket = when (this) {
|
fun Entity.createSpawnPacket(): OutgoingPacket = when (this) {
|
||||||
is LivingEntity -> if (this is HasMovableHead) SpawnLivingEntityPacket(
|
is LivingEntity -> {
|
||||||
numericID,
|
val (velX, velY, velZ) = velocity.getAsVelocityPacketValues()
|
||||||
uuid,
|
|
||||||
type,
|
if (this is HasMovableHead) SpawnLivingEntityPacket(
|
||||||
position,
|
numericID,
|
||||||
headYaw,
|
uuid,
|
||||||
headPitch,
|
type,
|
||||||
headYaw,
|
position,
|
||||||
velocity
|
headYaw,
|
||||||
) else SpawnLivingEntityPacket(
|
headPitch,
|
||||||
numericID,
|
headYaw,
|
||||||
uuid,
|
velX, velY, velZ
|
||||||
type,
|
) else SpawnLivingEntityPacket(
|
||||||
position,
|
numericID,
|
||||||
if (this is YawRotatable) yaw else 0f,
|
uuid,
|
||||||
if (this is PitchRotatable) pitch else 0f,
|
type,
|
||||||
0f,
|
position,
|
||||||
velocity
|
if (this is YawRotatable) yaw else 0f,
|
||||||
)
|
if (this is PitchRotatable) pitch else 0f,
|
||||||
is ObjectEntity -> SpawnObjectEntityPacket(
|
0f,
|
||||||
numericID,
|
velX, velY, velZ
|
||||||
uuid,
|
)
|
||||||
type.numericID,
|
}
|
||||||
position.x,
|
is ObjectEntity -> {
|
||||||
position.y,
|
val (velX, velY, velZ) = velocity.getAsVelocityPacketValues()
|
||||||
position.z,
|
|
||||||
if (this is YawRotatable) yaw.mapToUByte(360f) else 0u,
|
SpawnObjectEntityPacket(
|
||||||
if (this is PitchRotatable) pitch.mapToUByte(360f) else 0u,
|
numericID,
|
||||||
getDataValue(),
|
uuid,
|
||||||
velocity
|
type.numericID,
|
||||||
)
|
position.x,
|
||||||
|
position.y,
|
||||||
|
position.z,
|
||||||
|
if (this is YawRotatable) yaw.mapToUByte(360f) else 0u,
|
||||||
|
if (this is PitchRotatable) pitch.mapToUByte(360f) else 0u,
|
||||||
|
getDataValue(),
|
||||||
|
velX, velY, velZ
|
||||||
|
)
|
||||||
|
}
|
||||||
is PaintingEntity -> SpawnPaintingPacket(
|
is PaintingEntity -> SpawnPaintingPacket(
|
||||||
numericID,
|
numericID,
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -51,7 +59,7 @@ fun Entity.createSpawnPacket(): OutgoingPacket = when (this) {
|
||||||
|
|
||||||
fun ObjectEntity.getDataValue(): Int = when (this) {
|
fun ObjectEntity.getDataValue(): Int = when (this) {
|
||||||
is ItemEntity -> 1
|
is ItemEntity -> 1
|
||||||
is MinecartEntity -> 2
|
is RideableMinecartEntity -> 0
|
||||||
// TODO: Add remaining
|
// TODO: Add remaining
|
||||||
else -> throw IllegalArgumentException("Unknown entity type")
|
else -> throw IllegalArgumentException("Unknown entity type")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package space.uranos.util
|
||||||
|
|
||||||
|
import space.uranos.Vector
|
||||||
|
|
||||||
|
fun Vector.getAsVelocityPacketValues() = Triple(
|
||||||
|
(x * 8000).toInt().toShort(),
|
||||||
|
(y * 8000).toInt().toShort(),
|
||||||
|
(z * 8000).toInt().toShort()
|
||||||
|
)
|
|
@ -0,0 +1,16 @@
|
||||||
|
package space.uranos
|
||||||
|
|
||||||
|
import space.uranos.entity.*
|
||||||
|
import space.uranos.entity.impl.UranosBatEntity
|
||||||
|
import space.uranos.entity.impl.UranosCowEntity
|
||||||
|
import space.uranos.entity.impl.UranosCreeperEntity
|
||||||
|
import space.uranos.entity.impl.UranosRideableMinecartEntity
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : Entity> createEntityInstance(server: UranosServer, type: EntityType<T>): T = when (type) {
|
||||||
|
CowEntity -> UranosCowEntity(server)
|
||||||
|
BatEntity -> UranosBatEntity(server)
|
||||||
|
CreeperEntity -> UranosCreeperEntity(server)
|
||||||
|
RideableMinecartEntity -> UranosRideableMinecartEntity(server)
|
||||||
|
else -> throw IllegalArgumentException("Entities of this type cannot be created with this function")
|
||||||
|
} as T
|
|
@ -5,8 +5,10 @@ import com.sksamuel.hoplite.ConfigLoader
|
||||||
import com.sksamuel.hoplite.ConfigSource
|
import com.sksamuel.hoplite.ConfigSource
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import space.uranos.config.UranosConfig
|
import space.uranos.config.UranosConfig
|
||||||
import space.uranos.entity.*
|
import space.uranos.entity.Entity
|
||||||
import space.uranos.entity.impl.*
|
import space.uranos.entity.EntityType
|
||||||
|
import space.uranos.entity.UranosEntity
|
||||||
|
import space.uranos.entity.impl.UranosPlayerEntity
|
||||||
import space.uranos.event.UranosEventBus
|
import space.uranos.event.UranosEventBus
|
||||||
import space.uranos.event.UranosEventHandlerPositionManager
|
import space.uranos.event.UranosEventHandlerPositionManager
|
||||||
import space.uranos.logging.Logger
|
import space.uranos.logging.Logger
|
||||||
|
@ -79,20 +81,8 @@ class UranosServer internal constructor() : Server() {
|
||||||
private val internalEntities = HashSet<UranosEntity>()
|
private val internalEntities = HashSet<UranosEntity>()
|
||||||
override val entities: Set<Entity> = internalEntities
|
override val entities: Set<Entity> = internalEntities
|
||||||
|
|
||||||
override fun <T : Entity> create(type: EntityType<T>): T {
|
override fun <T : Entity> create(type: EntityType<T>): T =
|
||||||
val entity: UranosEntity = when (type) {
|
createEntityInstance(this, type).also { internalEntities.add(it as UranosEntity) }
|
||||||
CowEntity -> UranosCowEntity(this)
|
|
||||||
BatEntity -> UranosBatEntity(this)
|
|
||||||
CreeperEntity -> UranosCreeperEntity(this)
|
|
||||||
MinecartEntity -> UranosMinecartEntity(this)
|
|
||||||
else -> throw IllegalArgumentException("Entities of this type cannot be created with this function")
|
|
||||||
}
|
|
||||||
|
|
||||||
internalEntities.add(entity)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return entity as T
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createPlayerEntity(player: UranosPlayer) =
|
fun createPlayerEntity(player: UranosPlayer) =
|
||||||
UranosPlayerEntity(this, player).also { internalEntities.add(it) }
|
UranosPlayerEntity(this, player).also { internalEntities.add(it) }
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package space.uranos.entity
|
||||||
|
|
||||||
|
class EntityMetadataSynchronizer(val entity: UranosEntity) {
|
||||||
|
init {
|
||||||
|
// println(entity::class.allSuperclasses)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,6 @@ import java.util.UUID
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
sealed class UranosEntity(server: UranosServer) : Entity {
|
sealed class UranosEntity(server: UranosServer) : Entity {
|
||||||
override fun belongsToChunk(key: Chunk.Key): Boolean = key == chunkKey
|
|
||||||
|
|
||||||
override val numericID: Int = server.claimEntityID()
|
override val numericID: Int = server.claimEntityID()
|
||||||
override val uuid: UUID = UUID.randomUUID()
|
override val uuid: UUID = UUID.randomUUID()
|
||||||
|
|
||||||
|
@ -65,19 +63,31 @@ sealed class UranosEntity(server: UranosServer) : Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "Entity($uuid)"
|
override var glowing: Boolean = false
|
||||||
|
override var ignoreGravity: Boolean = false
|
||||||
|
override var invisible: Boolean = false
|
||||||
|
override var silent: Boolean = false
|
||||||
|
|
||||||
|
override fun toString(): String = "Entity($uuid)"
|
||||||
|
override fun belongsToChunk(key: Chunk.Key): Boolean = key == chunkKey
|
||||||
|
|
||||||
abstract suspend fun tick()
|
|
||||||
abstract val chunkKey: Chunk.Key
|
abstract val chunkKey: Chunk.Key
|
||||||
|
|
||||||
protected val container = TickSynchronizationContainer()
|
protected val container = TickSynchronizationContainer()
|
||||||
|
|
||||||
protected fun sendSpawnAndDestroyPackets() {
|
private fun sendSpawnAndDestroyPackets() {
|
||||||
if (addedViewers.isNotEmpty()) createSpawnPacket().let { packet -> addedViewers.forEach { it.session.send(packet) } }
|
if (addedViewers.isNotEmpty()) createSpawnPacket().let { packet -> addedViewers.forEach { it.session.send(packet) } }
|
||||||
if (removedViewers.isNotEmpty()) DestroyEntitiesPacket(arrayOf(numericID)).let { packet -> removedViewers.forEach { it.session.send(packet) } }
|
if (removedViewers.isNotEmpty()) DestroyEntitiesPacket(arrayOf(numericID)).let { packet -> removedViewers.forEach { it.session.send(packet) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun finishTick() {
|
@Suppress("LeakingThis")
|
||||||
|
private val metadataSynchronizer = EntityMetadataSynchronizer(this)
|
||||||
|
|
||||||
|
open suspend fun tick() {
|
||||||
|
container.tick()
|
||||||
|
metadataSynchronizer.tick()
|
||||||
|
|
||||||
|
sendSpawnAndDestroyPackets()
|
||||||
addedViewers.clear()
|
addedViewers.clear()
|
||||||
removedViewers.clear()
|
removedViewers.clear()
|
||||||
}
|
}
|
||||||
|
@ -106,8 +116,6 @@ abstract class UranosNotHasMovableHeadLivingEntity(server: UranosServer) : Urano
|
||||||
private var lastSentPitch: Float = 0f
|
private var lastSentPitch: Float = 0f
|
||||||
|
|
||||||
final override suspend fun tick() {
|
final override suspend fun tick() {
|
||||||
container.tick()
|
|
||||||
|
|
||||||
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
||||||
if (viewersWithoutAdded.isNotEmpty()) {
|
if (viewersWithoutAdded.isNotEmpty()) {
|
||||||
val packet = createMovementPacket()
|
val packet = createMovementPacket()
|
||||||
|
@ -119,8 +127,7 @@ abstract class UranosNotHasMovableHeadLivingEntity(server: UranosServer) : Urano
|
||||||
if (this is PitchRotatable) lastSentPitch = pitch
|
if (this is PitchRotatable) lastSentPitch = pitch
|
||||||
if (this is YawRotatable) lastSentYaw = yaw
|
if (this is YawRotatable) lastSentYaw = yaw
|
||||||
|
|
||||||
sendSpawnAndDestroyPackets()
|
super.tick()
|
||||||
finishTick()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMovementPacket(): OutgoingPacket? = createNotHasMovableHeadMovementPacket(position, lastSentPosition, lastSentYaw, lastSentPitch)
|
private fun createMovementPacket(): OutgoingPacket? = createNotHasMovableHeadMovementPacket(position, lastSentPosition, lastSentYaw, lastSentPitch)
|
||||||
|
@ -138,8 +145,6 @@ abstract class UranosHasMovableHeadLivingEntity(server: UranosServer) : UranosLi
|
||||||
}
|
}
|
||||||
|
|
||||||
final override suspend fun tick() {
|
final override suspend fun tick() {
|
||||||
container.tick()
|
|
||||||
|
|
||||||
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
||||||
if (viewersWithoutAdded.isNotEmpty()) {
|
if (viewersWithoutAdded.isNotEmpty()) {
|
||||||
val packets = createMovementPackets()
|
val packets = createMovementPackets()
|
||||||
|
@ -152,8 +157,7 @@ abstract class UranosHasMovableHeadLivingEntity(server: UranosServer) : UranosLi
|
||||||
oldHeadPitch = headPitch
|
oldHeadPitch = headPitch
|
||||||
oldHeadYaw = headYaw
|
oldHeadYaw = headYaw
|
||||||
|
|
||||||
sendSpawnAndDestroyPackets()
|
super.tick()
|
||||||
finishTick()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var oldPosition: Position = Position.ZERO
|
private var oldPosition: Position = Position.ZERO
|
||||||
|
@ -216,8 +220,6 @@ abstract class UranosObjectEntity(server: UranosServer) : UranosEntity(server),
|
||||||
private var lastSentPitch: Float = 0f
|
private var lastSentPitch: Float = 0f
|
||||||
|
|
||||||
final override suspend fun tick() {
|
final override suspend fun tick() {
|
||||||
container.tick()
|
|
||||||
|
|
||||||
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
||||||
if (viewersWithoutAdded.isNotEmpty()) {
|
if (viewersWithoutAdded.isNotEmpty()) {
|
||||||
val packet = createMovementPacket()
|
val packet = createMovementPacket()
|
||||||
|
@ -228,9 +230,9 @@ abstract class UranosObjectEntity(server: UranosServer) : UranosEntity(server),
|
||||||
|
|
||||||
if (this is PitchRotatable) lastSentPitch = pitch
|
if (this is PitchRotatable) lastSentPitch = pitch
|
||||||
if (this is YawRotatable) lastSentYaw = yaw
|
if (this is YawRotatable) lastSentYaw = yaw
|
||||||
|
lastSentPosition = position
|
||||||
|
|
||||||
sendSpawnAndDestroyPackets()
|
super.tick()
|
||||||
finishTick()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMovementPacket(): OutgoingPacket? = createNotHasMovableHeadMovementPacket(position, lastSentPosition, lastSentYaw, lastSentPitch)
|
private fun createMovementPacket(): OutgoingPacket? = createNotHasMovableHeadMovementPacket(position, lastSentPosition, lastSentYaw, lastSentPitch)
|
||||||
|
@ -247,8 +249,6 @@ class UranosPaintingEntity(
|
||||||
private var lastSentTopLeftLocation: VoxelLocation = topLeftLocation
|
private var lastSentTopLeftLocation: VoxelLocation = topLeftLocation
|
||||||
|
|
||||||
override suspend fun tick() {
|
override suspend fun tick() {
|
||||||
container.tick()
|
|
||||||
|
|
||||||
if (lastSentTopLeftLocation != topLeftLocation) {
|
if (lastSentTopLeftLocation != topLeftLocation) {
|
||||||
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
val viewersWithoutAdded = viewers.subtract(addedViewers)
|
||||||
if (viewersWithoutAdded.isNotEmpty()) {
|
if (viewersWithoutAdded.isNotEmpty()) {
|
||||||
|
@ -269,8 +269,7 @@ class UranosPaintingEntity(
|
||||||
|
|
||||||
lastSentTopLeftLocation = topLeftLocation
|
lastSentTopLeftLocation = topLeftLocation
|
||||||
|
|
||||||
sendSpawnAndDestroyPackets()
|
super.tick()
|
||||||
finishTick()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package space.uranos.entity.impl
|
package space.uranos.entity
|
||||||
|
|
||||||
import space.uranos.UranosServer
|
import space.uranos.UranosServer
|
||||||
import space.uranos.entity.MinecartEntity
|
|
||||||
import space.uranos.entity.UranosObjectEntity
|
|
||||||
import space.uranos.util.numbers.validatePitch
|
import space.uranos.util.numbers.validatePitch
|
||||||
import space.uranos.util.numbers.validateYaw
|
import space.uranos.util.numbers.validateYaw
|
||||||
|
|
||||||
class UranosMinecartEntity(server: UranosServer) : UranosObjectEntity(server), MinecartEntity {
|
abstract class UranosMinecartEntity(server: UranosServer) : UranosObjectEntity(server), MinecartEntity {
|
||||||
override var yaw: Float = 0f
|
override var yaw: Float = 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
validateYaw(value); field = value
|
validateYaw(value); field = value
|
||||||
|
@ -16,4 +14,8 @@ class UranosMinecartEntity(server: UranosServer) : UranosObjectEntity(server), M
|
||||||
set(value) {
|
set(value) {
|
||||||
validatePitch(value); field = value
|
validatePitch(value); field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val shakingPower: Int = 0
|
||||||
|
override val shakingDirection: Int = 1
|
||||||
|
override val shakingMultiplier: Float = 0f
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package space.uranos.entity.impl
|
||||||
|
|
||||||
|
import space.uranos.UranosServer
|
||||||
|
import space.uranos.entity.RideableMinecartEntity
|
||||||
|
import space.uranos.entity.UranosMinecartEntity
|
||||||
|
import space.uranos.world.block.Block
|
||||||
|
|
||||||
|
class UranosRideableMinecartEntity(server: UranosServer) : UranosMinecartEntity(server), RideableMinecartEntity {
|
||||||
|
override val customBlock: Block? = null
|
||||||
|
override val blockOffset: Int = 6
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
||||||
val result = AuthenticationHelper.authenticate(hashString, state.username)
|
val result = AuthenticationHelper.authenticate(hashString, state.username)
|
||||||
|
|
||||||
session.sendNow(SetCompressionPacket(session.server.config.packetCompressionThreshold))
|
session.sendNow(SetCompressionPacket(session.server.config.packetCompressionThreshold))
|
||||||
|
// TODO: Handle disconnect errors
|
||||||
session.enableCompressionCodec()
|
session.enableCompressionCodec()
|
||||||
|
|
||||||
session.sendNow(LoginSuccessPacket(result.uuid, result.username))
|
session.sendNow(LoginSuccessPacket(result.uuid, result.username))
|
||||||
|
|
|
@ -18,6 +18,7 @@ class PacketsAdapter(val session: UranosSession) {
|
||||||
private val packetsForNextTick = ArrayList<OutgoingPacket>()
|
private val packetsForNextTick = ArrayList<OutgoingPacket>()
|
||||||
|
|
||||||
suspend fun tick() {
|
suspend fun tick() {
|
||||||
|
// TODO: Fix ConcurrentModificationException
|
||||||
packetsForNextTick.forEach { send(it) }
|
packetsForNextTick.forEach { send(it) }
|
||||||
packetsForNextTick.clear()
|
packetsForNextTick.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ class UranosSession(val channel: io.netty.channel.Channel, val server: UranosSer
|
||||||
if (!expected && currentProtocol != HandshakingProtocol && currentProtocol != StatusProtocol)
|
if (!expected && currentProtocol != HandshakingProtocol && currentProtocol != StatusProtocol)
|
||||||
logger trace "The client disconnected unexpectedly"
|
logger trace "The client disconnected unexpectedly"
|
||||||
|
|
||||||
|
// TODO: Remove the player entity and send PlayerInfo packet
|
||||||
packetsAdapter.stopProcessingIncomingPackets()
|
packetsAdapter.stopProcessingIncomingPackets()
|
||||||
coroutineContext.cancel(DisconnectedCancellationException())
|
coroutineContext.cancel(DisconnectedCancellationException())
|
||||||
state = State.Disconnected
|
state = State.Disconnected
|
||||||
|
|
Reference in a new issue