1.21 groundwork

This commit is contained in:
Auxilor
2024-06-18 17:18:04 +01:00
parent 303f5eedac
commit ad44891f5f
23 changed files with 1191 additions and 0 deletions

View File

@@ -32,6 +32,7 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_20_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_6", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21", configuration = "reobf"))
}
allprojects {
@@ -162,6 +163,10 @@ allprojects {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
withSourcesJar()
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
test {

View File

@@ -8,3 +8,9 @@ version = rootProject.version
dependencies {
paperweight.paperDevBundle("1.17.1-R0.1-SNAPSHOT")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View File

@@ -8,6 +8,7 @@ version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:nms-common"))
paperweight.paperDevBundle("1.17.1-R0.1-SNAPSHOT")
pluginRemapper("net.fabricmc:tiny-remapper:0.10.3:fat")
implementation("net.kyori:adventure-text-minimessage:4.2.0-SNAPSHOT") {
version {
@@ -37,3 +38,9 @@ tasks {
)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View File

@@ -37,3 +37,9 @@ tasks {
)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View File

@@ -37,3 +37,9 @@ tasks {
)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View File

@@ -37,3 +37,9 @@ tasks {
)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View File

@@ -0,0 +1,51 @@
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:nms-common"))
paperweight.paperDevBundle("1.21-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.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_21.minimessage"
)
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
withSourcesJar()
}
compileKotlin {
kotlinOptions {
jvmTarget = "21"
}
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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
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 = CraftMagicNumbers.NBT.TAG_STRING
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.allKeys
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").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.allKeys.toSet()) {
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,59 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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
nmsEntity.isCustomNameVisible
val entityData = SynchedEntityData.Builder(nmsEntity).build()
entityData.set(displayNameAccessor, Optional.of(nmsComponent), true)
entityData.set(customNameVisibleAccessor, visible, true)
val packet = ClientboundSetEntityDataPacket(
nmsEntity.id,
entityData.packDirty() ?: throw IllegalStateException("No packed entity data")
)
player.sendPacket(Packet(packet))
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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
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.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,82 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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
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
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.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,392 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.asNMSStack
import com.willfp.eco.internal.spigot.proxy.common.item.ContinuallyAppliedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.common.item.ImplementedFIS
import com.willfp.eco.internal.spigot.proxy.common.makePdc
import com.willfp.eco.internal.spigot.proxy.common.mergeIfNeeded
import com.willfp.eco.internal.spigot.proxy.common.setPdc
import com.willfp.eco.internal.spigot.proxy.common.toAdventure
import com.willfp.eco.internal.spigot.proxy.common.toItem
import com.willfp.eco.internal.spigot.proxy.common.toMaterial
import com.willfp.eco.internal.spigot.proxy.common.toNMS
import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.toComponent
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.TextDecoration
import net.minecraft.core.component.DataComponentType
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Unit
import net.minecraft.world.item.component.CustomData
import net.minecraft.world.item.component.CustomModelData
import net.minecraft.world.item.component.ItemLore
import org.bukkit.craftbukkit.CraftRegistry
import org.bukkit.craftbukkit.enchantments.CraftEnchantment
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import kotlin.math.max
private val unstyledComponent = Component.empty().style {
it.color(null).decoration(TextDecoration.ITALIC, false)
}
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return NewEcoFastItemStack(itemStack)
}
@Suppress("UsePropertyAccessSyntax")
class NewEcoFastItemStack(
private val bukkit: ItemStack
) : ImplementedFIS {
// Cast is there because, try as I might, I can't get IntellIJ to recognise half the classes in the dev bundle
@Suppress("USELESS_CAST")
private val handle = bukkit.asNMSStack() as net.minecraft.world.item.ItemStack
private val pdc = (handle.get(DataComponents.CUSTOM_DATA)?.copyTag() ?: CompoundTag()).makePdc()
override fun getEnchants(checkStored: Boolean): Map<Enchantment, Int> {
val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return emptyMap()
val map = mutableMapOf<Enchantment, Int>()
for ((enchantment, level) in enchantments.entrySet()) {
val bukkit = CraftEnchantment.minecraftToBukkit(enchantment.value())
map[bukkit] = level
}
return map
}
override fun getEnchantmentLevel(
enchantment: Enchantment,
checkStored: Boolean
): Int {
val minecraft = CraftRegistry
.bukkitToMinecraft<Enchantment, net.minecraft.world.item.enchantment.Enchantment>(enchantment)
val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return 0
var level = enchantments.getLevel(minecraft)
if (checkStored) {
val storedEnchantments = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return 0
level = max(level, storedEnchantments.getLevel(minecraft))
}
return level
}
override fun setLore(lore: List<String>?) = setLoreComponents(lore?.map { it.toComponent() })
override fun setLoreComponents(lore: List<Component>?) {
if (lore == null) {
handle.set<ItemLore>(DataComponents.LORE, null)
} else {
val components = lore
.map { unstyledComponent.append(it) }
.map { it.toNMS() }
handle.set(
DataComponents.LORE, ItemLore(
components,
components
)
)
}
apply()
}
override fun getLoreComponents(): List<Component> {
return handle.get(DataComponents.LORE)?.lines?.map { it.toAdventure() } ?: emptyList()
}
override fun getLore(): List<String> =
getLoreComponents().map { StringUtils.toLegacy(it) }
override fun setDisplayName(name: Component?) {
if (name == null) {
handle.set<net.minecraft.network.chat.Component>(DataComponents.CUSTOM_NAME, null)
} else {
handle.set(DataComponents.CUSTOM_NAME, name.toNMS())
}
apply()
}
override fun setDisplayName(name: String?) = setDisplayName(name?.toComponent())
override fun getDisplayNameComponent(): Component {
return handle.get(DataComponents.CUSTOM_NAME)?.toAdventure()
?: Component.translatable(bukkit.type.toItem().getDescriptionId())
}
override fun getDisplayName(): String = displayNameComponent.toLegacy()
private fun <T> net.minecraft.world.item.ItemStack.modifyComponent(
component: DataComponentType<T>,
modifier: (T) -> T
) {
val current = handle.get(component) ?: return
this.set(component, modifier(current))
}
override fun addItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.ENCHANTMENTS) { enchantments ->
enchantments.withTooltip(false)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.ATTRIBUTE_MODIFIERS) { attributes ->
attributes.withTooltip(false)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.UNBREAKABLE) { unbreakable ->
unbreakable.withTooltip(false)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.CAN_BREAK) { destroys ->
destroys.withTooltip(false)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.CAN_PLACE_ON) { placedOn ->
placedOn.withTooltip(false)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.set(DataComponents.HIDE_ADDITIONAL_TOOLTIP, Unit.INSTANCE)
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.DYED_COLOR) { dyed ->
dyed.withTooltip(false)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TRIM) { trim ->
trim.withTooltip(false)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.STORED_ENCHANTMENTS) { storedEnchants ->
storedEnchants.withTooltip(false)
}
}
}
}
apply()
}
override fun removeItemFlags(vararg hideFlags: ItemFlag) {
for (flag in hideFlags) {
when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
handle.modifyComponent(DataComponents.ENCHANTMENTS) { enchantments ->
enchantments.withTooltip(true)
}
}
ItemFlag.HIDE_ATTRIBUTES -> {
handle.modifyComponent(DataComponents.ATTRIBUTE_MODIFIERS) { attributes ->
attributes.withTooltip(true)
}
}
ItemFlag.HIDE_UNBREAKABLE -> {
handle.modifyComponent(DataComponents.UNBREAKABLE) { unbreakable ->
unbreakable.withTooltip(true)
}
}
ItemFlag.HIDE_DESTROYS -> {
handle.modifyComponent(DataComponents.CAN_BREAK) { destroys ->
destroys.withTooltip(true)
}
}
ItemFlag.HIDE_PLACED_ON -> {
handle.modifyComponent(DataComponents.CAN_PLACE_ON) { placedOn ->
placedOn.withTooltip(true)
}
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.remove(DataComponents.HIDE_ADDITIONAL_TOOLTIP)
}
ItemFlag.HIDE_DYE -> {
handle.modifyComponent(DataComponents.DYED_COLOR) { dyed ->
dyed.withTooltip(true)
}
}
ItemFlag.HIDE_ARMOR_TRIM -> {
handle.modifyComponent(DataComponents.TRIM) { trim ->
trim.withTooltip(true)
}
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
handle.modifyComponent(DataComponents.STORED_ENCHANTMENTS) { storedEnchants ->
storedEnchants.withTooltip(true)
}
}
}
}
apply()
}
override fun getItemFlags(): Set<ItemFlag> {
val currentFlags = mutableSetOf<ItemFlag>()
for (f in ItemFlag.values()) {
if (hasItemFlag(f)) {
currentFlags.add(f)
}
}
return currentFlags
}
override fun hasItemFlag(flag: ItemFlag): Boolean {
return when (flag) {
ItemFlag.HIDE_ENCHANTS -> {
val enchantments = handle.get(DataComponents.ENCHANTMENTS) ?: return false
!enchantments.showInTooltip
}
ItemFlag.HIDE_ATTRIBUTES -> {
val attributes = handle.get(DataComponents.ATTRIBUTE_MODIFIERS) ?: return false
!attributes.showInTooltip
}
ItemFlag.HIDE_UNBREAKABLE -> {
val unbreakable = handle.get(DataComponents.UNBREAKABLE) ?: return false
!unbreakable.showInTooltip
}
ItemFlag.HIDE_DESTROYS -> {
val destroys = handle.get(DataComponents.CAN_BREAK) ?: return false
!destroys.showInTooltip()
}
ItemFlag.HIDE_PLACED_ON -> {
val placedOn = handle.get(DataComponents.CAN_PLACE_ON) ?: return false
!placedOn.showInTooltip()
}
ItemFlag.HIDE_ADDITIONAL_TOOLTIP -> {
handle.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null
}
ItemFlag.HIDE_DYE -> {
val dyed = handle.get(DataComponents.DYED_COLOR) ?: return false
!dyed.showInTooltip()
}
ItemFlag.HIDE_ARMOR_TRIM -> {
val armorTrim = handle.get(DataComponents.TRIM) ?: return false
!armorTrim.showInTooltip
}
ItemFlag.HIDE_STORED_ENCHANTS -> {
val storedEnchants = handle.get(DataComponents.STORED_ENCHANTMENTS) ?: return false
!storedEnchants.showInTooltip
}
}
}
override fun getRepairCost(): Int {
return handle.get(DataComponents.REPAIR_COST) ?: 0
}
override fun setRepairCost(cost: Int) {
handle.set(DataComponents.REPAIR_COST, cost)
apply()
}
override fun getPersistentDataContainer(): PersistentDataContainer {
return ContinuallyAppliedPersistentDataContainer(this.pdc, this)
}
override fun getAmount(): Int = handle.getCount()
override fun setAmount(amount: Int) {
handle.setCount(amount)
apply()
}
override fun setType(material: org.bukkit.Material) {
@Suppress("DEPRECATION")
handle.setItem(material.toItem())
apply()
}
override fun getType(): org.bukkit.Material = handle.getItem().toMaterial()
override fun getCustomModelData(): Int? =
handle.get(DataComponents.CUSTOM_MODEL_DATA)?.value
override fun setCustomModelData(data: Int?) {
if (data == null) {
handle.remove(DataComponents.CUSTOM_MODEL_DATA)
} else {
handle.set(DataComponents.CUSTOM_MODEL_DATA, CustomModelData(data))
}
apply()
}
override fun equals(other: Any?): Boolean {
if (other !is NewEcoFastItemStack) {
return false
}
return other.hashCode() == this.hashCode()
}
override fun hashCode(): Int {
return net.minecraft.world.item.ItemStack.hashItemAndComponents(handle)
}
override fun apply() {
val customData = handle.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY)
val updated = customData.update {
it.setPdc(pdc)
}
if (updated.isEmpty) {
handle.remove(DataComponents.CUSTOM_DATA)
} else {
handle.set(DataComponents.CUSTOM_DATA, updated)
}
bukkit.mergeIfNeeded(handle)
}
override fun unwrap(): ItemStack {
return bukkit
}
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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
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.PacketAutoRecipe
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.PacketWindowItems
import com.willfp.eco.internal.spigot.proxy.common.packet.display.frame.clearFrames
import com.willfp.eco.internal.spigot.proxy.v1_21.packet.NewItemsPacketOpenWindowMerchant
import com.willfp.eco.internal.spigot.proxy.v1_21.packet.NewItemsPacketSetCreativeSlot
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> {
return listOf(
PacketAutoRecipe(plugin),
PacketHeldItemSlot,
NewItemsPacketOpenWindowMerchant,
NewItemsPacketSetCreativeSlot,
PacketSetSlot,
PacketWindowItems(plugin)
)
}
}

View File

@@ -0,0 +1,80 @@
package com.willfp.eco.internal.spigot.proxy.v1_21
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.parseTag(snbt) }.getOrNull() ?: return null
val dataVersion = if (nbt.contains("DataVersion")) {
nbt.getInt("DataVersion")
} 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
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.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
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.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,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_21.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.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

@@ -22,6 +22,7 @@ include(":eco-core:core-nms:v1_20_R1")
include(":eco-core:core-nms:v1_20_R2")
include(":eco-core:core-nms:v1_20_R3")
include(":eco-core:core-nms:v1_20_6")
include(":eco-core:core-nms:v1_21")
include(":eco-core:core-proxy")
include(":eco-core:core-plugin")
include(":eco-core:core-backend")