Add player movement related packets and work on entities
This commit is contained in:
parent
cb50c7bb39
commit
4e34dbc1e2
28 changed files with 249 additions and 78 deletions
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
class CalledFromWrongThread(message: String) : Exception(message)
|
|
@ -35,13 +35,13 @@ interface Scheduler {
|
|||
*
|
||||
* If you do not need the return value of [block], you should use [executeAfter] instead.
|
||||
*/
|
||||
suspend fun <R : Any> runAfter(delay: Int, block: suspend () -> R): R
|
||||
suspend fun <R : Any> runAfter(delay: Int, inServerThread: Boolean = false, block: suspend () -> R): R
|
||||
|
||||
/**
|
||||
* Like [runAfter], but the task is *not* cancelled when the current coroutine scope is cancelled.
|
||||
*/
|
||||
suspend fun <R : Any> runDetachedAfter(delay: Int, block: suspend () -> R): R =
|
||||
withContext(NonCancellable) { runAfter(delay, block) }
|
||||
suspend fun <R : Any> runDetachedAfter(delay: Int, inServerThread: Boolean = false, block: suspend () -> R): R =
|
||||
withContext(NonCancellable) { runAfter(delay, inServerThread, block) }
|
||||
|
||||
interface Task {
|
||||
fun cancel()
|
||||
|
|
|
@ -28,6 +28,9 @@ abstract class Entity internal constructor() {
|
|||
var world: World? = null; private set
|
||||
|
||||
suspend fun setWorld(world: World?) {
|
||||
if (world == null && this is PlayerEntity)
|
||||
throw IllegalArgumentException("You cannot set the world of a PlayerEntity to null")
|
||||
|
||||
if (world == this.world) return
|
||||
|
||||
worldMutex.withLock {
|
||||
|
|
|
@ -9,7 +9,7 @@ import space.uranos.Position
|
|||
import space.uranos.Vector
|
||||
|
||||
abstract class LivingEntity : Entity(), Mobile {
|
||||
abstract val headPitch: Float
|
||||
abstract override val position: Position
|
||||
abstract override val velocity: Vector
|
||||
abstract var headPitch: Float
|
||||
abstract override var position: Position
|
||||
abstract override var velocity: Vector
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ import space.uranos.Position
|
|||
import space.uranos.Vector
|
||||
|
||||
interface Mobile {
|
||||
val position: Position
|
||||
var position: Position
|
||||
|
||||
/**
|
||||
* The velocity in blocks per tick.
|
||||
*/
|
||||
val velocity: Vector
|
||||
var velocity: Vector
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.entity
|
||||
|
||||
import space.uranos.Position
|
||||
import space.uranos.Vector
|
||||
import space.uranos.player.Player
|
||||
import space.uranos.world.World
|
||||
|
||||
open class PlayerEntity(
|
||||
override var position: Position,
|
||||
/**
|
||||
* The player to which this entity belongs.
|
||||
*
|
||||
* **Not every player entity belongs to a player.**
|
||||
*/
|
||||
open val player: Player? = null,
|
||||
override var headPitch: Float = 0f
|
||||
) : LivingEntity() {
|
||||
final override val type: EntityType = Type
|
||||
override var velocity: Vector = Vector.ZERO
|
||||
|
||||
/**
|
||||
* Because [world] is never `null` for player entities, you can use this property instead of writing `world!!`.
|
||||
*/
|
||||
val safeWorld: World get() = world!!
|
||||
|
||||
companion object Type : PlayerEntityType()
|
||||
}
|
|
@ -19,8 +19,7 @@ import java.util.*
|
|||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
abstract class Session {
|
||||
@Suppress("LeakingThis")
|
||||
val events = EventBusWrapper<Session>(this)
|
||||
val events by lazy { EventBusWrapper<Session>(this) }
|
||||
|
||||
/**
|
||||
* The IP address of this session
|
||||
|
@ -76,6 +75,7 @@ abstract class Session {
|
|||
val gameMode: GameMode,
|
||||
val world: World,
|
||||
val position: Position,
|
||||
val headPitch: Float,
|
||||
val invulnerable: Boolean,
|
||||
val reducedDebugInfo: Boolean,
|
||||
val selectedHotbarSlot: Int
|
||||
|
|
|
@ -30,6 +30,8 @@ class SessionAfterLoginEvent(override val target: Session) : SessionEvent(), Can
|
|||
*/
|
||||
var initialWorldAndLocation: Pair<World, Position>? = null
|
||||
|
||||
var headPitch: Float = 0f
|
||||
|
||||
var maxViewDistance: Int = 32
|
||||
set(value) {
|
||||
if (value in 2..32) field = value
|
||||
|
@ -61,7 +63,7 @@ class SessionAfterLoginEvent(override val target: Session) : SessionEvent(), Can
|
|||
var reducedDebugInfo: Boolean = false
|
||||
|
||||
/**
|
||||
* See [Player.invulnerable].
|
||||
* See [Player.vulnerable].
|
||||
*/
|
||||
var invulnerable: Boolean = false
|
||||
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
|
||||
package space.uranos.player
|
||||
|
||||
import space.uranos.Position
|
||||
import space.uranos.chat.TextComponent
|
||||
import space.uranos.entity.PlayerEntity
|
||||
import space.uranos.net.Session
|
||||
import space.uranos.world.Chunk
|
||||
import space.uranos.world.VoxelLocation
|
||||
import space.uranos.world.World
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
@ -22,6 +21,8 @@ interface Player {
|
|||
*/
|
||||
val session: Session
|
||||
|
||||
val entity: PlayerEntity
|
||||
|
||||
/**
|
||||
* The name of this player.
|
||||
*/
|
||||
|
@ -51,16 +52,6 @@ interface Player {
|
|||
val chatMode: ChatMode
|
||||
)
|
||||
|
||||
/**
|
||||
* The current position of this player.
|
||||
*/
|
||||
var position: Position
|
||||
|
||||
/**
|
||||
* The world which currently contains this player.
|
||||
*/
|
||||
val world: World
|
||||
|
||||
/**
|
||||
* The index of the hotbar slot which is currently selected.
|
||||
* Must be in `0..8`.
|
||||
|
@ -75,9 +66,9 @@ interface Player {
|
|||
var reducedDebugInfo: Boolean
|
||||
|
||||
/**
|
||||
* Whether the player cannot take damage through other entities or the world.
|
||||
* Whether the player can take damage through other entities or the world.
|
||||
*/
|
||||
var invulnerable: Boolean
|
||||
var vulnerable: Boolean
|
||||
|
||||
/**
|
||||
* Whether the player can start flying by itself.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package space.uranos.server
|
||||
|
||||
import space.uranos.CalledFromWrongThread
|
||||
import space.uranos.Registry
|
||||
import space.uranos.Scheduler
|
||||
import space.uranos.command.Command
|
||||
|
@ -28,6 +29,8 @@ abstract class Server {
|
|||
abstract val eventBus: EventBus
|
||||
abstract val eventHandlerPositions: EventHandlerPositionManager
|
||||
|
||||
protected abstract val serverThread: Thread
|
||||
|
||||
/**
|
||||
* [CoroutineContext] confined to the server thread.
|
||||
*
|
||||
|
@ -65,6 +68,13 @@ abstract class Server {
|
|||
*/
|
||||
abstract fun shutdown()
|
||||
|
||||
/**
|
||||
* Throws [CalledFromWrongThread] when called from a thread which is not the server thread.
|
||||
*/
|
||||
fun ensureServerThread(errorMessage: String) {
|
||||
if (Thread.currentThread() != serverThread) throw CalledFromWrongThread(errorMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of all existing [Entity] instances.
|
||||
*
|
||||
|
|
|
@ -9,9 +9,9 @@ import io.netty.buffer.ByteBuf
|
|||
import space.uranos.net.MinecraftProtocolDataTypes.writeVoxelLocation
|
||||
import space.uranos.net.packet.OutgoingPacketCodec
|
||||
|
||||
object SetCompassTargetPacketCodec :
|
||||
OutgoingPacketCodec<SetCompassTargetPacket>(0x42, SetCompassTargetPacket::class) {
|
||||
override fun SetCompassTargetPacket.encode(dst: ByteBuf) {
|
||||
object CompassTargetPacketCodec :
|
||||
OutgoingPacketCodec<CompassTargetPacket>(0x42, CompassTargetPacket::class) {
|
||||
override fun CompassTargetPacket.encode(dst: ByteBuf) {
|
||||
dst.writeVoxelLocation(target)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.Position
|
||||
import space.uranos.net.packet.IncomingPacketCodec
|
||||
|
||||
object IncomingPlayerPositionPacketCodec :
|
||||
IncomingPacketCodec<IncomingPlayerPositionPacket>(0x13, IncomingPlayerPositionPacket::class) {
|
||||
override fun decode(msg: ByteBuf): IncomingPlayerPositionPacket = IncomingPlayerPositionPacket(
|
||||
Position(msg.readDouble(), msg.readDouble(), msg.readDouble(), 360 - msg.readFloat() % 360, msg.readFloat()),
|
||||
msg.readBoolean()
|
||||
)
|
||||
}
|
|
@ -10,9 +10,9 @@ import space.uranos.net.MinecraftProtocolDataTypes.writeVarInt
|
|||
import space.uranos.net.packet.OutgoingPacketCodec
|
||||
import space.uranos.util.bitmask
|
||||
|
||||
object PlayerPositionAndLookPacketCodec :
|
||||
OutgoingPacketCodec<PlayerPositionAndLookPacket>(0x34, PlayerPositionAndLookPacket::class) {
|
||||
override fun PlayerPositionAndLookPacket.encode(dst: ByteBuf) {
|
||||
object OutgoingPlayerPositionPacketCodec :
|
||||
OutgoingPacketCodec<OutgoingPlayerPositionPacket>(0x34, OutgoingPlayerPositionPacket::class) {
|
||||
override fun OutgoingPlayerPositionPacket.encode(dst: ByteBuf) {
|
||||
dst.writeDouble(position.x)
|
||||
dst.writeDouble(position.y)
|
||||
dst.writeDouble(position.z)
|
|
@ -12,20 +12,23 @@ object PlayProtocol : Protocol(
|
|||
ChunkDataPacketCodec,
|
||||
ChunkLightDataPacketCodec,
|
||||
ClientSettingsPacketCodec,
|
||||
CompassTargetPacketCodec,
|
||||
DeclareCommandsPacketCodec,
|
||||
DeclareRecipesPacketCodec,
|
||||
DisconnectPacketCodec,
|
||||
IncomingKeepAlivePacketCodec,
|
||||
IncomingPlayerPositionPacketCodec,
|
||||
IncomingPluginMessagePacketCodec,
|
||||
JoinGamePacketCodec,
|
||||
OutgoingKeepAlivePacketCodec,
|
||||
OutgoingPlayerPositionPacketCodec,
|
||||
OutgoingPluginMessagePacketCodec,
|
||||
PlayerAbilitiesPacketCodec,
|
||||
PlayerInfoPacketCodec,
|
||||
PlayerPositionAndLookPacketCodec,
|
||||
PlayerLocationPacketCodec,
|
||||
PlayerOrientationPacketCodec,
|
||||
SelectedHotbarSlotPacketCodec,
|
||||
ServerDifficultyPacketCodec,
|
||||
SetCompassTargetPacketCodec,
|
||||
SetSelectedHotbarSlotPacketCodec,
|
||||
SpawnExperienceOrbPacketCodec,
|
||||
SpawnLivingEntityPacketCodec,
|
||||
SpawnObjectEntityPacketCodec,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.Location
|
||||
import space.uranos.net.packet.IncomingPacketCodec
|
||||
|
||||
object PlayerLocationPacketCodec :
|
||||
IncomingPacketCodec<PlayerLocationPacket>(0x12, PlayerLocationPacket::class) {
|
||||
override fun decode(msg: ByteBuf): PlayerLocationPacket = PlayerLocationPacket(
|
||||
Location(msg.readDouble(), msg.readDouble(), msg.readDouble()),
|
||||
msg.readBoolean()
|
||||
)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.packet.IncomingPacketCodec
|
||||
|
||||
object PlayerOrientationPacketCodec :
|
||||
IncomingPacketCodec<PlayerOrientationPacket>(0x14, PlayerOrientationPacket::class) {
|
||||
override fun decode(msg: ByteBuf): PlayerOrientationPacket = PlayerOrientationPacket(
|
||||
360 - msg.readFloat() % 360,
|
||||
msg.readFloat(),
|
||||
msg.readBoolean()
|
||||
)
|
||||
}
|
|
@ -8,9 +8,9 @@ package space.uranos.net.packet.play
|
|||
import io.netty.buffer.ByteBuf
|
||||
import space.uranos.net.packet.OutgoingPacketCodec
|
||||
|
||||
object SetSelectedHotbarSlotPacketCodec :
|
||||
OutgoingPacketCodec<SetSelectedHotbarSlotPacket>(0x3F, SetSelectedHotbarSlotPacket::class) {
|
||||
override fun SetSelectedHotbarSlotPacket.encode(dst: ByteBuf) {
|
||||
object SelectedHotbarSlotPacketCodec :
|
||||
OutgoingPacketCodec<SelectedHotbarSlotPacket>(0x3F, SelectedHotbarSlotPacket::class) {
|
||||
override fun SelectedHotbarSlotPacket.encode(dst: ByteBuf) {
|
||||
dst.writeByte(index)
|
||||
}
|
||||
}
|
|
@ -8,4 +8,5 @@ package space.uranos.net.packet.play
|
|||
import space.uranos.net.packet.OutgoingPacket
|
||||
import space.uranos.world.VoxelLocation
|
||||
|
||||
data class SetCompassTargetPacket(val target: VoxelLocation) : OutgoingPacket()
|
||||
// TODO: Remove "Set" prefix for all packet names
|
||||
data class CompassTargetPacket(val target: VoxelLocation) : OutgoingPacket()
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 space.uranos.Position
|
||||
import space.uranos.net.packet.IncomingPacket
|
||||
|
||||
/**
|
||||
* Combination of [PlayerLocationPacket] and [PlayerOrientationPacket].
|
||||
*/
|
||||
data class IncomingPlayerPositionPacket(
|
||||
val position: Position,
|
||||
val onGround: Boolean
|
||||
) : IncomingPacket()
|
|
@ -12,7 +12,7 @@ import kotlin.random.Random
|
|||
/**
|
||||
* Teleports the receiving player to the specified position.
|
||||
*/
|
||||
data class PlayerPositionAndLookPacket(
|
||||
data class OutgoingPlayerPositionPacket(
|
||||
val position: Position,
|
||||
val relativeX: Boolean = false,
|
||||
val relativeY: Boolean = false,
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 space.uranos.Location
|
||||
import space.uranos.net.packet.IncomingPacket
|
||||
|
||||
/**
|
||||
* Sent by the client to update the player's x, y and z coordinates on the server.
|
||||
*/
|
||||
data class PlayerLocationPacket(
|
||||
val location: Location,
|
||||
val onGround: Boolean
|
||||
) : IncomingPacket()
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 space.uranos.Position
|
||||
import space.uranos.net.packet.IncomingPacket
|
||||
|
||||
/**
|
||||
* Sent by the client to update the player's orientation on the server.
|
||||
*
|
||||
* @see [Position]
|
||||
*/
|
||||
data class PlayerOrientationPacket(
|
||||
/**
|
||||
* Yaw in degrees.
|
||||
*/
|
||||
val yaw: Float,
|
||||
/**
|
||||
* Pitch in degrees.
|
||||
*/
|
||||
val pitch: Float,
|
||||
val onGround: Boolean
|
||||
) : IncomingPacket()
|
|
@ -10,7 +10,7 @@ import space.uranos.net.packet.OutgoingPacket
|
|||
/**
|
||||
* Sent by the server to select a specific hotbar slot.
|
||||
*/
|
||||
data class SetSelectedHotbarSlotPacket(val index: Int) : OutgoingPacket() {
|
||||
data class SelectedHotbarSlotPacket(val index: Int) : OutgoingPacket() {
|
||||
init {
|
||||
if (index !in 0..8) throw IllegalArgumentException("index must be between 0 and 8")
|
||||
}
|
|
@ -5,15 +5,13 @@
|
|||
|
||||
package space.uranos
|
||||
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.*
|
||||
import space.uranos.logging.Logger
|
||||
import space.uranos.server.Server
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.LinkedHashSet
|
||||
import kotlin.coroutines.resume
|
||||
|
@ -21,8 +19,7 @@ import kotlin.coroutines.resume
|
|||
/**
|
||||
* Basically ExecutorService but for coroutines and with ticks.
|
||||
*/
|
||||
class UranosScheduler : Scheduler {
|
||||
private lateinit var executor: ScheduledExecutorService
|
||||
class UranosScheduler(private val executor: ScheduledExecutorService) : Scheduler {
|
||||
private val tasks = ConcurrentHashMap.newKeySet<Task<out Any>>()
|
||||
private val shutdownTasks = Collections.synchronizedSet(LinkedHashSet<suspend () -> Unit>())
|
||||
|
||||
|
@ -42,12 +39,13 @@ class UranosScheduler : Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
fun startTicking() {
|
||||
if (this::executor.isInitialized) throw IllegalStateException("Ticking was already started")
|
||||
private var future: ScheduledFuture<*>? = null
|
||||
|
||||
fun start() {
|
||||
if (future != null) throw IllegalStateException("Already started")
|
||||
val interval = 1000L / Server.TICKS_PER_SECOND
|
||||
|
||||
executor = Executors.newSingleThreadScheduledExecutor { r -> Thread(r, "Scheduler") }
|
||||
executor.scheduleAtFixedRate({
|
||||
future = executor.scheduleAtFixedRate({
|
||||
runBlocking {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
|
@ -76,15 +74,14 @@ class UranosScheduler : Scheduler {
|
|||
}, 0, interval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
private fun stopTicking() {
|
||||
if (!this::executor.isInitialized) throw IllegalStateException("Ticking was not started")
|
||||
|
||||
executor.shutdown()
|
||||
executor.awaitTermination(3, TimeUnit.SECONDS)
|
||||
private fun stop() {
|
||||
if (future == null) throw IllegalArgumentException("Not started")
|
||||
future!!.cancel(false)
|
||||
future = null
|
||||
}
|
||||
|
||||
suspend fun shutdown() {
|
||||
stopTicking()
|
||||
stop()
|
||||
shutdownTasks.forEach { it.invoke() }
|
||||
}
|
||||
|
||||
|
@ -110,12 +107,14 @@ class UranosScheduler : Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun <R : Any> runAfter(delay: Int, block: suspend () -> R): R {
|
||||
// TODO: Use the current coroutine context for the task execution
|
||||
override suspend fun <R : Any> runAfter(delay: Int, inServerThread: Boolean, block: suspend () -> R): R {
|
||||
lateinit var continuation: CancellableContinuation<R>
|
||||
val context = currentCoroutineContext()
|
||||
|
||||
val fn = suspend {
|
||||
continuation.resume(block())
|
||||
}
|
||||
val fn =
|
||||
if (inServerThread) suspend { continuation.resume(block()) }
|
||||
else suspend { withContext(context) { continuation.resume(block()) } }
|
||||
|
||||
val task = Task(fn, null, delay)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import space.uranos.event.UranosEventHandlerPositionManager
|
|||
import space.uranos.logging.Logger
|
||||
import space.uranos.logging.UranosLoggingOutputProvider
|
||||
import space.uranos.net.UranosSocketServer
|
||||
import space.uranos.player.UranosPlayer
|
||||
import space.uranos.plugin.UranosPluginManager
|
||||
import space.uranos.recipe.Recipe
|
||||
import space.uranos.server.Server
|
||||
|
@ -28,6 +29,7 @@ import space.uranos.world.Dimension
|
|||
import java.io.File
|
||||
import java.security.KeyPair
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
@ -46,12 +48,15 @@ class UranosServer internal constructor() : Server() {
|
|||
|
||||
val x509EncodedPublicKey: ByteArray = EncryptionUtils.generateX509Key(keyPair.public).encoded
|
||||
|
||||
override lateinit var serverThread: Thread
|
||||
private val scheduledExecutorService: ScheduledExecutorService =
|
||||
Executors.newSingleThreadScheduledExecutor { r -> Thread(r, "server").also { serverThread = it } }
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
CoroutineName("Server") + SupervisorJob() +
|
||||
Executors.newSingleThreadExecutor { r -> Thread(r, "server") }.asCoroutineDispatcher()
|
||||
CoroutineName("Server") + SupervisorJob() + scheduledExecutorService.asCoroutineDispatcher()
|
||||
|
||||
override val sessions by socketServer::sessions
|
||||
override val players get() = sessions.mapNotNull { it.player }
|
||||
override val players get() = sessions.mapNotNull { it.player as UranosPlayer? }
|
||||
|
||||
override val pluginManager = UranosPluginManager(this)
|
||||
override val serverDirectory: File =
|
||||
|
@ -67,7 +72,7 @@ class UranosServer internal constructor() : Server() {
|
|||
override val biomeRegistry = BiomeRegistry()
|
||||
|
||||
override val loggingOutputProvider = UranosLoggingOutputProvider
|
||||
override val scheduler = UranosScheduler()
|
||||
override val scheduler = UranosScheduler(scheduledExecutorService)
|
||||
|
||||
val config = ConfigLoader.Builder()
|
||||
.addPropertySource(
|
||||
|
@ -110,7 +115,7 @@ class UranosServer internal constructor() : Server() {
|
|||
socketServer.bind()
|
||||
logger info "Listening on ${config.host}:${config.port}"
|
||||
|
||||
scheduler.startTicking()
|
||||
scheduler.start()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -135,6 +135,7 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
|||
event.gameMode,
|
||||
initialWorldAndLocation.first,
|
||||
initialWorldAndLocation.second,
|
||||
event.headPitch,
|
||||
event.invulnerable,
|
||||
event.reducedDebugInfo,
|
||||
event.selectedHotbarSlot
|
||||
|
@ -160,8 +161,8 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
|||
state.uuid,
|
||||
state.gameMode,
|
||||
settings,
|
||||
state.world,
|
||||
state.position,
|
||||
state.headPitch,
|
||||
state.reducedDebugInfo,
|
||||
state.fieldOfView,
|
||||
state.canFly,
|
||||
|
@ -174,7 +175,7 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
|||
|
||||
session.state = Session.State.Joining(player)
|
||||
|
||||
session.send(SetSelectedHotbarSlotPacket(state.selectedHotbarSlot))
|
||||
session.send(SelectedHotbarSlotPacket(state.selectedHotbarSlot))
|
||||
|
||||
session.send(DeclareRecipesPacket(session.server.recipeRegistry.items.values))
|
||||
session.send(tagsPacket)
|
||||
|
@ -182,7 +183,7 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
|||
// session.send(DeclareCommandsPacket(session.server.commandRegistry.items.values))
|
||||
// UnlockRecipes
|
||||
|
||||
session.send(PlayerPositionAndLookPacket(state.position))
|
||||
session.send(OutgoingPlayerPositionPacket(state.position))
|
||||
|
||||
session.send(PlayerInfoPacket(PlayerInfoPacket.Action.AddPlayer((session.server.players + player).map {
|
||||
it.uuid to PlayerInfoPacket.Action.AddPlayer.Data(
|
||||
|
@ -203,14 +204,14 @@ class LoginAndJoinProcedure(val session: UranosSession) {
|
|||
)
|
||||
)
|
||||
|
||||
session.send(UpdateViewPositionPacket(Chunk.Key.from(player.position.toVoxelLocation())))
|
||||
session.send(UpdateViewPositionPacket(Chunk.Key.from(player.entity.position.toVoxelLocation())))
|
||||
|
||||
session.scheduleKeepAlivePacket(true)
|
||||
player.sendChunksAndLight()
|
||||
|
||||
// WorldBorder
|
||||
session.send(SetCompassTargetPacket(player.compassTarget))
|
||||
session.send(PlayerPositionAndLookPacket(state.position))
|
||||
session.send(CompassTargetPacket(player.compassTarget))
|
||||
session.send(OutgoingPlayerPositionPacket(state.position))
|
||||
|
||||
// TODO: Wait for ClientStatus(action=0) packet
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U
|
|||
val codec = currentProtocol!!.incomingPacketCodecsByID[packetID]
|
||||
|
||||
if (codec == null) {
|
||||
val message = "Received an unknown packet (ID: $packetID)"
|
||||
val message = "Received an unknown packet (ID: 0x${packetID.toString(16).padStart(2, '0')})"
|
||||
|
||||
if (server.config.developmentMode)
|
||||
logger warn "$message. This will cause the client to disconnect in production mode."
|
||||
|
|
|
@ -7,13 +7,13 @@ package space.uranos.player
|
|||
|
||||
import space.uranos.Position
|
||||
import space.uranos.chat.TextComponent
|
||||
import space.uranos.entity.PlayerEntity
|
||||
import space.uranos.net.Session
|
||||
import space.uranos.net.packet.play.ChunkDataPacket
|
||||
import space.uranos.net.packet.play.ChunkLightDataPacket
|
||||
import space.uranos.util.clampArgument
|
||||
import space.uranos.world.Chunk
|
||||
import space.uranos.world.VoxelLocation
|
||||
import space.uranos.world.World
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
|
@ -23,14 +23,14 @@ class UranosPlayer(
|
|||
override val uuid: UUID,
|
||||
override var gameMode: GameMode,
|
||||
override var settings: Player.Settings,
|
||||
override val world: World,
|
||||
override var position: Position,
|
||||
position: Position,
|
||||
headPitch: Float,
|
||||
override var reducedDebugInfo: Boolean,
|
||||
override var fieldOfView: Float,
|
||||
override var canFly: Boolean,
|
||||
override var flying: Boolean,
|
||||
override var flyingSpeed: Float,
|
||||
override var invulnerable: Boolean,
|
||||
override var vulnerable: Boolean,
|
||||
override var compassTarget: VoxelLocation,
|
||||
selectedHotbarSlot: Int
|
||||
) : Player {
|
||||
|
@ -51,18 +51,20 @@ class UranosPlayer(
|
|||
updateCurrentlyViewedChunks()
|
||||
}
|
||||
|
||||
override val entity: PlayerEntity = PlayerEntity(position, this, headPitch)
|
||||
|
||||
/**
|
||||
* Sets [currentlyViewedChunks] to all chunks in the view distance.
|
||||
*/
|
||||
private fun updateCurrentlyViewedChunks() {
|
||||
val (centerX, centerZ) = Chunk.Key.from(position.toVoxelLocation())
|
||||
val (centerX, centerZ) = Chunk.Key.from(entity.position.toVoxelLocation())
|
||||
|
||||
val edgeLength = settings.viewDistance + 1
|
||||
|
||||
currentlyViewedChunks = buildList(edgeLength * edgeLength) {
|
||||
for (x in (centerX - settings.viewDistance)..(centerX + settings.viewDistance)) {
|
||||
for (z in (centerZ - settings.viewDistance)..(centerZ + settings.viewDistance)) {
|
||||
add(world.getChunk(Chunk.Key(x, z)))
|
||||
add(entity.safeWorld.getChunk(Chunk.Key(x, z)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue