Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions buildSrc/src/main/kotlin/core-convention.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ configurations {
}
}

tasks.withType<org.gradle.jvm.tasks.Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
tasks.withType<org.gradle.jvm.tasks.Jar>().configureEach {
if (this !is com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}

tasks {
shadowJar {
mergeServiceFiles()
duplicatesStrategy = DuplicatesStrategy.EXCLUDE

val relocationPrefix: String by project
relocate("net.kyori.adventure.nbt", "$relocationPrefix.kyori.nbt") {
Expand All @@ -78,8 +79,10 @@ tasks {
}

javadoc {
isFailOnError = false
val options = options as StandardJavadocDocletOptions
options.use()
options.tags("apiNote:a:API Note:")
options.addStringOption("Xdoclint:none", "-quiet")
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=26.2
group=dev.slne.surf.api
version=3.29.0
version=3.30.0
relocationPrefix=dev.slne.surf.api.libs
snapshot=false
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.slne.surf.api.paper.server.nms.v1_21_11.bridges

import ca.spottedleaf.moonrise.common.PlatformHooks
import ca.spottedleaf.moonrise.common.util.TickThread
import com.mojang.brigadier.exceptions.CommandSyntaxException
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException
Expand All @@ -12,11 +13,30 @@ import dev.slne.surf.api.paper.util.chunkX
import dev.slne.surf.api.paper.util.chunkZ
import io.papermc.paper.math.FinePosition
import net.kyori.adventure.nbt.CompoundBinaryTag
import net.kyori.adventure.text.logger.slf4j.ComponentLogger
import net.minecraft.SharedConstants
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtIo
import net.minecraft.nbt.NbtUtils
import net.minecraft.server.MinecraftServer
import net.minecraft.server.commands.SummonCommand
import net.minecraft.util.ProblemReporter
import net.minecraft.util.datafix.fixes.References
import net.minecraft.world.entity.EntitySpawnReason
import net.minecraft.world.entity.Pose.CODEC
import net.minecraft.world.level.storage.TagValueInput
import net.minecraft.world.level.storage.TagValueOutput
import org.bukkit.World
import org.bukkit.entity.Entity
import org.bukkit.entity.EntityType
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.*
import kotlin.jvm.optionals.getOrNull
import net.minecraft.world.entity.Entity as NmsEntity
import net.minecraft.world.entity.EntityType as NmsEntityType

@NmsUseWithCaution
class V1_21_11SurfPaperNmsEntityBridgeImpl : SurfPaperNmsEntityBridge {
Expand Down Expand Up @@ -59,4 +79,126 @@ class V1_21_11SurfPaperNmsEntityBridgeImpl : SurfPaperNmsEntityBridge {
override fun getById(world: World, id: Int): Entity? {
return world.toNms().getEntity(id)?.bukkitEntity
}

override fun captureVehicleNbt(rootVehicle: Entity): ByteArray {
val nmsEntity = rootVehicle.toNms()
TickThread.ensureTickThread(nmsEntity, "Cannot capture vehicle NBT asynchronously")

return ProblemReporter.ScopedCollector(VEHICLE_LOGGER).use { reporter ->
val output = TagValueOutput.createWithContext(reporter, nmsEntity.registryAccess())
nmsEntity.save(output)

val additions = output.child("surf-api-addtions")
nmsEntity.passengersAndSelf
.filter { it.type.canSerialize() }
.forEach { entity ->
val tag = additions.child(entity.uuid.toString())

tag.store("Pose", CODEC, entity.pose)

val living = entity.asLivingEntity()
if (living != null) {
tag.putInt("ArrowCount", living.arrowCount)
tag.putInt("Stingers", living.stingerCount)
}
}

serializeNbtToBytes(output.buildResult())
}
}

override fun restoreVehicle(
world: World,
nbt: ByteArray,
x: Double,
y: Double,
z: Double,
yaw: Float,
pitch: Float
): Entity? {
val level = world.toNms()
TickThread.ensureTickThread(level, x, z, "Cannot restore vehicle asynchronously")

var tag = deserializeNbtFromBytes(nbt)
val dataVersion = NbtUtils.getDataVersion(tag, 0)
tag = PlatformHooks.get().convertNBT(
References.ENTITY,
MinecraftServer.getServer().fixerUpper,
tag,
dataVersion,
SharedConstants.getCurrentVersion().dataVersion().version
)

val entity = ProblemReporter.ScopedCollector(VEHICLE_LOGGER).use { reporter ->
val input = TagValueInput.create(reporter, level.registryAccess(), tag)
val additions = input.child("surf-api-addtions").map { additionsInput ->
require(additionsInput is TagValueInput)
additionsInput.input.keySet()
.mapNotNull(fun(uuidStr: String): Pair<UUID, (NmsEntity) -> Unit>? {
val uuid = runCatching { UUID.fromString(uuidStr) }.getOrNull() ?: return null
val addition = additionsInput.child(uuidStr).getOrNull() ?: return null

val pose = addition.read("Pose", CODEC).getOrNull()
val arrowCount = addition.getInt("ArrowCount").getOrNull()
val stingerCount = addition.getInt("Stingers").getOrNull()

return uuid to { entity ->
if (pose != null) entity.pose = pose
if (arrowCount != null) entity.asLivingEntity()?.arrowCount = arrowCount
if (stingerCount != null) entity.asLivingEntity()?.stingerCount = stingerCount
}
})
.toMap()
}.getOrNull().orEmpty()

NmsEntityType.loadEntityRecursive(
input,
level,
EntitySpawnReason.LOAD
) { entity ->
additions[entity.uuid]?.invoke(entity)
entity
}
} ?: return null

entity.snapTo(x, y, z, yaw, pitch)

if (!level.tryAddFreshEntityWithPassengers(entity)) {
return null
}

return entity.bukkitEntity
}

private fun deserializeNbtFromBytes(data: ByteArray): CompoundTag {
val compound: CompoundTag
try {
compound = NbtIo.readCompressed(ByteArrayInputStream(data), NbtAccounter.unlimitedHeap())
} catch (ex: IOException) {
throw RuntimeException(ex)
}

return compound
}

private fun serializeNbtToBytes(compound: CompoundTag): ByteArray {
val baos = ByteArrayOutputStream()
try {
NbtIo.writeCompressed(compound, baos)
} catch (ex: IOException) {
throw RuntimeException(ex)
}

return baos.toByteArray()
}

companion object {
private val VEHICLE_LOGGER = ComponentLogger.logger("SurfPaperNmsEntityBridge Vehicle")
}

private data class VehicleTreeNbt(
val tag: CompoundTag,
val rootUuid: UUID,
val entityUuids: Set<UUID>,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import dev.slne.surf.api.paper.nms.common.dummy.DummyEntityEquipment
import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNms
import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections
import io.papermc.paper.adventure.PaperAdventure
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -27,6 +28,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtIo
import net.minecraft.network.chat.*
import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.players.NameAndId
import net.minecraft.util.ProblemReporter
Expand Down Expand Up @@ -90,6 +92,53 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge {
}
}

@Suppress("USELESS_ELVIS")
override fun resyncVehicleState(player: Player, swallowExceptions: Boolean): Int {
val nmsPlayer = player.toNms()
val connection = nmsPlayer.connection ?: return 0
val chunkMap = nmsPlayer.level().chunkSource.chunkMap

val root = nmsPlayer.rootVehicle
if (root === nmsPlayer && nmsPlayer.passengers.isEmpty()) {
return 0
}

val chain = ObjectLinkedOpenHashSet(root.passengersAndSelf.iterator())
chain.addFirst(root)

var resynced = 0
for (entity in chain) {
if (entity === nmsPlayer) continue
val tracker = chunkMap.entityMap.get(entity.id) ?: continue
try {
tracker.seenBy.add(connection)
tracker.serverEntity.removePairing(nmsPlayer)
tracker.serverEntity.addPairing(nmsPlayer)
resynced++
} catch (e: Throwable) {
if (!swallowExceptions) throw e
}
}

for (entity in chain) {
if (entity.passengers.isEmpty()) continue
try {
connection.send(ClientboundSetPassengersPacket(entity))
} catch (e: Throwable) {
if (!swallowExceptions) throw e
}
}

return resynced
}

@Suppress("USELESS_ELVIS")
override fun resyncPlayerState(player: Player) {
val nmsPlayer = player.toNms()
nmsPlayer.connection ?: return
nmsPlayer.onUpdateAbilities()
}

override fun getRemoteChatSessionData(player: Player): RemoteChatSessionData? {
val session = player.toNms().chatSession?.asData() ?: return null
val profilePublicKey = session.profilePublicKey()
Expand Down Expand Up @@ -432,4 +481,4 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge {
private val OFFLINE_INVENTORY_EDIT_LOGGER = ComponentLogger.logger("OfflinePlayer Inventory Edit")
private val CHAT_LOGGER = ComponentLogger.logger("SurfPaperNmsPlayerBridge Chat")
}
}
}
Loading