From 2e6eddf17a84e52f70352750328b89565b06790a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 22 Mar 2025 21:13:17 +0800 Subject: [PATCH] framework for external items --- bukkit-loader/build.gradle.kts | 1 + bukkit/build.gradle.kts | 1 + bukkit/compatibility/build.gradle.kts | 28 ++++++++++++++++ .../item/NeigeItemsProvider.java | 22 +++++++++++++ .../bukkit/item/BukkitItemManager.java | 12 +++++-- .../bukkit/item/ItemEventListener.java | 1 - .../item/factory/ComponentItemFactory.java | 14 +++++++- .../item/factory/UniversalItemFactory.java | 8 ++++- .../feature/DebugTargetBlockCommand.java | 1 - .../plugin/network/PacketConsumers.java | 4 ++- .../craftengine/core/item/AbstractItem.java | 10 ++++-- .../core/item/AbstractItemManager.java | 20 +++++++++++ .../core/item/ExternalItemProvider.java | 11 +++++++ .../craftengine/core/item/Item.java | 4 ++- .../craftengine/core/item/ItemFactory.java | 4 ++- .../craftengine/core/item/ItemManager.java | 4 +++ .../core/item/modifier/ExternalModifier.java | 33 +++++++++++++++++++ .../recipe/CustomSmithingTransformRecipe.java | 2 +- .../craftengine/core/plugin/CraftEngine.java | 2 +- settings.gradle.kts | 1 + 20 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 bukkit/compatibility/build.gradle.kts create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java diff --git a/bukkit-loader/build.gradle.kts b/bukkit-loader/build.gradle.kts index eef46cabe..fb30824d3 100644 --- a/bukkit-loader/build.gradle.kts +++ b/bukkit-loader/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(project(":core")) implementation(project(":bukkit")) implementation(project(":bukkit:legacy")) + implementation(project(":bukkit:compatibility")) implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}") implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 0fab7cd0e..cb9f6441b 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -15,6 +15,7 @@ repositories { dependencies { compileOnly(project(":core")) compileOnly(project(":shared")) + compileOnly(project(":bukkit:compatibility")) compileOnly(project(":bukkit:legacy")) // Anti Grief compileOnly("com.github.Xiao-MoMi:AntiGriefLib:${rootProject.properties["anti_grief_version"]}") diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts new file mode 100644 index 000000000..bc8304b72 --- /dev/null +++ b/bukkit/compatibility/build.gradle.kts @@ -0,0 +1,28 @@ +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://r.irepo.space/maven/") +} + +dependencies { + compileOnly(project(":core")) + // Platform + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // NeigeItems + compileOnly("pers.neige.neigeitems:NeigeItems:1.21.42") +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + withSourcesJar() +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(21) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java new file mode 100644 index 000000000..9fe7d0f18 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/NeigeItemsProvider.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.compatibility.item; + +import net.momirealms.craftengine.core.item.ExternalItemProvider; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import pers.neige.neigeitems.manager.ItemManager; + +import java.util.Optional; + +public class NeigeItemsProvider implements ExternalItemProvider { + + @Override + public String plugin() { + return "NeigeItems"; + } + + @Override + public ItemStack build(String id, ItemBuildContext context) { + return ItemManager.INSTANCE.getItemStack(id, Optional.ofNullable(context.player()).map(it -> (Player) it.platformPlayer()).orElse(null)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index fcbcbba5e..16a7458ad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.item; +import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider; import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior; import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior; @@ -69,8 +70,15 @@ public class BukkitItemManager extends AbstractItemManager { @Override public void delayedInit() { - Bukkit.getPluginManager().registerEvents(this.itemEventListener, plugin.bootstrap()); - Bukkit.getPluginManager().registerEvents(this.debugStickListener, plugin.bootstrap()); + Bukkit.getPluginManager().registerEvents(this.itemEventListener, this.plugin.bootstrap()); + Bukkit.getPluginManager().registerEvents(this.debugStickListener, this.plugin.bootstrap()); + this.hookExternalPlugins(); + } + + private void hookExternalPlugins() { + if (this.plugin.isPluginEnabled("NeigeItems")) { + registerExternalItemProvider(new NeigeItemsProvider()); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java index c6981358d..cc1a448d8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ItemEventListener.java @@ -29,7 +29,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java index 98db91ca1..e44302c71 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory.java @@ -299,7 +299,7 @@ public class ComponentItemFactory extends BukkitItemFactory { } @Override - protected ItemWrapper merge(ItemWrapper item1, ItemWrapper item2) { + protected ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2) { Object itemStack1 = item1.getLiteralObject(); Object itemStack2 = item2.getLiteralObject(); try { @@ -311,4 +311,16 @@ public class ComponentItemFactory extends BukkitItemFactory { } return null; } + + @Override + protected void merge(ItemWrapper item1, ItemWrapper item2) { + item1.load(); + Object itemStack1 = item1.getLiteralObject(); + Object itemStack2 = item2.getLiteralObject(); + try { + Reflections.method$ItemStack$applyComponents.invoke(itemStack1, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2)); + } catch (Exception e) { + plugin.logger().warn("Failed to merge item", e); + } + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 5550598db..c8d3890c4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -234,11 +234,17 @@ public class UniversalItemFactory extends BukkitItemFactory { } @Override - protected ItemWrapper merge(ItemWrapper item1, ItemWrapper item2) { + protected ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2) { Object itemStack = ItemObject.copy(item2.getLiteralObject()); ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject()))); // one more step than vanilla TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count()); } + + @Override + protected void merge(ItemWrapper item1, ItemWrapper item2) { + item1.load(); + TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true); + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java index e86731a62..c57ffc6da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugTargetBlockCommand.java @@ -10,7 +10,6 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.sender.Sender; -import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 9c4e1ad90..6deef92bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -33,7 +33,9 @@ import org.bukkit.util.RayTraceResult; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; import java.util.function.BiConsumer; public class PacketConsumers { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java index ae652add7..1d5ffbcbd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java @@ -280,7 +280,13 @@ public class AbstractItem, I> implements Item { @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public Item merge(Item another) { - return new AbstractItem<>(this.factory, this.factory.merge(this.item, ((AbstractItem) another).item)); + public Item mergeCopy(Item another) { + return new AbstractItem<>(this.factory, this.factory.mergeCopy(this.item, ((AbstractItem) another).item)); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public void merge(Item another) { + this.factory.merge(this.item, ((AbstractItem) another).item); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index baa2330b1..eb8d76b67 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -23,6 +23,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl protected static final List VANILLA_ITEMS = new ArrayList<>(); protected static final Map>> VANILLA_ITEM_TAGS = new HashMap<>(); + protected final Map> externalItemProviders = new HashMap<>(); protected final Map>> dataFunctions = new HashMap<>(); protected final Map> customItems = new HashMap<>(); protected final Map>> customItemTags; @@ -47,6 +48,18 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } + @Override + public ExternalItemProvider getExternalItemProvider(String name) { + return this.externalItemProviders.get(name); + } + + @Override + public boolean registerExternalItemProvider(ExternalItemProvider externalItemProvider) { + if (this.externalItemProviders.containsKey(externalItemProvider.plugin())) return false; + this.externalItemProviders.put(externalItemProvider.plugin(), externalItemProvider); + return true; + } + @Override public void unload() { super.clearModelsToGenerate(); @@ -165,6 +178,13 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } private void registerFunctions() { + registerDataFunction((obj) -> { + Map data = MiscUtils.castToMap(obj, false); + String plugin = data.get("plugin").toString(); + String id = data.get("id").toString(); + ExternalItemProvider provider = AbstractItemManager.this.getExternalItemProvider(plugin); + return new ExternalModifier<>(id, Objects.requireNonNull(provider, "Item provider " + plugin + " not found")); + }, "external"); registerDataFunction((obj) -> { String name = obj.toString(); return new DisplayNameModifier<>(name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java b/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java new file mode 100644 index 000000000..f5cea3ada --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ExternalItemProvider.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.item; + +import org.jetbrains.annotations.Nullable; + +public interface ExternalItemProvider { + + String plugin(); + + @Nullable + I build(String id, ItemBuildContext context); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 7de763c22..3895da0db 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -117,5 +117,7 @@ public interface Item { Object getLiteralObject(); - Item merge(Item another); + Item mergeCopy(Item another); + + void merge(Item another); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java index 8b59f1636..c65a15740 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java @@ -112,5 +112,7 @@ public abstract class ItemFactory

, I> protected abstract Optional repairCost(ItemWrapper item); - protected abstract ItemWrapper merge(ItemWrapper item1, ItemWrapper item2); + protected abstract ItemWrapper mergeCopy(ItemWrapper item1, ItemWrapper item2); + + protected abstract void merge(ItemWrapper item1, ItemWrapper item2); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index 8215252ca..3b2b406fb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -51,6 +51,10 @@ public interface ItemManager extends Reloadable, ModelGenerator, ConfigSectio Key customItemId(T itemStack); + ExternalItemProvider getExternalItemProvider(String name); + + boolean registerExternalItemProvider(ExternalItemProvider externalItemProvider); + Optional> getCustomItem(Key key); Optional> getItemBehavior(Key key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java new file mode 100644 index 000000000..296810e37 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ExternalItemProvider; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.CraftEngine; + +public class ExternalModifier implements ItemModifier { + private final String id; + private final ExternalItemProvider provider; + + public ExternalModifier(String id, ExternalItemProvider provider) { + this.id = id; + this.provider = provider; + } + + @Override + public String name() { + return "external"; + } + + @SuppressWarnings("unchecked") + @Override + public void apply(Item item, ItemBuildContext context) { + I another = this.provider.build(id, context); + if (another == null) { + CraftEngine.instance().logger().warn("'" + id + "' could not be found in " + provider.plugin()); + return; + } + Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); + item.merge(anotherWrapped); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 8d72de36b..46f9e9909 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -96,7 +96,7 @@ public class CustomSmithingTransformRecipe implements Recipe { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; if (this.mergeComponents) { - finalResult = base.merge(wrappedResult); + finalResult = base.mergeCopy(wrappedResult); } for (ItemDataProcessor processor : this.processors) { processor.accept(base, wrappedResult, finalResult); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 2deedf67f..931804201 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -127,11 +127,11 @@ public abstract class CraftEngine implements Plugin { // delay the reload so other plugins can register some parsers this.scheduler.sync().runDelayed(() -> { this.registerParsers(); + this.itemManager.delayedInit(); this.reload(); this.guiManager.delayedInit(); this.recipeManager.delayedInit(); this.blockManager.delayedInit(); - this.itemManager.delayedInit(); this.worldManager.delayedInit(); this.packManager.delayedInit(); this.furnitureManager.delayedInit(); diff --git a/settings.gradle.kts b/settings.gradle.kts index 02db6f8bf..cd96a4442 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ include(":shared") include(":core") include(":bukkit") include(":bukkit:legacy") +include(":bukkit:compatibility") include(":bukkit-loader") include(":server-mod") pluginManagement {