Compare commits

..

9 Commits

Author SHA1 Message Date
Will FP
a55407be51 Added 1.21.6 support 2025-06-20 10:20:03 +01:00
Will FP
1ffadf8927 Merge pull request #399
Fix FabledSkyBlock errors on new versions of the plugin
2025-06-07 14:58:54 +01:00
xsmeths
12f8117252 Update stripped FSB 4.2.2 lib jar (compile only) 2025-06-07 09:40:53 +01:00
xsmeths
57c365c158 Add stripped FSB 4.2.2 lib jar (compile only) 2025-06-07 08:30:00 +01:00
smeths
7bd6e4be90 Update AntigriefFabledSkyBlock.kt
change import to match newest versions of FSB
2025-06-07 03:49:59 +01:00
Will FP
ba9210e203 Updated cache 2025-06-03 15:17:00 +01:00
Will FP
a2a3ade0ae Cleaned up PacketWindowItems.kt 2025-06-03 15:15:42 +01:00
Will FP
08455b3694 Fixed 1.21.5 support 2025-06-02 15:01:33 +01:00
Will FP
76144013d5 Added 1.21.5 support, bumped version 2025-06-02 13:52:11 +01:00
52 changed files with 2122 additions and 16 deletions

View File

@@ -21,7 +21,7 @@ jobs:
java-version: 21
- name: Setup build cache
uses: actions/cache@v2.1.6
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}

View File

@@ -19,7 +19,7 @@ jobs:
java-version: 21
- name: Setup build cache
uses: actions/cache@v2.1.6
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}

View File

