All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.valaphee.netcode.mcbe.network.packet.WorldPacket.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2021-2022, Valaphee.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.valaphee.netcode.mcbe.network.packet

import com.fasterxml.jackson.module.kotlin.readValue
import com.valaphee.foundry.math.Float2
import com.valaphee.foundry.math.Float3
import com.valaphee.foundry.math.Int3
import com.valaphee.netcode.mcbe.network.GamePublishMode
import com.valaphee.netcode.mcbe.network.Packet
import com.valaphee.netcode.mcbe.network.PacketBuffer
import com.valaphee.netcode.mcbe.network.PacketHandler
import com.valaphee.netcode.mcbe.network.PacketReader
import com.valaphee.netcode.mcbe.network.Restrict
import com.valaphee.netcode.mcbe.network.Restriction
import com.valaphee.netcode.mcbe.world.Difficulty
import com.valaphee.netcode.mcbe.world.Dimension
import com.valaphee.netcode.mcbe.world.Experiment
import com.valaphee.netcode.mcbe.world.GameMode
import com.valaphee.netcode.mcbe.world.GameRule
import com.valaphee.netcode.mcbe.world.block.Block
import com.valaphee.netcode.mcbe.world.entity.player.Rank
import com.valaphee.netcode.mcbe.world.item.Item
import com.valaphee.netcode.mcbe.world.readExperiment
import com.valaphee.netcode.mcbe.world.readGameRule
import com.valaphee.netcode.mcbe.world.readGameRulePre440
import com.valaphee.netcode.mcbe.world.writeExperiment
import com.valaphee.netcode.mcbe.world.writeGameRule
import com.valaphee.netcode.mcbe.world.writeGameRulePre440
import com.valaphee.netcode.util.safeGet
import com.valaphee.netcode.util.safeList
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import it.unimi.dsi.fastutil.ints.Int2ObjectMap
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import java.io.InputStream
import java.io.OutputStream

/**
 * @author Kevin Ludwig
 */
