Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Old item descriptions / lore #775

Open
nichady opened this issue Dec 23, 2024 · 4 comments
Open

Old item descriptions / lore #775

nichady opened this issue Dec 23, 2024 · 4 comments
Labels
enhancement A feature or enhancement suggestion for OldCombatMechanics

Comments

@nichady
Copy link

nichady commented Dec 23, 2024

Does this plugin support the old item descriptions / lore for certain items such as swords?

For example, instead of

image

have this instead

image

@kernitus
Copy link
Owner

We do not modify the items at all, because that would lead to much worse performance and issues with removing the plugin. The attack speed property is applied to the player. This means there isn't currently a way for the plugin to change the item descriptions.

@kernitus kernitus added the enhancement A feature or enhancement suggestion for OldCombatMechanics label Dec 24, 2024
@nichady
Copy link
Author

nichady commented Jan 7, 2025

I've found a way to do this using packets. The below code uses NMS (1.21.3) and a custom packet listener from my own codebase, but I'm sure it could be adjusted to work with ProtocolLib.

Here's an example for iron sword.

@EventHandler
fun onSetPlayerInventorySlot(event: AsyncPacketOutEvent) {
    val key = NamespacedKey(plugin, "attack_damage_modifier")
    val transformItem = { it: net.minecraft.world.item.ItemStack ->
        val item = it.asBukkitCopy()
        if (item.type == Material.IRON_SWORD) {
            item.editMeta { meta ->
                // adding atleast 1 attribute modifier is needed
                // if not, then the HIDE_ATTRIBUTES item flag doesn't do anything
                // idk why
                meta.addAttributeModifier(
                    Attribute.ATTACK_DAMAGE,
                    AttributeModifier(key, 0.0, AttributeModifier.Operation.ADD_NUMBER)
                )
                meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
                meta.lore(
                    (item.lore() ?: emptyList()) + listOf(
                        Component.text(""),
                        Component.text("+6 Attack Damage", NamedTextColor.BLUE)
                            .decoration(TextDecoration.ITALIC, false)
                    )
                )
            }
        }

        CraftItemStack.unwrap(item)
    }

    val packet = event.packet

    if (packet is ClientboundContainerSetContentPacket) {
        event.packet = ClientboundContainerSetContentPacket(
            packet.containerId,
            packet.stateId,
            net.minecraft.core.NonNullList.of(
                net.minecraft.world.item.ItemStack.EMPTY,
                *packet.items.map(transformItem).toTypedArray()
            ),
            transformItem(packet.carriedItem),
        )
    } else if (packet is ClientboundContainerSetSlotPacket) {
        event.packet = ClientboundContainerSetSlotPacket(
            packet.containerId,
            packet.stateId,
            packet.slot,
            transformItem(packet.item),
        )
    }

    // There are probably other packets that need to be monitored
    // (packets with slot datatype: https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol#Type:Slot)
}

Basically, it doesn't actually modify the items on the server, but it changes the lore and item attributes inside certain packets before sending them to the client.

Something I've noticed is that when taking the iron sword from creative menu to inventory, the item still has new lore. It doesn't change until the player drops it and then picks it up again. There are probably some other packet types to capture to achieve this. Maybe Player#updateInventory() could be useful for this as well.

Also, another approach, that doesn't involve messing with lore, can be done by changing the editMeta block.

item.editMeta { meta ->
    meta.addAttributeModifier(
        Attribute.ATTACK_DAMAGE,
        AttributeModifier(key, 6.0, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.HAND),
    )
}

This just removes the unnecessary attack speed description, and gives a result somewhere in between the old and new versions:

image

@kernitus
Copy link
Owner

I've found a way to do this using packets. The below code uses NMS (1.21.3) and a custom packet listener from my own codebase, but I'm sure it could be adjusted to work with ProtocolLib.

Here's an example for iron sword.

@EventHandler
fun onSetPlayerInventorySlot(event: AsyncPacketOutEvent) {
    val key = NamespacedKey(plugin, "attack_damage_modifier")
    val transformItem = { it: net.minecraft.world.item.ItemStack ->
        val item = it.asBukkitCopy()
        if (item.type == Material.IRON_SWORD) {
            item.editMeta { meta ->
                // adding atleast 1 attribute modifier is needed
                // if not, then the HIDE_ATTRIBUTES item flag doesn't do anything
                // idk why
                meta.addAttributeModifier(
                    Attribute.ATTACK_DAMAGE,
                    AttributeModifier(key, 0.0, AttributeModifier.Operation.ADD_NUMBER)
                )
                meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
                meta.lore(
                    (item.lore() ?: emptyList()) + listOf(
                        Component.text(""),
                        Component.text("+6 Attack Damage", NamedTextColor.BLUE)
                            .decoration(TextDecoration.ITALIC, false)
                    )
                )
            }
        }

        CraftItemStack.unwrap(item)
    }

    val packet = event.packet

    if (packet is ClientboundContainerSetContentPacket) {
        event.packet = ClientboundContainerSetContentPacket(
            packet.containerId,
            packet.stateId,
            net.minecraft.core.NonNullList.of(
                net.minecraft.world.item.ItemStack.EMPTY,
                *packet.items.map(transformItem).toTypedArray()
            ),
            transformItem(packet.carriedItem),
        )
    } else if (packet is ClientboundContainerSetSlotPacket) {
        event.packet = ClientboundContainerSetSlotPacket(
            packet.containerId,
            packet.stateId,
            packet.slot,
            transformItem(packet.item),
        )
    }

    // There are probably other packets that need to be monitored
    // (packets with slot datatype: https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol#Type:Slot)
}

Basically, it doesn't actually modify the items on the server, but it changes the lore and item attributes inside certain packets before sending them to the client.

Something I've noticed is that when taking the iron sword from creative menu to inventory, the item still has new lore. It doesn't change until the player drops it and then picks it up again. There are probably some other packet types to capture to achieve this. Maybe Player#updateInventory() could be useful for this as well.

Also, another approach, that doesn't involve messing with lore, can be done by changing the editMeta block.

item.editMeta { meta ->
    meta.addAttributeModifier(
        Attribute.ATTACK_DAMAGE,
        AttributeModifier(key, 6.0, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.HAND),
    )
}

This just removes the unnecessary attack speed description, and gives a result somewhere in between the old and new versions:

image

We try to avoid unnecessary packet manipulation - this would not only need ProtocolLib and probably some reflection, but also an implementation for all versions of the involved packets from 1.9 to 1.21. It also means the plugin would have to be updated every time the protocol changes, which is a lot of maintenance. Still open to alternatives if there are easier ways of doing this.

@nichady
Copy link
Author

nichady commented Jan 10, 2025

That makes sense - it certainly would be lots of work to support every version. I'm not sure if there would be another way to do this though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A feature or enhancement suggestion for OldCombatMechanics
Projects
None yet
Development

No branches or pull requests

2 participants