@@ -36,6 +36,8 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_21", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_4", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_5", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21_6", configuration = "reobf"))
}
allprojects {

View File

@@ -6,10 +6,51 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a packet.
*
* @param handle The NMS handle.
*/
public record Packet(@NotNull Object handle) {
public class Packet {
/**
* The packet handle.
*/
private Object handle;
/**
* Create a new packet.
*
* @param handle The packet handle.
*/
public Packet(@NotNull final Object handle) {
this.handle = handle;
}
/**
* Get the packet handle.
*
* @return The packet handle.
*/
public Object getHandle() {
return handle;
}
/**
* Set the packet handle.
*
* @param handle The packet handle.
*/
public void setHandle(@NotNull final Object handle) {
this.handle = handle;
}
/**
* Get the packet handle, compatible with the old record-based packet system.
*
* @return The packet handle.
* @deprecated Use {@link #getHandle()} instead.
*/
@Deprecated
public Object handle() {
return handle;
}
/**
* Send to a player.
*

View File

@@ -31,7 +31,9 @@ public final class ProxyConstants {
"v1_20_R3",
"v1_21",
"v1_21_3",
"v1_21_4"
"v1_21_4",
"v1_21_5",
"v1_21_6"
);
private ProxyConstants() {

View File

@@ -1,5 +1,5 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.14" apply false
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" apply false
}

View File

@@ -23,7 +23,7 @@ class EcoChannelDuplexHandler(
event.handleReceive()
if (!event.isCancelled) {
super.channelRead(ctx, msg)
super.channelRead(ctx, event.packet.handle)
}
} else {
super.channelRead(ctx, msg)
@@ -39,7 +39,7 @@ class EcoChannelDuplexHandler(
event.handleSend()
if (!event.isCancelled) {
super.write(ctx, msg, promise)
super.write(ctx, event.packet.handle, promise)
}
} else {
super.write(ctx, msg, promise)

View File

@@ -15,7 +15,7 @@ import org.bukkit.inventory.ItemStack
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
class PacketWindowItems(
open class PacketWindowItems(
private val plugin: EcoPlugin
) : PacketListener {
private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>()
@@ -52,7 +52,7 @@ class PacketWindowItems(
}
private fun modifyWindowItems(
protected fun modifyWindowItems(
itemStacks: MutableList<ItemStack>,
windowId: Int,
player: Player

View File

@@ -144,7 +144,7 @@ open class NewEcoFastItemStack(
override fun getDisplayName(): String = displayNameComponent.toLegacy()
private fun <T> net.minecraft.world.item.ItemStack.modifyComponent(
protected fun <T> net.minecraft.world.item.ItemStack.modifyComponent(
component: DataComponentType<T>,
modifier: (T) -> T
) {

View File

@@ -6,6 +6,7 @@ import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object NewItemsPacketSetCreativeSlot : PacketListener {

View File

@@ -0,0 +1,52 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:modern"))
implementation(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_21_5.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_21_5.minimessage"
)
}
compileJava {
options.release.set(21)
}
compileKotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,171 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftMob
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.inventory.CraftMetaArmor
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.craftbukkit.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = CraftMetaArmor::class.java
.superclass // Access CraftMetaItem
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = 8
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.keySet()
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").get().toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.keySet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.remove(DataComponents.CUSTOM_DATA)
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
override fun toNMS(component: Component): net.minecraft.network.chat.Component {
val json = JSONComponentSerializer.json().serialize(component)
val holderLookupProvider = (Bukkit.getServer() as CraftServer).server.registryAccess()
return net.minecraft.network.chat.Component.Serializer.fromJson(json, holderLookupProvider)!!
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.entity.CraftLivingEntity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import java.util.Optional
@Suppress("UNCHECKED_CAST")
class DisplayName : DisplayNameProxy {
private val displayNameAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[2]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Optional<net.minecraft.network.chat.Component>>
private val customNameVisibleAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[3]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Boolean>
override fun setClientsideDisplayName(
entity: LivingEntity,
player: Player,
displayName: Component,
visible: Boolean
) {
if (entity !is CraftLivingEntity) {
return
}
val nmsComponent = displayName.toNMS()
val nmsEntity = entity.handle
val packet = ClientboundSetEntityDataPacket(
nmsEntity.id,
listOf(
SynchedEntityData.DataValue.create(displayNameAccessor, Optional.of(nmsComponent)),
SynchedEntityData.DataValue.create(customNameVisibleAccessor, visible)
)
)
player.sendPacket(Packet(packet))
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.Zombie
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
return EcoDummyEntity(world.createEntity(location, Zombie::class.java))
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.v1_21_5.entity.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
import java.lang.reflect.Field
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
// Cross-version compatibility:
val registryField: Field = try {
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
} catch (e: NoSuchFieldException) {
CraftPersistentDataContainer::class.java.superclass.getDeclaredField("registry")
}
this.registry = registryField
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ${pdc::class.java.name} is not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract<T, Tag>(dataType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import com.willfp.eco.internal.spigot.proxy.v1_21_5.item.NewerEcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return NewerEcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_21_5.packet.NewItemsPacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.v1_21_5.packet.NewItemsPacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.v1_21_5.packet.NewItemsPacketWindowItems
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
// No PacketAutoRecipe for 1.21.3+ because recipes have been changed internally
return listOf(
PacketHeldItemSlot,
NewItemsPacketOpenWindowMerchant,
NewItemsPacketSetCreativeSlot,
PacketSetSlot,
NewItemsPacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,81 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.mojang.serialization.Dynamic
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import net.minecraft.server.MinecraftServer
import net.minecraft.util.datafix.fixes.References
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.inventory.ItemStack
private val registryAccess = (Bukkit.getServer() as CraftServer).server.registryAccess()
class SNBTConverter : SNBTConverterProxy {
private fun parseItemSNBT(snbt: String): CompoundTag? {
val nbt = runCatching { TagParser.parseCompoundFully(snbt) }.getOrNull() ?: return null
val dataVersion = if (nbt.contains("DataVersion")) {
nbt.getInt("DataVersion").get()
} else null
// If the data version is the same as the server's data version, we don't need to fix it
if (dataVersion == CraftMagicNumbers.INSTANCE.dataVersion) {
return nbt
}
return MinecraftServer.getServer().fixerUpper.update(
References.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, nbt),
dataVersion ?: 3700, // 3700 is the 1.20.4 data version
CraftMagicNumbers.INSTANCE.dataVersion
).value as CompoundTag
}
override fun fromSNBT(snbt: String): ItemStack? {
val tag = parseItemSNBT(snbt) ?: return null
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null) ?: return null
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
val tag = nms.save(registryAccess) as CompoundTag
tag.putInt("DataVersion", CraftMagicNumbers.INSTANCE.dataVersion)
return SnbtPrinterTagVisitor().visit(tag)
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val tag = parseItemSNBT(snbt) ?: return EmptyTestableItem()
val nms = net.minecraft.world.item.ItemStack.parse(registryAccess, tag).orElse(null)
?: return EmptyTestableItem()
tag.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), tag)
}
class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(registryAccess) as CompoundTag
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.tps1.average
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5.entity
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -0,0 +1,185 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5.item
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import net.minecraft.core.component.DataComponents
import net.minecraft.world.item.component.TooltipDisplay
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
open class NewerEcoFastItemStack(
bukkit: ItemStack,
) : NewEcoFastItemStack(bukkit) {
override fun addItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ENCHANTMENTS, true)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, true)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.UNBREAKABLE, true)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_BREAK, true)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_PLACE_ON, true)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
TooltipDisplay(true, tooltip.hiddenComponents)
}
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.DYED_COLOR, true)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.TRIM, true)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, true)
}
}
}
}
apply()
}
override fun removeItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ENCHANTMENTS, false)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, false)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.UNBREAKABLE, false)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_BREAK, false)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_PLACE_ON, false)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
TooltipDisplay(false, tooltip.hiddenComponents)
}
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.DYED_COLOR, false)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.TRIM, false)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, false)
}
}
}
}
apply()
}
override fun hasItemFlag(flag: ItemFlag): Boolean {
return when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.ENCHANTMENTS)
}
ItemFlag.HIDE_ATTRIBUTES -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.ATTRIBUTE_MODIFIERS)
}
ItemFlag.HIDE_UNBREAKABLE -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.UNBREAKABLE)
}
ItemFlag.HIDE_DESTROYS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.CAN_BREAK)
}
ItemFlag.HIDE_PLACED_ON -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.CAN_PLACE_ON)
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
tooltip.hideTooltip
}
ItemFlag.HIDE_DYE -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.DYED_COLOR)
}
ItemFlag.HIDE_ARMOR_TRIM -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.TRIM)
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.STORED_ENCHANTMENTS)
}
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket
import net.minecraft.world.item.trading.MerchantOffers
object NewItemsPacketOpenWindowMerchant : PacketListener {
private val field = ClientboundMerchantOffersPacket::class.java
.declaredFields
.first { it.type == MerchantOffers::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return
val offers = MerchantOffers()
for (offer in packet.offers) {
val new = offer.copy()
Display.display(new.baseCostA.itemStack.asBukkitStack(), event.player)
if (new.costB.isPresent) {
Display.display(new.costB.get().itemStack.asBukkitStack(), event.player)
}
Display.display(new.result.asBukkitStack(), event.player)
offers += new
}
field.set(packet, offers)
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object NewItemsPacketSetCreativeSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return
Display.revert(packet.itemStack.asBukkitStack())
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_5.packet
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.asNMSStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
class NewItemsPacketWindowItems(
plugin: EcoPlugin
) : PacketWindowItems(plugin) {
private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>()
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundContainerSetContentPacket ?: return
val player = event.player
Display.display(packet.carriedItem.asBukkitStack(), player)
val windowId = packet.containerId
val lastKnownID = lastKnownWindowIDs[player.uniqueId]
lastKnownWindowIDs[player.uniqueId] = windowId
// If there is any change in window ID at any point,
// Remove the last display frame to prevent any potential conflicts.
// If the window ID is not zero (not a player inventory), then remove too,
// as GUIs are not player inventories.
if (lastKnownID != windowId || windowId != 0) {
player.lastDisplayFrame = DisplayFrame.EMPTY
}
val itemStacks = packet.items.map { it.asBukkitStack() }
val newItems = modifyWindowItems(itemStacks.toMutableList(), windowId, player)
val newPacket = ClientboundContainerSetContentPacket(
packet.containerId,
packet.stateId,
newItems.map { it.asNMSStack() },
packet.carriedItem,
)
event.packet.handle = newPacket
}
}

View File

@@ -0,0 +1,52 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:modern"))
implementation(project(":eco-core:core-nms:common"))
paperweight.paperDevBundle("1.21.6-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_21_6.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_21_6.minimessage"
)
}
compileJava {
options.release.set(21)
}
compileKotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.command.PluginCommandBase
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommandBase) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,172 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import ca.spottedleaf.dataconverter.minecraft.MCDataConverter
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.packet.PacketInjectorListener
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import io.papermc.paper.adventure.PaperAdventure
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftMob
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.inventory.CraftMetaArmor
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.craftbukkit.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init(plugin: EcoPlugin) {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
plugin.onEnable {
plugin.eventManager.registerListener(PacketInjectorListener)
}
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = CraftMetaArmor::class.java
.superclass // Access CraftMetaItem
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = 8
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asCraftMirror(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.keySet()
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").get().toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.keySet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.remove(DataComponents.CUSTOM_DATA)
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
override fun toNMS(player: Player): ServerPlayer {
return (player as CraftPlayer).handle
}
override fun toNMS(component: Component): net.minecraft.network.chat.Component {
// TODO: Don't have hard dependency on paper
return PaperAdventure.asVanilla(component)
}
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.packet.sendPacket
import com.willfp.eco.internal.spigot.proxy.DisplayNameProxy
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import net.kyori.adventure.text.Component
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity
import org.bukkit.craftbukkit.entity.CraftLivingEntity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import java.util.Optional
@Suppress("UNCHECKED_CAST")
class DisplayName : DisplayNameProxy {
private val displayNameAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[2]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Optional<net.minecraft.network.chat.Component>>
private val customNameVisibleAccessor = Entity::class.java
.declaredFields
.filter { it.type == EntityDataAccessor::class.java }
.toList()[3]
.apply { isAccessible = true }
.get(null) as EntityDataAccessor<Boolean>
override fun setClientsideDisplayName(
entity: LivingEntity,
player: Player,
displayName: Component,
visible: Boolean
) {
if (entity !is CraftLivingEntity) {
return
}
val nmsComponent = displayName.toNMS()
val nmsEntity = entity.handle
val packet = ClientboundSetEntityDataPacket(
nmsEntity.id,
listOf(
SynchedEntityData.DataValue.create(displayNameAccessor, Optional.of(nmsComponent)),
SynchedEntityData.DataValue.create(customNameVisibleAccessor, visible)
)
)
player.sendPacket(Packet(packet))
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.Zombie
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
return EcoDummyEntity(world.createEntity(location, Zombie::class.java))
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.v1_21_6.entity.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,91 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
import java.lang.reflect.Field
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
// Cross-version compatibility:
val registryField: Field = try {
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
} catch (e: NoSuchFieldException) {
CraftPersistentDataContainer::class.java.superclass.getDeclaredField("registry")
}
this.registry = registryField
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ${pdc::class.java.name} is not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract<T, Tag>(dataType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import com.willfp.eco.internal.spigot.proxy.v1_21_6.item.NewerEcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return NewerEcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.PacketHandlerProxy
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketHeldItemSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketSetSlot
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketSetCreativeSlot
import com.willfp.eco.internal.spigot.proxy.v1_21_6.packet.NewItemsPacketWindowItems
import net.minecraft.network.protocol.Packet
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.entity.Player
class PacketHandler : PacketHandlerProxy {
override fun sendPacket(player: Player, packet: com.willfp.eco.core.packet.Packet) {
if (player !is CraftPlayer) {
return
}
val handle = packet.handle
if (handle !is Packet<*>) {
return
}
player.handle.connection.send(handle)
}
override fun clearDisplayFrames() {
clearFrames()
}
override fun getPacketListeners(plugin: EcoPlugin): List<PacketListener> {
// No PacketAutoRecipe for 1.21.3+ because recipes have been changed internally
return listOf(
PacketHeldItemSlot,
NewItemsPacketOpenWindowMerchant,
NewItemsPacketSetCreativeSlot,
PacketSetSlot,
NewItemsPacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.mojang.serialization.Dynamic
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import net.minecraft.server.MinecraftServer
import net.minecraft.util.datafix.fixes.References
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.util.CraftMagicNumbers
import org.bukkit.inventory.ItemStack
private val registryAccess = (Bukkit.getServer() as CraftServer).server.registryAccess()
class SNBTConverter : SNBTConverterProxy {
private fun parseItemSNBT(snbt: String): CompoundTag? {
val nbt = runCatching { TagParser.parseCompoundFully(snbt) }.getOrNull() ?: return null
val dataVersion = if (nbt.contains("DataVersion")) {
nbt.getInt("DataVersion").get()
} else null
// If the data version is the same as the server's data version, we don't need to fix it
if (dataVersion == CraftMagicNumbers.INSTANCE.dataVersion) {
return nbt
}
return MinecraftServer.getServer().fixerUpper.update(
References.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, nbt),
dataVersion ?: 3700, // 3700 is the 1.20.4 data version
CraftMagicNumbers.INSTANCE.dataVersion
).value as CompoundTag
}
private fun nmsToNbt(nms: net.minecraft.world.item.ItemStack): CompoundTag {
return net.minecraft.world.item.ItemStack.CODEC
.encodeStart(registryAccess.createSerializationContext(NbtOps.INSTANCE), nms)
.result()
.get() as CompoundTag
}
private fun nbtToNms(tag: CompoundTag): net.minecraft.world.item.ItemStack? {
return net.minecraft.world.item.ItemStack.CODEC
.parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), tag)
.result()
.orElse(null)
}
override fun fromSNBT(snbt: String): ItemStack? {
val tag = parseItemSNBT(snbt) ?: return null
val nms = nbtToNms(tag) ?: return null
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
val tag = nmsToNbt(nms)
tag.putInt("DataVersion", CraftMagicNumbers.INSTANCE.dataVersion)
return SnbtPrinterTagVisitor().visit(tag)
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val tag = parseItemSNBT(snbt) ?: return EmptyTestableItem()
val nms = nbtToNms(tag)
tag.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), tag)
}
inner class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nmsToNbt(nms)
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.modern.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.tps1.average
}
}

View File

@@ -0,0 +1,95 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6.entity
import com.willfp.eco.core.entities.ai.CustomGoal
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.CustomGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.getGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.toPathfinderMob
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import org.bukkit.entity.Mob
class EcoEntityController<T : Mob>(
private val handle: T
) : EntityController<T> {
override fun addEntityGoal(priority: Int, goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.addGoal(
priority,
goal.getGoalFactory()?.create(goal, nms) ?: return this
)
return this
}
override fun removeEntityGoal(goal: EntityGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.goalSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.goalSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearEntityGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.goalSelector.availableGoals.clear()
return this
}
override fun addTargetGoal(priority: Int, goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.addGoal(
priority, goal.getGoalFactory()?.create(goal, nms) ?: return this
)
nms.targetSelector
return this
}
override fun removeTargetGoal(goal: TargetGoal<in T>): EntityController<T> {
val nms = getNms() ?: return this
val predicate: (Goal) -> Boolean = if (goal is CustomGoal<*>) {
{ CustomGoalFactory.isGoalOfType(it, goal) }
} else {
{ goal.getGoalFactory()?.isGoalOfType(it) == true }
}
for (wrapped in nms.targetSelector.availableGoals.toSet()) {
if (predicate(wrapped.goal)) {
nms.targetSelector.removeGoal(wrapped.goal)
}
}
return this
}
override fun clearTargetGoals(): EntityController<T> {
val nms = getNms() ?: return this
nms.targetSelector.availableGoals.clear()
return this
}
private fun getNms(): PathfinderMob? {
return handle.toPathfinderMob()
}
override fun getEntity(): T {
return handle
}
}

View File

@@ -0,0 +1,185 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6.item
import com.willfp.eco.internal.spigot.proxy.common.modern.NewEcoFastItemStack
import net.minecraft.core.component.DataComponents
import net.minecraft.world.item.component.TooltipDisplay
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
open class NewerEcoFastItemStack(
bukkit: ItemStack,
) : NewEcoFastItemStack(bukkit) {
override fun addItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ENCHANTMENTS, true)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, true)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.UNBREAKABLE, true)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_BREAK, true)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_PLACE_ON, true)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
TooltipDisplay(true, tooltip.hiddenComponents)
}
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.DYED_COLOR, true)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.TRIM, true)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, true)
}
}
}
}
apply()
}
override fun removeItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ENCHANTMENTS, false)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.ATTRIBUTE_MODIFIERS, false)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.UNBREAKABLE, false)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_BREAK, false)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.CAN_PLACE_ON, false)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
TooltipDisplay(false, tooltip.hiddenComponents)
}
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.DYED_COLOR, false)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.TRIM, false)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.TOOLTIP_DISPLAY) { tooltip ->
tooltip.withHidden(DataComponents.STORED_ENCHANTMENTS, false)
}
}
}
}
apply()
}
override fun hasItemFlag(flag: ItemFlag): Boolean {
return when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.ENCHANTMENTS)
}
ItemFlag.HIDE_ATTRIBUTES -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.ATTRIBUTE_MODIFIERS)
}
ItemFlag.HIDE_UNBREAKABLE -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.UNBREAKABLE)
}
ItemFlag.HIDE_DESTROYS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.CAN_BREAK)
}
ItemFlag.HIDE_PLACED_ON -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.CAN_PLACE_ON)
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
tooltip.hideTooltip
}
ItemFlag.HIDE_DYE -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.DYED_COLOR)
}
ItemFlag.HIDE_ARMOR_TRIM -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.TRIM)
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
val tooltip = handle.get(DataComponents.TOOLTIP_DISPLAY) ?: return false
!tooltip.shows(DataComponents.STORED_ENCHANTMENTS)
}
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket
import net.minecraft.world.item.trading.MerchantOffers
object NewItemsPacketOpenWindowMerchant : PacketListener {
private val field = ClientboundMerchantOffersPacket::class.java
.declaredFields
.first { it.type == MerchantOffers::class.java }
.apply { isAccessible = true }
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundMerchantOffersPacket ?: return
val offers = MerchantOffers()
for (offer in packet.offers) {
val new = offer.copy()
Display.display(new.baseCostA.itemStack.asBukkitStack(), event.player)
if (new.costB.isPresent) {
Display.display(new.costB.get().itemStack.asBukkitStack(), event.player)
}
Display.display(new.result.asBukkitStack(), event.player)
offers += new
}
field.set(packet, offers)
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket
object NewItemsPacketSetCreativeSlot : PacketListener {
override fun onReceive(event: PacketEvent) {
val packet = event.packet.handle as? ServerboundSetCreativeModeSlotPacket ?: return
Display.revert(packet.itemStack.asBukkitStack())
event.player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.internal.spigot.proxy.v1_21_6.packet
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.packet.PacketEvent
import com.willfp.eco.internal.spigot.proxy.common.asBukkitStack
import com.willfp.eco.internal.spigot.proxy.common.asNMSStack
import com.willfp.eco.internal.spigot.proxy.common.packet.display.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.DisplayFrame
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.lastDisplayFrame
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
class NewItemsPacketWindowItems(
plugin: EcoPlugin
) : PacketWindowItems(plugin) {
private val lastKnownWindowIDs = ConcurrentHashMap<UUID, Int>()
override fun onSend(event: PacketEvent) {
val packet = event.packet.handle as? ClientboundContainerSetContentPacket ?: return
val player = event.player
Display.display(packet.carriedItem.asBukkitStack(), player)
val windowId = packet.containerId
val lastKnownID = lastKnownWindowIDs[player.uniqueId]
lastKnownWindowIDs[player.uniqueId] = windowId
// If there is any change in window ID at any point,
// Remove the last display frame to prevent any potential conflicts.
// If the window ID is not zero (not a player inventory), then remove too,
// as GUIs are not player inventories.
if (lastKnownID != windowId || windowId != 0) {
player.lastDisplayFrame = DisplayFrame.EMPTY
}
val itemStacks = packet.items.map { it.asBukkitStack() }
val newItems = modifyWindowItems(itemStacks.toMutableList(), windowId, player)
val newPacket = ClientboundContainerSetContentPacket(
packet.containerId,
packet.stateId,
newItems.map { it.asNMSStack() },
packet.carriedItem,
)
event.packet.handle = newPacket
}
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.integrations.antigrief
import com.craftaro.skyblock.SkyBlock
import com.songoda.skyblock.SkyBlock
import com.willfp.eco.core.integrations.antigrief.AntigriefIntegration
import org.bukkit.Location
import org.bukkit.block.Block

View File

@@ -1,3 +1,3 @@
version = 6.75.2
version = 6.76.1
kotlin.daemon.jvmargs=-Xmx2g -XX:+UseG1GC -XX:MaxMetaspaceSize=512m
org.gradle.parallel=true

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,6 @@
pluginManagement {
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
gradlePluginPortal()
}
}
@@ -28,7 +29,9 @@ include(":eco-core:core-nms:v1_20_R3")
include(":eco-core:core-nms:v1_21")
include(":eco-core:core-nms:v1_21_3")
include(":eco-core:core-nms:v1_21_4")
include(":eco-core:core-nms:v1_21_5")
include(":eco-core:core-nms:v1_21_6")
include(":eco-core:core-proxy")
include(":eco-core:core-plugin")
include(":eco-core:core-backend")
include(":eco-core:core-backend-modern")
include(":eco-core:core-backend-modern")