@Restrict(Restriction.ToClient)
class WorldPacket(
    val uniqueEntityId: Long,
    val runtimeEntityId: Long,
    val gameMode: GameMode,
    var position: Float3?, // needed for je-be protocol translation
    var rotation: Float2?, // needed for je-be protocol translation
    val seed: Int,
    val biomeType: BiomeType,
    val biomeName: String,
    val dimension: Dimension,
    val generatorId: Int,
    val defaultGameMode: GameMode,
    val difficulty: Difficulty,
    var defaultSpawn: Int3?, // needed for je-be protocol translation
    val achievementsDisabled: Boolean,
    val time: Int,
    val educationEditionOffer: EducationEditionOffer,
    val educationModeId: Int,
    val educationFeaturesEnabled: Boolean,
    val educationProductId: String,
    val rainLevel: Float,
    val thunderLevel: Float,
    val platformLockedContentConfirmed: Boolean,
    val multiplayerGame: Boolean,
    val broadcastingToLan: Boolean,
    val xboxLiveBroadcastMode: GamePublishMode,
    val platformBroadcastMode: GamePublishMode,
    val commandsEnabled: Boolean,
    val resourcePacksRequired: Boolean,
    val gameRules: List>,
    val experiments: List,
    val experimentsPreviouslyToggled: Boolean,
    val bonusChestEnabled: Boolean,
    val startingWithMap: Boolean,
    val defaultRank: Rank,
    val serverChunkTickRange: Int,
    val behaviorPackLocked: Boolean,
    val resourcePackLocked: Boolean,
    val fromLockedWorldTemplate: Boolean,
    val usingMsaGamertagsOnly: Boolean,
    val fromWorldTemplate: Boolean,
    val worldTemplateOptionLocked: Boolean,
    val onlySpawningV1Villagers: Boolean,
    val version: String,
    val limitedWorldRadius: Int,
    val limitedWorldHeight: Int,
    val v2Nether: Boolean,
    val experimentalGameplay: Boolean,
    val worldId: String,
    val worldName: String,
    val premiumWorldTemplateId: String,
    val trial: Boolean,
    val movementAuthoritative: AuthoritativeMovement,
    val movementRewindHistory: Int,
    val blockBreakingServerAuthoritative: Boolean,
    val tick: Long,
    val enchantmentSeed: Int,
    val blocksData: Any?,
    val blocks: List?,
    val items: Int2ObjectMap,
    val multiplayerCorrelationId: String,
    val inventoriesServerAuthoritative: Boolean,
    val engineVersion: String,
    val blocksChecksum: Long
) : Packet() {
    enum class BiomeType {
        Default, UserDefined
    }

    companion object GeneratorId {
        const val FiniteOverworld = 0
        const val Overworld = 1
        const val Superflat = 2
        const val TheNether = 3
        const val TheEnd = 4
    }

    enum class EducationEditionOffer {
        None, EverywhereExceptChina, China
    }

    enum class AuthoritativeMovement {
        Client, Server, ServerWithRewind
    }

    override val id get() = 0x0B

    override fun write(buffer: PacketBuffer, version: Int) {
        buffer.writeVarLong(uniqueEntityId)
        buffer.writeVarULong(runtimeEntityId)
        buffer.writeVarInt(gameMode.ordinal)
        buffer.writeFloat3(position!!)
        buffer.writeFloat2(rotation!!)
        buffer.writeVarInt(seed)
        if (version >= 407) {
            buffer.writeShortLE(biomeType.ordinal)
            buffer.writeString(biomeName)
        }
        buffer.writeVarInt(dimension.ordinal)
        buffer.writeVarInt(generatorId)
        buffer.writeVarInt(defaultGameMode.ordinal)
        buffer.writeVarInt(difficulty.ordinal)
        buffer.writeInt3UnsignedY(defaultSpawn!!)
        buffer.writeBoolean(achievementsDisabled)
        buffer.writeVarInt(time)
        if (version >= 407) {
            buffer.writeVarInt(educationEditionOffer.ordinal)
            if (version < 419) buffer.writeByte(educationModeId)
            buffer.writeBoolean(educationFeaturesEnabled)
            buffer.writeString(educationProductId)
        } else {
            buffer.writeBoolean(educationFeaturesEnabled)
            buffer.writeVarInt(educationEditionOffer.ordinal)
        }
        buffer.writeFloatLE(rainLevel)
        buffer.writeFloatLE(thunderLevel)
        buffer.writeBoolean(platformLockedContentConfirmed)
        buffer.writeBoolean(multiplayerGame)
        buffer.writeBoolean(broadcastingToLan)
        buffer.writeVarInt(xboxLiveBroadcastMode.ordinal)
        buffer.writeVarInt(platformBroadcastMode.ordinal)
        buffer.writeBoolean(commandsEnabled)
        buffer.writeBoolean(resourcePacksRequired)
        buffer.writeVarUInt(gameRules.size)
        if (version >= 440) gameRules.forEach(buffer::writeGameRule) else gameRules.forEach(buffer::writeGameRulePre440)
        if (version >= 419) {
            experiments.let {
                buffer.writeIntLE(it.size)
                it.forEach(buffer::writeExperiment)
            }
            buffer.writeBoolean(experimentsPreviouslyToggled)
        }
        buffer.writeBoolean(bonusChestEnabled)
        buffer.writeBoolean(startingWithMap)
        buffer.writeVarInt(defaultRank.ordinal)
        buffer.writeIntLE(serverChunkTickRange)
        buffer.writeBoolean(behaviorPackLocked)
        buffer.writeBoolean(resourcePackLocked)
        buffer.writeBoolean(fromLockedWorldTemplate)
        buffer.writeBoolean(usingMsaGamertagsOnly)
        buffer.writeBoolean(fromWorldTemplate)
        buffer.writeBoolean(worldTemplateOptionLocked)
        buffer.writeBoolean(onlySpawningV1Villagers)
        buffer.writeString(this.version)
        if (version >= 407) {
            buffer.writeIntLE(limitedWorldRadius)
            buffer.writeIntLE(limitedWorldHeight)
            if (version >= 465) {
                buffer.writeString("")
                buffer.writeString("")
            }
            buffer.writeBoolean(v2Nether)
            buffer.writeBoolean(experimentalGameplay)
            if (version >= 419 && experimentalGameplay) buffer.writeBoolean(true)
        }
        buffer.writeString(worldId)
        buffer.writeString(worldName)
        buffer.writeString(premiumWorldTemplateId)
        buffer.writeBoolean(trial)
        if (version >= 419) buffer.writeVarInt(movementAuthoritative.ordinal) else buffer.writeBoolean(movementAuthoritative == AuthoritativeMovement.Server)
        if (version >= 428) {
            buffer.writeVarInt(movementRewindHistory)
            buffer.writeBoolean(blockBreakingServerAuthoritative)
        }
        buffer.writeLongLE(tick)
        buffer.writeVarInt(enchantmentSeed)
        if (version >= 419) {
            buffer.writeVarUInt(blocks!!.size)
            val nbtObjectWriter = buffer.nbtVarIntObjectMapper.writerFor(Block::class.java).withAttribute("version", version)
            blocks.forEach {
                buffer.writeString(it.description.key)
                nbtObjectWriter.writeValue(ByteBufOutputStream(buffer) as OutputStream, it)
            }
        } else buffer.nbtVarIntObjectMapper.writeValue(ByteBufOutputStream(buffer) as OutputStream, blocksData)
        buffer.writeVarUInt(items.size)
        items.forEach { (id, item) ->
            buffer.writeString(item.key)
            buffer.writeShortLE(id)
            if (version >= 419) buffer.writeBoolean(item.componentBased)
        }
        buffer.writeString(multiplayerCorrelationId)
        if (version >= 407) buffer.writeBoolean(inventoriesServerAuthoritative)
        if (version >= 440) buffer.writeString(engineVersion)
        if (version >= 475) buffer.writeLongLE(blocksChecksum)
    }

    override fun handle(handler: PacketHandler) = handler.world(this)

    override fun toString() = "WorldPacket(uniqueEntityId=$uniqueEntityId, runtimeEntityId=$runtimeEntityId, gameMode=$gameMode, position=$position, rotation=$rotation, seed=$seed, biomeType=$biomeType, biomeName='$biomeName', dimension=$dimension, generatorId=$generatorId, defaultGameMode=$defaultGameMode, difficulty=$difficulty, defaultSpawn=$defaultSpawn, achievementsDisabled=$achievementsDisabled, time=$time, educationEditionOffer=$educationEditionOffer, educationModeId=$educationModeId, educationFeaturesEnabled=$educationFeaturesEnabled, educationProductId='$educationProductId', rainLevel=$rainLevel, thunderLevel=$thunderLevel, platformLockedContentConfirmed=$platformLockedContentConfirmed, multiplayerGame=$multiplayerGame, broadcastingToLan=$broadcastingToLan, xboxLiveBroadcastMode=$xboxLiveBroadcastMode, platformBroadcastMode=$platformBroadcastMode, commandsEnabled=$commandsEnabled, resourcePacksRequired=$resourcePacksRequired, gameRules=$gameRules, experiments=$experiments, experimentsPreviouslyToggled=$experimentsPreviouslyToggled, bonusChestEnabled=$bonusChestEnabled, startingWithMap=$startingWithMap, defaultRank=$defaultRank, serverChunkTickRange=$serverChunkTickRange, behaviorPackLocked=$behaviorPackLocked, resourcePackLocked=$resourcePackLocked, fromLockedWorldTemplate=$fromLockedWorldTemplate, usingMsaGamertagsOnly=$usingMsaGamertagsOnly, fromWorldTemplate=$fromWorldTemplate, worldTemplateOptionLocked=$worldTemplateOptionLocked, onlySpawningV1Villagers=$onlySpawningV1Villagers, version='$version', limitedWorldRadius=$limitedWorldRadius, limitedWorldHeight=$limitedWorldHeight, v2Nether=$v2Nether, experimentalGameplay=$experimentalGameplay, worldId='$worldId', worldName='$worldName', premiumWorldTemplateId='$premiumWorldTemplateId', trial=$trial, movementAuthoritative=$movementAuthoritative, movementRewindHistory=$movementRewindHistory, blockBreakingServerAuthoritative=$blockBreakingServerAuthoritative, tick=$tick, enchantmentSeed=$enchantmentSeed, blocksData=$blocksData, blocks=$blocks, items=$items, multiplayerCorrelationId='$multiplayerCorrelationId', inventoriesServerAuthoritative=$inventoriesServerAuthoritative, engineVersion='$engineVersion', blocksChecksum=$blocksChecksum)"
}

/**
 * @author Kevin Ludwig
 */
object WorldPacketReader : PacketReader {
    override fun read(buffer: PacketBuffer, version: Int): WorldPacket {
        val uniqueEntityId = buffer.readVarLong()
        val runtimeEntityId = buffer.readVarULong()
        val gameMode = GameMode.values()[buffer.readVarInt()]
        val position = buffer.readFloat3()
        val rotation = buffer.readFloat2()
        val seed = buffer.readVarInt()
        val biomeType: WorldPacket.BiomeType
        val biomeName: String
        if (version >= 407) {
            biomeType = WorldPacket.BiomeType.values()[buffer.readShortLE().toInt()]
            biomeName = buffer.readString()
        } else {
            biomeType = WorldPacket.BiomeType.Default
            biomeName = ""
        }
        val dimension = Dimension.values()[buffer.readVarInt()]
        val generatorId = buffer.readVarInt()
        val defaultGameMode = GameMode.values()[buffer.readVarInt()]
        val difficulty = Difficulty.values()[buffer.readVarInt()]
        val defaultSpawn = buffer.readInt3UnsignedY()
        val achievementsDisabled = buffer.readBoolean()
        val time = buffer.readVarInt()
        val educationEditionOffer: WorldPacket.EducationEditionOffer
        val educationModeId: Int
        val educationFeaturesEnabled: Boolean
        val educationProductId: String?
        if (version >= 407) {
            educationEditionOffer = WorldPacket.EducationEditionOffer.values()[buffer.readVarInt()]
            educationModeId = if (version < 419) buffer.readByte().toInt() else 0
            educationFeaturesEnabled = buffer.readBoolean()
            educationProductId = buffer.readString()
        } else {
            educationFeaturesEnabled = buffer.readBoolean()
            educationModeId = 0
            educationEditionOffer = WorldPacket.EducationEditionOffer.values()[buffer.readVarInt()]
            educationProductId = ""
        }
        val rainLevel = buffer.readFloatLE()
        val thunderLevel = buffer.readFloatLE()
        val platformLockedContentConfirmed = buffer.readBoolean()
        val multiplayerGame = buffer.readBoolean()
        val broadcastingToLan = buffer.readBoolean()
        val xboxLiveBroadcastMode = GamePublishMode.values()[buffer.readVarInt()]
        val platformBroadcastMode = GamePublishMode.values()[buffer.readVarInt()]
        val commandsEnabled = buffer.readBoolean()
        val resourcePacksRequired = buffer.readBoolean()
        val gameRules = if (version >= 440) safeList(buffer.readVarUInt()) { buffer.readGameRule() } else safeList(buffer.readVarUInt()) { buffer.readGameRulePre440() }
        val experiments: List
        val experimentsPreviouslyToggled: Boolean
        if (version >= 419) {
            experiments = safeList(buffer.readIntLE()) { buffer.readExperiment() }
            experimentsPreviouslyToggled = buffer.readBoolean()
        } else {
            experiments = emptyList()
            experimentsPreviouslyToggled = false
        }
        val bonusChestEnabled = buffer.readBoolean()
        val startingWithMap = buffer.readBoolean()
        val defaultPlayerPermission = Rank.values()[buffer.readVarInt()]
        val serverChunkTickRange = buffer.readIntLE()
        val behaviorPackLocked = buffer.readBoolean()
        val resourcePackLocked = buffer.readBoolean()
        val fromLockedWorldTemplate = buffer.readBoolean()
        val usingMsaGamertagsOnly = buffer.readBoolean()
        val fromWorldTemplate = buffer.readBoolean()
        val worldTemplateOptionLocked = buffer.readBoolean()
        val onlySpawningV1Villagers = buffer.readBoolean()
        val version0 = buffer.readString()
        val limitedWorldRadius: Int
        val limitedWorldHeight: Int
        val v2Nether: Boolean
        val experimentalGameplay: Boolean
        if (version >= 407) {
            limitedWorldRadius = buffer.readIntLE()
            limitedWorldHeight = buffer.readIntLE()
            v2Nether = buffer.readBoolean()
            if (version >= 465) {
                buffer.readString()
                buffer.readString()
            }
            experimentalGameplay = if (version >= 419) {
                if (buffer.readBoolean()) buffer.readBoolean() else false
            } else buffer.readBoolean()
        } else {
            limitedWorldRadius = 0
            limitedWorldHeight = 0
            v2Nether = false
            experimentalGameplay = false
        }
        val levelId = buffer.readString()
        val worldName = buffer.readString()
        val premiumWorldTemplateId = buffer.readString()
        val trial = buffer.readBoolean()
        val movementAuthoritative = when {
            version >= 419 -> WorldPacket.AuthoritativeMovement.values().safeGet(buffer.readVarInt())
            buffer.readBoolean() -> WorldPacket.AuthoritativeMovement.Server
            else -> WorldPacket.AuthoritativeMovement.Client
        }
        val movementRewindHistory: Int
        val blockBreakingServerAuthoritative: Boolean
        if (version >= 428) {
            movementRewindHistory = buffer.readVarInt()
            blockBreakingServerAuthoritative = buffer.readBoolean()
        } else {
            movementRewindHistory = 0
            blockBreakingServerAuthoritative = false
        }
        val tick = buffer.readLongLE()
        val enchantmentSeed = buffer.readVarInt()
        val blocksData: Any?
        val blocks: List?
        if (version >= 419) {
            blocksData = null
            val nbtObjectReader = buffer.nbtVarIntObjectMapper.readerFor(Block::class.java).withAttribute("version", version)
            blocks = safeList(buffer.readVarUInt()) { nbtObjectReader.readValue(ByteBufInputStream(buffer) as InputStream) }
        } else {
            blocksData = buffer.nbtVarIntObjectMapper.readValue(ByteBufInputStream(buffer))
            blocks = null
        }
        val itemCount = buffer.readVarUInt()
        val items = Int2ObjectOpenHashMap().apply {
            repeat(itemCount) {
                val key = buffer.readString()
                val id = buffer.readShortLE()
                this[id.toInt()] = Item(key, version >= 419 && buffer.readBoolean())
            }
        }
        val multiplayerCorrelationId = buffer.readString()
        val inventoriesServerAuthoritative = buffer.readBoolean()
        val engineVersion = if (version >= 440) buffer.readString() else ""
        val blocksChecksum = if (version >= 475) buffer.readLongLE() else 0
        return WorldPacket(uniqueEntityId, runtimeEntityId, gameMode, position, rotation, seed, biomeType, biomeName, dimension, generatorId, defaultGameMode, difficulty, defaultSpawn, achievementsDisabled, time, educationEditionOffer, educationModeId, educationFeaturesEnabled, educationProductId, rainLevel, thunderLevel, platformLockedContentConfirmed, multiplayerGame, broadcastingToLan, xboxLiveBroadcastMode, platformBroadcastMode, commandsEnabled, resourcePacksRequired, gameRules, experiments, experimentsPreviouslyToggled, bonusChestEnabled, startingWithMap, defaultPlayerPermission, serverChunkTickRange, behaviorPackLocked, resourcePackLocked, fromLockedWorldTemplate, usingMsaGamertagsOnly, fromWorldTemplate, worldTemplateOptionLocked, onlySpawningV1Villagers, version0, limitedWorldRadius, limitedWorldHeight, v2Nether, experimentalGameplay, levelId, worldName, premiumWorldTemplateId, trial, movementAuthoritative, movementRewindHistory, blockBreakingServerAuthoritative, tick, enchantmentSeed, blocksData, blocks, items, multiplayerCorrelationId, inventoriesServerAuthoritative, engineVersion, blocksChecksum)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy