diff --git a/README.md b/README.md index 4c6e8fa0b..535c1c54d 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.59") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.59") + compileOnly("net.momirealms:craft-engine-core:0.0.61") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.61") } ``` \ No newline at end of file diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 78efcd56d..c9bf92f91 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta13" + id("com.gradleup.shadow") version "9.0.0-rc2" id("maven-publish") } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 656ba4791..a341e41fe 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -10,7 +10,8 @@ import net.momirealms.craftengine.bukkit.compatibility.leveler.*; import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel; import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils; -import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicMobsListener; +import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener; +import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook; @@ -39,6 +40,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager { private final Map levelerProviders; private boolean hasPlaceholderAPI; private boolean hasViaVersion; + private MythicSkillHelper skillExecute; public BukkitCompatibilityManager(BukkitCraftEngine plugin) { this.plugin = plugin; @@ -131,11 +133,16 @@ public class BukkitCompatibilityManager implements CompatibilityManager { } if (this.isPluginEnabled("MythicMobs")) { BukkitItemManager.instance().registerExternalItemSource(new MythicMobsSource()); - new MythicMobsListener(this.plugin); + new MythicItemDropListener(this.plugin); logHook("MythicMobs"); } } + @Override + public void executeMMSkill(String skill, float power, Player player) { + MythicSkillHelper.execute(skill, power, player); + } + @Override public void registerLevelerProvider(String plugin, LevelerProvider provider) { this.levelerProviders.put(plugin, provider); diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java similarity index 93% rename from bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java index 9c582cf1d..8b151df79 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/CraftEngineItemDrop.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDrop.java @@ -21,12 +21,12 @@ import org.bukkit.inventory.ItemStack; import java.lang.reflect.Constructor; -public class CraftEngineItemDrop extends ItemDrop implements IItemDrop { +public class MythicItemDrop extends ItemDrop implements IItemDrop { private final CustomItem customItem; private static final Constructor constructor$BukkitItemStack = ReflectionUtils.getConstructor(BukkitItemStack.class, ItemStack.class); private static final boolean useReflection = constructor$BukkitItemStack != null; - public CraftEngineItemDrop(String line, MythicLineConfig config, CustomItem customItem) { + public MythicItemDrop(String line, MythicLineConfig config, CustomItem customItem) { super(line, config); this.customItem = customItem; } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java similarity index 83% rename from bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java rename to bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java index 22101993b..96cacc0b7 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicMobsListener.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicItemDropListener.java @@ -8,10 +8,10 @@ import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -public class MythicMobsListener implements Listener { +public class MythicItemDropListener implements Listener { private final BukkitCraftEngine plugin; - public MythicMobsListener(BukkitCraftEngine plugin) { + public MythicItemDropListener(BukkitCraftEngine plugin) { this.plugin = plugin; Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin()); } @@ -24,7 +24,7 @@ public class MythicMobsListener implements Listener { this.plugin.itemManager().getCustomItem(itemId).ifPresent(customItem -> { String line = event.getContainer().getConfigLine(); MythicLineConfig config = event.getConfig(); - event.register(new CraftEngineItemDrop(line, config, customItem)); + event.register(new MythicItemDrop(line, config, customItem)); }); } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java new file mode 100644 index 000000000..8ac5a7356 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/mythicmobs/MythicSkillHelper.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.bukkit.compatibility.mythicmobs; + +import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.core.utils.MythicUtil; +import net.momirealms.craftengine.core.entity.player.Player; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; + +import java.util.ArrayList; +import java.util.List; + +public final class MythicSkillHelper { + + public static void execute(String skill, float power, Player player) { + org.bukkit.entity.Player casterPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + Location location = casterPlayer.getLocation(); + LivingEntity target = MythicUtil.getTargetedEntity(casterPlayer); + List targets = new ArrayList<>(); + List locations = null; + if (target != null) { + targets.add(target); + locations = List.of(target.getLocation()); + } + MythicBukkit.inst().getAPIHelper().castSkill(casterPlayer, skill, casterPlayer, location, targets, locations, power); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java index b03240f25..e116d6e6c 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/worldedit/FastAsyncWorldEditDelegate.java @@ -89,12 +89,14 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { if (levelChunk != null) { Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); CESection[] ceSections = ceChunk.sections(); - for (int i = 0; i < ceSections.length; i++) { - CESection ceSection = ceSections[i]; - Object section = sections[i]; - int finalI = i; - WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), - (injected) -> sections[finalI] = injected); + synchronized (sections) { + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + int finalI = i; + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + (injected) -> sections[finalI] = injected); + } } } } @@ -174,18 +176,18 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { @Override public @Nullable Operation commit() { - Operation operation = super.commit(); saveAllChunks(); + Operation operation = super.commit(); List chunks = new ArrayList<>(this.brokenChunks); this.brokenChunks.clear(); - Object worldServer = this.ceWorld.world().serverWorld(); - Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); - for (ChunkPos chunk : chunks) { - CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); - // only inject loaded chunks - if (loaded == null) continue; - injectLevelChunk(chunkSource, loaded); - } + Object worldServer = this.ceWorld.world().serverWorld(); + Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); + for (ChunkPos chunk : chunks) { + CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey()); + // only inject loaded chunks + if (loaded == null) continue; + injectLevelChunk(chunkSource, loaded); + } return operation; } @@ -214,11 +216,12 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent { try { CEChunk ceChunk = Optional.ofNullable(this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ)) .orElse(this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ))); + CESection ceSection = ceChunk.sectionById(SectionPos.blockToSectionCoord(blockY)); ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(newStateId); if (immutableBlockState == null) { - ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE); + ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, EmptyBlock.STATE); } else { - ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState); + ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, immutableBlockState); } this.chunksToSave.add(ceChunk); } catch (IOException e) { diff --git a/bukkit/legacy/build.gradle.kts b/bukkit/legacy/build.gradle.kts index da89d9df8..2de912779 100644 --- a/bukkit/legacy/build.gradle.kts +++ b/bukkit/legacy/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta13" + id("com.gradleup.shadow") version "9.0.0-rc2" } repositories { diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index 9e4762b96..29d893949 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta13" + id("com.gradleup.shadow") version "9.0.0-rc2" id("de.eldoria.plugin-yml.bukkit") version "0.7.1" } diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index ac2894f04..1d5ce6703 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -1,7 +1,7 @@ import net.minecrell.pluginyml.paper.PaperPluginDescription plugins { - id("com.gradleup.shadow") version "9.0.0-beta13" + id("com.gradleup.shadow") version "9.0.0-rc2" id("de.eldoria.plugin-yml.paper") version "0.7.1" } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 95a28e719..14ae935fc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -129,13 +129,12 @@ public final class BukkitCustomBlock extends AbstractCustomBlock { // set block side properties CoreReflections.field$BlockBehaviour$explosionResistance.set(nmsBlock, settings.resistance()); CoreReflections.field$BlockBehaviour$soundType.set(nmsBlock, SoundUtils.toSoundType(settings.sounds())); - // 1.21.2以前要在init cache之前设定 isConditionallyFullOpaque + // init cache + CoreReflections.method$BlockStateBase$initCache.invoke(nmsState); boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion; if (!VersionHelper.isOrAbove1_21_2()) { CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(nmsState, isConditionallyFullOpaque); } - // init cache - CoreReflections.method$BlockStateBase$initCache.invoke(nmsState); // modify cache if (VersionHelper.isOrAbove1_21_2()) { int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(immutableBlockState.vanillaBlockState().handle()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index 3f1d586b7..e4b503afb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -26,7 +26,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SLAB_BLOCK = Key.from("craftengine:slab_block"); public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block"); public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block"); - public static final Key DOUBLE_BLOCK = Key.from("craftengine:double_block"); + public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -51,6 +51,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SLAB_BLOCK, SlabBlockBehavior.FACTORY); register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY); register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY); - register(DOUBLE_BLOCK, DoubleBlockBehavior.FACTORY); + register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java similarity index 93% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleBlockBehavior.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java index 089510d9e..016979338 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.block.behavior; - import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -20,12 +19,11 @@ import net.momirealms.craftengine.core.world.*; import java.util.Map; import java.util.concurrent.Callable; - -public class DoubleBlockBehavior extends BukkitBlockBehavior { +public class DoubleHighBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final Property halfProperty; - public DoubleBlockBehavior(CustomBlock customBlock, Property halfProperty) { + public DoubleHighBlockBehavior(CustomBlock customBlock, Property halfProperty) { super(customBlock); this.halfProperty = halfProperty; } @@ -83,8 +81,8 @@ public class DoubleBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - Property half = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.double.missing_half"); - return new DoubleBlockBehavior(block, half); + Property half = (Property) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.double_high.missing_half"); + return new DoubleHighBlockBehavior(block, half); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java index f108d5e2a..b82f164f0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java @@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureElement; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Material; @@ -65,7 +66,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement { } } - private synchronized List getCachedValues(@Nullable Integer color, int @Nullable [] colors) { + private synchronized List getCachedValues(@Nullable Color color, int @Nullable [] colors) { List cachedValues = new ArrayList<>(this.commonValues); Item item = BukkitItemManager.instance().createWrappedItem(item(), null); if (item == null) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java index 4f8e68a97..5bd11d54c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java @@ -21,12 +21,12 @@ public class BukkitCustomItem extends AbstractCustomItem { private final Object item; private final Object clientItem; - public BukkitCustomItem(UniqueKey id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey, + public BukkitCustomItem(boolean isVanillaItem, UniqueKey id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey, List behaviors, List> modifiers, List> clientBoundModifiers, ItemSettings settings, Map>> events) { - super(id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events); + super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events); this.item = item; this.clientItem = clientItem; } @@ -64,6 +64,7 @@ public class BukkitCustomItem extends AbstractCustomItem { } public static class BuilderImpl implements Builder { + private boolean isVanillaItem; private UniqueKey id; private Key itemKey; private final Object item; @@ -80,6 +81,12 @@ public class BukkitCustomItem extends AbstractCustomItem { this.clientBoundItem = clientBoundItem; } + @Override + public Builder isVanillaItem(boolean is) { + this.isVanillaItem = is; + return this; + } + @Override public Builder id(UniqueKey id) { this.id = id; @@ -150,7 +157,7 @@ public class BukkitCustomItem extends AbstractCustomItem { public CustomItem build() { this.modifiers.addAll(this.settings.modifiers()); this.clientBoundModifiers.addAll(this.settings.clientBoundModifiers()); - return new BukkitCustomItem(this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors), + return new BukkitCustomItem(this.isVanillaItem, this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors), List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events); } } 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 64e8735e5..219e20ef5 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 @@ -12,15 +12,12 @@ import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener; import net.momirealms.craftengine.bukkit.item.listener.ItemEventListener; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.*; -import net.momirealms.craftengine.core.item.modifier.IdModifier; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.plugin.config.Config; @@ -73,10 +70,9 @@ public class BukkitItemManager extends AbstractItemManager { this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get(); this.registerCustomTrimMaterial(); this.loadLastRegisteredPatterns(); - ItemStack emptyStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.instance$ItemStack$EMPTY); - this.emptyItem = this.wrap(emptyStack); - this.emptyUniqueItem = new UniqueIdItem<>(UniqueKey.AIR, this.emptyItem); + this.emptyItem = this.factory.wrap(emptyStack); + this.emptyUniqueItem = UniqueIdItem.of(this.emptyItem); this.decoratedHashOpsGenerator = VersionHelper.isOrAbove1_21_5() ? (Function) FastNMS.INSTANCE.createDecoratedHashOpsGenerator(MRegistryOps.HASHCODE) : null; } @@ -161,13 +157,33 @@ public class BukkitItemManager extends AbstractItemManager { } } + @Override + public Item build(DatapackRecipeResult result) { + if (result.components() == null) { + ItemStack itemStack = createVanillaItemStack(Key.of(result.id())); + return wrap(itemStack).count(result.count()); + } else { + // 低版本无法应用nbt或组件,所以这里是1.20.5+ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("id", result.id()); + jsonObject.addProperty("count", result.count()); + jsonObject.add("components", result.components()); + Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject) + .resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null); + if (nmsStack == null) { + return this.emptyItem; + } + return wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack)); + } + } + @Override public Optional> getVanillaItem(Key key) { ItemStack vanilla = createVanillaItemStack(key); if (vanilla == null) { return Optional.empty(); } - return Optional.of(new CloneableConstantItem(key, this.wrap(vanilla))); + return Optional.of(CloneableConstantItem.of(this.wrap(vanilla))); } @Override @@ -359,31 +375,18 @@ public class BukkitItemManager extends AbstractItemManager { @Override public @NotNull Item wrap(ItemStack itemStack) { - if (itemStack == null) return this.emptyItem; + if (itemStack == null || itemStack.isEmpty()) return this.emptyItem; return this.factory.wrap(itemStack); } - @Override - public Key itemId(ItemStack itemStack) { - Item wrapped = wrap(itemStack); - return wrapped.id(); - } - - @Override - public Key customItemId(ItemStack itemStack) { - Item wrapped = wrap(itemStack); - if (!wrapped.hasTag(IdModifier.CRAFT_ENGINE_ID)) return null; - return wrapped.id(); - } - @Override protected CustomItem.Builder createPlatformItemBuilder(UniqueKey id, Key materialId, Key clientBoundMaterialId) { Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(materialId)); Object clientBoundItem = materialId == clientBoundMaterialId ? item : FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(clientBoundMaterialId)); - if (item == null) { + if (item == MItems.AIR) { throw new LocalizedResourceConfigException("warning.config.item.invalid_material", materialId.toString()); } - if (clientBoundItem == null) { + if (clientBoundItem == MItems.AIR) { throw new LocalizedResourceConfigException("warning.config.item.invalid_material", clientBoundMaterialId.toString()); } return BukkitCustomItem.builder(item, clientBoundItem) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java deleted file mode 100644 index 13fb185c6..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.momirealms.craftengine.bukkit.item; - -import net.momirealms.craftengine.core.item.BuildableItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.inventory.ItemStack; - -public class CloneableConstantItem implements BuildableItem { - private final Item item; - private final Key id; - - public CloneableConstantItem(Key id, Item item) { - this.item = item; - this.id = id; - } - - @Override - public Key id() { - return this.id; - } - - @Override - public Item buildItem(ItemBuildContext context, int count) { - return this.item.copyWithCount(count); - } - - @Override - public ItemStack buildItemStack(ItemBuildContext context, int count) { - return this.item.copyWithCount(count).getItem(); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java index 6c4d94cf2..aa98ccc73 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyNetworkItemHandler.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.item.modifier.ArgumentModifier; +import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; @@ -74,7 +74,7 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler> s2c(Item wrapped, Player player) { + Item original = wrapped; Optional> optionalCustomItem = wrapped.getCustomItem(); if (optionalCustomItem.isEmpty()) { if (!Config.interceptItem()) return Optional.empty(); @@ -76,7 +77,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler modifier : customItem.clientBoundDataModifiers()) { - modifier.prepareNetworkItem(wrapped, context, tag); + modifier.prepareNetworkItem(original, context, tag); } for (ItemDataModifier modifier : customItem.clientBoundDataModifiers()) { modifier.apply(wrapped, context); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index affec07c6..faa5afefc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -47,6 +47,8 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -125,15 +127,19 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { // it's just world + pos BlockState previousState = bukkitBlock.getState(); + List revertStates = new ArrayList<>(2); + revertStates.add(previousState); // place custom block - CraftEngineBlocks.place(placeLocation, blockStateToPlace, UpdateOption.UPDATE_ALL_IMMEDIATE, false); + placeBlock(placeLocation, blockStateToPlace, revertStates); if (player != null) { // call bukkit event BlockPlaceEvent bukkitPlaceEvent = new BlockPlaceEvent(bukkitBlock, previousState, againstBlock, (ItemStack) context.getItem().getItem(), bukkitPlayer, true, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); if (EventUtils.fireAndCheckCancel(bukkitPlaceEvent)) { // revert changes - previousState.update(true, false); + for (BlockState state : revertStates) { + state.update(true, false); + } return InteractionResult.FAIL; } @@ -141,7 +147,9 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { CustomBlockPlaceEvent customPlaceEvent = new CustomBlockPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace, world.getBlockAt(placeLocation), context.getHand()); if (EventUtils.fireAndCheckCancel(customPlaceEvent)) { // revert changes - previousState.update(true, false); + for (BlockState state : revertStates) { + state.update(true, false); + } return InteractionResult.FAIL; } } @@ -215,6 +223,10 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { } } + protected boolean placeBlock(Location location, ImmutableBlockState blockState, List revertStates) { + return CraftEngineBlocks.place(location, blockState, UpdateOption.UPDATE_ALL_IMMEDIATE, false); + } + @Override public Key block() { return this.blockId; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index 51a030ce8..79292cfda 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -11,6 +11,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item"); public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item"); public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); + public static final Key DOUBLE_HIGH_BLOCK_ITEM = Key.from("craftengine:double_high_block_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -20,5 +21,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY); register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); + register(DOUBLE_HIGH_BLOCK_ITEM, DoubleHighBlockItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java new file mode 100644 index 000000000..0acf2bfb3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/DoubleHighBlockItemBehavior.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import org.bukkit.Location; +import org.bukkit.block.BlockState; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +public class DoubleHighBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public DoubleHighBlockItemBehavior(Key blockId) { + super(blockId); + } + + @Override + protected boolean placeBlock(Location location, ImmutableBlockState blockState, List revertState) { + Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()); + Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()); + UpdateOption option = UpdateOption.builder().updateNeighbors().updateClients().updateImmediate().updateKnownShape().build(); + Object fluidData = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level, blockPos); + Object stateToPlace = fluidData == MFluids.WATER$defaultState ? MBlocks.WATER$defaultState : MBlocks.AIR$defaultState; + revertState.add(location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()).getState()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, stateToPlace, option.flags()); + return super.placeBlock(location, blockState, revertState); + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, Key key, Map arguments) { + Object id = arguments.get("block"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior")); + } + if (id instanceof Map map) { + if (map.containsKey(key.toString())) { + // 防呆 + BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); + } else { + BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); + } + return new DoubleHighBlockItemBehavior(key); + } else { + return new DoubleHighBlockItemBehavior(Key.of(id.toString())); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java index 0c5ee3b3c..19838cdc9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java @@ -66,6 +66,11 @@ public abstract class BukkitItemFactory> extend } } + @Override + protected boolean isEmpty(W item) { + return FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject()); + } + @SuppressWarnings("deprecation") @Override protected byte[] toByteArray(W item) { @@ -86,6 +91,9 @@ public abstract class BukkitItemFactory> extend @Override protected Key id(W item) { + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) { + return ItemKeys.AIR; + } return customId(item).orElse(vanillaId(item)); } @@ -96,6 +104,9 @@ public abstract class BukkitItemFactory> extend @Override protected UniqueKey recipeIngredientID(W item) { + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) { + return null; + } if (this.hasExternalRecipeSource) { for (ExternalItemSource source : this.recipeIngredientSources) { String id = source.id(item.getItem()); @@ -108,14 +119,10 @@ public abstract class BukkitItemFactory> extend } @Override - protected boolean is(W item, Key itemTag) { + protected boolean hasItemTag(W item, Key itemTag) { Object literalObject = item.getLiteralObject(); Object tag = ItemTags.getOrCreate(itemTag); - try { - return (boolean) CoreReflections.method$ItemStack$isTag.invoke(literalObject, tag); - } catch (ReflectiveOperationException e) { - return false; - } + return FastNMS.INSTANCE.method$ItemStack$is(literalObject, tag); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 9abbd8a16..d5552f058 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -10,20 +10,20 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInReg import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.util.EnchantmentUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.attribute.AttributeModifier; +import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.nio.charset.StandardCharsets; +import java.util.*; public class ComponentItemFactory1_20_5 extends BukkitItemFactory { @@ -31,11 +31,6 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory dyedColor(ComponentItemWrapper item) { + protected Optional dyedColor(ComponentItemWrapper item) { if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty(); Object javaObj = getJavaComponent(item, ComponentTypes.DYED_COLOR); if (javaObj instanceof Integer integer) { - return Optional.of(integer); + return Optional.of(Color.fromDecimal(integer)); } else if (javaObj instanceof Map map) { - return Optional.of((int) map.get("rgb")); + return Optional.of(Color.fromDecimal((int) map.get("rgb"))); } return Optional.empty(); } @Override - protected void dyedColor(ComponentItemWrapper item, Integer color) { + protected void dyedColor(ComponentItemWrapper item, Color color) { if (color == null) { item.resetComponent(ComponentTypes.DYED_COLOR); } else { - item.setJavaComponent(ComponentTypes.DYED_COLOR, color); + item.setJavaComponent(ComponentTypes.DYED_COLOR, color.color()); } } @@ -586,4 +581,26 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory modifierList) { + CompoundTag compoundTag = (CompoundTag) item.getSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS).orElseGet(CompoundTag::new); + ListTag modifiers = new ListTag(); + compoundTag.put("modifiers", modifiers); + for (AttributeModifier modifier : modifierList) { + CompoundTag modifierTag = new CompoundTag(); + modifierTag.putString("type", modifier.type()); + modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); + if (VersionHelper.isOrAbove1_21()) { + modifierTag.putString("id", modifier.id().toString()); + } else { + modifierTag.putIntArray("uuid", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8)))); + modifierTag.putString("name", modifier.id().toString()); + } + modifierTag.putDouble("amount", modifier.amount()); + modifierTag.putString("operation", modifier.operation().id()); + modifiers.add(modifierTag); + } + item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, compoundTag); + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_21_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_21_5.java index af0981b60..e8c513d0d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_21_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_21_5.java @@ -5,15 +5,20 @@ import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentTypes; +import net.momirealms.craftengine.core.attribute.AttributeModifier; +import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.GsonHelper; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; import net.momirealms.sparrow.nbt.Tag; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Optional; public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 { @@ -123,4 +128,29 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 { protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) { item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, data.song()); } + + @Override + protected void attributeModifiers(ComponentItemWrapper item, List modifierList) { + ListTag modifiers = new ListTag(); + for (AttributeModifier modifier : modifierList) { + CompoundTag modifierTag = new CompoundTag(); + modifierTag.putString("type", modifier.type()); + modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); + modifierTag.putString("id", modifier.id().toString()); + modifierTag.putDouble("amount", modifier.amount()); + modifierTag.putString("operation", modifier.operation().id()); + AttributeModifier.Display display = modifier.display(); + if (VersionHelper.isOrAbove1_21_6() && display != null) { + CompoundTag displayTag = new CompoundTag(); + AttributeModifier.Display.Type displayType = display.type(); + displayTag.putString("type", displayType.name().toLowerCase(Locale.ENGLISH)); + if (displayType == AttributeModifier.Display.Type.OVERRIDE) { + displayTag.put("value", AdventureHelper.componentToTag(display.value())); + } + modifierTag.put("display", displayTag); + } + modifiers.add(modifierTag); + } + item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, modifiers); + } } 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 1d77a5ef4..172279912 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 @@ -5,13 +5,18 @@ import net.momirealms.craftengine.bukkit.item.LegacyItemWrapper; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.modifier.IdModifier; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.SkullUtils; +import net.momirealms.craftengine.core.util.UUIDUtils; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.NamespacedKey; import org.bukkit.Registry; @@ -62,11 +67,6 @@ public class UniversalItemFactory extends BukkitItemFactory { return item.remove(path); } - @Override - protected boolean isEmpty(LegacyItemWrapper item) { - return item.getItem().isEmpty(); - } - @Override protected Optional customId(LegacyItemWrapper item) { Object id = item.getJavaTag(IdModifier.CRAFT_ENGINE_ID); @@ -169,17 +169,17 @@ public class UniversalItemFactory extends BukkitItemFactory { } @Override - protected Optional dyedColor(LegacyItemWrapper item) { + protected Optional dyedColor(LegacyItemWrapper item) { if (!item.hasTag("display", "color")) return Optional.empty(); - return Optional.of(item.getJavaTag("display", "color")); + return Optional.of(Color.fromDecimal(item.getJavaTag("display", "color"))); } @Override - protected void dyedColor(LegacyItemWrapper item, Integer color) { + protected void dyedColor(LegacyItemWrapper item, Color color) { if (color == null) { item.remove("display", "color"); } else { - item.setTag(color, "display", "color"); + item.setTag(color.color(), "display", "color"); } } @@ -343,4 +343,20 @@ public class UniversalItemFactory extends BukkitItemFactory { FastNMS.INSTANCE.method$ItemStack$setTag(newItemStack, FastNMS.INSTANCE.method$CompoundTag$copy(FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(item.getLiteralObject()))); return new LegacyItemWrapper(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(newItemStack)); } + + @Override + protected void attributeModifiers(LegacyItemWrapper item, List modifiers) { + ListTag listTag = new ListTag(); + for (AttributeModifier modifier : modifiers) { + CompoundTag modifierTag = new CompoundTag(); + modifierTag.putString("AttributeName", modifier.type()); + modifierTag.putString("Name", modifier.id().toString()); + modifierTag.putString("Slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); + modifierTag.putInt("Operation", modifier.operation().ordinal()); + modifierTag.putDouble("Amount", modifier.amount()); + modifierTag.putIntArray("UUID", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8)))); + listTag.add(modifierTag); + } + item.setTag(listTag, "AttributeModifiers"); + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java index b10dd7ad7..ab5660c90 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/ItemEventListener.java @@ -36,6 +36,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Openable; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -66,14 +67,22 @@ public class ItemEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onInteractEntity(PlayerInteractEntityEvent event) { - BukkitServerPlayer serverPlayer = this.plugin.adapt(event.getPlayer()); + Player player = event.getPlayer(); + Entity entity = event.getRightClicked(); + BukkitServerPlayer serverPlayer = this.plugin.adapt(player); if (serverPlayer == null) return; + InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; + // prevent duplicated interact air events + serverPlayer.updateLastInteractEntityTick(hand); + Item itemInHand = serverPlayer.getItemInHand(hand); if (ItemUtils.isEmpty(itemInHand)) return; Optional> optionalCustomItem = itemInHand.getCustomItem(); if (optionalCustomItem.isEmpty()) return; + // 如果目标实体与手中物品可以产生交互,那么忽略 + if (InteractUtils.isEntityInteractable(player, entity, itemInHand)) return; Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() @@ -263,25 +272,7 @@ public class ItemEventListener implements Listener { } } - // execute item right click functions - if (hasCustomItem) { - Cancellable dummy = Cancellable.dummy(); - PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() - .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) - .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) - .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) - .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) - .withParameter(DirectContextParameters.HAND, hand) - .withParameter(DirectContextParameters.EVENT, dummy) - ); - CustomItem customItem = optionalCustomItem.get(); - customItem.execute(context, EventTrigger.RIGHT_CLICK); - if (dummy.isCancelled()) { - event.setCancelled(true); - return; - } - } - + // 优先检查物品行为,再执行自定义事件 // 检查其他的物品行为,物品行为理论只在交互时处理 Optional> optionalItemBehaviors = itemInHand.getItemBehavior(); // 物品类型是否包含自定义物品行为,行为不一定来自于自定义物品,部分原版物品也包含了新的行为 @@ -309,8 +300,31 @@ public class ItemEventListener implements Listener { } } } + + // 执行物品右键事件 + if (hasCustomItem) { + // 要求服务端侧这个方块不可交互,或玩家处于潜行状态 + if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) { + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() + .withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block)) + .withOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, immutableBlockState) + .withOptionalParameter(DirectContextParameters.ITEM_IN_HAND, itemInHand) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(block.getLocation())) + .withParameter(DirectContextParameters.HAND, hand) + .withParameter(DirectContextParameters.EVENT, dummy) + ); + CustomItem customItem = optionalCustomItem.get(); + customItem.execute(context, EventTrigger.RIGHT_CLICK); + if (dummy.isCancelled()) { + event.setCancelled(true); + return; + } + } + } } + // 执行物品左键事件 if (hasCustomItem && action == Action.LEFT_CLICK_BLOCK) { Cancellable dummy = Cancellable.dummy(); PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() @@ -340,11 +354,16 @@ public class ItemEventListener implements Listener { return; // Gets the item in hand InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; + // prevents duplicated events + if (serverPlayer.lastInteractEntityCheck(hand)) { + return; + } + Item itemInHand = serverPlayer.getItemInHand(hand); // should never be null if (ItemUtils.isEmpty(itemInHand)) return; - // todo 真的需要这个吗 + // TODO 有必要存在吗? if (cancelEventIfHasInteraction(event, serverPlayer, hand)) { return; } @@ -449,7 +468,7 @@ public class ItemEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onEntityDamage(EntityDamageEvent event) { if (event.getEntity() instanceof org.bukkit.entity.Item item) { - Optional.ofNullable(this.plugin.itemManager().wrap(item.getItemStack())) + Optional.of(this.plugin.itemManager().wrap(item.getItemStack())) .flatMap(Item::getCustomItem) .ifPresent(it -> { if (it.settings().invulnerable().contains(DamageCauseUtils.fromBukkit(event.getCause()))) { @@ -459,6 +478,7 @@ public class ItemEventListener implements Listener { } } + // 禁止附魔 @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onEnchant(PrepareItemEnchantEvent event) { ItemStack itemToEnchant = event.getItem(); @@ -471,6 +491,7 @@ public class ItemEventListener implements Listener { } } + // 自定义堆肥改了 @EventHandler(ignoreCancelled = true) public void onCompost(CompostItemEvent event) { ItemStack itemToCompost = event.getItem(); @@ -480,6 +501,7 @@ public class ItemEventListener implements Listener { event.setWillRaiseLevel(RandomUtils.generateRandomFloat(0, 1) < optionalCustomItem.get().settings().compostProbability()); } + // 用于附魔台纠正 @EventHandler(ignoreCancelled = true) public void onInventoryClick(InventoryClickEvent event) { if (!(event.getInventory() instanceof EnchantingInventory inventory)) return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index 953f220fe..bcbe55a26 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -6,793 +6,216 @@ import io.papermc.paper.potion.PotionMix; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.item.CloneableConstantItem; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; +import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.MaterialUtils; -import net.momirealms.craftengine.bukkit.util.RecipeUtils; -import net.momirealms.craftengine.core.item.*; +import net.momirealms.craftengine.core.item.BuildableItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.recipe.*; -import net.momirealms.craftengine.core.item.recipe.Recipe; -import net.momirealms.craftengine.core.item.recipe.vanilla.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.*; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.event.HandlerList; -import org.bukkit.inventory.*; -import org.bukkit.inventory.recipe.CookingBookCategory; -import org.bukkit.inventory.recipe.CraftingBookCategory; +import org.bukkit.inventory.ItemStack; import java.io.Reader; import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; +// todo 在folia上替换recipe map使其线程安全 public class BukkitRecipeManager extends AbstractRecipeManager { private static BukkitRecipeManager instance; - // 将自定义配方转为“广义”配方,接受更加宽容的输入 - // 部分过程借助bukkit完成,部分直接通过nms方法注册 - private static final Map>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); - private static final List injectedIngredients = new ArrayList<>(); - private static final IdentityHashMap, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>(); - private static Object nmsRecipeManager; - - private static void registerNMSSmithingRecipe(Object recipe) { - try { - CoreReflections.method$RecipeManager$addRecipe.invoke(nmsRecipeManager(), recipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smithing recipe", e); - } - } - - private static void registerBukkitShapedRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftShapedRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register shaped recipe", e); - } - } - - private static void registerBukkitShapelessRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftShapelessRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register shapeless recipe", e); - } - } - - private static void registerBukkitSmeltingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftFurnaceRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smelting recipe", e); - } - } - - private static void registerBukkitSmokingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftSmokingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smoking recipe", e); - } - } - - private static void registerBukkitBlastingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftBlastingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register blasting recipe", e); - } - } - - private static void registerBukkitCampfireRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftCampfireRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register campfire recipe", e); - } - } - - private static void registerBukkitStoneCuttingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftStonecuttingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register stonecutting recipe", e); - } - } + private static final Consumer MINECRAFT_RECIPE_REMOVER = VersionHelper.isOrAbove1_21_2() ? + (id -> { + Object resourceKey = toRecipeResourceKey(id); + FastNMS.INSTANCE.method$RecipeMap$removeRecipe(FastNMS.INSTANCE.field$RecipeManager$recipes(minecraftRecipeManager()), resourceKey); + }) : + (id -> { + Object resourceLocation = KeyUtils.toResourceLocation(id); + FastNMS.INSTANCE.method$RecipeManager$removeRecipe(minecraftRecipeManager(), resourceLocation); + }); + private static final BiFunction MINECRAFT_RECIPE_ADDER = + VersionHelper.isOrAbove1_21_2() ? + (id, recipe) -> { + Object resourceKey = toRecipeResourceKey(id); + Object recipeHolder = FastNMS.INSTANCE.constructor$RecipeHolder(resourceKey, recipe); + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipeHolder); + return recipeHolder; + } : + VersionHelper.isOrAbove1_20_2() ? + (id, recipe) -> { + Object resourceLocation = KeyUtils.toResourceLocation(id); + Object recipeHolder = FastNMS.INSTANCE.constructor$RecipeHolder(resourceLocation, recipe); + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipeHolder); + return recipeHolder; + } : + (id, recipe) -> { + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipe); + return recipe; + }; static { - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRANSFORM, (BukkitRecipeConvertor>) (id, recipe) -> { - try { - Object nmsRecipe = createMinecraftSmithingTransformRecipe(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance( - CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())), nmsRecipe); - } else if (VersionHelper.isOrAbove1_20_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance(KeyUtils.toResourceLocation(id), nmsRecipe); - } else { - Object finalNmsRecipe0 = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe0); - } - Object finalNmsRecipe = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe); - } catch (InvalidRecipeIngredientException e) { - throw e; - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to convert smithing transform recipe", e); - return null; - } - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRIM, (BukkitRecipeConvertor>) (id, recipe) -> { - try { - Object nmsRecipe = createMinecraftSmithingTrimRecipe(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance( - CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())), nmsRecipe); - } else if (VersionHelper.isOrAbove1_20_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance(KeyUtils.toResourceLocation(id), nmsRecipe); - } else { - Object finalNmsRecipe0 = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe0); - } - Object finalNmsRecipe = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe); - } catch (InvalidRecipeIngredientException e) { - throw e; - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to convert smithing trim recipe", e); - return null; - } - }); - // TODO DO NOT USE BUKKIT RECIPE AS BRIDGE IN FUTURE VERSIONS, WE SHOULD DIRECTLY CONSTRUCT THOSE NMS RECIPES - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SHAPED, (BukkitRecipeConvertor>) (id, recipe) -> { - ShapedRecipe shapedRecipe = new ShapedRecipe(new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY)); - if (recipe.group() != null) shapedRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) shapedRecipe.setCategory(CraftingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - shapedRecipe.shape(recipe.pattern().pattern()); - for (Map.Entry> entry : recipe.pattern().ingredients().entrySet()) { - shapedRecipe.setIngredient(entry.getKey(), ingredientToBukkitRecipeChoice(entry.getValue())); - } - return () -> { - registerBukkitShapedRecipe(shapedRecipe); - injectShapedRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SHAPELESS, (BukkitRecipeConvertor>) (id, recipe) -> { - ShapelessRecipe shapelessRecipe = new ShapelessRecipe(new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY)); - if (recipe.group() != null) shapelessRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) shapelessRecipe.setCategory(CraftingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - for (Ingredient ingredient : recipe.ingredientsInUse()) { - shapelessRecipe.addIngredient(ingredientToBukkitRecipeChoice(ingredient)); - } - return () -> { - registerBukkitShapelessRecipe(shapelessRecipe); - injectShapelessRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMELTING, (BukkitRecipeConvertor>) (id, recipe) -> { - FurnaceRecipe furnaceRecipe = new FurnaceRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) furnaceRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) furnaceRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitSmeltingRecipe(furnaceRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMOKING, (BukkitRecipeConvertor>) (id, recipe) -> { - SmokingRecipe smokingRecipe = new SmokingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) smokingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) smokingRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitSmokingRecipe(smokingRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.BLASTING, (BukkitRecipeConvertor>) (id, recipe) -> { - BlastingRecipe blastingRecipe = new BlastingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) blastingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) blastingRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitBlastingRecipe(blastingRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.CAMPFIRE_COOKING, (BukkitRecipeConvertor>) (id, recipe) -> { - CampfireRecipe campfireRecipe = new CampfireRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) campfireRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) campfireRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitCampfireRecipe(campfireRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeTypes.STONECUTTING, (BukkitRecipeConvertor>) (id, recipe) -> { - List itemStacks = new ArrayList<>(); - for (UniqueKey item : recipe.ingredient().items()) { - itemStacks.add(BukkitItemManager.instance().buildItemStack(item.key(), null)); - } - StonecuttingRecipe stonecuttingRecipe = new StonecuttingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - new RecipeChoice.ExactChoice(itemStacks) - ); - if (recipe.group() != null) stonecuttingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - return () -> { - registerBukkitStoneCuttingRecipe(stonecuttingRecipe); - }; - }); - } - - /* - * 注册全过程: - * - * 0.准备阶段偷取flag以减少注册的性能开销 - * 1.先读取用户配置自定义配方 - * 2.延迟加载中为自定义配方生成转换为nms配方的任务 - * 3.读取全部的数据包配方并转换为自定义配方,对必要的含有tag配方添加先移除后注册nms配方的任务 - * 4.主线程完成剩余任务 - * 5.归还flag - */ - - private final BukkitCraftEngine plugin; - private final RecipeEventListener recipeEventListener; - private final CrafterEventListener crafterEventListener; - // To optimize recipes loading, will return the flag later - private Object stolenFeatureFlagSet; - // Some delayed tasks on main thread - private final List delayedTasksOnMainThread = new ArrayList<>(); - - public BukkitRecipeManager(BukkitCraftEngine plugin) { - instance = this; - this.plugin = plugin; - this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); - this.crafterEventListener = VersionHelper.isOrAbove1_21() ? new CrafterEventListener(plugin, this, plugin.itemManager()) : null; try { - nmsRecipeManager = CoreReflections.method$MinecraftServer$getRecipeManager.invoke(CoreReflections.method$MinecraftServer$getServer.invoke(null)); + Key dyeRecipeId = Key.from("armor_dye"); + MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); + Key repairRecipeId = Key.from("repair_item"); + MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); + MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); + Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); + MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); + MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to get minecraft recipe manager", e); + throw new ReflectionInitException("Failed to inject special recipes", e); } } - public Object nmsRecipeHolderByRecipe(Recipe recipe) { - if (super.isReloading) return null; - return CE_RECIPE_2_NMS_HOLDER.get(recipe); - } - - public static Object nmsRecipeManager() { - return nmsRecipeManager; - } - - public static BukkitRecipeManager instance() { - return instance; - } - - @Override - public void delayedInit() { - Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin()); - if (this.crafterEventListener != null) { - Bukkit.getPluginManager().registerEvents(this.crafterEventListener, this.plugin.javaPlugin()); - } - } - - @Override - public void load() { - if (!Config.enableRecipeSystem()) return; - super.isReloading = true; - if (VersionHelper.isOrAbove1_21_2()) { - try { - this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager); - CoreReflections.field$RecipeManager$featureflagset.set(nmsRecipeManager, null); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to steal featureflagset", e); + private static final List MODIFIED_INGREDIENTS = new ArrayList<>(); + private static final Map, Object>> ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER = Map.of( + RecipeSerializers.SHAPED, recipe -> { + CustomShapedRecipe shapedRecipe = (CustomShapedRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createShapedRecipe(shapedRecipe); + modifyShapedRecipeIngredients(shapedRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SHAPELESS, recipe -> { + CustomShapelessRecipe shapelessRecipe = (CustomShapelessRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createShapelessRecipe(shapelessRecipe); + modifyShapelessRecipeIngredients(shapelessRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMELTING, recipe -> { + CustomSmeltingRecipe smeltingRecipe = (CustomSmeltingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createSmeltingRecipe(smeltingRecipe); + modifyCookingRecipeIngredient(smeltingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.BLASTING, recipe -> { + CustomBlastingRecipe blastingRecipe = (CustomBlastingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createBlastingRecipe(blastingRecipe); + modifyCookingRecipeIngredient(blastingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMOKING, recipe -> { + CustomSmokingRecipe smokingRecipe = (CustomSmokingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createSmokingRecipe(smokingRecipe); + modifyCookingRecipeIngredient(smokingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.CAMPFIRE_COOKING, recipe -> { + CustomCampfireRecipe campfireRecipe = (CustomCampfireRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createCampfireRecipe(campfireRecipe); + modifyCookingRecipeIngredient(campfireRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.STONECUTTING, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createStonecuttingRecipe((CustomStoneCuttingRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMITHING_TRIM, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createSmithingTrimRecipe((CustomSmithingTrimRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMITHING_TRANSFORM, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createSmithingTransformRecipe((CustomSmithingTransformRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); } - } - } + ); - @Override - public void unload() { - if (!Config.enableRecipeSystem()) return; - // 安排卸载任务,这些任务会在load后执行。如果没有load说明服务器已经关闭了,那就不需要管卸载了。 - if (!Bukkit.isStopping()) { - for (Map.Entry> entry : this.byId.entrySet()) { - Key id = entry.getKey(); - if (isDataPackRecipe(id)) continue; - boolean isBrewingRecipe = entry.getValue() instanceof CustomBrewingRecipe; - this.delayedTasksOnMainThread.add(() -> this.unregisterPlatformRecipe(id, isBrewingRecipe)); - } - } - super.unload(); - } - - @Override - public void delayedLoad() { - if (!Config.enableRecipeSystem()) return; - this.injectDataPackRecipes(); - } - - @Override - public void disable() { - unload(); - CE_RECIPE_2_NMS_HOLDER.clear(); - // 不是服务器关闭造成disable,那么需要把配方卸载干净 - if (!Bukkit.isStopping()) { - for (Runnable task : this.delayedTasksOnMainThread) { - task.run(); - } - } - HandlerList.unregisterAll(this.recipeEventListener); - if (this.crafterEventListener != null) { - HandlerList.unregisterAll(this.crafterEventListener); - } - } - - @Override - protected void unregisterPlatformRecipe(Key key, boolean isBrewingRecipe) { - if (isBrewingRecipe) { - Bukkit.getPotionBrewer().removePotionMix(new NamespacedKey(key.namespace(), key.value())); - } else { - unregisterNMSRecipe(new NamespacedKey(key.namespace(), key.value())); - } - } - - @Override - protected void registerPlatformRecipe(Key id, Recipe recipe) { - if (recipe instanceof CustomBrewingRecipe brewingRecipe) { - if (!VersionHelper.isOrAbove1_20_2()) return; - PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()), - brewingRecipe.result(ItemBuildContext.EMPTY), - PotionMix.createPredicateChoice(container -> { - Item wrapped = this.plugin.itemManager().wrap(container); - return brewingRecipe.container().test(new UniqueIdItem<>(wrapped.recipeIngredientId(), wrapped)); - }), - PotionMix.createPredicateChoice(ingredient -> { - Item wrapped = this.plugin.itemManager().wrap(ingredient); - return brewingRecipe.ingredient().test(new UniqueIdItem<>(wrapped.recipeIngredientId(), wrapped)); - }) - ); - this.delayedTasksOnMainThread.add(() -> { - Bukkit.getPotionBrewer().addPotionMix(potionMix); - }); - } else { - try { - Runnable converted = findNMSRecipeConvertor(recipe).convert(id, recipe); - if (converted != null) { - this.delayedTasksOnMainThread.add(converted); - } - } catch (InvalidRecipeIngredientException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.invalid_ingredient", e.ingredient()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to convert recipe " + id, e); + private static void modifyShapedRecipeIngredients(CustomShapedRecipe recipe, Object shapedRecipe) { + try { + List> actualIngredients = recipe.parsedPattern().ingredients() + .stream() + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + if (VersionHelper.isOrAbove1_21_2()) { + CoreReflections.field$ShapedRecipe$placementInfo.set(shapedRecipe, null); } + List ingredients = getIngredientsFromShapedRecipe(shapedRecipe); + modifyIngredients(ingredients, actualIngredients); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e); } } @SuppressWarnings("unchecked") - private > BukkitRecipeConvertor findNMSRecipeConvertor(T recipe) { - return (BukkitRecipeConvertor) MIXED_RECIPE_CONVERTORS.get(recipe.type()); - } - - private void unregisterNMSRecipe(NamespacedKey key) { + public static List getIngredientsFromShapedRecipe(Object recipe) { + List ingredients = new ArrayList<>(); try { - if (VersionHelper.isOrAbove1_21_2()) { - Object recipeMap = CoreReflections.field$RecipeManager$recipes.get(nmsRecipeManager); - CoreReflections.method$RecipeMap$removeRecipe.invoke(recipeMap, CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, key)); + if (VersionHelper.isOrAbove1_20_3()) { + Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe); + if (VersionHelper.isOrAbove1_21_2()) { + List> optionals = (List>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern); + for (Optional optional : optionals) { + optional.ifPresent(ingredients::add); + } + } else { + List objectList = (List) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern); + for (Object object : objectList) { + Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); + // is empty or not + if (values.length != 0) { + ingredients.add(object); + } + } + } } else { - Bukkit.removeRecipe(key); + List objectList = (List) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe); + for (Object object : objectList) { + Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); + if (values.length != 0) { + ingredients.add(object); + } + } } } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to unregister nms recipes", e); + CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e); } + return ingredients; } - @SuppressWarnings("unchecked") - private void injectDataPackRecipes() { + private static void modifyShapelessRecipeIngredients(CustomShapelessRecipe recipe, Object shapelessRecipe) { try { - Object fileToIdConverter = CoreReflections.method$FileToIdConverter$json.invoke(null, VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"); - Object minecraftServer = CoreReflections.method$MinecraftServer$getServer.invoke(null); - Object packRepository = CoreReflections.method$MinecraftServer$getPackRepository.invoke(minecraftServer); - List selected = (List) CoreReflections.field$PackRepository$selected.get(packRepository); - List packResources = new ArrayList<>(); - for (Object pack : selected) { - packResources.add(CoreReflections.method$Pack$open.invoke(pack)); - } - - boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty(); - try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.constructor$MultiPackResourceManager.newInstance(CoreReflections.instance$PackType$SERVER_DATA, packResources)) { - Map scannedResources = (Map) CoreReflections.method$FileToIdConverter$listMatchingResources.invoke(fileToIdConverter, resourceManager); - for (Map.Entry entry : scannedResources.entrySet()) { - Key id = extractKeyFromResourceLocation(entry.getKey().toString()); - // now CraftEngine takes over everything -// // Maybe it's unregistered by other plugins -// if (Bukkit.getRecipe(new NamespacedKey(id.namespace(), id.value())) == null) { -// continue; -// } - if (Config.disableAllVanillaRecipes()) { - this.delayedTasksOnMainThread.add(() -> unregisterPlatformRecipe(id, false)); - continue; - } - if (hasDisabledAny && Config.disabledVanillaRecipes().contains(id)) { - this.delayedTasksOnMainThread.add(() -> unregisterPlatformRecipe(id, false)); - continue; - } - markAsDataPackRecipe(id); - Reader reader = (Reader) CoreReflections.method$Resource$openAsReader.invoke(entry.getValue()); - JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); - String type = jsonObject.get("type").getAsString(); - switch (type) { - case "minecraft:crafting_shaped" -> { - VanillaShapedRecipe recipe = this.recipeReader.readShaped(jsonObject); - handleDataPackShapedRecipe(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:crafting_shapeless" -> { - VanillaShapelessRecipe recipe = this.recipeReader.readShapeless(jsonObject); - handleDataPackShapelessRecipe(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smelting" -> { - VanillaSmeltingRecipe recipe = this.recipeReader.readSmelting(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomSmeltingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:blasting" -> { - VanillaBlastingRecipe recipe = this.recipeReader.readBlasting(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomBlastingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smoking" -> { - VanillaSmokingRecipe recipe = this.recipeReader.readSmoking(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomSmokingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:campfire_cooking" -> { - VanillaCampfireRecipe recipe = this.recipeReader.readCampfire(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomCampfireRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smithing_transform" -> { - VanillaSmithingTransformRecipe recipe = this.recipeReader.readSmithingTransform(jsonObject); - handleDataPackSmithingTransform(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smithing_trim" -> { - VanillaSmithingTrimRecipe recipe = this.recipeReader.readSmithingTrim(jsonObject); - handleDataPackSmithingTrim(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:stonecutting" -> { - VanillaStoneCuttingRecipe recipe = this.recipeReader.readStoneCutting(jsonObject); - handleDataPackStoneCuttingRecipe(id, recipe); - } - } - } - } - } catch (Exception e) { - this.plugin.logger().warn("Failed to read data pack recipes", e); - } - } - - @Override - public void runDelayedSyncTasks() { - if (!Config.enableRecipeSystem()) return; - try { - // run delayed tasks - for (Runnable r : this.delayedTasksOnMainThread) { - r.run(); - } - this.delayedTasksOnMainThread.clear(); - - // give flags back on 1.21.2+ - if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { - CoreReflections.field$RecipeManager$featureflagset.set(nmsRecipeManager(), this.stolenFeatureFlagSet); - this.stolenFeatureFlagSet = null; - } - - // refresh recipes + List> actualIngredients = recipe.ingredientsInUse(); if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(nmsRecipeManager()); + CoreReflections.field$ShapelessRecipe$placementInfo.set(shapelessRecipe, null); } - - // send to players - CoreReflections.method$DedicatedPlayerList$reloadRecipes.invoke(CraftBukkitReflections.field$CraftServer$playerList.get(Bukkit.getServer())); - - // now we need to remove the fake `exact` - if (VersionHelper.isOrAbove1_21_4()) { - for (Object ingredient : injectedIngredients) { - CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, null); - } - } else if (VersionHelper.isOrAbove1_21_2()) { - for (Object ingredient : injectedIngredients) { - CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, null); - } - } - - // clear cache - injectedIngredients.clear(); - - CE_RECIPE_2_NMS_HOLDER.clear(); - // create mappings - for (Map.Entry> entry : this.byId.entrySet()) { - Optional nmsRecipe = getOptionalNMSRecipe(entry.getKey()); - nmsRecipe.ifPresent(o -> CE_RECIPE_2_NMS_HOLDER.put(entry.getValue(), o)); - } - - super.isReloading = false; - } catch (Exception e) { - this.plugin.logger().warn("Failed to run delayed recipe tasks", e); + @SuppressWarnings("unchecked") + List ingredients = (List) CoreReflections.field$ShapelessRecipe$ingredients.get(shapelessRecipe); + modifyIngredients(ingredients, actualIngredients); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e); } } - private void handleDataPackStoneCuttingRecipe(Key id, VanillaStoneCuttingRecipe recipe) { - ItemStack result = createDataPackResultStack(recipe.result()); - Set holders = new HashSet<>(); - for (String item : recipe.ingredient()) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - holders.addAll(this.plugin.itemManager().tagToItems(tag)); + private static void modifyCookingRecipeIngredient(CustomCookingRecipe recipe, Object cookingRecipe) { + try { + Ingredient actualIngredient = recipe.ingredient(); + Object ingredient; + if (VersionHelper.isOrAbove1_21_2()) { + ingredient = CoreReflections.field$SingleItemRecipe$input.get(cookingRecipe); } else { - holders.add(UniqueKey.create(Key.from(item))); + ingredient = CoreReflections.field$AbstractCookingRecipe$input.get(cookingRecipe); } + modifyIngredients(List.of(ingredient), List.of(actualIngredient)); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e); } - CustomStoneCuttingRecipe ceRecipe = new CustomStoneCuttingRecipe<>( - id, recipe.group(), Ingredient.of(holders), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag = false; - List> ingredientList = new ArrayList<>(); - for (List list : recipe.ingredients()) { - Set holders = new HashSet<>(); - for (String item : list) { - if (item.charAt(0) == '#') { - Key tag = Key.of(item.substring(1)); - if (!hasCustomItemInTag) { - if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; - } - } - holders.addAll(plugin.itemManager().tagToItems(tag)); - } else { - holders.add(UniqueKey.create(Key.from(item))); - } - } - ingredientList.add(Ingredient.of(holders)); - } - CustomShapelessRecipe ceRecipe = new CustomShapelessRecipe<>( - id, recipe.category(), recipe.group(), ingredientList, - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - unregisterNMSRecipe(key); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackShapedRecipe(Key id, VanillaShapedRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag = false; - Map> ingredients = new HashMap<>(); - for (Map.Entry> entry : recipe.ingredients().entrySet()) { - Set holders = new HashSet<>(); - for (String item : entry.getValue()) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - if (!hasCustomItemInTag) { - if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; - } - } - holders.addAll(plugin.itemManager().tagToItems(tag)); - } else { - holders.add(UniqueKey.create(Key.from(item))); - } - } - ingredients.put(entry.getKey(), Ingredient.of(holders)); - } - CustomShapedRecipe ceRecipe = new CustomShapedRecipe<>( - id, recipe.category(), recipe.group(), - new CustomShapedRecipe.Pattern<>(recipe.pattern(), ingredients), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - unregisterNMSRecipe(key); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackCookingRecipe(Key id, - VanillaCookingRecipe recipe, - HeptaFunction, Integer, Float, CustomRecipeResult, CustomCookingRecipe> constructor2, - Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - ItemStack result = createDataPackResultStack(recipe.result()); - Set holders = new HashSet<>(); - boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), holders::add); - CustomCookingRecipe ceRecipe = constructor2.apply( - id, recipe.category(), recipe.group(), - Ingredient.of(holders), - recipe.cookingTime(), recipe.experience(), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - unregisterNMSRecipe(key); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag; - - Set additionHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add); - Set templateHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.template(), templateHolders::add); - Set baseHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.base(), baseHolders::add); - - CustomSmithingTransformRecipe ceRecipe = new CustomSmithingTransformRecipe<>( - id, - baseHolders.isEmpty() ? null : Ingredient.of(baseHolders), - templateHolders.isEmpty() ? null : Ingredient.of(templateHolders), - additionHolders.isEmpty() ? null : Ingredient.of(additionHolders), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null), - true, - List.of() - ); - - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - unregisterNMSRecipe(key); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackSmithingTrim(Key id, VanillaSmithingTrimRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - - boolean hasCustomItemInTag; - Set additionHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add); - Set templateHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.template(), templateHolders::add); - Set baseHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.base(), baseHolders::add); - - CustomSmithingTrimRecipe ceRecipe = new CustomSmithingTrimRecipe<>( - id, - Ingredient.of(baseHolders), - Ingredient.of(templateHolders), - Ingredient.of(additionHolders), - Optional.ofNullable(recipe.pattern()).map(Key::of).orElse(null) - ); - - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - unregisterNMSRecipe(key); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private boolean readVanillaIngredients(boolean hasCustomItemInTag, List ingredients, Consumer holderConsumer) { - for (String item : ingredients) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - if (!hasCustomItemInTag) { - if (!this.plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; - } - } - for (UniqueKey holder : this.plugin.itemManager().tagToItems(tag)) { - holderConsumer.accept(holder); - } - } else { - holderConsumer.accept(UniqueKey.create(Key.from(item))); - } - } - return hasCustomItemInTag; - } - - private ItemStack createDataPackResultStack(RecipeResult result) { - ItemStack itemStack; - if (result.components() == null) { - itemStack = new ItemStack(Objects.requireNonNull(MaterialUtils.getMaterial(result.id()))); - itemStack.setAmount(result.count()); - } else { - // 低版本无法应用nbt或组件,所以这里是1.20.5+ - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("id", result.id()); - jsonObject.addProperty("count", result.count()); - jsonObject.add("components", result.components()); - Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject) - .resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null); - if (nmsStack == null) { - return new ItemStack(Material.STONE); - } - try { - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack); - } catch (Exception e) { - this.plugin.logger().warn("Failed to create ItemStack mirror", e); - return new ItemStack(Material.STICK); - } - } - return itemStack; - } - - private Key extractKeyFromResourceLocation(String input) { - int prefixEndIndex = input.indexOf(':'); - String prefix = input.substring(0, prefixEndIndex); - int lastSlashIndex = input.lastIndexOf('/'); - int lastDotIndex = input.lastIndexOf('.'); - String fileName = input.substring(lastSlashIndex + 1, lastDotIndex); - return Key.of(prefix, fileName); - } - - private static RecipeChoice ingredientToBukkitRecipeChoice(Ingredient ingredient) { - Set materials = new HashSet<>(); - for (UniqueKey holder : ingredient.items()) { - materials.add(getMaterialById(holder.key())); - } - return new RecipeChoice.MaterialChoice(new ArrayList<>(materials)); - } - - private static Material getMaterialById(Key key) { - Material material = MaterialUtils.getMaterial(key); - if (material != null) { - return material; - } - Optional> optionalItem = BukkitItemManager.instance().getCustomItem(key); - return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElseThrow(() -> new InvalidRecipeIngredientException(key.asString())); } private static List getIngredientLooks(List holders) { @@ -805,96 +228,14 @@ public class BukkitRecipeManager extends AbstractRecipeManager { itemStacks.add(nmsStack); } else { Item barrier = BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null); + assert barrier != null; barrier.customNameJson(AdventureHelper.componentToJson(Component.text(holder.key().asString()).color(NamedTextColor.RED))); } } return itemStacks; } - // 无论是什么注入什么配方类型的方法,其本质都是注入ingredient - private static void injectShapedRecipe(Key id, CustomShapedRecipe recipe) { - try { - List> actualIngredients = recipe.parsedPattern().ingredients() - .stream() - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - - Object shapedRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe); - } - - if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.field$ShapedRecipe$placementInfo.set(shapedRecipe, null); - } - - List ingredients = RecipeUtils.getIngredientsFromShapedRecipe(shapedRecipe); - injectIngredients(ingredients, actualIngredients); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e); - } - } - - private static void injectShapelessRecipe(Key id, CustomShapelessRecipe recipe) { - try { - List> actualIngredients = recipe.ingredientsInUse(); - - Object shapelessRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe); - } - - if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.field$ShapelessRecipe$placementInfo.set(shapelessRecipe, null); - } - @SuppressWarnings("unchecked") - List ingredients = (List) CoreReflections.field$ShapelessRecipe$ingredients.get(shapelessRecipe); - injectIngredients(ingredients, actualIngredients); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e); - } - } - - private static void injectCookingRecipe(Key id, CustomCookingRecipe recipe) { - try { - Ingredient actualIngredient = recipe.ingredient(); - Object smeltingRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe); - } - - Object ingredient; - if (VersionHelper.isOrAbove1_21_2()) { - ingredient = CoreReflections.field$SingleItemRecipe$input.get(smeltingRecipe); - } else { - ingredient = CoreReflections.field$AbstractCookingRecipe$input.get(smeltingRecipe); - } - injectIngredients(List.of(ingredient), List.of(actualIngredient)); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e); - } - } - - // 获取nms配方,请注意1.20.1获取配方本身,而1.20.2+获取的是配方的holder - // recipe on 1.20.1 and holder on 1.20.2+ - private static Optional getOptionalNMSRecipe(Key id) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_2()) { - Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); - @SuppressWarnings("unchecked") - Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey); - return optional; - } else { - Object resourceLocation = KeyUtils.toResourceLocation(id); - @SuppressWarnings("unchecked") - Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation); - return optional; - } - } - - // 注入原料,这个方法受不同服务端fork和版本影响极大,需要每个版本测试 - // 此过程是为了避免自己处理“广义”配方与客户端的注册通讯 - private static void injectIngredients(List fakeIngredients, List> actualIngredients) throws ReflectiveOperationException { + private static void modifyIngredients(List fakeIngredients, List> actualIngredients) throws ReflectiveOperationException { if (fakeIngredients.size() != actualIngredients.size()) { throw new IllegalArgumentException("Ingredient count mismatch"); } @@ -913,100 +254,256 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } CoreReflections.field$Ingredient$itemStacks1_20_1.set(ingredient, itemStackArray); } - injectedIngredients.add(ingredient); + MODIFIED_INGREDIENTS.add(ingredient); } } - // 1.20-1.21.2 - private static Object toMinecraftIngredient(Ingredient ingredient) throws ReflectiveOperationException { - if (ingredient == null) { - return CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, null, true); - } else { - RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient); - return CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, choice, true); + public static Object toRecipeResourceKey(Key id) { + return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); + } + + /* + * 注册全过程: + * + * 0.准备阶段偷取flag以减少注册的性能开销 + * 1.先读取用户配置自定义配方 + * 2.延迟加载中为自定义配方生成转换为nms配方的任务 + * 3.读取全部的数据包配方并转换为自定义配方,对必要的含有tag配方添加先移除后注册nms配方的任务 + * 4.主线程完成剩余任务 + * 5.归还flag + */ + private final BukkitCraftEngine plugin; + private final RecipeEventListener recipeEventListener; + // 欺骗服务端使其以为自己处于启动阶段 + private Object stolenFeatureFlagSet; + // 需要在主线程卸载的配方 + private final List> recipesToUnregister = new ArrayList<>(); + // 已经被替换过的数据包配方 + private final Set replacedDatapackRecipes = new HashSet<>(); + // 换成的数据包配方 + private Map> lastDatapackRecipes = Map.of(); + private Object lastRecipeManager = null; + + public BukkitRecipeManager(BukkitCraftEngine plugin) { + instance = this; + this.plugin = plugin; + this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); + } + + public static Object minecraftRecipeManager() { + return FastNMS.INSTANCE.method$MinecraftServer$getRecipeManager(FastNMS.INSTANCE.method$MinecraftServer$getServer()); + } + + public static BukkitRecipeManager instance() { + return instance; + } + + @Override + public void delayedInit() { + Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin()); + } + + @Override + public void load() { + if (!Config.enableRecipeSystem()) return; + if (VersionHelper.isOrAbove1_21_2()) { + try { + this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(minecraftRecipeManager()); + CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), null); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to steal feature flag set", e); + } } } - // 1.21.2+ - private static Optional toOptionalMinecraftIngredient(Ingredient ingredient) throws ReflectiveOperationException { - if (ingredient == null) { - return Optional.empty(); + @Override + public void unload() { + if (!Config.enableRecipeSystem()) return; + // 安排卸载任务,这些任务会在load后执行。如果没有load说明服务器已经关闭了,那就不需要管卸载了。 + if (!Bukkit.isStopping()) { + for (Map.Entry> entry : this.byId.entrySet()) { + Key id = entry.getKey(); + // 不要卸载数据包配方,只记录自定义的配方 + if (isDataPackRecipe(id)) continue; + boolean isBrewingRecipe = entry.getValue() instanceof CustomBrewingRecipe; + this.recipesToUnregister.add(Pair.of(id, isBrewingRecipe)); + } + } + super.unload(); + } + + @Override + public void delayedLoad() { + if (!Config.enableRecipeSystem()) return; + this.loadDataPackRecipes(); + } + + @Override + public void disable() { + unload(); + HandlerList.unregisterAll(this.recipeEventListener); + } + + @Override + protected void unregisterPlatformRecipeMainThread(Key key, boolean isBrewingRecipe) { + if (isBrewingRecipe) { + Bukkit.getPotionBrewer().removePotionMix(new NamespacedKey(key.namespace(), key.value())); } else { - RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient); - Object mcIngredient = CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, choice, true); - return Optional.of(mcIngredient); + MINECRAFT_RECIPE_REMOVER.accept(key); } } - // 1.21.5+ - private static Object toTransmuteResult(ItemStack item) throws InvocationTargetException, IllegalAccessException, InstantiationException { - Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item); - Object nmsItem = CoreReflections.method$ItemStack$getItem.invoke(itemStack); - return CoreReflections.constructor$TransmuteResult.newInstance(nmsItem); - } - - // create nms smithing recipe for different versions - private static Object createMinecraftSmithingTransformRecipe(CustomSmithingTransformRecipe recipe) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_5()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()), - toTransmuteResult(recipe.result(ItemBuildContext.EMPTY)) - ); - } else if (VersionHelper.isOrAbove1_21_2()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toOptionalMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) - ); - } else if (VersionHelper.isOrAbove1_20_2()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) + @Override + protected void registerPlatformRecipeMainThread(Recipe recipe) { + Key id = recipe.id(); + if (recipe instanceof CustomBrewingRecipe brewingRecipe) { + if (!VersionHelper.isOrAbove1_20_2()) return; + PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()), + brewingRecipe.result(ItemBuildContext.EMPTY), + PotionMix.createPredicateChoice(container -> { + Item wrapped = this.plugin.itemManager().wrap(container); + return brewingRecipe.container().test(UniqueIdItem.of(wrapped)); + }), + PotionMix.createPredicateChoice(ingredient -> { + Item wrapped = this.plugin.itemManager().wrap(ingredient); + return brewingRecipe.ingredient().test(UniqueIdItem.of(wrapped)); + }) ); + Bukkit.getPotionBrewer().addPotionMix(potionMix); } else { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - KeyUtils.toResourceLocation(recipe.id()), - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) - ); + // 如果是数据包配方 + if (isDataPackRecipe(id)) { + // 如果这个数据包配方已经被换成了注入配方,那么是否需要重新注册取决于其是否含有tag,且tag里有自定义物品 + if (!this.replacedDatapackRecipes.add(id)) { + outer: { + for (Ingredient ingredient : recipe.ingredientsInUse()) { + if (ingredient.hasCustomItem()) { + break outer; + } + } + // 没有自定义物品,且被注入过了,那么就不需要移除后重新注册 + return; + } + } + MINECRAFT_RECIPE_REMOVER.accept(id); + } + ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER.get(recipe.serializerType()).apply(recipe); } } - private static Object createMinecraftSmithingTrimRecipe(CustomSmithingTrimRecipe recipe) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_5()) { - Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.TRIM_PATTERN); - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$Registry$getHolderByResourceLocation(registry, KeyUtils.toResourceLocation(recipe.pattern())).orElseThrow(() -> new RuntimeException("Pattern " + recipe.pattern() + " doesn't exist.")) - ); - } else if (VersionHelper.isOrAbove1_21_2()) { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toOptionalMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()) - ); - } else if (VersionHelper.isOrAbove1_20_2()) { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()) - ); - } else { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - KeyUtils.toResourceLocation(recipe.id()), - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()) - ); + private void loadDataPackRecipes() { + Object currentRecipeManager = minecraftRecipeManager(); + if (currentRecipeManager != this.lastRecipeManager) { + this.lastRecipeManager = currentRecipeManager; + this.replacedDatapackRecipes.clear(); + try { + this.lastDatapackRecipes = scanResources(); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to load datapack recipes", e); + } + } + for (Map.Entry> entry : this.lastDatapackRecipes.entrySet()) { + markAsDataPackRecipe(entry.getKey()); + registerInternalRecipe(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + private Map> scanResources() throws ReflectiveOperationException { + Object fileToIdConverter = CoreReflections.method$FileToIdConverter$json.invoke(null, VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"); + Object minecraftServer = FastNMS.INSTANCE.method$MinecraftServer$getServer(); + Object packRepository = CoreReflections.method$MinecraftServer$getPackRepository.invoke(minecraftServer); + List selected = (List) CoreReflections.field$PackRepository$selected.get(packRepository); + List packResources = new ArrayList<>(); + for (Object pack : selected) { + packResources.add(CoreReflections.method$Pack$open.invoke(pack)); + } + Map> recipes = new HashMap<>(); + boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty(); + try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.constructor$MultiPackResourceManager.newInstance(CoreReflections.instance$PackType$SERVER_DATA, packResources)) { + Map scannedResources = (Map) CoreReflections.method$FileToIdConverter$listMatchingResources.invoke(fileToIdConverter, resourceManager); + for (Map.Entry entry : scannedResources.entrySet()) { + Key id = extractKeyFromResourceLocation(entry.getKey().toString()); + if (Config.disableAllVanillaRecipes()) { + this.recipesToUnregister.add(new Pair<>(id, false)); + continue; + } + if (hasDisabledAny && Config.disabledVanillaRecipes().contains(id)) { + this.recipesToUnregister.add(new Pair<>(id, false)); + continue; + } + Reader reader = (Reader) CoreReflections.method$Resource$openAsReader.invoke(entry.getValue()); + JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); + Key serializerType = Key.of(jsonObject.get("type").getAsString()); + RecipeSerializer> serializer = (RecipeSerializer>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType); + if (serializer == null) { + continue; + } + try { + Recipe recipe = serializer.readJson(id, jsonObject); + recipes.put(id, recipe); + } catch (Exception e) { + this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e); + } + } + } catch (Exception e) { + this.plugin.logger().warn("Unknown error occurred when loading data pack recipes", e); + } + return recipes; + } + + private Key extractKeyFromResourceLocation(String input) { + int prefixEndIndex = input.indexOf(':'); + String prefix = input.substring(0, prefixEndIndex); + int lastSlashIndex = input.lastIndexOf('/'); + int lastDotIndex = input.lastIndexOf('.'); + String fileName = input.substring(lastSlashIndex + 1, lastDotIndex); + return Key.of(prefix, fileName); + } + + @Override + public void runDelayedSyncTasks() { + if (!Config.enableRecipeSystem()) return; + + // 卸载掉需要卸载的配方(禁用的原版配方+注册的自定义配方) + for (Pair pair : this.recipesToUnregister) { + unregisterPlatformRecipeMainThread(pair.left(), pair.right()); + } + // 注册新的配方 + for (Recipe recipe : this.byId.values()) { + registerPlatformRecipeMainThread(recipe); + } + + try { + // give flags back on 1.21.2+ + if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { + CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), this.stolenFeatureFlagSet); + this.stolenFeatureFlagSet = null; + } + + // refresh recipes + if (VersionHelper.isOrAbove1_21_2()) { + CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(minecraftRecipeManager()); + } + + // send to players + CoreReflections.method$DedicatedPlayerList$reloadRecipes.invoke(CraftBukkitReflections.field$CraftServer$playerList.get(Bukkit.getServer())); + + // now we need to remove the fake `exact` choices + if (VersionHelper.isOrAbove1_21_4()) { + for (Object ingredient : MODIFIED_INGREDIENTS) { + CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, null); + } + } else if (VersionHelper.isOrAbove1_21_2()) { + for (Object ingredient : MODIFIED_INGREDIENTS) { + CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, null); + } + } + + // clear cache + MODIFIED_INGREDIENTS.clear(); + } catch (Exception e) { + this.plugin.logger().warn("Failed to run delayed recipe tasks", e); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java deleted file mode 100644 index 5b5d2fd37..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.momirealms.craftengine.bukkit.item.recipe; - -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.ItemStackUtils; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.ItemManager; -import net.momirealms.craftengine.core.item.recipe.Recipe; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; -import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.block.Crafter; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.CrafterCraftEvent; -import org.bukkit.inventory.CraftingRecipe; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.List; - -public class CrafterEventListener implements Listener { - private final ItemManager itemManager; - private final BukkitRecipeManager recipeManager; - private final BukkitCraftEngine plugin; - - public CrafterEventListener(BukkitCraftEngine plugin, BukkitRecipeManager recipeManager, ItemManager itemManager) { - this.itemManager = itemManager; - this.recipeManager = recipeManager; - this.plugin = plugin; - } - - @EventHandler - public void onCrafting(CrafterCraftEvent event) { - if (!Config.enableRecipeSystem()) return; - CraftingRecipe recipe = event.getRecipe(); - if (!(event.getBlock().getState() instanceof Crafter crafter)) { - return; - } - - Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { - return; - } - - Inventory inventory = crafter.getInventory(); - ItemStack[] ingredients = inventory.getStorageContents(); - - List> uniqueIdItems = new ArrayList<>(); - for (ItemStack itemStack : ingredients) { - if (ItemStackUtils.isEmpty(itemStack)) { - uniqueIdItems.add(this.itemManager.uniqueEmptyItem()); - } else { - Item wrappedItem = this.itemManager.wrap(itemStack); - uniqueIdItems.add(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - } - } - - CraftingInput input; - if (ingredients.length == 9) { - input = CraftingInput.of(3, 3, uniqueIdItems); - } else if (ingredients.length == 4) { - input = CraftingInput.of(2, 2, uniqueIdItems); - } else { - return; - } - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input); - if (ceRecipe != null) { - event.setResult(ceRecipe.assemble(input, ItemBuildContext.EMPTY)); - return; - } - ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input); - if (ceRecipe != null) { - event.setResult(ceRecipe.assemble(input, ItemBuildContext.EMPTY)); - return; - } - // clear result if not met - event.setCancelled(true); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 2f1da0cb7..eb1004ba3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -4,12 +4,9 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.ComponentTypes; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.InventoryUtils; @@ -28,21 +25,14 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.util.*; import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Campfire; -import org.bukkit.block.Furnace; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.*; import org.bukkit.event.inventory.*; import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.*; import org.bukkit.inventory.view.AnvilView; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -74,14 +64,14 @@ public class RecipeEventListener implements Listener { ItemStack item = event.getCurrentItem(); if (ItemStackUtils.isEmpty(item)) return; if (ItemStackUtils.isEmpty(fuelStack)) { - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(item)); - Key recipeType; + SingleItemInput input = new SingleItemInput<>(ItemStackUtils.getUniqueIdItem(item)); + RecipeType recipeType; if (furnaceInventory.getType() == InventoryType.FURNACE) { - recipeType = RecipeTypes.SMELTING; + recipeType = RecipeType.SMELTING; } else if (furnaceInventory.getType() == InventoryType.BLAST_FURNACE) { - recipeType = RecipeTypes.BLASTING; + recipeType = RecipeType.BLASTING; } else { - recipeType = RecipeTypes.SMOKING; + recipeType = RecipeType.SMOKING; } Recipe ceRecipe = this.recipeManager.recipeByInput(recipeType, input); @@ -258,166 +248,9 @@ public class RecipeEventListener implements Listener { } } - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onFurnaceInventoryOpen(InventoryOpenEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!(event.getInventory() instanceof FurnaceInventory furnaceInventory)) { - return; - } - Furnace furnace = furnaceInventory.getHolder(); - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - - // for 1.20.1-1.21.1 - @EventHandler(ignoreCancelled = true) - public void onBlockIgnite(BlockIgniteEvent event) { - if (!Config.enableRecipeSystem()) return; - if (VersionHelper.isOrAbove1_21_2()) return; - Block block = event.getBlock(); - Material material = block.getType(); - if (material == Material.CAMPFIRE) { - if (block.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPlaceBlock(BlockPlaceEvent event) { - if (!Config.enableRecipeSystem()) return; - Block block = event.getBlock(); - Material material = block.getType(); - if (material == Material.FURNACE || material == Material.BLAST_FURNACE || material == Material.SMOKER) { - if (block.getState() instanceof Furnace furnace) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } else if (!VersionHelper.isOrAbove1_21_2() && material == Material.CAMPFIRE) { - if (block.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } - } - - // for 1.21.2+ - @EventHandler(ignoreCancelled = true) - public void onPutItemOnCampfire(PlayerInteractEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - Block clicked = event.getClickedBlock(); - if (clicked == null) return; - Material type = clicked.getType(); - if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; - if (clicked.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - - ItemStack itemStack = event.getItem(); - if (ItemStackUtils.isEmpty(itemStack)) return; - try { - @SuppressWarnings("unchecked") - Optional optionalMCRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor( - BukkitRecipeManager.nmsRecipeManager(), - MRecipeTypes.CAMPFIRE_COOKING, - CoreReflections.constructor$SingleRecipeInput.newInstance(FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)), - FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()), - null - ); - if (optionalMCRecipe.isEmpty()) { - return; - } - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setCancelled(true); - } - } catch (Exception e) { - this.plugin.logger().warn("Failed to handle interact campfire", e); - } - } - - // for 1.21.2+ - @SuppressWarnings("UnstableApiUsage") - @EventHandler(ignoreCancelled = true) - public void onCampfireCook(CampfireStartEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - CampfireRecipe recipe = event.getRecipe(); - Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - return; - } - - ItemStack itemStack = event.getSource(); - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setTotalCookTime(Integer.MAX_VALUE); - return; - } - - event.setTotalCookTime(ceRecipe.cookingTime()); - } - - // for 1.21.2+ - @EventHandler(ignoreCancelled = true) - public void onCampfireCook(BlockCookEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - Material type = event.getBlock().getType(); - if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; - CampfireRecipe recipe = (CampfireRecipe) event.getRecipe(); - if (recipe == null) return; - - Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - return; - } - - ItemStack itemStack = event.getSource(); - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setCancelled(true); - return; - } - - event.setResult(ceRecipe.result(ItemBuildContext.EMPTY)); - } - // Paper only @EventHandler public void onPrepareResult(PrepareResultEvent event) { -// if (!ConfigManager.enableRecipeSystem()) return; if (event.getInventory() instanceof CartographyInventory cartographyInventory) { if (ItemStackUtils.hasCustomItem(cartographyInventory.getStorageContents())) { event.setResult(new ItemStack(Material.AIR)); @@ -481,7 +314,7 @@ public class RecipeEventListener implements Listener { // 如果禁止在铁砧使用两个相同物品修复 firstCustom.ifPresent(it -> { - if (!it.settings().canRepair()) { + if (it.settings().canRepair() == Tristate.FALSE) { event.setResult(null); } }); @@ -522,7 +355,7 @@ public class RecipeEventListener implements Listener { Key firstId = wrappedFirst.id(); Optional> optionalCustomTool = wrappedFirst.getCustomItem(); // 物品无法被修复 - if (optionalCustomTool.isPresent() && !optionalCustomTool.get().settings().canRepair()) { + if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().canRepair() == Tristate.FALSE) { return; } @@ -535,7 +368,7 @@ public class RecipeEventListener implements Listener { repairItem = item; break; } - if (wrappedFirst.is(tag)) { + if (wrappedFirst.hasItemTag(tag)) { repairItem = item; break; } @@ -679,89 +512,52 @@ public class RecipeEventListener implements Listener { } // only handle repair items for the moment - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onSpecialRecipe(PrepareItemCraftEvent event) { -// if (!ConfigManager.enableRecipeSystem()) return; org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (recipe == null) - return; if (!(recipe instanceof ComplexRecipe complexRecipe)) return; CraftingInventory inventory = event.getInventory(); + ItemStack result = inventory.getResult(); + if (ItemStackUtils.isEmpty(result)) + return; boolean hasCustomItem = ItemStackUtils.hasCustomItem(inventory.getMatrix()); - if (!hasCustomItem) { + if (!hasCustomItem) return; - } - if (!CraftBukkitReflections.clazz$CraftComplexRecipe.isInstance(complexRecipe)) { - inventory.setResult(null); return; } - try { Object mcRecipe = CraftBukkitReflections.field$CraftComplexRecipe$recipe.get(complexRecipe); - - // Repair recipe + if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) { + return; + } + // 处理修复配方,在此处理才能使用玩家参数构建物品 if (CoreReflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) { - // repair item - ItemStack[] itemStacks = inventory.getMatrix(); - Pair onlyTwoItems = getTheOnlyTwoItem(itemStacks); - if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) { - inventory.setResult(null); - return; - } - - Item left = plugin.itemManager().wrap(onlyTwoItems.left()); - Item right = plugin.itemManager().wrap(onlyTwoItems.right()); - if (!left.id().equals(right.id())) { - inventory.setResult(null); - return; - } - - int totalDamage = right.damage().orElse(0) + left.damage().orElse(0); - int totalMaxDamage = left.maxDamage() + right.maxDamage(); - // should be impossible, but take care - if (totalDamage >= totalMaxDamage) { - inventory.setResult(null); - return; - } - - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - - Optional> customItemOptional = plugin.itemManager().getCustomItem(left.id()); + Pair theOnlyTwoItem = getTheOnlyTwoItem(inventory.getMatrix()); + if (theOnlyTwoItem == null) return; + Item first = BukkitItemManager.instance().wrap(theOnlyTwoItem.left()); + Item right = BukkitItemManager.instance().wrap(theOnlyTwoItem.right()); + int max = Math.max(first.maxDamage(), right.maxDamage()); + int durability1 = first.maxDamage() - first.damage().orElse(0); + int durability2 = right.maxDamage() - right.damage().orElse(0); + int finalDurability = durability1 + durability2 + max * 5 / 100; + Optional> customItemOptional = plugin.itemManager().getCustomItem(first.id()); if (customItemOptional.isEmpty()) { inventory.setResult(null); return; } - - CustomItem customItem = customItemOptional.get(); - if (!customItem.settings().canRepair()) { - inventory.setResult(null); - return; - } - - Item newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player))); - int remainingDurability = totalMaxDamage - totalDamage; - int newItemDamage = Math.max(0, newItem.maxDamage() - remainingDurability); - newItem.damage(newItemDamage); + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + Item newItem = customItemOptional.get().buildItem(plugin.adapt(player)); + newItem.maxDamage(max); + newItem.damage(Math.max(max - finalDurability, 0)); inventory.setResult(newItem.getItem()); - } else if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) { - ItemStack[] itemStacks = inventory.getMatrix(); - for (ItemStack itemStack : itemStacks) { - if (itemStack == null) continue; - Item item = plugin.itemManager().wrap(itemStack); - Optional> optionalCustomItem = item.getCustomItem(); - if (optionalCustomItem.isPresent() && !optionalCustomItem.get().settings().dyeable()) { - inventory.setResult(null); - return; - } - } - } else { - inventory.setResult(null); return; } + // 其他配方不允许使用自定义物品 + inventory.setResult(null); } catch (Exception e) { - this.plugin.logger().warn("Failed to handle minecraft custom recipe", e); + this.plugin.logger().warn("Failed to handle custom recipe", e); } } @@ -772,134 +568,54 @@ public class RecipeEventListener implements Listener { if (itemStack == null) continue; if (first == null) { first = itemStack; - } else if (second == null) { + } else { + if (second != null) { + return null; + } second = itemStack; } } return new Pair<>(first, second); } - // 不是完美的解决方案,仍然需要更多的探讨 - // TODO 生成类代理掉ResultSlot,并注入menu的slots对象,修改掉onTake方法 - // TODO 对于耐久度降低的配方,应该注册special recipe? - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onCraft(CraftItemEvent event) { - org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (!(recipe instanceof ShapelessRecipe) && !(recipe instanceof ShapedRecipe)) return; - HumanEntity humanEntity = event.getWhoClicked(); - if (!(humanEntity instanceof Player player)) return; - CraftingInventory inventory = event.getInventory(); - ItemStack result = inventory.getResult(); - if (result == null) return; - ItemStack[] usedItems = inventory.getMatrix(); - ItemStack[] replacements = new ItemStack[usedItems.length]; - boolean hasReplacement = false; - for (int i = 0; i < usedItems.length; i++) { - ItemStack usedItem = usedItems[i]; - if (ItemStackUtils.isEmpty(usedItem)) continue; - if (usedItem.getAmount() != 1) continue; - Item wrapped = BukkitItemManager.instance().wrap(usedItem); - if (ItemUtils.isEmpty(wrapped)) continue; - Optional> optionalCustomItem = wrapped.getCustomItem(); - if (optionalCustomItem.isPresent()) { - CustomItem customItem = optionalCustomItem.get(); - Key remainingItem = customItem.settings().craftRemainder(); - if (remainingItem != null) { - replacements[i] = BukkitItemManager.instance().buildItemStack(remainingItem, this.plugin.adapt(player)); - hasReplacement = true; - } - } - } - if (!hasReplacement) return; - Runnable delayedTask = () -> { - for (int i = 0; i < replacements.length; i++) { - if (replacements[i] == null) continue; - inventory.setItem(i + 1, replacements[i]); - } - }; - if (VersionHelper.isFolia()) { - player.getScheduler().run(this.plugin.javaPlugin(), (t) -> delayedTask.run(), () -> {}); - } else { - this.plugin.scheduler().sync().runDelayed(delayedTask); - } - } - @EventHandler(ignoreCancelled = true) public void onCraftingRecipe(PrepareItemCraftEvent event) { if (!Config.enableRecipeSystem()) return; org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (recipe == null) - return; - - // we only handle shaped and shapeless recipes - boolean shapeless = event.getRecipe() instanceof ShapelessRecipe; - boolean shaped = event.getRecipe() instanceof ShapedRecipe; - if (!shaped && !shapeless) return; - - CraftingRecipe craftingRecipe = (CraftingRecipe) recipe; + if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + // 也许是其他插件注册的配方,直接无视 + if (optionalRecipe.isEmpty()) { return; } - CraftingInventory inventory = event.getInventory(); - ItemStack[] ingredients = inventory.getMatrix(); + if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe craftingTableRecipe)) { + inventory.setResult(null); + return; + } + CraftingInput input = getCraftingInput(inventory); + if (input == null) return; + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + BukkitServerPlayer serverPlayer = this.plugin.adapt(player); + inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); + } + private CraftingInput getCraftingInput(CraftingInventory inventory) { + ItemStack[] ingredients = inventory.getMatrix(); List> uniqueIdItems = new ArrayList<>(); for (ItemStack itemStack : ingredients) { - uniqueIdItems.add(getUniqueIdItem(itemStack)); + uniqueIdItems.add(ItemStackUtils.getUniqueIdItem(itemStack)); } - CraftingInput input; if (ingredients.length == 9) { input = CraftingInput.of(3, 3, uniqueIdItems); } else if (ingredients.length == 4) { input = CraftingInput.of(2, 2, uniqueIdItems); } else { - return; - } - - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - - BukkitServerPlayer serverPlayer = this.plugin.adapt(player); - Key lastRecipe = serverPlayer.lastUsedRecipe(); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPELESS, input, lastRecipe); - if (ceRecipe != null) { - inventory.setResult(ceRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); - serverPlayer.setLastUsedRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(recipeId)) { - correctCraftingRecipeUsed(inventory, ceRecipe); - } - return; - } - ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe); - if (ceRecipe != null) { - inventory.setResult(ceRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); - serverPlayer.setLastUsedRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(recipeId)) { - correctCraftingRecipeUsed(inventory, ceRecipe); - } - return; - } - // clear result if not met - inventory.setResult(null); - } - - private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe recipe) { - Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); - if (holderOrRecipe == null) { - return; - } - try { - Object resultInventory = CraftBukkitReflections.field$CraftInventoryCrafting$resultInventory.get(inventory); - CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to correct used recipe", e); + return null; } + return input; } @EventHandler(ignoreCancelled = true) @@ -923,30 +639,21 @@ public class RecipeEventListener implements Listener { } Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + if (optionalRecipe.isEmpty()) { return; } - - SmithingInput input = new SmithingInput<>( - getUniqueIdItem(inventory.getInputEquipment()), - getUniqueIdItem(inventory.getInputTemplate()), - getUniqueIdItem(inventory.getInputMineral()) - ); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SMITHING_TRIM, input); - if (ceRecipe == null) { + if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe smithingTrimRecipe)) { event.setResult(null); return; } - - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - CustomSmithingTrimRecipe trimRecipe = (CustomSmithingTrimRecipe) ceRecipe; - ItemStack result = trimRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); - event.setResult(result); - if (!ceRecipe.id().equals(recipeId)) { - correctSmithingRecipeUsed(inventory, ceRecipe); + SmithingInput input = getSmithingInput(inventory); + if (smithingTrimRecipe.matches(input)) { + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); + event.setResult(result); + } else { + event.setResult(null); } } @@ -955,59 +662,30 @@ public class RecipeEventListener implements Listener { if (!Config.enableRecipeSystem()) return; SmithingInventory inventory = event.getInventory(); if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return; - Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + if (optionalRecipe.isEmpty()) { return; } - - ItemStack base = inventory.getInputEquipment(); - ItemStack template = inventory.getInputTemplate(); - ItemStack addition = inventory.getInputMineral(); - - SmithingInput input = new SmithingInput<>( - getUniqueIdItem(base), - getUniqueIdItem(template), - getUniqueIdItem(addition) - ); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SMITHING_TRANSFORM, input); - if (ceRecipe == null) { + if (!(optionalRecipe.get() instanceof CustomSmithingTransformRecipe smithingTransformRecipe)) { event.setResult(null); return; } - - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - - CustomSmithingTransformRecipe transformRecipe = (CustomSmithingTransformRecipe) ceRecipe; - ItemStack processed = transformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); - event.setResult(processed); - if (!ceRecipe.id().equals(recipeId)) { - correctSmithingRecipeUsed(inventory, ceRecipe); - } - } - - private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe recipe) { - Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); - if (holderOrRecipe == null) { - return; - } - try { - Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory); - CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to correct used recipe", e); - } - } - - private UniqueIdItem getUniqueIdItem(@Nullable ItemStack itemStack) { - if (ItemStackUtils.isEmpty(itemStack)) { - return this.itemManager.uniqueEmptyItem(); + SmithingInput input = getSmithingInput(inventory); + if (smithingTransformRecipe.matches(input)) { + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); + event.setResult(processed); } else { - Item wrappedItem = this.itemManager.wrap(itemStack); - return new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem); + event.setResult(null); } } + + private SmithingInput getSmithingInput(SmithingInventory inventory) { + return new SmithingInput<>( + ItemStackUtils.getUniqueIdItem(inventory.getInputEquipment()), + ItemStackUtils.getUniqueIdItem(inventory.getInputTemplate()), + ItemStackUtils.getUniqueIdItem(inventory.getInputMineral()) + ); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 7ae11cc4f..0cea939f3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.pack; import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.ResourcePackUtils; @@ -22,7 +21,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; public class BukkitPackManager extends AbstractPackManager implements Listener { @@ -60,28 +58,12 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { @Override public void unload() { super.unload(); - if (ReloadCommand.RELOAD_PACK_FLAG) { - if (VersionHelper.isOrAbove1_20_2()) { - this.resetServerSettings(); - } - } } @Override public void disable() { super.disable(); HandlerList.unregisterAll(this); - this.resetServerSettings(); - } - - public void resetServerSettings() { - try { - Object settings = CoreReflections.field$DedicatedServer$settings.get(CoreReflections.method$MinecraftServer$getServer.invoke(null)); - Object properties = CoreReflections.field$DedicatedServerSettings$properties.get(settings); - CoreReflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to reset resource pack settings", e); - } } @EventHandler(priority = EventPriority.MONITOR) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java index ddacca6b9..885b223cd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/classpath/PaperClassPathAppender.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.classpath; import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; import net.momirealms.craftengine.core.plugin.classpath.URLClassLoaderAccess; import net.momirealms.craftengine.core.util.ReflectionUtils; +import org.bukkit.Bukkit; import java.lang.reflect.Field; import java.net.MalformedURLException; @@ -17,27 +18,38 @@ public class PaperClassPathAppender implements ClassPathAppender { public static final Field field$PaperPluginClassLoader$libraryLoader = Optional.ofNullable(clazz$PaperPluginClassLoader) .map(it -> ReflectionUtils.getDeclaredField(it, URLClassLoader.class, 0)) .orElse(null); - private final URLClassLoaderAccess classLoaderAccess; + private final URLClassLoaderAccess libraryClassLoaderAccess; + // todo 是否有更好的方法让库被其他插件共享 public PaperClassPathAppender(ClassLoader classLoader) { - try { - if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { - URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); - this.classLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); - } else if (classLoader instanceof URLClassLoader) { - this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader); - } else { - throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + // 这个类加载器用于加载重定位后的依赖库,这样所有插件都能访问到 + ClassLoader bukkitClassLoader = Bukkit.class.getClassLoader(); + if (bukkitClassLoader instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + } else { + // ignite会把Bukkit放置于EmberClassLoader中,获取其父DynamicClassLoader + if (bukkitClassLoader.getClass().getName().equals("space.vectrix.ignite.launch.ember.EmberClassLoader") && bukkitClassLoader.getParent() instanceof URLClassLoader urlClassLoader) { + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(urlClassLoader); + return; + } + try { + // 最次的方案,使用paper自带的classloader去加载依赖,这种情况会发生依赖隔离 + if (clazz$PaperPluginClassLoader != null && clazz$PaperPluginClassLoader.isInstance(classLoader)) { + URLClassLoader libraryClassLoader = (URLClassLoader) field$PaperPluginClassLoader$libraryLoader.get(classLoader); + this.libraryClassLoaderAccess = URLClassLoaderAccess.create(libraryClassLoader); + } else { + throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to instantiate PaperPluginClassLoader", e); } } @Override public void addJarToClasspath(Path file) { try { - this.classLoaderAccess.addURL(file.toUri().toURL()); + this.libraryClassLoaderAccess.addURL(file.toUri().toURL()); } catch (MalformedURLException e) { throw new RuntimeException(e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java index b4d3e40e4..76625f057 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java @@ -44,7 +44,7 @@ public class BukkitSenderFactory extends SenderFactory { @@ -29,35 +18,9 @@ public class TestCommand extends BukkitCommandFeature { public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder .senderType(Player.class) - .required("location", LocationParser.locationParser()) - .required("remove", IntegerParser.integerParser()) .handler(context -> { Player player = context.sender(); - int removeEntityId = context.get("remove"); - if (removeEntityId >= 0) { - try { - Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{removeEntityId}); - plugin().adapt(player).sendPacket(packet, true); - player.sendMessage("发送成功"); - } catch (ReflectiveOperationException e) { - player.sendMessage("发送失败"); - } - return; - } - Location location = context.get("location"); - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List packets = new ArrayList<>(); - List cachedShulkerValues = new ArrayList<>(); - HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, cachedShulkerValues); // NO AI - // HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedShulkerValues); // Invisible - HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(true, cachedShulkerValues); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), location.x(), location.y(), location.z(), 0, location.getYaw(), - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(cachedShulkerValues))); - plugin().adapt(player).sendPackets(packets, true); - player.sendMessage("发送成功 id: " + entityId); + player.sendMessage("客户端模组状态: " + BukkitNetworkManager.instance().getUser(player).clientModEnabled()); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java index 750a17039..33367919e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java @@ -98,7 +98,7 @@ public final class BlockStateGenerator { if (vec3 == null) return List.of(); Object tool = FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.TOOL); - Item item = BukkitItemManager.instance().wrap(tool == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(tool) ? null : FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(tool)); + Item item = BukkitItemManager.instance().wrap(tool == null ? null : FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(tool)); Object optionalPlayer = FastNMS.INSTANCE.method$LootParams$Builder$getOptionalParameter(builder, MLootContextParams.THIS_ENTITY); if (!CoreReflections.clazz$Player.isInstance(optionalPlayer)) { optionalPlayer = null; @@ -107,7 +107,7 @@ public final class BlockStateGenerator { // do not drop if it's not the correct tool BlockSettings settings = state.settings(); if (optionalPlayer != null && settings.requireCorrectTool()) { - if (item == null) return List.of(); + if (item.isEmpty()) return List.of(); if (!settings.isCorrectTool(item.id()) && (!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool, state.customBlockState().handle()))) { return List.of(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java index f8fc33654..03ff73844 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.injector; +import net.momirealms.craftengine.core.item.recipe.RecipeType; import net.momirealms.craftengine.core.util.Key; public interface InjectedCacheCheck { @@ -8,9 +9,9 @@ public interface InjectedCacheCheck { void recipeType(Object recipeType); - Key customRecipeType(); + RecipeType customRecipeType(); - void customRecipeType(Key customRecipeType); + void customRecipeType(RecipeType customRecipeType); Object lastRecipe(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java index 7a8016ace..208dc973a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -1,284 +1,405 @@ package net.momirealms.craftengine.bukkit.plugin.injector; import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.ClassFileVersion; -import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.bind.annotation.AllArguments; import net.bytebuddy.implementation.bind.annotation.RuntimeType; -import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; +import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.util.ItemTags; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; -import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.*; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; public class RecipeInjector { - private static Class clazz$InjectedCacheChecker; - + private static Class clazz$InjectedArmorDyeRecipe; + private static Class clazz$InjectedRepairItemRecipe; + private static Class clazz$InjectedFireworkStarFadeRecipe; + public static void init() { ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); - clazz$InjectedCacheChecker = byteBuddy - .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") - .implement(CoreReflections.clazz$RecipeManager$CachedCheck) - .implement(InjectedCacheCheck.class) - .defineField("recipeType", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("recipeType")) - .intercept(FieldAccessor.ofField("recipeType")) + ElementMatcher.Junction matches = (VersionHelper.isOrAbove1_21() ? + ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$Level) : + ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$Level) + ).and(ElementMatchers.returns(boolean.class)); + ElementMatcher.Junction assemble = ( + VersionHelper.isOrAbove1_21() ? + ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$HolderLookup$Provider) : + VersionHelper.isOrAbove1_20_5() ? + ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$HolderLookup$Provider) : + ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$RegistryAccess) + ).and(ElementMatchers.returns(CoreReflections.clazz$ItemStack)); - .defineField("customRecipeType", Key.class, Visibility.PUBLIC) - .method(ElementMatchers.named("customRecipeType")) - .intercept(FieldAccessor.ofField("customRecipeType")) + clazz$InjectedArmorDyeRecipe = byteBuddy + .subclass(CoreReflections.clazz$ArmorDyeRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name("net.momirealms.craftengine.bukkit.item.recipe.ArmorDyeRecipe") + .method(matches) + .intercept(MethodDelegation.to(DyeMatchesInterceptor.INSTANCE)) + .method(assemble) + .intercept(MethodDelegation.to(DyeAssembleInterceptor.INSTANCE)) + .make() + .load(RecipeInjector.class.getClassLoader()) + .getLoaded(); - .defineField("lastRecipe", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastRecipe")) - .intercept(FieldAccessor.ofField("lastRecipe")) + clazz$InjectedFireworkStarFadeRecipe = byteBuddy + .subclass(CoreReflections.clazz$FireworkStarFadeRecipe) + .name("net.momirealms.craftengine.bukkit.item.recipe.FireworkStarFadeRecipe") + .method(matches) + .intercept(MethodDelegation.to(FireworkStarFadeMatchesInterceptor.INSTANCE)) + .method(assemble) + .intercept(MethodDelegation.to(FireworkStarFadeAssembleInterceptor.INSTANCE)) + .make() + .load(RecipeInjector.class.getClassLoader()) + .getLoaded(); - .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastCustomRecipe")) - .intercept(FieldAccessor.ofField("lastCustomRecipe")) - - .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) - .intercept(MethodDelegation.to( - VersionHelper.isOrAbove1_21_2() ? - GetRecipeForMethodInterceptor1_21_2.INSTANCE : - (VersionHelper.isOrAbove1_21() ? - GetRecipeForMethodInterceptor1_21.INSTANCE : - VersionHelper.isOrAbove1_20_5() ? - GetRecipeForMethodInterceptor1_20_5.INSTANCE : - GetRecipeForMethodInterceptor1_20.INSTANCE) - )) + clazz$InjectedRepairItemRecipe = byteBuddy + .subclass(CoreReflections.clazz$RepairItemRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + .name("net.momirealms.craftengine.bukkit.item.recipe.RepairItemRecipe") + // 只修改match逻辑,合并需要在事件里处理,否则无法应用变量 + .method(matches) + .intercept(MethodDelegation.to(RepairMatchesInterceptor.INSTANCE)) .make() .load(RecipeInjector.class.getClassLoader()) .getLoaded(); } - public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException { - if (CoreReflections.clazz$AbstractFurnaceBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - if (recipeType == MRecipeTypes.SMELTING) { - injectedChecker.customRecipeType(RecipeTypes.SMELTING); - injectedChecker.recipeType(MRecipeTypes.SMELTING); - } else if (recipeType == MRecipeTypes.BLASTING) { - injectedChecker.customRecipeType(RecipeTypes.BLASTING); - injectedChecker.recipeType(MRecipeTypes.BLASTING); - } else if (recipeType == MRecipeTypes.SMOKING) { - injectedChecker.customRecipeType(RecipeTypes.SMOKING); - injectedChecker.recipeType(MRecipeTypes.SMOKING); + public static Object createRepairItemRecipe(Key id) throws ReflectiveOperationException { + return createSpecialRecipe(id, clazz$InjectedRepairItemRecipe); + } + + public static Object createCustomDyeRecipe(Key id) throws ReflectiveOperationException { + return createSpecialRecipe(id, clazz$InjectedArmorDyeRecipe); + } + + public static Object createFireworkStarFadeRecipe(Key id) throws ReflectiveOperationException { + return createSpecialRecipe(id, clazz$InjectedFireworkStarFadeRecipe); + } + + @NotNull + private static Object createSpecialRecipe(Key id, Class clazz$InjectedRepairItemRecipe) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException { + if (VersionHelper.isOrAbove1_20_2()) { + Constructor constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$CraftingBookCategory); + return constructor.newInstance(CoreReflections.instance$CraftingBookCategory$MISC); + } else { + Constructor constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$CraftingBookCategory); + return constructor.newInstance(KeyUtils.toResourceLocation(id), CoreReflections.instance$CraftingBookCategory$MISC); + } + } + + private static final Function INGREDIENT_SIZE_GETTER = + VersionHelper.isOrAbove1_21() ? + FastNMS.INSTANCE::method$CraftingInput$size : + FastNMS.INSTANCE::method$Container$getContainerSize; + private static final BiFunction INGREDIENT_GETTER = + VersionHelper.isOrAbove1_21() ? + FastNMS.INSTANCE::method$CraftingInput$getItem : + FastNMS.INSTANCE::method$Container$getItem; + + private static final Function REPAIR_INGREDIENT_COUNT_CHECKER = + VersionHelper.isOrAbove1_21() ? + (input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) != 2 : + (container) -> false; + + public static class FireworkStarFadeMatchesInterceptor { + public static final FireworkStarFadeMatchesInterceptor INSTANCE = new FireworkStarFadeMatchesInterceptor(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) { + Object input = args[0]; + if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) { + return false; + } + boolean hasDye = false; + boolean hasFireworkStar = false; + int size = INGREDIENT_SIZE_GETTER.apply(input); + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isFireworkDye(wrapped)) { + hasDye = true; + } else { + if (!wrapped.id().equals(ItemKeys.FIREWORK_STAR)) { + return false; + } + if (hasFireworkStar) { + return false; + } + hasFireworkStar = true; + } + } + return hasDye && hasFireworkStar; + } + } + + public static class FireworkStarFadeAssembleInterceptor { + public static final FireworkStarFadeAssembleInterceptor INSTANCE = new FireworkStarFadeAssembleInterceptor(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) { + IntList colors = new IntArrayList(); + Item starItem = null; + Object input = args[0]; + int size = INGREDIENT_SIZE_GETTER.apply(input); + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isFireworkDye(wrapped)) { + Color color = getFireworkColor(wrapped); + if (color == null) { + return CoreReflections.instance$ItemStack$EMPTY; + } + colors.add(color.color()); + } else if (wrapped.id().equals(ItemKeys.FIREWORK_STAR)) { + starItem = wrapped.copyWithCount(1); + } + } + if (starItem == null || colors.isEmpty()) { + return CoreReflections.instance$ItemStack$EMPTY; + } + FireworkExplosion explosion = starItem.fireworkExplosion().orElse(FireworkExplosion.DEFAULT); + starItem.fireworkExplosion(explosion.withFadeColors(colors)); + return starItem.getLiteralObject(); + } + } + + public static class RepairMatchesInterceptor { + public static final RepairMatchesInterceptor INSTANCE = new RepairMatchesInterceptor(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) { + Object input = args[0]; + if (REPAIR_INGREDIENT_COUNT_CHECKER.apply(input)) { + return false; + } + return getItemsToCombine(input) != null; + } + } + + @Nullable + private static Pair, Item> getItemsToCombine(Object input) { + Item item1 = null; + Item item2 = null; + int size = INGREDIENT_SIZE_GETTER.apply(input); + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (item1 == null) { + item1 = wrapped; } else { - throw new IllegalStateException("RecipeType " + recipeType + " not supported"); + if (item2 != null) { + return null; + } + item2 = wrapped; } - CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); - } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - injectedChecker.customRecipeType(RecipeTypes.CAMPFIRE_COOKING); - injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); - CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); + } + if (item1 == null || item2 == null) { + return null; + } + if (!canCombine(item1, item2)) { + return null; + } + return new Pair<>(item1, item2); + } + + private static boolean canCombine(Item input1, Item input2) { + if (input1.count() != 1 || !isDamageableItem(input1)) return false; + if (input2.count() != 1 || !isDamageableItem(input2)) return false; + if (!input1.id().equals(input2.id())) return false; + Optional> customItem = input1.getCustomItem(); + return customItem.isEmpty() || customItem.get().settings().canRepair() != Tristate.FALSE; + } + + private static boolean isDamageableItem(Item item) { + if (VersionHelper.isOrAbove1_20_5()) { + return item.hasComponent(ComponentTypes.MAX_DAMAGE) && item.hasComponent(ComponentTypes.DAMAGE); + } else { + return FastNMS.INSTANCE.method$Item$canBeDepleted(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject())); } } - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_20 { - public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); + private static final Function DYE_INGREDIENT_COUNT_CHECKER = + VersionHelper.isOrAbove1_21() ? + (input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) < 2 : + (container) -> false; + + public static class DyeMatchesInterceptor { + public static final DyeMatchesInterceptor INSTANCE = new DyeMatchesInterceptor(); - @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); + public Object intercept(@AllArguments Object[] args) { + Object input = args[0]; + if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) { + return false; } - - Pair resourceLocationAndRecipe = optionalRecipe.get(); - Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst(); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return Optional.of(resourceLocationAndRecipe.getSecond()); + int size = INGREDIENT_SIZE_GETTER.apply(input); + Item itemToDye = null; + boolean hasDye = false; + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isDyeable(wrapped)) { + if (itemToDye != null) { + return false; + } + itemToDye = wrapped; + } else { + if (!isArmorDye(wrapped)) { + return false; + } + hasDye = true; + } } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( - injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? - FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : - FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) - ); - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); + return hasDye && itemToDye != null; } } - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_20_5 { - public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); + public static class DyeAssembleInterceptor { + public static final DyeAssembleInterceptor INSTANCE = new DyeAssembleInterceptor(); - @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); + public Object intercept(@AllArguments Object[] args) { + List colors = new ArrayList<>(); + Item itemToDye = null; + Object input = args[0]; + int size = INGREDIENT_SIZE_GETTER.apply(input); + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isDyeable(wrapped)) { + itemToDye = wrapped.copyWithCount(1); + } else { + Color dyeColor = getDyeColor(wrapped); + if (dyeColor != null) { + colors.add(dyeColor); + } else { + return CoreReflections.instance$ItemStack$EMPTY; + } + } } - - Object rawRecipeHolder = optionalRecipe.get(); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( - injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? - FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : - FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) - ); - - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return optionalRecipe; + if (itemToDye == null || itemToDye.isEmpty() || colors.isEmpty()) { + return CoreReflections.instance$ItemStack$EMPTY; } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); + return itemToDye.applyDyedColors(colors).getLiteralObject(); } } - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_21 { - public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - Object rawRecipeHolder = optionalRecipe.get(); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return optionalRecipe; - } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); + @Nullable + private static Color getDyeColor(final Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return Optional.ofNullable(customItem.settings().dyeColor()).orElseGet(() -> getVanillaDyeColor(dyeItem)); } + return getVanillaDyeColor(dyeItem); } - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_21_2 { - public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - // 获取配方的基础信息 - Object recipeHolder = optionalRecipe.get(); - Object rawRecipeResourceKey = FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolder); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(rawRecipeResourceKey); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - // 来自其他插件注册的自定义配方 - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceKey); - return optionalRecipe; - } - - // 获取唯一内存地址id - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - // 这个ce配方并不存在,那么应该返回空 - if (ceRecipe == null) { - return Optional.empty(); - } - - // 记录上一次使用的配方(ce) - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // 更新上一次使用的配方(nms) - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id()))); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); + @Nullable + private static Color getFireworkColor(final Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return Optional.ofNullable(customItem.settings().fireworkColor()).orElseGet(() -> getVanillaFireworkColor(dyeItem)); } + return getVanillaFireworkColor(dyeItem); + } + + private static final Predicate> IS_DYEABLE = + VersionHelper.isOrAbove1_20_5() ? + (item -> item.hasItemTag(ItemTags.DYEABLE)) : + (item -> { + Object itemLike = FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()); + return CoreReflections.clazz$DyeableLeatherItem.isInstance(itemLike); + }); + + private static boolean isDyeable(final Item item) { + Optional> optionalCustomItem = item.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + if (customItem.settings().dyeable() == Tristate.FALSE) { + return false; + } + if (customItem.settings().dyeable() == Tristate.TRUE) { + return true; + } + } + return IS_DYEABLE.test(item); + } + + @Nullable + private static Color getVanillaDyeColor(final Item item) { + Object itemStack = item.getLiteralObject(); + Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack); + if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return null; + return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem))); + } + + @Nullable + private static Color getVanillaFireworkColor(final Item item) { + Object itemStack = item.getLiteralObject(); + Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack); + if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return null; + return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getFireworkColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem))); + } + + private static boolean isArmorDye(Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return customItem.settings().dyeColor() != null || isVanillaDyeItem(dyeItem); + } + return isVanillaDyeItem(dyeItem); + } + + private static boolean isFireworkDye(Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return customItem.settings().fireworkColor() != null || isVanillaDyeItem(dyeItem); + } + return isVanillaDyeItem(dyeItem); + } + + private static boolean isVanillaDyeItem(Item item) { + return CoreReflections.clazz$DyeItem.isInstance(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject())); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index bdc6aa116..291e542c3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -67,10 +67,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes C2S_GAME_BYTE_BUFFER_PACKET_HANDLERS[id] = function; } - private final BiConsumer packetConsumer; - private final BiConsumer> packetsConsumer; - private final BiConsumer immediatePacketConsumer; - private final BiConsumer> immediatePacketsConsumer; + private final TriConsumer packetConsumer; + private final TriConsumer, Object> packetsConsumer; + private final TriConsumer immediatePacketConsumer; + private final TriConsumer, Runnable> immediatePacketsConsumer; private final BukkitCraftEngine plugin; private final Map users = new ConcurrentHashMap<>(); @@ -105,21 +105,30 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.registerPacketHandlers(); // set up packet senders this.packetConsumer = FastNMS.INSTANCE::method$Connection$send; - this.packetsConsumer = ((connection, packets) -> { + this.packetsConsumer = ((connection, packets, sendListener) -> { Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - this.packetConsumer.accept(connection, bundle); + this.packetConsumer.accept(connection, bundle, sendListener); }); - this.immediatePacketConsumer = ChannelOutboundInvoker::writeAndFlush; - this.immediatePacketsConsumer = (channel, packets) -> { + this.immediatePacketConsumer = (channel, packet, sendListener) -> { + ChannelFuture future = channel.writeAndFlush(packet); + if (sendListener == null) return; + future.addListener((ChannelFutureListener) channelFuture -> { + sendListener.run(); + if (!channelFuture.isSuccess()) { + channelFuture.channel().pipeline().fireExceptionCaught(channelFuture.cause()); + } + }); + }; + this.immediatePacketsConsumer = (channel, packets, sendListener) -> { Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - this.immediatePacketConsumer.accept(channel, bundle); + this.immediatePacketConsumer.accept(channel, bundle, sendListener); }; // set up mod channel this.plugin.javaPlugin().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL, this); this.plugin.javaPlugin().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL); // Inject server channel try { - Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null); + Object server = FastNMS.INSTANCE.method$MinecraftServer$getServer(); Object serverConnection = CoreReflections.field$MinecraftServer$connection.get(server); @SuppressWarnings("unchecked") List channels = (List) CoreReflections.field$ServerConnectionListener$channels.get(serverConnection); @@ -127,7 +136,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Channel channel = future.channel(); injectServerChannel(channel); this.injectedChannels.add(channel); - }, (object) -> {}); + }, (object) -> { + }); CoreReflections.field$ServerConnectionListener$channels.set(serverConnection, monitor); } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to init server connection", e); @@ -206,7 +216,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.resetUserArray(); if (VersionHelper.isFolia()) { player.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> user.tick(), - () -> {}, 1, 1); + () -> { + }, 1, 1); } } } @@ -305,20 +316,20 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } @Override - public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately) { + public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately, Runnable sendListener) { if (immediately) { - this.immediatePacketConsumer.accept(player.nettyChannel(), packet); + this.immediatePacketConsumer.accept(player.nettyChannel(), packet, sendListener); } else { - this.packetConsumer.accept(player.connection(), packet); + this.packetConsumer.accept(player.connection(), packet, sendListener != null ? FastNMS.INSTANCE.method$PacketSendListener$thenRun(sendListener) : null); } } @Override - public void sendPackets(@NotNull NetWorkUser player, List packet, boolean immediately) { + public void sendPackets(@NotNull NetWorkUser player, List packet, boolean immediately, Runnable sendListener) { if (immediately) { - this.immediatePacketsConsumer.accept(player.nettyChannel(), packet); + this.immediatePacketsConsumer.accept(player.nettyChannel(), packet, sendListener); } else { - this.packetsConsumer.accept(player.connection(), packet); + this.packetsConsumer.accept(player.connection(), packet, sendListener != null ? FastNMS.INSTANCE.method$PacketSendListener$thenRun(sendListener) : null); } } @@ -685,7 +696,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes ByteBuf temp = ctx.alloc().buffer(); try { if (compressor != null) { - callEncode(compressor, ctx, input, temp); + callEncode(compressor, ctx, input, temp); } } finally { input.clear().writeBytes(temp); 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 21685624a..298009399 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 @@ -29,6 +29,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.handler.*; import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload; import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes; import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload; +import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; @@ -105,7 +106,7 @@ public class PacketConsumers { byte yHeadRot = buf.readByte(); int data = buf.readVarInt(); // Falling blocks - int remapped = remap(data); + int remapped = user.clientModEnabled() ? remapMOD(data) : remap(data); if (remapped != data) { int xa = buf.readShort(); int ya = buf.readShort(); @@ -428,7 +429,7 @@ public class PacketConsumers { if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) { return; } - int state = remap(before); + int state = user.clientModEnabled() ? remapMOD(before) : remap(before); if (state == before) { return; } @@ -450,7 +451,7 @@ public class PacketConsumers { BlockPos blockPos = buf.readBlockPos(); int state = buf.readInt(); boolean global = buf.readBoolean(); - int newState = remap(state); + int newState = user.clientModEnabled() ? remapMOD(state) : remap(state); if (newState == state) { return; } @@ -1006,7 +1007,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1046,7 +1047,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1086,7 +1087,7 @@ public class PacketConsumers { if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return; Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option); int id = BlockStateUtils.blockStateToId(blockState); - int remapped = remap(id); + int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id); if (remapped == id) return; Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option); Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped)); @@ -1601,6 +1602,10 @@ public class PacketConsumers { } mainThreadTask = () -> { + if (!furniture.isValid()) { + return; + } + FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint); if (EventUtils.fireAndCheckCancel(interactEvent)) { return; @@ -1622,7 +1627,7 @@ public class PacketConsumers { } // 必须从网络包层面处理,否则无法获取交互的具体实体 - if (serverPlayer.isSecondaryUseActive() && itemInHand != null) { + if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty()) { // try placing another furniture above it AABB hitBox = furniture.aabbByEntityId(entityId); if (hitBox == null) return; @@ -1884,34 +1889,38 @@ public class PacketConsumers { public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { try { - if (!VersionHelper.isOrAbove1_20_5()) return; + if (!VersionHelper.isOrAbove1_20_2()) return; Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet); + Payload clientPayload; if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) { - Payload discardedPayload = DiscardedPayload.from(payload); - if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) + clientPayload = DiscardedPayload.from(payload); + } else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(payload)) { + clientPayload = UnknownPayload.from(payload); + } else { + return; + } + if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY)) + return; + FriendlyByteBuf buf = clientPayload.toBuffer(); + NetWorkDataTypes dataType = buf.readEnumConstant(NetWorkDataTypes.class); + if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { + int clientBlockRegistrySize = dataType.decode(buf); + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (clientBlockRegistrySize != serverBlockRegistrySize) { + user.kick(Component.translatable( + "disconnect.craftengine.block_registry_mismatch", + TranslationArgument.numeric(clientBlockRegistrySize), + TranslationArgument.numeric(serverBlockRegistrySize) + )); return; - FriendlyByteBuf buf = discardedPayload.toBuffer(); - NetWorkDataTypes dataType = NetWorkDataTypes.readType(buf); - if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) { - int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf); - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (clientBlockRegistrySize != serverBlockRegistrySize) { - user.kick(Component.translatable( - "disconnect.craftengine.block_registry_mismatch", - TranslationArgument.numeric(clientBlockRegistrySize), - TranslationArgument.numeric(serverBlockRegistrySize) - )); - return; - } - user.setClientModState(true); - } else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { - if (!VersionHelper.isOrAbove1_20_2()) return; - if (dataType.as(Boolean.class).decode(buf)) { - FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); - dataType.writeType(bufPayload); - dataType.as(Boolean.class).encode(bufPayload, true); - user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array()); - } + } + user.setClientModState(true); + } else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) { + if (dataType.decode(buf)) { + FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer()); + bufPayload.writeEnumConstant(dataType); + dataType.encode(bufPayload, true); + user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array()); } } } catch (Throwable e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java index 5655e1028..37da1618d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java @@ -1,64 +1,28 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload; +import io.netty.buffer.ByteBuf; -import net.momirealms.craftengine.core.util.FriendlyByteBuf; +public enum NetWorkDataTypes { + CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER), + CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN); -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Function; + private final NetWorkCodec codec; -public class NetWorkDataTypes { - private static final Map> id2NetWorkDataTypes = new HashMap<>(); - - public static final NetWorkDataTypes CLIENT_CUSTOM_BLOCK = - new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt); - - public static final NetWorkDataTypes CANCEL_BLOCK_UPDATE = - new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean); - - static { - register(CLIENT_CUSTOM_BLOCK); - register(CANCEL_BLOCK_UPDATE); + NetWorkDataTypes(NetWorkCodec codec) { + this.codec = codec; } - private static void register(NetWorkDataTypes type) { - id2NetWorkDataTypes.put(type.id, type); + public NetWorkCodec codec() { + return codec; } - private final int id; - private final Function decoder; - private final BiConsumer encoder; - - public NetWorkDataTypes(int id, Function decoder, BiConsumer encoder) { - this.id = id; - this.decoder = decoder; - this.encoder = encoder; + @SuppressWarnings("unchecked") + public V decode(ByteBuf buf) { + return (V) codec.decode(buf); } - public T decode(FriendlyByteBuf buf) { - return decoder.apply(buf); - } - - public void encode(FriendlyByteBuf buf, T data) { - encoder.accept(buf, data); - } - - public int id() { - return id; - } - - public void writeType(FriendlyByteBuf buf) { - buf.writeVarInt(id); - } - - public static NetWorkDataTypes readType(FriendlyByteBuf buf) { - int id = buf.readVarInt(); - return id2NetWorkDataTypes.get(id); - } - - @SuppressWarnings({"unchecked", "unused"}) - public NetWorkDataTypes as(Class clazz) { - return (NetWorkDataTypes) this; + @SuppressWarnings("unchecked") + public void encode(ByteBuf buf, V value) { + ((NetWorkCodec) codec).encode(buf, value); } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java new file mode 100644 index 000000000..4143eea27 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/UnknownPayload.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.Key; + +public record UnknownPayload(Key channel, ByteBuf rawPayload) implements Payload{ + + public static UnknownPayload from(Object payload) { + try { + Object id = NetworkReflections.field$UnknownPayload$id.get(payload); + ByteBuf data = (ByteBuf) NetworkReflections.field$UnknownPayload$data.get(payload); + Key channel = KeyUtils.resourceLocationToKey(id); + return new UnknownPayload(channel, data); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to create UnknownPayload", e); + return null; + } + } + + @Override + public FriendlyByteBuf toBuffer() { + return new FriendlyByteBuf(rawPayload); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 77076e777..c5b48563d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -2849,10 +2849,6 @@ public final class CoreReflections { ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("server.MinecraftServer")) ); - public static final Method method$MinecraftServer$getServer = requireNonNull( - ReflectionUtils.getMethod(clazz$MinecraftServer, new String[] { "getServer" }) - ); - public static final Field field$MinecraftServer$registries = requireNonNull( ReflectionUtils.getDeclaredField(clazz$MinecraftServer, clazz$LayeredRegistryAccess, 0) ); @@ -3746,7 +3742,7 @@ public final class CoreReflections { .orElse( null); public static final Field field$ServerCommonPacketListenerImpl$closed = MiscUtils.requireNonNullIf( - ReflectionUtils.getDeclaredField(clazz$ServerCommonPacketListenerImpl, boolean.class, VersionHelper.isOrAbove1_21_6() ? 1 : 2), + ReflectionUtils.getDeclaredField(clazz$ServerCommonPacketListenerImpl, "closed", "n"), VersionHelper.isOrAbove1_20_5() ); @@ -3759,11 +3755,14 @@ public final class CoreReflections { methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask = ReflectionUtils.unreflectMethod(method$ServerConfigurationPacketListenerImpl$finishCurrentTask) .asType(MethodType.methodType(void.class, Object.class, Object.class)); + } else { + methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask = null; + } + if (VersionHelper.isOrAbove1_20_5()) { methodHandle$ServerCommonPacketListenerImpl$closedSetter = ReflectionUtils.unreflectSetter(field$ServerCommonPacketListenerImpl$closed) .asType(MethodType.methodType(void.class, Object.class, boolean.class)); } else { - methodHandle$ServerConfigurationPacketListenerImpl$finishCurrentTask = null; methodHandle$ServerCommonPacketListenerImpl$closedSetter = null; } } catch (ReflectiveOperationException e) { @@ -3896,4 +3895,74 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to initialize SnowLayerBlock$LAYERS", e); } } + + public static final Class clazz$DyeItem = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.ItemDye", + "world.item.DyeItem" + ) + ); + + public static final Method method$Recipe$matches = requireNonNull( + VersionHelper.isOrAbove1_21() ? + ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$RecipeInput, clazz$Level) : + ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$Container, clazz$Level) + ); + + public static final Method method$Recipe$assemble = requireNonNull( + VersionHelper.isOrAbove1_21() ? + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$RecipeInput, clazz$HolderLookup$Provider) : + VersionHelper.isOrAbove1_20_5() ? + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$HolderLookup$Provider) : + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$RegistryAccess) + ); + + public static final Class clazz$CraftingBookCategory = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.crafting.CraftingBookCategory", + "world.item.crafting.CraftingBookCategory" + ) + ); + + public static final Method method$CraftingBookCategory$values = requireNonNull( + ReflectionUtils.getStaticMethod(clazz$CraftingBookCategory, clazz$CraftingBookCategory.arrayType()) + ); + + public static final Object instance$CraftingBookCategory$BUILDING; + public static final Object instance$CraftingBookCategory$REDSTONE; + public static final Object instance$CraftingBookCategory$EQUIPMENT; + public static final Object instance$CraftingBookCategory$MISC; + + static { + try { + Object[] values = (Object[]) method$CraftingBookCategory$values.invoke(null); + instance$CraftingBookCategory$BUILDING = values[0]; + instance$CraftingBookCategory$REDSTONE = values[1]; + instance$CraftingBookCategory$EQUIPMENT = values[2]; + instance$CraftingBookCategory$MISC = values[3]; + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize CraftingBookCategory", e); + } + } + + public static final Class clazz$CraftingInput = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.crafting.CraftingInput", + "world.item.crafting.CraftingInput" + ), VersionHelper.isOrAbove1_21() + ); + + public static final Class clazz$CraftingContainer = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.inventory.InventoryCrafting", + "world.inventory.CraftingContainer" + ) + ); + + public static final Class clazz$DyeableLeatherItem = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.IDyeable", + "world.item.DyeableLeatherItem" + ), !VersionHelper.isOrAbove1_20_5() + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java index 34d2f0e65..5255de607 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MBlocks.java @@ -18,6 +18,8 @@ public final class MBlocks { public static final Object SHULKER_BOX; public static final Object COMPOSTER; public static final Object SNOW; + public static final Object WATER; + public static final Object WATER$defaultState; private static Object getById(String id) { Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); @@ -37,5 +39,7 @@ public final class MBlocks { SHULKER_BOX = getById("shulker_box"); COMPOSTER = getById("composter"); SNOW = getById("snow"); + WATER = getById("water"); + WATER$defaultState = FastNMS.INSTANCE.method$Block$defaultState(WATER); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MFluids.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MFluids.java index a03660080..414d2472d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MFluids.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MFluids.java @@ -7,6 +7,7 @@ public final class MFluids { private MFluids() {} public static final Object WATER; + public static final Object WATER$defaultState; public static final Object FLOWING_WATER; public static final Object LAVA; public static final Object FLOWING_LAVA; @@ -21,6 +22,7 @@ public final class MFluids { static { try { WATER = getById("water"); + WATER$defaultState = CoreReflections.method$Fluid$defaultFluidState.invoke(WATER); FLOWING_WATER = getById("flowing_water"); LAVA = getById("lava"); FLOWING_LAVA = getById("flowing_lava"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java index cfe080d81..258666205 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; public final class MItems { private MItems() {} @@ -9,11 +12,17 @@ public final class MItems { public static final Object WATER_BUCKET; public static final Object BARRIER; + @Nullable private static Object getById(String id) { Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, rl); } + @Nullable + public static Object getById(Key id) { + return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(id)); + } + static { AIR = getById("air"); WATER_BUCKET = getById("water_bucket"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 6c1d427a9..6c11c869e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -1248,7 +1248,9 @@ public final class NetworkReflections { ); public static final Constructor constructor$ClientboundCustomPayloadPacket = requireNonNull( - ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0) + VersionHelper.isOrAbove1_20_2() + ? ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, clazz$CustomPacketPayload) + : ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$FriendlyByteBuf) ); // 1.20.2+ @@ -1621,4 +1623,27 @@ public final class NetworkReflections { throw new ReflectionInitException("Failed to initialize HashedStack$STREAM_CODEC", e); } } + + // 1.20.2~1.20.4 + public static final Class clazz$UnknownPayload = MiscUtils.requireNonNullIf( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload") + ), + VersionHelper.isOrAbove1_20_2() && !VersionHelper.isOrAbove1_20_5() + ); + + // 1.20.2~1.20.4 + public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$UnknownPayload) + .map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ResourceLocation, 0)) + .orElse(null); + + // 1.20.2~1.20.4 + public static final Field field$UnknownPayload$data = Optional.ofNullable(clazz$UnknownPayload) + .map(it -> ReflectionUtils.getDeclaredField(it, ByteBuf.class, 0)) + .orElse(null); + + // 1.20.2~1.20.4 + public static final Constructor constructor$UnknownPayload = Optional.ofNullable(clazz$UnknownPayload) + .map(ReflectionUtils::getTheOnlyConstructor) + .orElse(null); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 74430caf7..a2c501de6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -76,6 +76,9 @@ public class BukkitServerPlayer extends Player { private Key clientSideDimension; // check main hand/offhand interaction private int lastSuccessfulInteraction; + // to prevent duplicated events + private int lastInteractEntityWithMainHand; + private int lastInteractEntityWithOffHand; // re-sync attribute timely to prevent some bugs private long lastAttributeSyncTime; // for breaking blocks @@ -90,8 +93,6 @@ public class BukkitServerPlayer extends Player { // for client visual sync private int resentSoundTick; private int resentSwingTick; - // cache used recipe - private Key lastUsedRecipe = null; // has fabric client mod or not private boolean hasClientMod = false; // cache if player can break blocks @@ -234,6 +235,24 @@ public class BukkitServerPlayer extends Player { return this.lastSuccessfulInteraction; } + @Override + public void updateLastInteractEntityTick(@NotNull InteractionHand hand) { + if (hand == InteractionHand.MAIN_HAND) { + this.lastInteractEntityWithMainHand = gameTicks(); + } else { + this.lastInteractEntityWithOffHand = gameTicks(); + } + } + + @Override + public boolean lastInteractEntityCheck(@NotNull InteractionHand hand) { + if (hand == InteractionHand.MAIN_HAND) { + return gameTicks() == this.lastInteractEntityWithMainHand; + } else { + return gameTicks() == this.lastInteractEntityWithOffHand; + } + } + @Override public int gameTicks() { return this.gameTicks; @@ -302,17 +321,29 @@ public class BukkitServerPlayer extends Player { this.plugin.networkManager().sendPacket(this, packet, immediately); } + @Override + public void sendPacket(Object packet, boolean immediately, Runnable sendListener) { + this.plugin.networkManager().sendPacket(this, packet, immediately, sendListener); + } + @Override public void sendCustomPayload(Key channel, byte[] data) { try { Object channelKey = KeyUtils.toResourceLocation(channel); - Object dataPayload; - if (DiscardedPayload.useNewMethod) { - dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data); + Object responsePacket; + if (VersionHelper.isOrAbove1_20_2()) { + Object dataPayload; + if (NetworkReflections.clazz$UnknownPayload != null) { + dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + } else if (DiscardedPayload.useNewMethod) { + dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data); + } else { + dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + } + responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload); } else { - dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data)); + responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelKey, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data))); } - Object responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload); this.sendPacket(responsePacket, true); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to send custom payload to " + name(), e); @@ -324,8 +355,10 @@ public class BukkitServerPlayer extends Player { try { Object reason = ComponentUtils.adventureToMinecraft(message); Object kickPacket = NetworkReflections.constructor$ClientboundDisconnectPacket.newInstance(reason); - this.sendPacket(kickPacket, true); - this.nettyChannel().disconnect(); + this.sendPacket(kickPacket, false, () -> FastNMS.INSTANCE.method$Connection$disconnect(this.connection(), reason)); + this.nettyChannel().config().setAutoRead(false); + Runnable handleDisconnection = () -> FastNMS.INSTANCE.method$Connection$handleDisconnection(this.connection()); + FastNMS.INSTANCE.method$BlockableEventLoop$scheduleOnMain(handleDisconnection); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to kick " + name(), e); } @@ -336,6 +369,11 @@ public class BukkitServerPlayer extends Player { this.plugin.networkManager().sendPackets(this, packet, immediately); } + @Override + public void sendPackets(List packet, boolean immediately, Runnable sendListener) { + this.plugin.networkManager().sendPackets(this, packet, immediately, sendListener); + } + @Override public void simulatePacket(Object packet) { this.plugin.networkManager().simulatePacket(this, packet); @@ -600,7 +638,7 @@ public class BukkitServerPlayer extends Player { if (canInstabuild() && (itemMaterial == Material.DEBUG_STICK || itemMaterial == Material.TRIDENT || (VersionHelper.isOrAbove1_20_5() && itemMaterial == MaterialUtils.MACE) - || item.is(ItemTags.SWORDS))) { + || item.hasItemTag(ItemTags.SWORDS))) { return; } } @@ -691,7 +729,8 @@ public class BukkitServerPlayer extends Player { FastNMS.INSTANCE.method$CraftPlayer$getHandle(player) ) ), - packet + packet, + null ); } } @@ -844,14 +883,6 @@ public class BukkitServerPlayer extends Player { return resentSwingTick == gameTicks(); } - public Key lastUsedRecipe() { - return lastUsedRecipe; - } - - public void setLastUsedRecipe(Key lastUsedRecipe) { - this.lastUsedRecipe = lastUsedRecipe; - } - public boolean clientModEnabled() { return this.hasClientMod; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java index dce7ca633..a2981f6be 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java @@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.sound.JukeboxSong; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; +import java.util.Collection; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -26,6 +27,37 @@ public class BukkitSoundManager extends AbstractSoundManager { } } + @Override + protected void registerSounds(Collection sounds) { + if (sounds.isEmpty()) return; + Object registry = MBuiltInRegistries.SOUND_EVENT; + try { + CoreReflections.field$MappedRegistry$frozen.set(registry, false); + for (Key soundEventId : sounds) { + Object resourceLocation = KeyUtils.toResourceLocation(soundEventId); + // 检查之前有没有注册过了 + Object soundEvent = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation); + // 只有没注册才注册,否则会报错 + if (soundEvent == null) { + soundEvent = VersionHelper.isOrAbove1_21_2() ? + CoreReflections.constructor$SoundEvent.newInstance(resourceLocation, Optional.of(0)) : + CoreReflections.constructor$SoundEvent.newInstance(resourceLocation, 0, false); + Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, soundEvent); + CoreReflections.method$Holder$Reference$bindValue.invoke(holder, soundEvent); + CoreReflections.field$Holder$Reference$tags.set(holder, Set.of()); + int id = FastNMS.INSTANCE.method$Registry$getId(registry, soundEvent); + super.customSoundsInRegistry.put(id, soundEventId); + } + } + } catch (Exception e) { + this.plugin.logger().warn("Failed to register jukebox songs.", e); + } finally { + try { + CoreReflections.field$MappedRegistry$frozen.set(registry, true); + } catch (ReflectiveOperationException ignored) {} + } + } + @Override protected void registerSongs(Map songs) { if (songs.isEmpty()) return; @@ -40,13 +72,12 @@ public class BukkitSoundManager extends AbstractSoundManager { Object soundId = KeyUtils.toResourceLocation(jukeboxSong.sound()); // 检查之前有没有注册过了 Object song = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation); - - Object soundEvent = VersionHelper.isOrAbove1_21_2() ? - CoreReflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) : - CoreReflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false); - Object soundHolder = CoreReflections.method$Holder$direct.invoke(null, soundEvent); // 只有没注册才注册,否则会报错 if (song == null) { + Object soundEvent = VersionHelper.isOrAbove1_21_2() ? + CoreReflections.constructor$SoundEvent.newInstance(soundId, Optional.of(jukeboxSong.range())) : + CoreReflections.constructor$SoundEvent.newInstance(soundId, jukeboxSong.range(), false); + Object soundHolder = CoreReflections.method$Holder$direct.invoke(null, soundEvent); song = CoreReflections.constructor$JukeboxSong.newInstance(soundHolder, ComponentUtils.adventureToMinecraft(jukeboxSong.description()), jukeboxSong.lengthInSeconds(), jukeboxSong.comparatorOutput()); Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, song); CoreReflections.method$Holder$Reference$bindValue.invoke(holder, song); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index 30fd6fa4b..a4b87cde2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; @@ -15,7 +17,8 @@ import java.util.function.Consumer; public class EntityUtils { - private EntityUtils() {} + private EntityUtils() { + } public static BlockPos getOnPos(Player player) { try { @@ -34,4 +37,11 @@ public class EntityUtils { return LegacyEntityUtils.spawnEntity(world, loc, type, function); } } -} + + public static Key getEntityType(Entity entity) { + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(entity); + Object entityType = FastNMS.INSTANCE.method$Entity$getType(nmsEntity); + Object id = FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ENTITY_TYPE, entityType); + return KeyUtils.resourceLocationToKey(id); + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java index 11f5213b8..34c7cf27e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java @@ -1,10 +1,15 @@ package net.momirealms.craftengine.bukkit.util; +import io.papermc.paper.entity.Shearable; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior; import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; import net.momirealms.craftengine.core.block.BlockKeys; +import net.momirealms.craftengine.core.entity.EntityTypeKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.recipe.RecipeType; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -12,26 +17,35 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuadFunction; +import net.momirealms.craftengine.core.util.TriFunction; import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.GameMode; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Levelled; import org.bukkit.block.data.type.Bell; -import org.bukkit.entity.Player; +import org.bukkit.block.data.type.ChiseledBookshelf; +import org.bukkit.block.data.type.RespawnAnchor; +import org.bukkit.entity.*; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; public class InteractUtils { private static final Map, BlockData, BlockHitResult, Boolean>> INTERACTIONS = new HashMap<>(); private static final Map, BlockData, BlockHitResult, Boolean>> WILL_CONSUME = new HashMap<>(); + private static final Map, Boolean>> ENTITY_INTERACTIONS = new HashMap<>(); private static final Key NOTE_BLOCK_TOP_INSTRUMENTS = Key.of("minecraft:noteblock_top_instruments"); private InteractUtils() {} static { - registerInteraction(BlockKeys.NOTE_BLOCK, (player, item, blockState, result) -> result.getDirection() != Direction.UP || !item.is(NOTE_BLOCK_TOP_INSTRUMENTS)); + registerInteraction(BlockKeys.NOTE_BLOCK, (player, item, blockState, result) -> result.getDirection() != Direction.UP || !item.hasItemTag(NOTE_BLOCK_TOP_INSTRUMENTS)); registerInteraction(BlockKeys.CAKE, (player, item, blockState, result) -> !canEat(player, false)); registerInteraction(BlockKeys.CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false)); registerInteraction(BlockKeys.WHITE_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false)); @@ -50,6 +64,43 @@ public class InteractUtils { registerInteraction(BlockKeys.GREEN_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false)); registerInteraction(BlockKeys.RED_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false)); registerInteraction(BlockKeys.BLACK_CANDLE_CAKE, (player, item, blockState, result) -> !canEat(player, false)); + registerInteraction(BlockKeys.COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.CHAIN_COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.REPEATING_COMMAND_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.JIGSAW, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.STRUCTURE_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.TEST_INSTANCE_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.TEST_BLOCK, (player, item, blockState, result) -> player.isOp() && player.getGameMode() == GameMode.CREATIVE); + registerInteraction(BlockKeys.LIGHT, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.LIGHT)); + registerInteraction(BlockKeys.LODESTONE, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.COMPASS)); + registerInteraction(BlockKeys.BEE_NEST, (player, item, blockState, result) -> { + Key id = item.vanillaId(); + return ItemKeys.SHEARS.equals(id) || ItemKeys.GLASS_BOTTLE.equals(id); + }); + registerInteraction(BlockKeys.BEEHIVE, (player, item, blockState, result) -> { + Key id = item.vanillaId(); + return ItemKeys.SHEARS.equals(id) || ItemKeys.GLASS_BOTTLE.equals(id); + }); + registerInteraction(BlockKeys.POWDER_SNOW, (player, item, blockState, result) -> item.vanillaId().equals(ItemKeys.BUCKET)); + registerInteraction(BlockKeys.REDSTONE_ORE, (player, item, blockState, result) -> { + Optional> behaviors = item.getItemBehavior(); + if (behaviors.isPresent()) { + for (ItemBehavior behavior : behaviors.get()) { + if (behavior instanceof BlockItemBehavior) return false; + } + } + return true; + }); + registerInteraction(BlockKeys.DEEPSLATE_REDSTONE_ORE, (player, item, blockState, result) -> { + Optional> behaviors = item.getItemBehavior(); + if (behaviors.isPresent()) { + for (ItemBehavior behavior : behaviors.get()) { + if (behavior instanceof BlockItemBehavior) return false; + } + } + return true; + }); + registerInteraction(BlockKeys.BELL, (player, item, blockState, result) -> { Direction direction = result.getDirection(); BlockPos pos = result.getBlockPos(); @@ -78,17 +129,26 @@ public class InteractUtils { }); registerInteraction(BlockKeys.SOUL_CAMPFIRE, (player, item, blockState, result) -> { if (!Config.enableRecipeSystem()) return false; - return BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>( - item.recipeIngredientId(), item - ))) != null; + return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null; }); registerInteraction(BlockKeys.CAMPFIRE, (player, item, blockState, result) -> { if (!Config.enableRecipeSystem()) return false; - return BukkitRecipeManager.instance().recipeByInput(RecipeTypes.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>( - item.recipeIngredientId(), item - ))) != null; + return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null; + }); + registerInteraction(BlockKeys.CHISELED_BOOKSHELF, (player, item, blockState, result) -> { + if (!(blockState instanceof ChiseledBookshelf chiseledBookshelf)) return false; + return DirectionUtils.toDirection(chiseledBookshelf.getFacing()) == result.getDirection(); + }); + registerInteraction(BlockKeys.COMPOSTER, (player, item, blockState, result) -> { + if (item.getItem().getType().isCompostable()) return true; + return blockState instanceof Levelled levelled && levelled.getLevel() == levelled.getMaximumLevel(); + }); + registerInteraction(BlockKeys.RESPAWN_ANCHOR, (player, item, blockState, result) -> { + if (item.vanillaId().equals(ItemKeys.GLOWSTONE)) return true; + return blockState instanceof RespawnAnchor respawnAnchor && respawnAnchor.getCharges() != 0; }); registerInteraction(BlockKeys.DECORATED_POT, (player, item, blockState, result) -> true); + registerInteraction(BlockKeys.FLOWER_POT, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.HOPPER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.DISPENSER, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.DROPPER, (player, item, blockState, result) -> true); @@ -264,14 +324,129 @@ public class InteractUtils { registerInteraction(BlockKeys.RED_BED, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.BLACK_BED, (player, item, blockState, result) -> true); registerInteraction(BlockKeys.DRAGON_EGG, (player, item, blockState, result) -> true); - registerInteraction(BlockKeys.REPEATING_COMMAND_BLOCK, (player, item, blockState, result) -> true); - registerInteraction(BlockKeys.CHAIN_COMMAND_BLOCK, (player, item, blockState, result) -> true); - registerInteraction(BlockKeys.COMMAND_BLOCK, (player, item, blockState, result) -> true); } static { registerWillConsume(BlockKeys.CACTUS, (player, item, blockState, result) -> result.getDirection() == Direction.UP && item.id().equals(ItemKeys.CACTUS)); + registerWillConsume(BlockKeys.CAULDRON, (player, item, blockState, result) -> { + Key id = item.vanillaId(); + return ItemKeys.WATER_BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id); + }); + registerWillConsume(BlockKeys.LAVA_CAULDRON, (player, item, blockState, result) -> { + Key id = item.vanillaId(); + return ItemKeys.BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id) || ItemKeys.WATER_BUCKET.equals(id); + }); + registerWillConsume(BlockKeys.WATER_CAULDRON, (player, item, blockState, result) -> { + if (blockState instanceof Levelled levelled && levelled.getLevel() == levelled.getMaximumLevel()) + return item.vanillaId().equals(ItemKeys.BUCKET); + Key id = item.vanillaId(); + return ItemKeys.GLASS_BOTTLE.equals(id) || ItemKeys.WATER_BUCKET.equals(id) || ItemKeys.LAVA_BUCKET.equals(id); + }); + } + + static { + registerEntityInteraction(EntityTypeKeys.BEE, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.FOX, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.FROG, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.PANDA, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.HOGLIN, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.OCELOT, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.RABBIT, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.TURTLE, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.CHICKEN, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.SNIFFER, (player, entity, item) -> canFeed(entity, item)); + registerEntityInteraction(EntityTypeKeys.AXOLOTL, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET))); + registerEntityInteraction(EntityTypeKeys.COD, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)); + registerEntityInteraction(EntityTypeKeys.SALMON, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)); + registerEntityInteraction(EntityTypeKeys.TROPICAL_FISH, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)); + registerEntityInteraction(EntityTypeKeys.PUFFERFISH, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)); + registerEntityInteraction(EntityTypeKeys.TADPOLE, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.WATER_BUCKET)); + registerEntityInteraction(EntityTypeKeys.SNOW_GOLEM, (player, entity, item) -> + shearable(entity, item)); + registerEntityInteraction(EntityTypeKeys.SHEEP, (player, entity, item) -> + canFeed(entity, item) || shearable(entity, item)); + registerEntityInteraction(EntityTypeKeys.BOGGED, (player, entity, item) -> + canFeed(entity, item) || shearable(entity, item)); + registerEntityInteraction(EntityTypeKeys.MOOSHROOM, (player, entity, item) -> + canFeed(entity, item) || shearable(entity, item) || (item != null && (item.vanillaId().equals(ItemKeys.BUCKET) || item.vanillaId().equals(ItemKeys.BOWL)))); + registerEntityInteraction(EntityTypeKeys.COW, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BUCKET))); + registerEntityInteraction(EntityTypeKeys.GOAT, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BUCKET))); + registerEntityInteraction(EntityTypeKeys.CREEPER, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.FLINT_AND_STEEL)); + registerEntityInteraction(EntityTypeKeys.PIGLIN, (player, entity, item) -> + item != null && item.vanillaId().equals(ItemKeys.GOLD_INGOT)); + registerEntityInteraction(EntityTypeKeys.ARMADILLO, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.BRUSH))); + registerEntityInteraction(EntityTypeKeys.ZOMBIE_HORSE, (player, entity, item) -> + entity instanceof Tameable tameable && tameable.isTamed()); + registerEntityInteraction(EntityTypeKeys.SKELETON_HORSE, (player, entity, item) -> + entity instanceof Tameable tameable && tameable.isTamed()); + registerEntityInteraction(EntityTypeKeys.PIG, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.SADDLE) && !hasSaddle(player, entity)) || (hasSaddle(player, entity) && !player.isSneaking())); + registerEntityInteraction(EntityTypeKeys.STRIDER, (player, entity, item) -> + canFeed(entity, item) || (item != null && item.vanillaId().equals(ItemKeys.SADDLE) && !hasSaddle(player, entity)) || (hasSaddle(player, entity) && !player.isSneaking())); + registerEntityInteraction(EntityTypeKeys.WOLF, (player, entity, item) -> canFeed(entity, item) || isPetOwner(player, entity)); + registerEntityInteraction(EntityTypeKeys.CAT, (player, entity, item) -> canFeed(entity, item) || isPetOwner(player, entity)); + registerEntityInteraction(EntityTypeKeys.BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.OAK_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.SPRUCE_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.BIRCH_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.JUNGLE_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.ACACIA_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.DARK_OAK_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.MANGROVE_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.CHERRY_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.PALE_OAK_BOAT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.BAMBOO_RAFT, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.MINECART, (player, entity, item) -> !player.isSneaking()); + registerEntityInteraction(EntityTypeKeys.PARROT, (player, entity, item) -> { + if (item != null && item.hasItemTag(Key.of("parrot_poisonous_food"))) return true; + return canFeed(entity, item) || isPetOwner(player, entity); + }); + registerEntityInteraction(EntityTypeKeys.HAPPY_GHAST, (player, entity, item) -> { + if (item != null && item.vanillaId().equals(ItemKeys.HARNESS)) return true; + if (entity instanceof HappyGhast happyGhast && !player.isSneaking()) { + ItemStack bodyItem = happyGhast.getEquipment().getItem(EquipmentSlot.BODY); + return BukkitItemManager.instance().wrap(bodyItem).hasItemTag(Key.of("harnesses")); + } + return canFeed(entity, item); + }); + registerEntityInteraction(EntityTypeKeys.ALLAY, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.HORSE, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.DONKEY, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.MULE, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.VILLAGER, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.WANDERING_TRADER, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.LLAMA, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.TRADER_LLAMA, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.CAMEL, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.ITEM_FRAME, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.GLOW_ITEM_FRAME, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.INTERACTION, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.OAK_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.SPRUCE_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.BIRCH_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.JUNGLE_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.ACACIA_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.DARK_OAK_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.MANGROVE_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.CHERRY_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.PALE_OAK_CHEST_BOAT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.BAMBOO_CHEST_RAFT, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.CHEST_MINECART, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.FURNACE_MINECART, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.HOPPER_MINECART, (player, entity, item) -> true); + registerEntityInteraction(EntityTypeKeys.COMMAND_BLOCK_MINECART, (player, entity, item) -> true); } private static void registerInteraction(Key key, QuadFunction, BlockData, BlockHitResult, Boolean> function) { @@ -288,6 +463,13 @@ public class InteractUtils { } } + private static void registerEntityInteraction(Key key, TriFunction, Boolean> function) { + var previous = ENTITY_INTERACTIONS.put(key, function); + if (previous != null) { + CraftEngine.instance().logger().warn("Duplicated entity interaction check: " + key); + } + } + public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, @Nullable Item item) { Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state); if (INTERACTIONS.containsKey(blockType)) { @@ -297,6 +479,11 @@ public class InteractUtils { } } + public static boolean isEntityInteractable(Player player, Entity entity, @Nullable Item item) { + TriFunction, Boolean> func = ENTITY_INTERACTIONS.get(EntityUtils.getEntityType(entity)); + return func != null && func.apply(player, entity, item); + } + public static boolean willConsume(Player player, BlockData state, BlockHitResult hit, @Nullable Item item) { if (item == null) return false; Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state); @@ -306,8 +493,24 @@ public class InteractUtils { return false; } } - + private static boolean canEat(Player player, boolean ignoreHunger) { return ignoreHunger || player.isInvulnerable() || player.getFoodLevel() < 20; } + + private static boolean canFeed(Entity entity, Item item) { + return entity instanceof Animals && item.hasItemTag(Key.of(EntityUtils.getEntityType(entity).value() + "_food")); + } + + private static boolean hasSaddle(Player player, Entity entity) { + return entity instanceof Steerable steerable && steerable.hasSaddle() && !player.isSneaking(); + } + + private static boolean shearable(Entity entity, Item item) { + return entity instanceof Shearable shearable && item.vanillaId().equals(ItemKeys.SHEARS) && shearable.readyToBeSheared(); + } + + private static boolean isPetOwner(Player player, Entity entity) { + return entity instanceof Tameable tameable && tameable.isTamed() && player.getUniqueId().equals(tameable.getOwnerUniqueId()); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java index 1ade133c7..bbebf2a97 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java @@ -3,11 +3,14 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; -public class ItemStackUtils { +public final class ItemStackUtils { private ItemStackUtils() {} @@ -43,4 +46,9 @@ public class ItemStackUtils { return FastNMS.INSTANCE.method$CraftItemStack$asCraftCopy(itemStack); } } + + public static UniqueIdItem getUniqueIdItem(@Nullable ItemStack itemStack) { + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + return UniqueIdItem.of(wrappedItem); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java index 1f35e3e90..e94bc4a32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java @@ -12,6 +12,7 @@ public class ItemTags { public static final Key AXES = Key.of("minecraft:axes"); public static final Key SWORDS = Key.of("minecraft:swords"); + public static final Key DYEABLE = Key.of("minecraft:dyeable"); private ItemTags() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java index 8fed8c247..ca67f6b6b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java @@ -1,50 +1,7 @@ package net.momirealms.craftengine.bukkit.util; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.VersionHelper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - public class RecipeUtils { private RecipeUtils() {} - @SuppressWarnings("unchecked") - public static List getIngredientsFromShapedRecipe(Object recipe) { - List ingredients = new ArrayList<>(); - try { - if (VersionHelper.isOrAbove1_20_3()) { - Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - List> optionals = (List>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern); - for (Optional optional : optionals) { - optional.ifPresent(ingredients::add); - } - } else { - List objectList = (List) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern); - for (Object object : objectList) { - Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); - // is empty or not - if (values.length != 0) { - ingredients.add(object); - } - } - } - } else { - List objectList = (List) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe); - for (Object object : objectList) { - Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); - if (values.length != 0) { - ingredients.add(object); - } - } - } - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e); - } - return ingredients; - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 61aa6dabb..7d557cd21 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.world; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; @@ -88,6 +87,11 @@ public class BukkitWorldManager implements WorldManager, Listener { } } + @Override + public CEWorld[] getWorlds() { + return this.worldArray; + } + private void resetWorldArray() { this.worldArray = this.worlds.values().toArray(new CEWorld[0]); } @@ -218,7 +222,6 @@ public class BukkitWorldManager implements WorldManager, Listener { this.lastVisitedUUID = null; } this.resetWorldArray(); - } finally { this.worldMapLock.writeLock().unlock(); } @@ -328,80 +331,75 @@ public class BukkitWorldManager implements WorldManager, Listener { Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer); Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ()); Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk); - for (int i = 0; i < ceSections.length; i++) { - CESection ceSection = ceSections[i]; - Object section = sections[i]; - if (Config.syncCustomBlocks()) { - Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section); - Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer); - Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data); - boolean requiresSync = false; - if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) { - Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette); - if (BlockStateUtils.isCustomBlock(onlyBlockState)) { + synchronized (sections) { + for (int i = 0; i < ceSections.length; i++) { + CESection ceSection = ceSections[i]; + Object section = sections[i]; + if (Config.syncCustomBlocks()) { + Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section); + Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer); + Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data); + boolean requiresSync = false; + if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) { + Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette); + if (BlockStateUtils.isCustomBlock(onlyBlockState)) { + requiresSync = true; + } + } else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) { + Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette); + for (Object blockState : blockStates) { + if (blockState != null) { + if (BlockStateUtils.isCustomBlock(blockState)) { + requiresSync = true; + break; + } + } + } + } else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) { + Object biMap = CoreReflections.field$HashMapPalette$values.get(palette); + Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap); + for (Object blockState : blockStates) { + if (blockState != null) { + if (BlockStateUtils.isCustomBlock(blockState)) { + requiresSync = true; + break; + } + } + } + } else { requiresSync = true; } - } else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) { - Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette); - for (Object blockState : blockStates) { - if (blockState != null) { - if (BlockStateUtils.isCustomBlock(blockState)) { - requiresSync = true; - break; - } - } - } - } else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) { - Object biMap = CoreReflections.field$HashMapPalette$values.get(palette); - Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap); - for (Object blockState : blockStates) { - if (blockState != null) { - if (BlockStateUtils.isCustomBlock(blockState)) { - requiresSync = true; - break; - } - } - } - } else { - requiresSync = true; - } - if (requiresSync) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < 16; y++) { - Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z); - Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState); - if (optionalCustomState.isPresent()) { - ceSection.setBlockState(x, y, z, optionalCustomState.get()); + if (requiresSync) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < 16; y++) { + Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState); + if (optionalCustomState.isPresent()) { + ceSection.setBlockState(x, y, z, optionalCustomState.get()); + } } } } } } - } - if (Config.restoreCustomBlocks()) { - if (!ceSection.statesContainer().isEmpty()) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < 16; y++) { - ImmutableBlockState customState = ceSection.getBlockState(x, y, z); - if (!customState.isEmpty() && customState.customBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false); + if (Config.restoreCustomBlocks()) { + if (!ceSection.statesContainer().isEmpty()) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < 16; y++) { + ImmutableBlockState customState = ceSection.getBlockState(x, y, z); + if (!customState.isEmpty() && customState.customBlockState() != null) { + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false); + } } } } } } - } - int finalI = i; - WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), - (injected) -> sections[finalI] = injected); - } - if (Config.enableRecipeSystem()) { - @SuppressWarnings("unchecked") - Map blockEntities = (Map) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk); - for (Object blockEntity : blockEntities.values()) { - RecipeInjector.injectCookingBlockEntity(blockEntity); + int finalI = i; + WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z), + (injected) -> sections[finalI] = injected); } } } catch (ReflectiveOperationException e) { diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 5acc916c1..f7ea43ab2 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -57,8 +57,12 @@ resource-pack: enable: true # Define the name of the overlay folders overlay-format: "ce_overlay_{version}" + # Allowed values: + # - 1.20.1, 1.21, 1.21.8, etc. + # - LATEST: the latest client version + # - SERVER: the current server version supported-version: - min: "1.20" + min: SERVER max: LATEST # Remove 1.21.5+ tinted_leaves particles remove-tinted-leaves-particle: true @@ -84,8 +88,6 @@ resource-pack: ip: "localhost" port: 8163 protocol: "http" - # The optional URL must be complete and include a trailing slash / at the end. - #url: "http://localhost:8163/" deny-non-minecraft-request: true one-time-token: true rate-limit: @@ -380,12 +382,12 @@ chunk-system: # Settings for injection injection: # Requires a restart to apply - # SECTION: Inject the LevelChunkSection (Faster & Experimental) since 0.0.53 + # SECTION: Inject the LevelChunkSection # PALETTE: Inject the PalettedContainer - target: PALETTE + target: SECTION # Enables faster injection method # Note: May not work with certain server forks that alter chunk class structure (In most cases it won't conflict) - use-fast-method: false + use-fast-method: true # Auto-convert custom blocks -> vanilla blocks when unloading chunks # # - When ENABLED (true): diff --git a/common-files/src/main/resources/resources/default/configuration/categories.yml b/common-files/src/main/resources/resources/default/configuration/categories.yml index 8bbcc8cf8..10ef8b0f2 100644 --- a/common-files/src/main/resources/resources/default/configuration/categories.yml +++ b/common-files/src/main/resources/resources/default/configuration/categories.yml @@ -57,6 +57,7 @@ categories: - default:bench - default:table_lamp - default:wooden_chair + - default:flower_basket default:misc: name: hidden: true diff --git a/common-files/src/main/resources/resources/default/configuration/furniture.yml b/common-files/src/main/resources/resources/default/configuration/furniture.yml index c28d53cdf..23212868d 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture.yml @@ -132,4 +132,113 @@ items: loot: template: default:loot_table/furniture arguments: - item: default:wooden_chair \ No newline at end of file + item: default:wooden_chair + +items#flower_basket: + default:flower_basket: + material: nether_brick + custom-model-data: 2003 + data: + item-name: + model: + template: default:model/simplified_generated + arguments: + path: minecraft:item/custom/flower_basket_2d + behavior: + type: furniture_item + furniture: default:flower_basket + default:flower_basket_ground: + material: nether_brick + custom-model-data: 2004 + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_ground + default:flower_basket_wall: + material: nether_brick + custom-model-data: 2005 + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_wall + default:flower_basket_ceiling: + material: nether_brick + custom-model-data: 2006 + model: + type: minecraft:model + path: minecraft:item/custom/flower_basket_ceiling + +furniture#flower_basket: + default:flower_basket: + settings: + item: default:flower_basket + sounds: + break: minecraft:block.grass.break + place: minecraft:block.grass.place + loot: + template: default:loot_table/furniture + arguments: + item: default:flower_basket + placement: + ground: + rules: + rotation: ANY + alignment: ANY + elements: + - item: default:flower_basket_ground + display-transform: NONE + billboard: FIXED + position: 0,0,0 + translation: 0,0.5,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0,0,0 + width: 0.7 + height: 0.5 + interactive: true + wall: + rules: + alignment: ANY + elements: + - item: default:flower_basket_wall + display-transform: NONE + billboard: FIXED + position: 0,0,0.2 + translation: 0,0,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0.215,-0.3,0.23 + width: 0.46 + height: 0.75 + interactive: true + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: -0.215,-0.3,0.23 + width: 0.46 + height: 0.75 + interactive: true + ceiling: + rules: + rotation: ANY + alignment: ANY + elements: + - item: default:flower_basket_ceiling + display-transform: NONE + billboard: FIXED + position: 0,-0.46,0 + translation: 0,0,0 + hitboxes: + - type: interaction + can-use-item-on: true + can-be-hit-by-projectile: true + blocks-building: true + position: 0,-0.7,0 + width: 0.7 + height: 0.7 + interactive: true \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/configuration/i18n.yml b/common-files/src/main/resources/resources/default/configuration/i18n.yml index 815e41006..b032f9c78 100644 --- a/common-files/src/main/resources/resources/default/configuration/i18n.yml +++ b/common-files/src/main/resources/resources/default/configuration/i18n.yml @@ -44,6 +44,7 @@ i18n: item.flame_elytra: Flame Elytra item.pebble: Pebble item.cap: Cap + item.flower_basket: Flower Basket category.default.name: Default Assets category.default.lore: Contains the default configuration of CraftEngine category.palm_tree: Palm Tree @@ -98,6 +99,7 @@ i18n: item.flame_elytra: 烈焰鞘翅 item.pebble: 石子 item.cap: 鸭舌帽 + item.flower_basket: 花篮 category.default.name: 默认资产 category.default.lore: 包含了CraftEngine的默认配置 category.palm_tree: 棕榈树 diff --git a/common-files/src/main/resources/resources/default/configuration/items.yml b/common-files/src/main/resources/resources/default/configuration/items.yml index 33bc98478..b25111d18 100644 --- a/common-files/src/main/resources/resources/default/configuration/items.yml +++ b/common-files/src/main/resources/resources/default/configuration/items.yml @@ -326,6 +326,7 @@ equipments#topaz: recipes#topaz: default:topaz_shovel: type: shaped + category: equipment pattern: - A - B @@ -338,6 +339,7 @@ recipes#topaz: count: 1 default:topaz_axe: type: shaped + category: equipment pattern: - 'AA' - 'AB' @@ -350,6 +352,7 @@ recipes#topaz: count: 1 default:topaz_sword: type: shaped + category: equipment pattern: - A - A @@ -362,6 +365,7 @@ recipes#topaz: count: 1 default:topaz_hoe: type: shaped + category: equipment pattern: - 'AA' - ' B' @@ -374,6 +378,7 @@ recipes#topaz: count: 1 default:topaz_pickaxe: type: shaped + category: equipment pattern: - 'AAA' - ' B ' @@ -386,6 +391,7 @@ recipes#topaz: count: 1 default:topaz_helmet: type: shaped + category: equipment pattern: - AAA - A A @@ -396,6 +402,7 @@ recipes#topaz: count: 1 default:topaz_chestplate: type: shaped + category: equipment pattern: - A A - AAA @@ -407,6 +414,7 @@ recipes#topaz: count: 1 default:topaz_leggings: type: shaped + category: equipment pattern: - AAA - A A @@ -418,6 +426,7 @@ recipes#topaz: count: 1 default:topaz_boots: type: shaped + category: equipment pattern: - A A - A A diff --git a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml index bd0b2d3ac..a47b29498 100644 --- a/common-files/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/palm_tree.yml @@ -348,7 +348,7 @@ items: arguments: path: minecraft:item/custom/palm_door behavior: - type: block_item + type: double_high_block_item block: behavior: type: door_block diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json new file mode 100644 index 000000000..31e1ef903 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json @@ -0,0 +1,285 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "1": "item/custom/flower_basket", + "particle": "item/custom/flower_basket" + }, + "elements": [ + { + "from": [2, 7, -1], + "to": [14, 7, 11], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 7, 5]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "up": {"uv": [0, 16, 4, 12], "texture": "#1"}, + "down": {"uv": [0, 12, 4, 16], "texture": "#1"} + } + }, + { + "from": [2, 4, 12.5], + "to": [14, 16, 12.5], + "rotation": {"angle": -22.5, "axis": "x", "origin": [8, 10, 12.5]}, + "faces": { + "north": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "east": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "south": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "west": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "up": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "down": {"uv": [4, 12, 8, 8], "texture": "#1"} + } + }, + { + "from": [2, 7, 5], + "to": [14, 7, 17], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 7, 11]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "up": {"uv": [0, 12, 4, 16], "texture": "#1"}, + "down": {"uv": [0, 16, 4, 12], "texture": "#1"} + } + }, + { + "from": [-1, 7, 2], + "to": [11, 7, 14], + "rotation": {"angle": -45, "axis": "z", "origin": [5, 7, 8]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "up": {"uv": [4, 16, 0, 12], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 16, 4, 12], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [5, 7, 2], + "to": [17, 7, 14], + "rotation": {"angle": 45, "axis": "z", "origin": [11, 7, 8]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "up": {"uv": [4, 12, 0, 16], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 12, 4, 16], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [12.5, 4, 2], + "to": [12.5, 16, 14], + "rotation": {"angle": 22.5, "axis": "z", "origin": [12.5, 10, 8]}, + "faces": { + "north": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "east": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "south": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "west": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "up": {"uv": [4, 8, 0, 12], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 8, 4, 12], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [3.5, 7.75, 3.5], + "to": [12.5, 9.25, 12.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.1875, 8]}, + "faces": { + "north": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"}, + "east": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"}, + "south": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"}, + "west": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#1"}, + "up": {"uv": [13.5, 12.5, 10.5, 9.5], "texture": "#1"}, + "down": {"uv": [13.5, 12.5, 10.5, 15.5], "texture": "#1"} + } + }, + { + "from": [4.1, 5.35, 4.1], + "to": [11.9, 9.4, 11.9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.6875, 8]}, + "faces": { + "north": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"}, + "east": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"}, + "south": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"}, + "west": {"uv": [13.5, 8.5, 16, 9.75], "texture": "#1"}, + "up": {"uv": [16, 8.5, 13.5, 6], "texture": "#1"}, + "down": {"uv": [16, 3.5, 13.5, 6], "texture": "#1"} + } + }, + { + "from": [8, 1, 0.5], + "to": [8, 13, 15.5], + "rotation": {"angle": 45, "axis": "y", "origin": [8, 7, 8]}, + "faces": { + "north": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "east": {"uv": [0, 4, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 4, 0, 0], "texture": "#1"}, + "up": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "down": {"uv": [0, 4, 0, 0], "texture": "#1"} + } + }, + { + "from": [8, 1, 0.5], + "to": [8, 13, 15.5], + "rotation": {"angle": -45, "axis": "y", "origin": [8, 7, 8]}, + "faces": { + "north": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "east": {"uv": [0, 4, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 4, 0, 0], "texture": "#1"}, + "up": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "down": {"uv": [0, 4, 0, 0], "texture": "#1"} + } + }, + { + "from": [8, 1, 0.5], + "to": [8, 13, 15.5], + "rotation": {"angle": -45, "axis": "y", "origin": [8, 7, 8]}, + "faces": { + "north": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 4, 8, 0], "texture": "#1"}, + "south": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "west": {"uv": [8, 4, 4, 0], "texture": "#1"}, + "up": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "down": {"uv": [0, 4, 0, 0], "texture": "#1"} + } + }, + { + "from": [8, 1, 0.5], + "to": [8, 13, 15.5], + "rotation": {"angle": 45, "axis": "y", "origin": [8, 7, 8]}, + "faces": { + "north": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 4, 8, 0], "texture": "#1"}, + "south": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "west": {"uv": [8, 4, 4, 0], "texture": "#1"}, + "up": {"uv": [0, 4, 0, 0], "texture": "#1"}, + "down": {"uv": [0, 4, 0, 0], "texture": "#1"} + } + }, + { + "from": [3.5, 4, 2], + "to": [3.5, 16, 14], + "rotation": {"angle": -22.5, "axis": "z", "origin": [3.5, 10, 8]}, + "faces": { + "north": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "east": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "south": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "west": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "up": {"uv": [4, 12, 0, 8], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 12, 4, 8], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [-1, 6.25, 2], + "to": [11, 6.25, 14], + "rotation": {"angle": -45, "axis": "z", "origin": [5, 6.25, 8]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "up": {"uv": [10, 8, 6, 4], "rotation": 270, "texture": "#1"}, + "down": {"uv": [6, 8, 10, 4], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [5, 6.25, 2], + "to": [17, 6.25, 14], + "rotation": {"angle": 45, "axis": "z", "origin": [11, 6.25, 8]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "up": {"uv": [10, 4, 6, 8], "rotation": 270, "texture": "#1"}, + "down": {"uv": [6, 4, 10, 8], "rotation": 270, "texture": "#1"} + } + }, + { + "from": [2, 6.25, -1], + "to": [14, 6.25, 11], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 6.25, 5]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#1"}, + "up": {"uv": [6, 8, 10, 4], "texture": "#1"}, + "down": {"uv": [6, 4, 10, 8], "texture": "#1"} + } + }, + { + "from": [2, 6.25, 5], + "to": [14, 6.25, 17], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 6.25, 11]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#1"}, + "up": {"uv": [6, 4, 10, 8], "texture": "#1"}, + "down": {"uv": [6, 8, 10, 4], "texture": "#1"} + } + }, + { + "from": [2, 4, 3.5], + "to": [14, 16, 3.5], + "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 10, 3.5]}, + "faces": { + "north": {"uv": [8, 8, 4, 12], "texture": "#1"}, + "east": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "south": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "west": {"uv": [4, 8, 8, 12], "texture": "#1"}, + "up": {"uv": [4, 12, 8, 8], "texture": "#1"}, + "down": {"uv": [4, 8, 8, 12], "texture": "#1"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "ground": { + "translation": [0, 3.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [25, 135, 0], + "translation": [0, -0.25, 0], + "scale": [0.9, 0.9, 0.9] + }, + "fixed": { + "rotation": [90, 0, 0], + "translation": [0, 0, -15], + "scale": [2, 2, 2] + } + }, + "groups": [ + { + "name": "bone3", + "origin": [-3, 4, -3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json new file mode 100644 index 000000000..38d9eba22 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json @@ -0,0 +1,206 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "item/custom/flower_basket" + }, + "elements": [ + { + "from": [2, 6.75, -1], + "to": [14, 6.75, 11], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 6.75, 5]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [0, 16, 4, 12], "texture": "#0"}, + "down": {"uv": [0, 12, 4, 16], "texture": "#0"} + } + }, + { + "from": [2, 8.25, -1], + "to": [14, 8.25, 11], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 8.25, 5]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [0, 12, 4, 8], "texture": "#0"}, + "down": {"uv": [0, 8, 4, 12], "texture": "#0"} + } + }, + { + "from": [2, 8.25, 5], + "to": [14, 8.25, 17], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 8.25, 11]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [0, 8, 4, 12], "texture": "#0"}, + "down": {"uv": [0, 12, 4, 8], "texture": "#0"} + } + }, + { + "from": [2, 6.75, 5], + "to": [14, 6.75, 17], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 6.75, 11]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [0, 12, 4, 16], "texture": "#0"}, + "down": {"uv": [0, 16, 4, 12], "texture": "#0"} + } + }, + { + "from": [-1, 6.75, 2], + "to": [11, 6.75, 14], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 6.75, 8]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [4, 16, 0, 12], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 16, 4, 12], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 6.75, 2], + "to": [17, 6.75, 14], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 6.75, 8]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [4, 12, 0, 16], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 12, 4, 16], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 8.25, 2], + "to": [17, 8.25, 14], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 8.25, 8]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [4, 8, 0, 12], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 8, 4, 12], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [-1, 8.25, 2], + "to": [11, 8.25, 14], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 8.25, 8]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [4, 12, 0, 8], "rotation": 270, "texture": "#0"}, + "down": {"uv": [0, 12, 4, 8], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [3.5, 3.75, 3.5], + "to": [12.5, 5.25, 12.5], + "rotation": {"angle": 0, "axis": "y", "origin": [4.25, 3.75, 4.25]}, + "faces": { + "north": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"}, + "east": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"}, + "south": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"}, + "west": {"uv": [10.5, 15.5, 13.5, 16], "texture": "#0"}, + "up": {"uv": [13.5, 12.5, 10.5, 9.5], "texture": "#0"}, + "down": {"uv": [13.5, 12.5, 10.5, 15.5], "texture": "#0"} + } + }, + { + "from": [4.25, 0, 4.25], + "to": [11.75, 3.75, 11.75], + "rotation": {"angle": 0, "axis": "y", "origin": [5, 0, 5]}, + "faces": { + "north": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"}, + "east": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"}, + "south": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"}, + "west": {"uv": [13.5, 14.75, 16, 16], "texture": "#0"}, + "up": {"uv": [16, 14.75, 13.5, 12.25], "texture": "#0"}, + "down": {"uv": [16, 9.75, 13.5, 12.25], "texture": "#0"} + } + }, + { + "from": [8, 0, 0.5], + "to": [8, 12, 15.5], + "rotation": {"angle": 45, "axis": "y", "origin": [8, 6, 8]}, + "faces": { + "north": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 4], "texture": "#0"}, + "south": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 4], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "down": {"uv": [0, 0, 0, 4], "texture": "#0"} + } + }, + { + "from": [8, 0, 0.5], + "to": [8, 12, 15.5], + "rotation": {"angle": -45, "axis": "y", "origin": [8, 6, 8]}, + "faces": { + "north": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 4], "texture": "#0"}, + "south": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 4], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "down": {"uv": [0, 0, 0, 4], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "ground": { + "translation": [0, 3.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [25, -40, 0], + "translation": [-0.25, 2, 0], + "scale": [0.9, 0.9, 0.9] + }, + "fixed": { + "rotation": [-90, 0, 0], + "translation": [0, 0, -16], + "scale": [2, 2, 2] + } + }, + "groups": [ + { + "name": "bone", + "origin": [-3, 0, -3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json new file mode 100644 index 000000000..b627c0756 --- /dev/null +++ b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json @@ -0,0 +1,233 @@ +{ + "format_version": "1.21.6", + "credit": "Made with Blockbench", + "textures": { + "0": "item/custom/flower_basket" + }, + "elements": [ + { + "from": [-1, 11.5625, 4.43375], + "to": [11, 11.5625, 13.43375], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 11.5625, 7.43375]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [3, 4, 0, 8], "rotation": 90, "texture": "#0"}, + "down": {"uv": [3, 8, 0, 4], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [-1, 10.0625, 4.43375], + "to": [11, 10.0625, 13.43375], + "rotation": {"angle": 45, "axis": "z", "origin": [5, 10.0625, 7.43375]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [6, 4, 3, 8], "rotation": 90, "texture": "#0"}, + "down": {"uv": [6, 8, 3, 4], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 11.5625, 4.43375], + "to": [17, 11.5625, 13.43375], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 11.5625, 7.43375]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [3, 8, 0, 4], "rotation": 90, "texture": "#0"}, + "down": {"uv": [3, 4, 0, 8], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 10.0625, 4.43375], + "to": [17, 10.0625, 13.43375], + "rotation": {"angle": -45, "axis": "z", "origin": [11, 10.0625, 7.43375]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [6, 8, 3, 4], "rotation": 90, "texture": "#0"}, + "down": {"uv": [6, 4, 3, 8], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [2, 10.0625, 4.43375], + "to": [14, 10.0625, 16.43375], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 10.0625, 10.43375]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [4, 12, 0, 16], "texture": "#0"}, + "down": {"uv": [0, 12, 4, 16], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [2, 10.0625, 1.43375], + "to": [14, 10.0625, 13.43375], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 10.0625, 7.43375]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [4, 16, 0, 12], "texture": "#0"}, + "down": {"uv": [0, 16, 4, 12], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [2, 11.5625, 2.18375], + "to": [14, 11.5625, 14.18375], + "rotation": {"angle": -45, "axis": "x", "origin": [8, 11.5625, 8.18375]}, + "faces": { + "north": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 4, 0], "texture": "#0"}, + "up": {"uv": [4, 12, 0, 8], "texture": "#0"}, + "down": {"uv": [0, 12, 4, 8], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [2, 11.5625, 3.68375], + "to": [14, 11.5625, 15.68375], + "rotation": {"angle": 45, "axis": "x", "origin": [8, 11.5625, 9.68375]}, + "faces": { + "north": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [4, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [4, 8, 0, 12], "texture": "#0"}, + "down": {"uv": [0, 8, 4, 12], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [3.5, 7.0625, 5.93375], + "to": [12.5, 8.5625, 11.93375], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]}, + "faces": { + "north": {"uv": [13.5, 15.5, 10.5, 16], "texture": "#0"}, + "east": {"uv": [9, 15.5, 7, 16], "texture": "#0"}, + "south": {"uv": [13.5, 15.5, 10.5, 16], "texture": "#0"}, + "west": {"uv": [9, 15.5, 7, 16], "texture": "#0"}, + "up": {"uv": [11.5, 6.5, 13.5, 3.5], "rotation": 90, "texture": "#0"}, + "down": {"uv": [11.5, 6.5, 13.5, 9.5], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [4.25, 3.3125, 6.68375], + "to": [11.75, 7.0625, 11.18375], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]}, + "faces": { + "north": {"uv": [16, 14.75, 13.5, 16], "texture": "#0"}, + "east": {"uv": [10.5, 14.75, 9, 16], "texture": "#0"}, + "south": {"uv": [16, 14.75, 13.5, 16], "texture": "#0"}, + "west": {"uv": [10.5, 14.75, 9, 16], "texture": "#0"}, + "up": {"uv": [8, 14.75, 10.5, 12.25], "rotation": 90, "texture": "#0"}, + "down": {"uv": [9, 9.75, 10.5, 12.25], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [4.1, 3.1625, 6.53375], + "to": [11.9, 7.2125, 11.33375], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 6.5, 7.43375]}, + "faces": { + "north": {"uv": [16, 8.5, 13.5, 9.75], "texture": "#0"}, + "east": {"uv": [16, 8.5, 14.5, 9.75], "texture": "#0"}, + "south": {"uv": [16, 8.5, 13.5, 9.75], "texture": "#0"}, + "west": {"uv": [14.5, 8.5, 16, 9.75], "texture": "#0"}, + "up": {"uv": [13.5, 8.5, 16, 6], "rotation": 90, "texture": "#0"}, + "down": {"uv": [15, 3.5, 13.5, 6], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [0.5, 3.3125, 8.93375], + "to": [15.5, 15.3125, 8.93375], + "rotation": {"angle": -22.5, "axis": "y", "origin": [8, 9.3125, 8.93375]}, + "faces": { + "north": {"uv": [4, 0, 0, 4], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 4], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 4], "rotation": 90, "texture": "#0"}, + "down": {"uv": [0, 0, 0, 4], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [0.5, 3.3125, 8.93375], + "to": [15.5, 15.3125, 8.93375], + "rotation": {"angle": 22.5, "axis": "y", "origin": [8, 9.3125, 8.93375]}, + "faces": { + "north": {"uv": [4, 0, 0, 4], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "south": {"uv": [0, 0, 4, 4], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 4], "texture": "#0"}, + "up": {"uv": [0, 0, 0, 4], "rotation": 90, "texture": "#0"}, + "down": {"uv": [0, 0, 0, 4], "rotation": 270, "texture": "#0"} + } + }, + { + "from": [5, 3.3125, 4.44125], + "to": [11, 5.5625, 6.69125], + "rotation": {"angle": 0, "axis": "y", "origin": [10.25, 3.3125, 5.93375]}, + "faces": { + "north": {"uv": [16, 0, 14, 0.75], "texture": "#0"}, + "east": {"uv": [14, 2.75, 13.25, 3.5], "texture": "#0"}, + "south": {"uv": [16, 0.75, 14, 1.5], "texture": "#0"}, + "west": {"uv": [13.25, 2.75, 14, 3.5], "texture": "#0"}, + "up": {"uv": [13.25, 0, 12.5, 2], "rotation": 90, "texture": "#0"}, + "down": {"uv": [14, 0, 13.25, 2], "rotation": 270, "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [60, -34, 0], + "translation": [-0.5, 3, 1], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "firstperson_lefthand": { + "rotation": [0, 45, 0], + "translation": [3.5, 0, 0] + }, + "ground": { + "translation": [0, 3.25, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [25, 135, 0], + "translation": [-0.25, 2, 0], + "scale": [0.9, 0.9, 0.9] + }, + "fixed": { + "rotation": [-90, 0, 0], + "translation": [0, 0, -16], + "scale": [2, 2, 2] + } + }, + "groups": [ + { + "name": "bone2", + "origin": [-5, 5.3125, -4.43375], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + } + ] +} \ No newline at end of file diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket.png new file mode 100644 index 000000000..d7a8f86ba Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket.png differ diff --git a/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket_2d.png b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket_2d.png new file mode 100644 index 000000000..7430df69f Binary files /dev/null and b/common-files/src/main/resources/resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket_2d.png differ diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 863854368..8f505365b 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -49,11 +49,11 @@ command.item.get.failure.not_exist: "':'':''>" command.item.give.success.multiple: "':'':''>" command.item.give.failure.not_exist: "'>" -command.search_recipe.not_found: "Kein Rezept für diesen Gegenstand gefunden" -command.search_usage.not_found: "Keine Verwendung für diesen Gegenstand gefunden" -command.search_recipe.no_item: "Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen" -command.search_usage.no_item: "Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen" -command.totem_animation.failure.not_totem: "Gegenstand '' ist kein minecraft:totem_of_undying" +command.search_recipe.not_found: "Kein Rezept für diesen Item gefunden" +command.search_usage.not_found: "Keine Verwendung für dieses Item gefunden" +command.search_recipe.no_item: "Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen" +command.search_usage.no_item: "Bitte halten Sie ein Item, bevor Sie diesen Befehl ausführen" +command.totem_animation.failure.not_totem: "Item '' ist kein minecraft:totem_of_undying" command.resource.enable.success: "Ressource aktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" command.resource.enable.failure.unknown: "Unbekannte Ressource " command.resource.disable.success: "Ressource deaktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" @@ -65,6 +65,7 @@ command.send_resource_pack.success.single: "Ressourcenpaket an ge command.send_resource_pack.success.multiple: "Ressourcenpakete an Spieler gesendet." warning.config.pack.duplicated_files: "Duplizierte Dateien gefunden. Bitte beheben Sie diese im Abschnitt 'resource-pack.duplicated-files-handler' in config.yml." warning.config.yaml.duplicated_key: "Problem in Datei gefunden - Duplizierter Schlüssel '' in Zeile gefunden, dies kann zu unerwarteten Ergebnissen führen." +warning.config.yaml.inconsistent_value_type: "Fehler in der Datei - Der duplizierte Schlüssel '' wurde in Zeile mit einem anderen Werttyp gefunden. Dies könnte zu unerwarteten Ergebnissen führen." warning.config.type.int: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Integer-Typ für Option '' umwandeln." warning.config.type.float: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Float-Typ für Option '' umwandeln." warning.config.type.double: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Double-Typ für Option '' umwandeln." @@ -154,72 +155,72 @@ warning.config.furniture.element.missing_item: "Problem in Datei warning.config.furniture.settings.unknown: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen unbekannten Einstellungstyp ''." warning.config.furniture.hitbox.invalid_type: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen ungültigen Hitbox-Typ ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei gefunden - Das Möbelstück '' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entitätstyp ''." -warning.config.item.duplicate: "Problem in Datei gefunden - Duplizierter Gegenstand ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." -warning.config.item.settings.unknown: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen unbekannten Einstellungstyp ''." -warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine unbekannte Schadensquelle ''. Erlaubte Quellen: []." -warning.config.item.settings.equippable.missing_slot: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung." -warning.config.item.missing_material: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'material'-Argument." -warning.config.item.invalid_material: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Materialtyp ''." -warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Der Gegenstand '' verwendet negative benutzerdefinierte Modelldaten '', die ungültig sind." -warning.config.item.bad_custom_model_data: "Problem in Datei gefunden - Der Gegenstand '' verwendet benutzerdefinierte Modelldaten '', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden." -warning.config.item.custom_model_data_conflict: "Problem in Datei gefunden - Der Gegenstand '' verwendet benutzerdefinierte Modelldaten '', die bereits von Gegenstand '' belegt sind." -warning.config.item.invalid_component: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen nicht existierenden Komponententyp ''." -warning.config.item.missing_model_id: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument." -warning.config.item.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen." -warning.config.item.behavior.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für sein Gegenstandsverhalten." -warning.config.item.behavior.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Gegenstandsverhaltenstyp ''." -warning.config.item.behavior.block.missing_block: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten." -warning.config.item.behavior.furniture.missing_furniture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten." -warning.config.item.behavior.liquid_collision.missing_block: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten." -warning.config.item.legacy_model.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Legacy-Modell." -warning.config.item.legacy_model.overrides.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen." -warning.config.item.legacy_model.overrides.missing_predicate: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen." -warning.config.item.legacy_model.cannot_convert: "Problem in Datei gefunden - Kann 1.21.4+-Gegenstände für Gegenstand '' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Gegenstand manuell." -warning.config.item.model.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Modelltyp ''." -warning.config.item.model.tint.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für die Tönung." -warning.config.item.model.tint.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Tönungstyp ''." -warning.config.item.model.tint.constant.missing_value: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'value'-Argument für die konstante Tönung." -warning.config.item.model.tint.grass.invalid_temp: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Temperatur '' für die Grastönung, die zwischen 0 und 1 liegen sollte." -warning.config.item.model.tint.grass.invalid_downfall: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Niederschlag '' für die Grastönung, der zwischen 0 und 1 liegen sollte." -warning.config.item.model.tint.invalid_value: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Tönung ''." -warning.config.item.model.base.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'." -warning.config.item.model.base.invalid_path: "Problem in Datei gefunden - Der Gegenstand '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.item.model.condition.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:condition'." -warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.keybind.missing_keybind: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." -warning.config.item.model.condition.component.missing_predicate: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.condition.component.missing_value: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'." -warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'." -warning.config.item.model.range_dispatch.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:range_dispatch'." -warning.config.item.model.range_dispatch.missing_entries: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.entry.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'." -warning.config.item.model.range_dispatch.compass.missing_target: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'." -warning.config.item.model.range_dispatch.time.missing_source: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'." -warning.config.item.model.select.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'." -warning.config.item.model.select.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:select'." -warning.config.item.model.select.missing_cases: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'." -warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'." -warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'." -warning.config.item.model.select.component.missing_component: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'." -warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'." -warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'." -warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'." -warning.config.item.model.special.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'." -warning.config.item.model.special.invalid_path: "Problem in Datei gefunden - Der Gegenstand '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." -warning.config.item.model.special.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Typ '' für das Modell 'minecraft:special'." -warning.config.item.model.special.banner.missing_color: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'." -warning.config.item.model.special.bed.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'." -warning.config.item.model.special.sign.missing_wood_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." -warning.config.item.model.special.sign.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." -warning.config.item.model.special.chest.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'." -warning.config.item.model.special.chest.invalid_openness: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'" -warning.config.item.model.special.shulker_box.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'." -warning.config.item.model.special.shulker_box.invalid_openness: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'" -warning.config.item.model.special.head.missing_kind: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'." +warning.config.item.duplicate: "Problem in Datei gefunden - Dupliziertes Item ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.item.settings.unknown: "Problem in Datei gefunden - Das Item '' verwendet einen unbekannten Einstellungstyp ''." +warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Das Item '' verwendet eine unbekannte Schadensquelle ''. Erlaubte Quellen: []." +warning.config.item.settings.equippable.missing_slot: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung." +warning.config.item.missing_material: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'material'-Argument." +warning.config.item.invalid_material: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Materialtyp ''." +warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet negative benutzerdefinierte Modelldaten '', die ungültig sind." +warning.config.item.bad_custom_model_data: "Problem in Datei gefunden - Das Item '' verwendet benutzerdefinierte Modelldaten '', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden." +warning.config.item.custom_model_data_conflict: "Problem in Datei gefunden - Das Item '' verwendet benutzerdefinierte Modelldaten '', die bereits von Item '' belegt sind." +warning.config.item.invalid_component: "Problem in Datei gefunden - Das Item '' verwendet einen nicht existierenden Komponententyp ''." +warning.config.item.missing_model_id: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument." +warning.config.item.missing_model: "Problem in Datei gefunden - Das Item '' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen." +warning.config.item.behavior.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für sein Itemsverhalten." +warning.config.item.behavior.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Itemsverhaltenstyp ''." +warning.config.item.behavior.block.missing_block: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten." +warning.config.item.behavior.furniture.missing_furniture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten." +warning.config.item.behavior.liquid_collision.missing_block: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten." +warning.config.item.legacy_model.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Legacy-Modell." +warning.config.item.legacy_model.overrides.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen." +warning.config.item.legacy_model.overrides.missing_predicate: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen." +warning.config.item.legacy_model.cannot_convert: "Problem in Datei gefunden - Kann 1.21.4+-Gegenstände für Item '' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Item manuell." +warning.config.item.model.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Modelltyp ''." +warning.config.item.model.tint.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für die Tönung." +warning.config.item.model.tint.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Tönungstyp ''." +warning.config.item.model.tint.constant.missing_value: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'value'-Argument für die konstante Tönung." +warning.config.item.model.tint.grass.invalid_temp: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Temperatur '' für die Grastönung, die zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.grass.invalid_downfall: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Niederschlag '' für die Grastönung, der zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.invalid_value: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Tönung ''." +warning.config.item.model.base.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'." +warning.config.item.model.base.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.condition.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:condition'." +warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.keybind.missing_keybind: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." +warning.config.item.model.condition.component.missing_predicate: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'." +warning.config.item.model.condition.component.missing_value: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'." +warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'." +warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.missing_entries: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.entry.missing_model: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.compass.missing_target: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'." +warning.config.item.model.range_dispatch.time.missing_source: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'." +warning.config.item.model.select.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'." +warning.config.item.model.select.invalid_property: "Problem in Datei gefunden - Das Item '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:select'." +warning.config.item.model.select.missing_cases: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'." +warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'." +warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'." +warning.config.item.model.select.component.missing_component: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'." +warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'." +warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'." +warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'." +warning.config.item.model.special.missing_path: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'." +warning.config.item.model.special.invalid_path: "Problem in Datei gefunden - Das Item '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.special.invalid_type: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen Typ '' für das Modell 'minecraft:special'." +warning.config.item.model.special.banner.missing_color: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'." +warning.config.item.model.special.bed.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'." +warning.config.item.model.special.sign.missing_wood_type: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.sign.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.chest.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'." +warning.config.item.model.special.chest.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.shulker_box.missing_texture: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'." +warning.config.item.model.special.shulker_box.invalid_openness: "Problem in Datei gefunden - Das Item '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.head.missing_kind: "Problem in Datei gefunden - Das Item '' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'." warning.config.block.duplicate: "Problem in Datei gefunden - Duplizierter Block ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." warning.config.block.missing_state: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'state'-Argument." warning.config.block.state.property.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'type'-Argument für die Eigenschaft ''." @@ -378,13 +379,41 @@ warning.config.function.remove_cooldown.missing_id: "Problem in Datei Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für den Selektor." warning.config.selector.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Selektortyp ''." warning.config.selector.invalid_target: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Selektorziel ''." -warning.config.resource_pack.item_model.conflict.vanilla: "Fehler beim Generieren des Gegenstandsmodells für '' da dieses Gegenstandsmodell von einem Vanilla-Gegenstand belegt wurde." -warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Gegenstandsmodells für '' da die Datei '' bereits existiert." +warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Itemsmodells für '' da die Datei '' bereits existiert." warning.config.resource_pack.model.generation.already_exist: "Fehler beim Generieren des Modells, da die Modelldatei '' bereits existiert." warning.config.resource_pack.generation.missing_font_texture: "Schriftart '' fehlt die erforderliche Textur: ''" warning.config.resource_pack.generation.missing_model_texture: "Modell '' fehlt Textur ''" -warning.config.resource_pack.generation.missing_item_model: "Gegenstand '' fehlt Modelldatei: ''" +warning.config.resource_pack.generation.missing_item_model: "Item '' fehlt Modelldatei: ''" warning.config.resource_pack.generation.missing_block_model: "Block '' fehlt Modelldatei: ''" warning.config.resource_pack.generation.missing_parent_model: "Modell '' kann das Elternmodell nicht finden: ''" warning.config.resource_pack.generation.malformatted_json: "Json-Datei '' ist fehlerhaft formatiert." warning.config.resource_pack.invalid_overlay_format: "Problem in config.yml gefunden - Ungültiges Overlay-Format '' im Abschnitt 'resource-pack'. Unterstützte Overlays: []" +warning.config.type.map: "Fehler in der Datei - Das Laden von '' ist fehlgeschlagen: Kann den Typ '' nicht in den Typ 'Map' für die Option '' umwandeln." +warning.config.recipe.missing_pattern: "Fehler in der Datei - Dem geformten Rezept '' fehlt das erforderliche Argument 'pattern'." +warning.config.recipe.too_large_pattern: "Fehler in der Datei - Das geformte Rezept '' hat ein Muster mit mehr als 3 Zeilen oder Spalten." +warning.config.recipe.mismatched_shaped_pattern: "Fehler in der Datei - Das geformte Rezept '' hat ein fehlerhaftes Muster und Zutaten." +warning.config.recipe.mismatched_shulker_box: "Fehler in der Datei - Das Shulker-Box-Rezept '' hat ein fehlerhaftes Muster und Zutaten." +warning.config.recipe.mismatched_unshaped_pattern: "Fehler in der Datei - Das ungeformte Rezept '' hat ein fehlerhaftes Muster und Zutaten." +warning.config.recipe.missing_category: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'category'-Argument." +warning.config.recipe.missing_experience: "Fehler in der Datei - Dem Kochrezept '' fehlt das erforderliche 'experience'-Argument." +warning.config.recipe.missing_cooking_time: "Fehler in der Datei - Dem Kochrezept '' fehlt das erforderliche 'cooking-time'-Argument." +warning.config.recipe.missing_group: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'group'-Argument." +warning.config.recipe.missing_book: "Fehler in der Datei - Dem Rezept '' fehlt das erforderliche 'book'-Argument." +warning.config.loot_table.duplicate: "Fehler in der Datei - Doppelte Loot-Tabelle ''. Bitte prüfen Sie, ob in anderen Dateien die gleiche Konfiguration vorhanden ist." +warning.config.loot_table.missing_type: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.invalid_type: "Fehler in der Datei - Die Loot-Tabelle '' verwendet einen ungültigen Loot-Tabellen-Typ ''." +warning.config.loot_table.missing_rolls: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'rolls'-Argument." +warning.config.loot_table.missing_entries: "Fehler in der Datei - Der Loot-Tabelle '' fehlt das erforderliche 'entries'-Argument." +warning.config.loot_table.missing_entry_type: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'type'-Argument." +warning.config.loot_table.invalid_entry_type: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' verwendet einen ungültigen Typ ''." +warning.config.loot_table.missing_entry_name: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'name'-Argument." +warning.config.loot_table.missing_entry_weight: "Fehler in der Datei - Einer der Loot-Tabellen-Einträge in '' hat kein erforderliches 'weight'-Argument." +warning.config.loot_table.missing_function_type: "Fehler in der Datei - Eine der Loot-Tabellen-Funktionen in '' hat kein erforderliches 'type'-Argument." +warning.config.loot_table.invalid_function_type: "Fehler in der Datei - Eine der Loot-Tabellen-Funktionen in '' verwendet einen ungültigen Typ ''." +warning.config.loot_table.missing_condition_type: "Fehler in der Datei - Eine der Loot-Tabellen-Bedingungen in '' hat kein erforderliches 'type'-Argument." +warning.config.loot_table.invalid_condition_type: "Fehler in der Datei - Eine der Loot-Tabellen-Bedingungen in '' verwendet einen ungültigen Typ ''." +warning.config.resource_pack.generation.missing_equipment_texture: "Ausrüstung '' hat keine Textur ''" +warning.config.equipment.duplicate: "Problem in der Datei gefunden – Duplizierte Ausrüstung ''. Bitte prüfe, ob es dieselbe Konfiguration in anderen Dateien gibt." +warning.config.equipment.missing_type: "Problem in der Datei gefunden – Der Ausrüstung '' fehlt das erforderliche Argument 'type'." +warning.config.equipment.invalid_type: "Problem in der Datei gefunden – Die Ausrüstung '' verwendet ein ungültiges Argument 'type'." +warning.config.equipment.invalid_sacrificed_armor: "Problem in config.yml bei 'equipment.sacrificed-vanilla-armor' gefunden – Ungültiger Vanille-Rüstungstyp ''." \ No newline at end of file diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 56a24d8f5..8a428d817 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -72,6 +72,7 @@ warning.config.type.float: "Issue found in file - Failed to load warning.config.type.double: "Issue found in file - Failed to load '': Cannot cast '' to double type for option ''." warning.config.type.quaternionf: "Issue found in file - Failed to load '': Cannot cast '' to Quaternionf type for option ''." warning.config.type.vector3f: "Issue found in file - Failed to load '': Cannot cast '' to Vector3f type for option ''." +warning.config.type.map: "Issue found in file - Failed to load '': Cannot cast '' to Map type for option ''." warning.config.type.snbt.invalid_syntax: "Issue found in file - Failed to load '': Invalid snbt syntax ''." warning.config.number.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for number argument." warning.config.number.invalid_type: "Issue found in file - The config '' is using an invalid number argument type ''." @@ -142,6 +143,8 @@ warning.config.recipe.smithing_trim.missing_addition: "Issue found in fi warning.config.recipe.smithing_trim.missing_pattern: "Issue found in file - The smithing trim recipe '' is missing the required 'pattern' argument." warning.config.recipe.brewing.missing_container: "Issue found in file - The brewing recipe '' is missing the required 'container' argument." warning.config.recipe.brewing.missing_ingredient: "Issue found in file - The brewing recipe '' is missing the required 'ingredient' argument." +warning.config.recipe.result.post_processor.missing_type: "Issue found in file - The recipe '' is missing the required 'type' argument for result post processors." +warning.config.recipe.result.post_processor.invalid_type: "Issue found in file - The recipe '' is using an invalid result post processor type ''." warning.config.i18n.unknown_locale: "Issue found in file - Unknown locale ''." warning.config.template.duplicate: "Issue found in file - Duplicated template ''. Please check if there is the same configuration in other files." warning.config.template.invalid: "Issue found in file - The config '' is using an invalid template ''." @@ -173,6 +176,9 @@ warning.config.item.data.attribute_modifiers.missing_amount: "Issue foun warning.config.item.data.attribute_modifiers.missing_operation: "Issue found in file - The item '' is missing the required 'operation' argument for 'attribute-modifiers' data." warning.config.item.data.attribute_modifiers.display.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for 'attribute-modifiers' display data." warning.config.item.data.attribute_modifiers.display.missing_value: "Issue found in file - The item '' is missing the required 'value' argument for 'attribute-modifiers' display data." +warning.config.item.data.external.missing_source: "Issue found in file - The item '' is missing the required 'source' argument for 'external' data." +warning.config.item.data.external.missing_id: "Issue found in file - The item '' is missing the required 'id' argument for 'external' data." +warning.config.item.data.external.invalid_source: "Issue found in file - The item '' is using an invalid item source '' for 'external' data." warning.config.item.missing_material: "Issue found in file - The item '' is missing the required 'material' argument." warning.config.item.invalid_material: "Issue found in file - The item '' is using an invalid material type ''." warning.config.item.invalid_custom_model_data: "Issue found in file - The item '' is using a negative custom model data '' which is invalid." @@ -187,6 +193,7 @@ warning.config.item.behavior.invalid_type: "Issue found in file warning.config.item.behavior.block.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'block_item' behavior." warning.config.item.behavior.furniture.missing_furniture: "Issue found in file - The item '' is missing the required 'furniture' argument for 'furniture_item' behavior." warning.config.item.behavior.liquid_collision.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'liquid_collision_block_item' behavior." +warning.config.item.behavior.double_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'double_high_block_item' behavior." warning.config.item.legacy_model.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model." warning.config.item.legacy_model.overrides.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model overrides." warning.config.item.legacy_model.overrides.missing_predicate: "Issue found in file - The item '' is missing the require 'predicate' argument for legacy-model overrides." @@ -291,7 +298,7 @@ warning.config.block.behavior.stairs.missing_half: "Issue found in file warning.config.block.behavior.stairs.missing_shape: "Issue found in file - The block '' is missing the required 'shape' property for 'stairs_block' behavior." warning.config.block.behavior.pressure_plate.missing_powered: "Issue found in file - The block '' is missing the required 'powered' property for 'pressure_plate_block' behavior." warning.config.block.behavior.grass.missing_feature: "Issue found in file - The block '' is missing the required 'feature' argument for 'grass_block' behavior." -warning.config.block.behavior.double.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'double_block' behavior." +warning.config.block.behavior.double_high.missing_half: "Issue found in file - The block '' is missing the required 'half' property for 'double_block' behavior." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" warning.config.model.generation.invalid_gui_light: "Issue found in file - The config '' is using an invalid gui-light option '' in 'generation' section. Allowed gui light options: []" @@ -393,6 +400,7 @@ warning.config.function.potion_effect.missing_potion_effect: "Issue foun warning.config.function.set_cooldown.missing_time: "Issue found in file - The config '' is missing the required 'time' argument for 'set_cooldown' function." warning.config.function.set_cooldown.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'set_cooldown' function." warning.config.function.remove_cooldown.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'remove_cooldown' function." +warning.config.function.mythic_mobs_skill.missing_skill: "Issue found in file - The config '' is missing the required 'skill' argument for 'mythic_mobs_skill' function." warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector." warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''." warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 11b6758f2..3512ddf1b 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -59,20 +59,21 @@ command.resource.enable.failure.unknown: "未知资源 " command.resource.disable.success: "已禁用 . 执行 /ce reload all 以应用更改" command.resource.disable.failure.unknown: "未知资源 " command.resource.list: "启用的资源(): 禁用的资源(): " -command.upload.failure.not_supported: "当前托管模式 '' 不支持上传资源包." -command.upload.on_progress: "已开始上传进程. 检查控制台以获取详细信息." +command.upload.failure.not_supported: "当前托管模式 '' 不支持上传资源包" +command.upload.on_progress: "已开始上传进程. 检查控制台以获取详细信息" command.send_resource_pack.success.single: "发送资源包给 " command.send_resource_pack.success.multiple: "发送资源包给 个玩家" warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" -warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题." -warning.config.yaml.inconsistent_value_type: "在文件 发现问题 - 在第行发现重复且值类型不同的键 '', 这可能会导致一些意料之外的问题." +warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题" +warning.config.yaml.inconsistent_value_type: "在文件 发现问题 - 在第行发现重复且值类型不同的键 '', 这可能会导致一些意料之外的问题" warning.config.type.int: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为整数类型 (选项 '')" -warning.config.type.float: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为浮点数类型 (选项 '')" warning.config.type.boolean: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为布尔类型 (选项 '')" +warning.config.type.float: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为浮点数类型 (选项 '')" warning.config.type.double: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度类型 (选项 '')" warning.config.type.quaternionf: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为四元数类型 (选项 '')" warning.config.type.vector3f: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为三维向量类型 (选项 '')" -warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''." +warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" +warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" warning.config.number.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的数字类型 ''" warning.config.number.missing_argument: "在文件 发现问题 - 配置项 '' 缺少数字参数" @@ -102,11 +103,11 @@ warning.config.condition.string_contains.missing_value2: "在文件 在文件 中发现问题 - 配置项 '' 缺少 'string_regex' 条件必需的 'value' 参数" warning.config.condition.string_regex.missing_regex: "在文件 中发现问题 - 配置项 '' 缺少 'string_regex' 条件必需的 'regex' 参数" warning.config.condition.expression.missing_expression: "在文件 中发现问题 - 配置项 '' 缺少 'expression' 条件必需的 'expression' 参数" -warning.config.condition.is_null.missing_argument: "在文件 发现问题 - 配置项 '' 缺少 'is_null' 条件的必需的 'argument' 参数." -warning.config.structure.not_section: "在文件 发现问题 - 配置项 '' 应为配置段落 但实际类型为 ''" +warning.config.condition.is_null.missing_argument: "在文件 发现问题 - 配置项 '' 缺少 'is_null' 条件的必需的 'argument' 参数" warning.config.condition.hand.missing_hand: "在文件 发现问题 - 配置项 '' 缺少 'hand' 条件必需的 'hand' 参数" warning.config.condition.hand.invalid_hand: "在文件 发现问题 - 配置项 '' 使用了无效的 'hand' 参数 ''('hand' 条件)。允许的手部类型: []" warning.config.condition.on_cooldown.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'on_cooldown' 条件必需的 'id' 参数" +warning.config.structure.not_section: "在文件 发现问题 - 配置项 '' 应为配置段落 但实际类型为 ''" warning.config.image.duplicate: "在文件 发现问题 - 重复的图片配置 '' 请检查其他文件中是否存在相同配置" warning.config.image.missing_height: "在文件 发现问题 - 图片 '' 缺少必需的 'height' 参数" warning.config.image.height_ascent_conflict: "在文件 发现问题 - 图片 '' 违反位图规则: 'height' 参数 '' 必须不小于 'ascent' 参数 ''" @@ -140,12 +141,16 @@ warning.config.recipe.smithing_trim.missing_base: "在文件 发 warning.config.recipe.smithing_trim.missing_template_type: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'template-type' 参数" warning.config.recipe.smithing_trim.missing_addition: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'addition' 参数" warning.config.recipe.smithing_trim.missing_pattern: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'pattern' 参数" +warning.config.recipe.brewing.missing_container: "在文件 发现问题 - 酿造配方 '' 缺少必需的 'container' 参数" +warning.config.recipe.brewing.missing_ingredient: "在文件 发现问题 - 酿造配方 '' 缺少必需的 'ingredient' 参数" +warning.config.recipe.result.post_processor.missing_type: "在文件 发现问题 - 配方 '' 缺少结果后处理器必需的 'type' 参数" +warning.config.recipe.result.post_processor.invalid_type: "在文件 发现问题 - 配方 '' 使用了无效结果后处理器类型 ''" warning.config.i18n.unknown_locale: "在文件 发现问题 - 未知的语言环境 ''" warning.config.template.duplicate: "在文件 发现问题 - 重复的模板 '' 请检查其他文件中是否存在相同配置" +warning.config.template.invalid: "在文件 发现问题 - 配置 '' 使用了无效的模板 ''" warning.config.template.argument.self_increase_int.invalid_range: "在文件 发现问题 - 模板 '' 在 'self_increase_int' 参数中使用了一个起始值 '' 大于终止值 ''" -warning.config.template.invalid: "在文件 发现问题 - 配置 '' 使用了无效的模板 ''." warning.config.template.argument.list.invalid_type: "在文件 发现问题 - 模板 '' 的 'list' 参数需要列表类型 但输入参数类型为 ''" -warning.config.template.argument.missing_value: "在文件 发现问题 - 配置 '' 缺少了 '' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值." +warning.config.template.argument.missing_value: "在文件 发现问题 - 配置 '' 缺少了 '' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值" warning.config.vanilla_loot.missing_type: "在文件 发现问题 - 原版战利品 '' 缺少必需的 'type' 参数" warning.config.vanilla_loot.invalid_type: "在文件 发现问题 - 原版战利品 '' 使用了无效类型 '' 允许的类型: []" warning.config.vanilla_loot.block.invalid_target: "在文件 发现问题 - 原版战利品 '' 中存在无效的方块目标 ''" @@ -163,21 +168,24 @@ warning.config.furniture.hitbox.custom.invalid_entity: "在文件 在文件 发现问题 - 重复的物品 '' 请检查其他文件中是否存在相同配置" warning.config.item.settings.unknown: "在文件 发现问题 - 物品 '' 使用了未知的设置类型 ''" warning.config.item.settings.invulnerable.invalid_damage_source: "在文件 发现问题 - 物品 '' 物品使用了未知的伤害来源类型 '' 允许的来源: []" -warning.config.item.settings.equipment.missing_asset_id: "在文件 发现问题 - 物品 '' 缺少 'equipment' 设置所需的 'asset-id' 参数." -warning.config.item.settings.equipment.invalid_asset_id: "在文件 发现问题 - 物品 '' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id." -warning.config.item.settings.projectile.missing_item: "在文件 发现问题 - 物品 '' 缺少 'projectile' 设置所需的 'item' 参数." -warning.config.item.data.attribute_modifiers.missing_type: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'type' 参数." -warning.config.item.data.attribute_modifiers.missing_amount: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'amount' 参数." -warning.config.item.data.attribute_modifiers.missing_operation: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'operation' 参数." -warning.config.item.data.attribute_modifiers.display.missing_type: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 显示数据所需的 'type' 参数。" -warning.config.item.data.attribute_modifiers.display.missing_value: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 显示数据所需的 'value' 参数。" +warning.config.item.settings.equipment.missing_asset_id: "在文件 发现问题 - 物品 '' 缺少 'equipment' 设置所需的 'asset-id' 参数" +warning.config.item.settings.equipment.invalid_asset_id: "在文件 发现问题 - 物品 '' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id" +warning.config.item.settings.projectile.missing_item: "在文件 发现问题 - 物品 '' 缺少 'projectile' 设置所需的 'item' 参数" +warning.config.item.data.attribute_modifiers.missing_type: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'type' 参数" +warning.config.item.data.attribute_modifiers.missing_amount: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'amount' 参数" +warning.config.item.data.attribute_modifiers.missing_operation: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 数据所需的 'operation' 参数" +warning.config.item.data.attribute_modifiers.display.missing_type: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 显示数据所需的 'type' 参数" +warning.config.item.data.attribute_modifiers.display.missing_value: "在文件 发现问题 - 物品 '' 缺少 'attribute-modifiers' 显示数据所需的 'value' 参数" +warning.config.item.data.external.missing_source: "在文件 发现问题 - 物品 '' 缺少 'external' 数据所需的 'source' 参数" +warning.config.item.data.external.missing_id: "在文件 发现问题 - 物品 '' 缺少 'external' 数据所需的 'id' 参数" +warning.config.item.data.external.invalid_source: "在文件 发现问题 - 物品 '' 在 'external' 数据中使用了无效的物品来源 ''" warning.config.item.missing_material: "在文件 发现问题 - 物品 '' 缺少必需的 'material' 参数" warning.config.item.invalid_material: "在文件 发现问题 - 物品 '' 使用了无效的材料类型 ''" -warning.config.item.invalid_custom_model_data: "在文件 发现问题 - 物品 '' 使用了无效的负数模型值 ''." +warning.config.item.invalid_custom_model_data: "在文件 发现问题 - 物品 '' 使用了无效的负数模型值 ''" warning.config.item.bad_custom_model_data: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 数值过大 建议使用小于 16,777,216 的值" +warning.config.item.item_model.conflict: "在文件 发现问题 - 物品 '' 使用了无效的 'item-model' 选项. 这个 item-model 已经存在对应的原版物品" warning.config.item.custom_model_data_conflict: "在文件 发现问题 - 物品 '' 使用的自定义模型数据 '' 已被物品 '' 占用" warning.config.item.invalid_component: "在文件 发现问题 - 物品 '' 使用了未知的数据组件 ''" -warning.config.item.item_model.conflict: "在文件 发现问题 - 物品 '' 使用了无效的 'item-model' 选项. 这个 item-model 已经存在对应的原版物品." warning.config.item.missing_model_id: "在文件 发现问题 - 物品 '' 缺少必需的 'custom-model-data' 或 'item-model' 参数" warning.config.item.missing_model: "在文件 中发现问题 - 物品 '' 缺少支持 1.21.4+ 资源包必需的 'model' 配置项" warning.config.item.behavior.missing_type: "在文件 发现问题 - 物品 '' 的行为配置缺少必需的 'type' 参数" @@ -185,6 +193,7 @@ warning.config.item.behavior.invalid_type: "在文件 发现问 warning.config.item.behavior.block.missing_block: "在文件 发现问题 - 物品 '' 的 'block_item' 行为缺少必需的 'block' 参数" warning.config.item.behavior.furniture.missing_furniture: "在文件 发现问题 - 物品 '' 的 'furniture_item' 行为缺少必需的 'furniture' 参数" warning.config.item.behavior.liquid_collision.missing_block: "在文件 发现问题 - 物品 '' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数" +warning.config.item.behavior.double_high.missing_block: "在文件 发现问题 - 物品 '' 的 'double_high_block_item' 行为缺少必需的 'block' 参数" warning.config.item.legacy_model.missing_path: "在文件 中发现问题 - 物品 '' 的旧版模型(legacy-model)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_path: "在文件 中发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_predicate: "在文件 中发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'predicate' 参数" @@ -204,8 +213,8 @@ warning.config.item.model.condition.missing_on_true: "在文件 warning.config.item.model.condition.missing_on_false: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'on-false' 参数" warning.config.item.model.condition.keybind.missing_keybind: "在文件 发现问题 - 物品 '' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数" warning.config.item.model.condition.has_component.missing_component: "在文件 发现问题 - 物品 '' 的 'minecraft:has_component' 属性缺少必需的 'component' 参数" -warning.config.item.model.condition.component.missing_predicate: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'predicate' 参数." -warning.config.item.model.condition.component.missing_value: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'value' 参数." +warning.config.item.model.condition.component.missing_predicate: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'predicate' 参数" +warning.config.item.model.condition.component.missing_value: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'value' 参数/yellow>" warning.config.item.model.composite.missing_models: "在文件 发现问题 - 物品 '' 的 'minecraft:composite' 模型缺少必需的 'models' 参数" warning.config.item.model.range_dispatch.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型缺少必需的 'property' 参数" warning.config.item.model.range_dispatch.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型使用了无效属性 ''" @@ -239,7 +248,7 @@ warning.config.block.missing_state: "在文件 发现问题 - warning.config.block.state.property.missing_type: "在文件 发现问题 - 方块 '' 的属性 '' 缺少必需的 'type' 参数" warning.config.block.state.property.invalid_type: "在文件 发现问题 - 方块 '' 的属性 '' 使用了无效的类型参数 ''" warning.config.block.state.property.integer.invalid_range: "在文件 发现问题 - 方块 '' 的整数属性 '' 使用了无效的范围参数 '' 正确语法: 1~2" -warning.config.block.state.property.invalid_format: "在文件 发现问题 - 方块 '' 使用了无效的方块状态属性格式 ''." +warning.config.block.state.property.invalid_format: "在文件 发现问题 - 方块 '' 使用了无效的方块状态属性格式 ''" warning.config.block.state.missing_real_id: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'id' 参数 该 ID 是服务端方块 ID 用于唯一标识每种方块状态类型" warning.config.block.state.missing_state: "在文件 发现问题 - 方块 '' 的 'state' 缺少必需的 'state' 参数" warning.config.block.state.missing_properties: "在文件 发现问题 - 方块 '' 的 'states' 缺少必需的 'properties' 段落" @@ -289,7 +298,7 @@ warning.config.block.behavior.stairs.missing_half: "在文件 warning.config.block.behavior.stairs.missing_shape: "在文件 发现问题 - 方块 '' 的 'stairs_block' 行为缺少必需的 'shape' 属性" warning.config.block.behavior.pressure_plate.missing_powered: "在文件 发现问题 - 方块 '' 的 'pressure_plate_block' 行为缺少必需的 'powered' 属性" warning.config.block.behavior.grass.missing_feature: "在文件 发现问题 - 方块 '' 的 'grass_block' 行为缺少必需的 'feature' 参数" -warning.config.block.behavior.double.missing_half: "在文件 发现问题 - 方块 '' 的 'double_block' 行为缺少必需的 'half' 属性" +warning.config.block.behavior.double_high.missing_half: "在文件 发现问题 - 方块 '' 的 'double_block' 行为缺少必需的 'half' 属性" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" @@ -371,9 +380,9 @@ warning.config.function.command.missing_command: "在文件 中 warning.config.function.actionbar.missing_actionbar: "在文件 中发现问题 - 配置项 '' 缺少 'actionbar' 函数必需的 'actionbar' 参数" warning.config.function.message.missing_message: "在文件 中发现问题 - 配置项 '' 缺少 'message' 函数必需的 'message' 参数" warning.config.function.open_window.missing_gui_type: "在文件 中发现问题 - 配置项 '' 缺少 'open_window' 函数必需的 'gui-type' 参数" -warning.config.function.open_window.invalid_gui_type: "在文件 中发现问题 - 配置项 '' 为 'open_window' 函数使用了无效的 GUI 类型 . 允许的类型: []。" +warning.config.function.open_window.invalid_gui_type: "在文件 中发现问题 - 配置项 '' 为 'open_window' 函数使用了无效的 GUI 类型 . 允许的类型: []" warning.config.function.run.missing_functions: "在文件 中发现问题 - 配置项 '' 缺少 'run' 函数必需的 'functions' 参数" -warning.config.function.place_block.missing_block_state: "在文件 中发现问题 - 配置项 '' 缺少 'place_block' 函数必需的 'block-state' 参数." +warning.config.function.place_block.missing_block_state: "在文件 中发现问题 - 配置项 '' 缺少 'place_block' 函数必需的 'block-state' 参数" warning.config.function.set_food.missing_food: "在文件 中发现问题 - 配置项 '' 缺少 'set_food' 函数必需的 'food' 参数" warning.config.function.set_saturation.missing_saturation: "在文件 中发现问题 - 配置项 '' 缺少 'set_saturation' 函数必需的 'saturation' 参数" warning.config.function.play_sound.missing_sound: "在文件 中发现问题 - 配置项 '' 缺少 'play_sound' 函数必需的 'sound' 参数" @@ -391,6 +400,7 @@ warning.config.function.potion_effect.missing_potion_effect: "在文件 warning.config.function.set_cooldown.missing_time: "在文件 中发现问题 - 配置项 '' 缺少 'set_cooldown' 函数必需的 'time' 参数" warning.config.function.set_cooldown.missing_id: "在文件 中发现问题 - 配置项 '' 缺少 'set_cooldown' 函数必需的 'id' 参数" warning.config.function.remove_cooldown.missing_id: "在文件 中发现问题 - 配置项 '' 缺少 'remove_cooldown' 函数必需的 'id' 参数" +warning.config.function.mythic_mobs_skill.missing_skill: "在文件 中发现问题 - 配置项 '' 缺少 'mythic_mobs_skill' 函数必需的 'skill' 参数" warning.config.selector.missing_type: "在文件 中发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器类型 ''" warning.config.selector.invalid_target: "在文件 中发现问题 - 配置项 '' 使用了无效的选择器目标 ''" @@ -402,9 +412,9 @@ warning.config.resource_pack.generation.texture_not_in_atlas: "纹理'物品''缺少模型文件: ''" warning.config.resource_pack.generation.missing_block_model: "方块状态''缺少模型文件: ''" warning.config.resource_pack.generation.missing_parent_model: "模型''找不到父级模型文件: ''" -warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误." +warning.config.resource_pack.generation.malformatted_json: "Json文件 '' 格式错误" warning.config.resource_pack.generation.missing_equipment_texture: "装备 '' 缺少纹理 ''" -warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 ''. Overlay格式必须包含占位符 '{version}'." +warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 ''. Overlay格式必须包含占位符 '{version}'" warning.config.equipment.duplicate: "在文件 发现问题 - 重复的装备配置 ''。请检查其他文件中是否存在相同配置" warning.config.equipment.missing_type: "在文件 发现问题 - 装备 '' 缺少必需的 'type' 参数" warning.config.equipment.invalid_type: "在文件 发现问题 - 装备 '' 使用了无效的 'type' 参数" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index d41f1c2f1..df535d1cd 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.0.0-beta13" + id("com.gradleup.shadow") version "9.0.0-rc2" id("maven-publish") } diff --git a/core/src/main/java/net/momirealms/craftengine/core/attribute/AttributeModifier.java b/core/src/main/java/net/momirealms/craftengine/core/attribute/AttributeModifier.java index 0d5ff2dfb..1353e5769 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/attribute/AttributeModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/attribute/AttributeModifier.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.attribute; +import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -72,7 +73,7 @@ public class AttributeModifier { } } - public record Display(AttributeModifier.Display.Type type, String value) { + public record Display(AttributeModifier.Display.Type type, Component value) { public enum Type { DEFAULT, HIDDEN, OVERRIDE diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java index fba210d01..6f5daecc9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockKeys.java @@ -41,7 +41,25 @@ public final class BlockKeys { public static final Key COMMAND_BLOCK = Key.of("minecraft:command_block"); public static final Key CHAIN_COMMAND_BLOCK = Key.of("minecraft:chain_command_block"); public static final Key REPEATING_COMMAND_BLOCK = Key.of("minecraft:repeating_command_block"); + public static final Key JIGSAW = Key.of("minecraft:jigsaw"); + public static final Key STRUCTURE_BLOCK = Key.of("minecraft:structure_block"); + public static final Key TEST_INSTANCE_BLOCK = Key.of("minecraft:test_instance_block"); + public static final Key TEST_BLOCK = Key.of("minecraft:test_block"); + public static final Key LIGHT = Key.of("minecraft:light"); public static final Key DECORATED_POT = Key.of("minecraft:decorated_pot"); + public static final Key FLOWER_POT = Key.of("minecraft:flower_pot"); + public static final Key CHISELED_BOOKSHELF = Key.of("minecraft:chiseled_bookshelf"); + public static final Key REDSTONE_ORE = Key.of("minecraft:redstone_ore"); + public static final Key DEEPSLATE_REDSTONE_ORE = Key.of("minecraft:deepslate_redstone_ore"); + public static final Key BEE_NEST = Key.of("minecraft:bee_nest"); + public static final Key BEEHIVE = Key.of("minecraft:beehive"); + public static final Key POWDER_SNOW = Key.of("minecraft:powder_snow"); + public static final Key COMPOSTER = Key.of("minecraft:composter"); + public static final Key CAULDRON = Key.of("minecraft:cauldron"); + public static final Key WATER_CAULDRON = Key.of("minecraft:water_cauldron"); + public static final Key LAVA_CAULDRON = Key.of("minecraft:lava_cauldron"); + public static final Key RESPAWN_ANCHOR = Key.of("minecraft:respawn_anchor"); + public static final Key LODESTONE = Key.of("minecraft:lodestone"); public static final Key CAKE = Key.of("minecraft:cake"); public static final Key CANDLE_CAKE = Key.of("minecraft:candle_cake"); @@ -158,6 +176,8 @@ public final class BlockKeys { public static final Key WAXED_OXIDIZED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_oxidized_copper_trapdoor"); public static final Key WAXED_WEATHERED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_weathered_copper_trapdoor"); + + public static final Key OAK_FENCE_GATE = Key.of("minecraft:oak_fence_gate"); public static final Key SPRUCE_FENCE_GATE = Key.of("minecraft:spruce_fence_gate"); public static final Key BIRCH_FENCE_GATE = Key.of("minecraft:birch_fence_gate"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index 1cfb119f9..8abe2a871 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -464,7 +464,7 @@ public class BlockSettings { LazyReference> correctTools = LazyReference.lazyReference(() -> { Set ids = new HashSet<>(); for (String tool : tools) { - if (tool.charAt(0) == '#') ids.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(tool.substring(1))).stream().map(UniqueKey::key).toList()); + if (tool.charAt(0) == '#') ids.addAll(CraftEngine.instance().itemManager().itemIdsByTag(Key.of(tool.substring(1))).stream().map(UniqueKey::key).toList()); else ids.add(Key.of(tool)); } return ids; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java index 5636e7ae9..a54c56afa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Properties.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.*; import java.util.Map; -public class Properties { +public final class Properties { public static final Key BOOLEAN = Key.of("craftengine:boolean"); public static final Key INT = Key.of("craftengine:int"); public static final Key STRING = Key.of("craftengine:string"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/EntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/entity/EntityTypeKeys.java new file mode 100644 index 000000000..c4829722a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/EntityTypeKeys.java @@ -0,0 +1,84 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; + +public class EntityTypeKeys { + private EntityTypeKeys() {} + + public static final Key BEE = Key.of("minecraft:bee"); + public static final Key FOX = Key.of("minecraft:fox"); + public static final Key FROG = Key.of("minecraft:frog"); + public static final Key PANDA = Key.of("minecraft:panda"); + public static final Key SHEEP = Key.of("minecraft:sheep"); + public static final Key BOGGED = Key.of("minecraft:bogged"); + public static final Key SNOW_GOLEM = Key.of("minecraft:snow_golem"); + public static final Key HOGLIN = Key.of("minecraft:hoglin"); + public static final Key OCELOT = Key.of("minecraft:ocelot"); + public static final Key RABBIT = Key.of("minecraft:rabbit"); + public static final Key TURTLE = Key.of("minecraft:turtle"); + public static final Key AXOLOTL = Key.of("minecraft:axolotl"); + public static final Key CHICKEN = Key.of("minecraft:chicken"); + public static final Key SNIFFER = Key.of("minecraft:sniffer"); + public static final Key ARMADILLO = Key.of("minecraft:armadillo"); + public static final Key COD = Key.of("minecraft:cod"); + public static final Key SALMON = Key.of("minecraft:salmon"); + public static final Key TROPICAL_FISH = Key.of("minecraft:tropical_fish"); + public static final Key PUFFERFISH = Key.of("minecraft:pufferfish"); + public static final Key TADPOLE = Key.of("minecraft:tadpole"); + public static final Key COW = Key.of("minecraft:cow"); + public static final Key MOOSHROOM = Key.of("minecraft:mooshroom"); + public static final Key GOAT = Key.of("minecraft:goat"); + public static final Key PIG = Key.of("minecraft:pig"); + public static final Key STRIDER = Key.of("minecraft:strider"); + public static final Key WOLF = Key.of("minecraft:wolf"); + public static final Key CAT = Key.of("minecraft:cat"); + public static final Key PARROT = Key.of("minecraft:parrot"); + + public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast"); + public static final Key PIGLIN = Key.of("minecraft:piglin"); + public static final Key CREEPER = Key.of("minecraft:creeper"); + public static final Key ALLAY = Key.of("minecraft:allay"); + public static final Key HORSE = Key.of("minecraft:horse"); + public static final Key ZOMBIE_HORSE = Key.of("minecraft:zombie_horse"); + public static final Key SKELETON_HORSE = Key.of("minecraft:skeleton_horse"); + public static final Key DONKEY = Key.of("minecraft:donkey"); + public static final Key MULE = Key.of("minecraft:mule"); + public static final Key VILLAGER = Key.of("minecraft:villager"); + public static final Key WANDERING_TRADER = Key.of("minecraft:wandering_trader"); + public static final Key LLAMA = Key.of("minecraft:llama"); + public static final Key TRADER_LLAMA = Key.of("minecraft:trader_llama"); + public static final Key CAMEL = Key.of("minecraft:camel"); + public static final Key ITEM_FRAME = Key.of("minecraft:item_frame"); + public static final Key GLOW_ITEM_FRAME = Key.of("minecraft:glow_item_frame"); + public static final Key INTERACTION = Key.of("minecraft:interaction"); + + public static final Key BOAT = Key.of("minecraft:boat"); + public static final Key OAK_BOAT = Key.of("minecraft:oak_boat"); + public static final Key SPRUCE_BOAT = Key.of("minecraft:spruce_boat"); + public static final Key BIRCH_BOAT = Key.of("minecraft:birch_boat"); + public static final Key JUNGLE_BOAT = Key.of("minecraft:jungle_boat"); + public static final Key ACACIA_BOAT = Key.of("minecraft:acacia_boat"); + public static final Key DARK_OAK_BOAT = Key.of("minecraft:dark_oak_boat"); + public static final Key MANGROVE_BOAT = Key.of("minecraft:mangrove_boat"); + public static final Key CHERRY_BOAT = Key.of("minecraft:cherry_boat"); + public static final Key PALE_OAK_BOAT = Key.of("minecraft:pale_oak_boat"); + public static final Key BAMBOO_RAFT = Key.of("minecraft:bamboo_raft"); + + public static final Key CHEST_BOAT = Key.of("minecraft:chest_boat"); + public static final Key OAK_CHEST_BOAT = Key.of("minecraft:oak_chest_boat"); + public static final Key SPRUCE_CHEST_BOAT = Key.of("minecraft:spruce_chest_boat"); + public static final Key BIRCH_CHEST_BOAT = Key.of("minecraft:birch_chest_boat"); + public static final Key JUNGLE_CHEST_BOAT = Key.of("minecraft:jungle_chest_boat"); + public static final Key ACACIA_CHEST_BOAT = Key.of("minecraft:acacia_chest_boat"); + public static final Key DARK_OAK_CHEST_BOAT = Key.of("minecraft:dark_oak_chest_boat"); + public static final Key MANGROVE_CHEST_BOAT = Key.of("minecraft:mangrove_chest_boat"); + public static final Key CHERRY_CHEST_BOAT = Key.of("minecraft:cherry_chest_boat"); + public static final Key PALE_OAK_CHEST_BOAT = Key.of("minecraft:pale_oak_chest_boat"); + public static final Key BAMBOO_CHEST_RAFT = Key.of("minecraft:bamboo_chest_raft"); + + public static final Key MINECART = Key.of("minecraft:minecart"); + public static final Key CHEST_MINECART = Key.of("minecraft:chest_minecart"); + public static final Key FURNACE_MINECART = Key.of("minecraft:furnace_minecart"); + public static final Key HOPPER_MINECART = Key.of("minecraft:hopper_minecart"); + public static final Key COMMAND_BLOCK_MINECART = Key.of("minecraft:command_block_minecart"); +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java index c7c5704e9..72587f2a1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; @@ -49,8 +50,8 @@ public class FurnitureExtraData { return Optional.empty(); } - public Optional dyedColor() { - if (this.data.containsKey(DYED_COLOR)) return Optional.of(this.data.getInt(DYED_COLOR)); + public Optional dyedColor() { + if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR))); return Optional.empty(); } @@ -92,9 +93,9 @@ public class FurnitureExtraData { return this; } - public Builder dyedColor(Integer color) { + public Builder dyedColor(Color color) { if (color == null) return this; - this.data.putInt(DYED_COLOR, color); + this.data.putInt(DYED_COLOR, color.color()); return this; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 073a8d41c..956a55571 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -28,6 +28,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void sendPackets(List packet, boolean immediately); + public abstract void sendPackets(List packet, boolean immediately, Runnable sendListener); + public abstract float getDestroyProgress(Object blockState, BlockPos pos); public abstract void setClientSideCanBreakBlock(boolean canBreak); @@ -68,6 +70,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract int lastSuccessfulInteractionTick(); + public abstract void updateLastInteractEntityTick(@NotNull InteractionHand hand); + + public abstract boolean lastInteractEntityCheck(@NotNull InteractionHand hand); + public abstract int gameTicks(); public abstract void swingHand(InteractionHand hand); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index b7789dc4c..bb4c46dd2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -368,7 +368,13 @@ public abstract class AbstractFontManager implements FontManager { if (keywords.isEmpty()) { throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); } - String content = section.getOrDefault("content", "").toString(); + Object rawContent = section.getOrDefault("content", ""); + String content; + if (rawContent instanceof List list) { + content = list.stream().map(Object::toString).collect(Collectors.joining()); + } else { + content = rawContent.toString(); + } String image = null; if (section.containsKey("image")) { String rawImage = section.get("image").toString(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java index db56a096f..66e6c4d53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java @@ -15,6 +15,7 @@ import java.util.Map; import java.util.Optional; public abstract class AbstractCustomItem implements CustomItem { + protected final boolean isVanillaItem; protected final UniqueKey id; protected final Key material; protected final Key clientBoundMaterial; @@ -25,12 +26,13 @@ public abstract class AbstractCustomItem implements CustomItem { protected final Map>> events; @SuppressWarnings("unchecked") - public AbstractCustomItem(UniqueKey id, Key material, Key clientBoundMaterial, + public AbstractCustomItem(boolean isVanillaItem, UniqueKey id, Key material, Key clientBoundMaterial, List behaviors, List> modifiers, List> clientBoundModifiers, ItemSettings settings, Map>> events) { + this.isVanillaItem = isVanillaItem; this.id = id; this.material = material; this.clientBoundMaterial = clientBoundMaterial; @@ -75,6 +77,11 @@ public abstract class AbstractCustomItem implements CustomItem { return this.modifiers; } + @Override + public boolean isVanillaItem() { + return isVanillaItem; + } + @Override public boolean hasClientBoundDataModifier() { return this.clientBoundModifiers.length != 0; 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 74f5d96a3..3f7a75d8f 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 @@ -2,15 +2,19 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.setting.EquipmentData; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; @@ -107,13 +111,13 @@ public class AbstractItem, I> implements Item { } @Override - public Item dyedColor(Integer data) { + public Item dyedColor(Color data) { this.factory.dyedColor(this.item, data); return this; } @Override - public Optional dyedColor() { + public Optional dyedColor() { return this.factory.dyedColor(this.item); } @@ -150,17 +154,17 @@ public class AbstractItem, I> implements Item { } @Override - public Key id() { + public @NotNull Key id() { return this.factory.id(this.item); } @Override - public Key vanillaId() { + public @NotNull Key vanillaId() { return this.factory.vanillaId(this.item); } @Override - public UniqueKey recipeIngredientId() { + public @Nullable UniqueKey recipeIngredientId() { return this.factory.recipeIngredientID(this.item); } @@ -252,6 +256,12 @@ public class AbstractItem, I> implements Item { return this.factory.loreComponent(this.item); } + @Override + public Item attributeModifiers(List modifiers) { + this.factory.attributeModifiers(this.item, modifiers); + return this; + } + @Override public Item unbreakable(boolean unbreakable) { this.factory.unbreakable(this.item, unbreakable); @@ -433,8 +443,8 @@ public class AbstractItem, I> implements Item { } @Override - public boolean is(Key itemTag) { - return this.factory.is(this.item, itemTag); + public boolean hasItemTag(Key itemTag) { + return this.factory.hasItemTag(this.item, itemTag); } @Override 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 6b75dc648..77d581dc1 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 @@ -2,14 +2,10 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; -import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviors; -import net.momirealms.craftengine.core.item.data.Enchantment; -import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.equipment.*; import net.momirealms.craftengine.core.item.modifier.*; -import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; @@ -22,20 +18,18 @@ import net.momirealms.craftengine.core.pack.model.select.TrimMaterialSelectPrope import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; -import net.momirealms.craftengine.core.plugin.context.text.TextProvider; -import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.*; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.type.Either; -import org.joml.Vector3f; import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Stream; public abstract class AbstractItemManager extends AbstractModelGenerator implements ItemManager { @@ -46,7 +40,6 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl private final ItemParser itemParser; private final EquipmentParser equipmentParser; protected final Map> externalItemSources = new HashMap<>(); - protected final Map>> dataFunctions = new HashMap<>(); protected final Map> customItems = new HashMap<>(); protected final Map> customItemTags = new HashMap<>(); protected final Map> cmdConflictChecker = new HashMap<>(); @@ -63,14 +56,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl super(plugin); this.itemParser = new ItemParser(); this.equipmentParser = new EquipmentParser(); - this.registerFunctions(); - } - - @Override - public void registerDataType(Function> factory, String... alias) { - for (String a : alias) { - this.dataFunctions.put(a, factory); - } + ItemDataModifiers.init(); } protected static void registerVanillaItemExtraBehavior(ItemBehavior behavior, Key... items) { @@ -79,23 +65,23 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } - protected void applyDataFunctions(Map dataSection, Consumer> consumer) { + @SuppressWarnings("unchecked") + protected void applyDataModifiers(Map dataSection, Consumer> callback) { + ExceptionCollector errorCollector = new ExceptionCollector<>(); if (dataSection != null) { for (Map.Entry dataEntry : dataSection.entrySet()) { - Optional.ofNullable(this.dataFunctions.get(dataEntry.getKey())).ifPresent(function -> { + Object value = dataEntry.getValue(); + if (value == null) continue; + Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(dataEntry.getKey(), Key.DEFAULT_NAMESPACE))).ifPresent(factory -> { try { - consumer.accept(function.apply(dataEntry.getValue())); - } catch (IllegalArgumentException e) { - this.plugin.logger().warn("Invalid data format", e); + callback.accept((ItemDataModifier) factory.create(value)); + } catch (LocalizedResourceConfigException e) { + errorCollector.add(e); } }); } } - } - - @Override - public Function> getDataType(String key) { - return this.dataFunctions.get(key); + errorCollector.throwIfPresent(); } @Override @@ -151,43 +137,31 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl Key id = customItem.id(); if (this.customItems.containsKey(id)) return false; this.customItems.put(id, customItem); - // cache command suggestions - this.cachedSuggestions.add(Suggestion.suggestion(id.toString())); - // totem animations - if (VersionHelper.isOrAbove1_21_2()) { - this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); - } else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) { - this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); - } - // tags - Set tags = customItem.settings().tags(); - for (Key tag : tags) { - this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); + if (!customItem.isVanillaItem()) { + // cache command suggestions + this.cachedSuggestions.add(Suggestion.suggestion(id.toString())); + // totem animations + if (VersionHelper.isOrAbove1_21_2()) { + this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); + } else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) { + this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); + } + // tags + Set tags = customItem.settings().tags(); + for (Key tag : tags) { + this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); + } } return true; } @Override - public List tagToItems(Key tag) { - List items = new ArrayList<>(); - List holders = VANILLA_ITEM_TAGS.get(tag); - if (holders != null) { - items.addAll(holders); - } - List customItems = this.customItemTags.get(tag); - if (customItems != null) { - items.addAll(customItems); - } - return items; - } - - @Override - public List tagToVanillaItems(Key tag) { + public List vanillaItemIdsByTag(Key tag) { return Collections.unmodifiableList(VANILLA_ITEM_TAGS.getOrDefault(tag, List.of())); } @Override - public List tagToCustomItems(Key tag) { + public List customItemIdsByTag(Key tag) { return Collections.unmodifiableList(this.customItemTags.getOrDefault(tag, List.of())); } @@ -378,23 +352,63 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey)); } + // 对于不重要的配置,可以仅警告,不返回 + ExceptionCollector collector = new ExceptionCollector<>(); + // 应用物品数据 - applyDataFunctions(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); - applyDataFunctions(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); + try { + applyDataModifiers(MiscUtils.castToMap(section.get("data"), true), itemBuilder::dataModifier); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + } + try { + applyDataModifiers(MiscUtils.castToMap(section.get("client-bound-data"), true), itemBuilder::clientBoundDataModifier); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + } // 如果不是原版物品,那么加入ce的标识符 if (!isVanillaItem) itemBuilder.dataModifier(new IdModifier<>(id)); + // 事件 + Map>> eventTriggerListMap; + try { + eventTriggerListMap = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + eventTriggerListMap = Map.of(); + } + + // 设置 + ItemSettings settings; + try { + settings = Optional.ofNullable(ResourceConfigUtils.get(section, "settings")) + .map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true))) + .map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it) + .orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem)); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + settings = ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem); + } + + // 行为 + List behaviors; + try { + behaviors = ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors")); + } catch (LocalizedResourceConfigException e) { + collector.add(e); + behaviors = Collections.emptyList(); + } + // 构建自定义物品 CustomItem customItem = itemBuilder - .behaviors(ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors"))) - .settings(Optional.ofNullable(ResourceConfigUtils.get(section, "settings")) - .map(map -> ItemSettings.fromMap(MiscUtils.castToMap(map, true))) - .map(it -> isVanillaItem ? it.canPlaceRelatedVanillaBlock(true) : it) - .orElse(ItemSettings.of().canPlaceRelatedVanillaBlock(isVanillaItem))) - .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) + .isVanillaItem(isVanillaItem) + .behaviors(behaviors) + .settings(settings) + .events(eventTriggerListMap) .build(); + // 添加到缓存 addCustomItem(customItem); @@ -407,6 +421,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl Map modelSection = MiscUtils.castToMap(section.get("model"), true); Map legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true); if (modelSection == null && legacyModelSection == null) { + collector.throwIfPresent(); return; } @@ -415,39 +430,47 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl if (!isVanillaItem) { // 既没有模型值也没有item-model if (customModelData == 0 && itemModelKey == null) { - throw new LocalizedResourceConfigException("warning.config.item.missing_model_id"); + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id")); } } - // 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model - if (needsModelSection && modelSection == null) { - throw new LocalizedResourceConfigException("warning.config.item.missing_model"); - } - // 新版格式 ItemModel modernModel = null; // 旧版格式 TreeSet legacyOverridesModels = null; // 如果需要支持新版item model 或者用户需要旧版本兼容,但是没配置legacy-model if (needsModelSection) { - modernModel = ItemModels.fromMap(modelSection); - for (ModelGeneration generation : modernModel.modelsToGenerate()) { - prepareModelGeneration(generation); + // 1.21.4+必须要配置model区域,如果不需要高版本兼容,则可以只写legacy-model + if (modelSection == null) { + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model")); + return; + } + try { + modernModel = ItemModels.fromMap(modelSection); + for (ModelGeneration generation : modernModel.modelsToGenerate()) { + prepareModelGeneration(generation); + } + } catch (LocalizedResourceConfigException e) { + collector.addAndThrow(e); } } // 如果需要旧版本兼容 if (needsLegacyCompatibility()) { if (legacyModelSection != null) { - LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData); - for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) { - prepareModelGeneration(generation); + try { + LegacyItemModel legacyItemModel = LegacyItemModel.fromMap(legacyModelSection, customModelData); + for (ModelGeneration generation : legacyItemModel.modelsToGenerate()) { + prepareModelGeneration(generation); + } + legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides()); + } catch (LocalizedResourceConfigException e) { + collector.addAndThrow(e); } - legacyOverridesModels = new TreeSet<>(legacyItemModel.overrides()); } else { legacyOverridesModels = new TreeSet<>(); processModelRecursively(modernModel, new LinkedHashMap<>(), legacyOverridesModels, clientBoundMaterial, customModelData); if (legacyOverridesModels.isEmpty()) { - TranslationManager.instance().log("warning.config.item.legacy_model.cannot_convert", path.toString(), id.asString()); + collector.add(new LocalizedResourceConfigException("warning.config.item.legacy_model.cannot_convert", path.toString(), id.asString())); } } } @@ -463,7 +486,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 检查cmd冲突 Map conflict = AbstractItemManager.this.cmdConflictChecker.computeIfAbsent(finalBaseModel, k -> new HashMap<>()); if (conflict.containsKey(customModelData)) { - throw new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString()); + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.custom_model_data_conflict", String.valueOf(customModelData), conflict.get(customModelData).toString())); } conflict.put(customModelData, id); // 添加新版item model @@ -481,7 +504,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl lom.addAll(legacyOverridesModels); } } else if (isVanillaItemModel) { - throw new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString()); + collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString())); } // 使用了item-model组件,且不是原版物品的 @@ -508,162 +531,9 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl )); } } - } - } - private void registerFunctions() { - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - String plugin = data.get("plugin").toString(); - String id = data.get("id").toString(); - ExternalItemSource provider = AbstractItemManager.this.getExternalItemSource(plugin.toLowerCase(Locale.ENGLISH)); - return new ExternalModifier<>(id, Objects.requireNonNull(provider, "Item provider " + plugin + " not found")); - }, "external"); - if (VersionHelper.isOrAbove1_20_5()) { - registerDataType((obj) -> { - String name = obj.toString(); - return new CustomNameModifier<>(name); - }, "custom-name"); - registerDataType((obj) -> { - String name = obj.toString(); - return new ItemNameModifier<>(name); - }, "item-name", "display-name"); - } else { - registerDataType((obj) -> { - String name = obj.toString(); - return new CustomNameModifier<>(name); - }, "custom-name", "item-name", "display-name"); - } - registerDataType((obj) -> { - List lore = MiscUtils.getAsStringList(obj); - return new LoreModifier<>(lore); - }, "lore", "display-lore", "description"); - registerDataType((obj) -> { - Map> dynamicLore = new LinkedHashMap<>(); - if (obj instanceof Map map) { - for (Map.Entry entry : map.entrySet()) { - dynamicLore.put(entry.getKey().toString(), MiscUtils.getAsStringList(entry.getValue())); - } - } - return new DynamicLoreModifier<>(dynamicLore); - }, "dynamic-lore"); - registerDataType((obj) -> { - if (obj instanceof Integer integer) { - return new DyedColorModifier<>(integer); - } else { - Vector3f vector3f = MiscUtils.getAsVector3f(obj, "dyed-color"); - return new DyedColorModifier<>(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vector3f.x) << 16 | MCUtils.fastFloor(vector3f.y) << 8 | MCUtils.fastFloor(vector3f.z)); - } - }, "dyed-color"); - if (!VersionHelper.isOrAbove1_21_5()) { - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - return new TagsModifier<>(data); - }, "tags", "tag", "nbt"); - } - registerDataType((object -> { - MutableInt mutableInt = new MutableInt(0); - List attributeModifiers = ResourceConfigUtils.parseConfigAsList(object, (map) -> { - String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type"); - Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type)); - AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH)); - Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> { - mutableInt.add(1); - return Key.of("craftengine", "modifier_" + mutableInt.intValue()); - }); - double amount = ResourceConfigUtils.getAsDouble( - ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount"), "amount" - ); - AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf( - ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH) - ); - AttributeModifier.Display display = null; - if (VersionHelper.isOrAbove1_21_6() && map.containsKey("display")) { - Map displayMap = MiscUtils.castToMap(map.get("display"), false); - AttributeModifier.Display.Type displayType = AttributeModifier.Display.Type.valueOf(ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("type"), "warning.config.item.data.attribute_modifiers.display.missing_type").toUpperCase(Locale.ENGLISH)); - if (displayType == AttributeModifier.Display.Type.OVERRIDE) { - String miniMessageValue = ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("value"), "warning.config.item.data.attribute_modifiers.display.missing_value"); - display = new AttributeModifier.Display(displayType, miniMessageValue); - } else { - display = new AttributeModifier.Display(displayType, null); - } - } - return new AttributeModifier(nativeType.value(), slot, id, - amount, operation, display); - }); - return new AttributeModifiersModifier<>(attributeModifiers); - }), "attributes", "attribute-modifiers", "attribute-modifier"); - registerDataType((obj) -> { - boolean value = TypeUtils.checkType(obj, Boolean.class); - return new UnbreakableModifier<>(value); - }, "unbreakable"); - registerDataType((obj) -> { - int customModelData = ResourceConfigUtils.getAsInt(obj, "custom-model-data"); - return new CustomModelDataModifier<>(customModelData); - }, "custom-model-data"); - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - List enchantments = new ArrayList<>(); - for (Map.Entry e : data.entrySet()) { - if (e.getValue() instanceof Number number) { - enchantments.add(new Enchantment(Key.of(e.getKey()), number.intValue())); - } - } - return new EnchantmentModifier<>(enchantments); - }, "enchantment", "enchantments", "enchant"); - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - String material = data.get("material").toString().toLowerCase(Locale.ENGLISH); - String pattern = data.get("pattern").toString().toLowerCase(Locale.ENGLISH); - return new TrimModifier<>(Key.of(material), Key.of(pattern)); - }, "trim"); - registerDataType((obj) -> { - List components = MiscUtils.getAsStringList(obj).stream().map(Key::of).toList(); - return new HideTooltipModifier<>(components); - }, "hide-tooltip", "hide-flags"); - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - Map arguments = new HashMap<>(); - for (Map.Entry entry : data.entrySet()) { - arguments.put(entry.getKey(), TextProviders.fromString(entry.getValue().toString())); - } - return new ArgumentModifier<>(arguments); - }, "args", "argument", "arguments"); - if (VersionHelper.isOrAbove1_20_5()) { - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - return new ComponentModifier<>(data); - }, "components", "component"); - registerDataType((obj) -> { - List data = MiscUtils.getAsStringList(obj); - return new RemoveComponentModifier<>(data); - }, "remove-components", "remove-component"); - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - int nutrition = ResourceConfigUtils.getAsInt(data.get("nutrition"), "nutrition"); - float saturation = ResourceConfigUtils.getAsFloat(data.get("saturation"), "saturation"); - return new FoodModifier<>(nutrition, saturation, ResourceConfigUtils.getAsBoolean(data.getOrDefault("can-always-eat", false), "can-always-eat")); - }, "food"); - } - if (VersionHelper.isOrAbove1_21()) { - registerDataType((obj) -> { - String song = obj.toString(); - return new JukeboxSongModifier<>(new JukeboxPlayable(song, true)); - }, "jukebox-playable"); - } - if (VersionHelper.isOrAbove1_21_2()) { - registerDataType((obj) -> { - String id = obj.toString(); - return new TooltipStyleModifier<>(Key.of(id)); - }, "tooltip-style"); - registerDataType((obj) -> { - Map data = MiscUtils.castToMap(obj, false); - return new EquippableModifier<>(EquipmentData.fromMap(data)); - }, "equippable"); - registerDataType((obj) -> { - String id = obj.toString(); - return new ItemModelModifier<>(Key.of(id)); - }, "item-model"); + // 抛出异常 + collector.throwIfPresent(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java new file mode 100644 index 000000000..639d8d137 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.item; + +import net.momirealms.craftengine.core.util.Key; + +public class CloneableConstantItem implements BuildableItem { + private final Item item; + + private CloneableConstantItem(Item item) { + this.item = item; + } + + public static CloneableConstantItem of(Item item) { + return new CloneableConstantItem<>(item); + } + + @Override + public Key id() { + return this.item.id(); + } + + @Override + public Item buildItem(ItemBuildContext context, int count) { + return this.item.copyWithCount(count); + } + + @Override + public I buildItemStack(ItemBuildContext context, int count) { + return this.item.copyWithCount(count).getItem(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index 2ab666cf9..5d0415a30 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -14,6 +14,8 @@ import java.util.Map; public interface CustomItem extends BuildableItem { + boolean isVanillaItem(); + Key id(); UniqueKey uniqueId(); @@ -40,6 +42,8 @@ public interface CustomItem extends BuildableItem { List behaviors(); interface Builder { + Builder isVanillaItem(boolean isVanillaItem); + Builder id(UniqueKey id); Builder clientBoundMaterial(Key clientBoundMaterialKey); 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 86b4c4729..69c9a68f8 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; @@ -9,9 +10,12 @@ import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.setting.EquipmentData; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; @@ -35,10 +39,13 @@ public interface Item { boolean isBlockItem(); + @NotNull Key id(); + @NotNull Key vanillaId(); + @Nullable UniqueKey recipeIngredientId(); Optional customId(); @@ -69,9 +76,10 @@ public interface Item { int maxDamage(); - Item dyedColor(Integer data); + // todo 考虑部分版本的show in tooltip保留 + Item dyedColor(Color data); - Optional dyedColor(); + Optional dyedColor(); Item fireworkExplosion(FireworkExplosion explosion); @@ -117,6 +125,8 @@ public interface Item { Optional> loreComponent(); + Item attributeModifiers(List modifiers); + Optional jukeboxSong(); Item jukeboxSong(JukeboxPlayable song); @@ -185,7 +195,7 @@ public interface Item { Item copyWithCount(int count); - boolean is(Key itemTag); + boolean hasItemTag(Key itemTag); Object getLiteralObject(); @@ -208,4 +218,34 @@ public interface Item { } byte[] toByteArray(); + + default Item applyDyedColors(List colors) { + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + int totalMaxComponent = 0; + int colorCount = 0; + Optional existingColor = dyedColor(); + existingColor.ifPresent(colors::add); + for (Color color : colors) { + int dyeRed = color.r(); + int dyeGreen = color.g(); + int dyeBlue = color.b(); + totalMaxComponent += Math.max(dyeRed, Math.max(dyeGreen, dyeBlue)); + totalRed += dyeRed; + totalGreen += dyeGreen; + totalBlue += dyeBlue; + ++colorCount; + } + int avgRed = totalRed / colorCount; + int avgGreen = totalGreen / colorCount; + int avgBlue = totalBlue / colorCount; + float avgMaxComponent = (float) totalMaxComponent / (float)colorCount; + float currentMaxComponent = (float) Math.max(avgRed, Math.max(avgGreen, avgBlue)); + avgRed = (int) ((float) avgRed * avgMaxComponent / currentMaxComponent); + avgGreen = (int) ((float) avgGreen * avgMaxComponent / currentMaxComponent); + avgBlue = (int) ((float) avgBlue * avgMaxComponent / currentMaxComponent); + Color finalColor = new Color(0, avgRed, avgGreen, avgBlue); + return dyedColor(finalColor); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemDataModifierFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemDataModifierFactory.java new file mode 100644 index 000000000..446ed2272 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemDataModifierFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.item; + +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; + +public interface ItemDataModifierFactory { + + ItemDataModifier create(Object arg); +} 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 d403ba310..43de1de6f 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; @@ -9,6 +10,7 @@ import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; import net.momirealms.sparrow.nbt.Tag; @@ -16,7 +18,6 @@ import net.momirealms.sparrow.nbt.Tag; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; public abstract class ItemFactory, I> { protected final CraftEngine plugin; @@ -112,7 +113,7 @@ public abstract class ItemFactory, I> { protected void loreComponent(W item, List component) { if (component != null && !component.isEmpty()) { - loreJson(item, component.stream().map(AdventureHelper::componentToJson).collect(Collectors.toList())); + loreJson(item, component.stream().map(AdventureHelper::componentToJson).toList()); } else { loreJson(item, null); } @@ -136,9 +137,9 @@ public abstract class ItemFactory, I> { protected abstract void damage(W item, Integer damage); - protected abstract Optional dyedColor(W item); + protected abstract Optional dyedColor(W item); - protected abstract void dyedColor(W item, Integer color); + protected abstract void dyedColor(W item, Color color); protected abstract int maxDamage(W item); @@ -164,7 +165,7 @@ public abstract class ItemFactory, I> { protected abstract void maxStackSize(W item, Integer maxStackSize); - protected abstract boolean is(W item, Key itemTag); + protected abstract boolean hasItemTag(W item, Key itemTag); protected abstract boolean isBlockItem(W item); @@ -211,4 +212,6 @@ public abstract class ItemFactory, I> { protected abstract boolean isEmpty(W item); protected abstract UniqueKey recipeIngredientID(W item); + + protected abstract void attributeModifiers(W item, List modifiers); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 89916d9e3..cf0c172e5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.util.Key; -public class ItemKeys { +public final class ItemKeys { public static final Key AIR = Key.of("minecraft:air"); public static final Key FLINT_AND_STEEL = Key.of("minecraft:flint_and_steel"); public static final Key STONE = Key.of("minecraft:stone"); @@ -26,6 +26,7 @@ public class ItemKeys { public static final Key TROPICAL_FISH_BUCKET = Key.of("minecraft:tropical_fish_bucket"); public static final Key PUFFERFISH_BUCKET = Key.of("minecraft:pufferfish_bucket"); public static final Key AXOLOTL_BUCKET = Key.of("minecraft:axolotl_bucket"); + public static final Key LAVA_BUCKET = Key.of("minecraft:lava_bucket"); public static final Key BUCKET = Key.of("minecraft:bucket"); public static final Key BONE_MEAL = Key.of("minecraft:bone_meal"); public static final Key ENCHANTED_BOOK = Key.of("minecraft:enchanted_book"); @@ -33,6 +34,33 @@ public class ItemKeys { public static final Key BARRIER = Key.of("minecraft:barrier"); public static final Key CACTUS = Key.of("minecraft:cactus"); public static final Key REDSTONE = Key.of("minecraft:redstone"); + public static final Key GOLD_INGOT = Key.of("minecraft:gold_ingot"); + public static final Key SHEARS = Key.of("minecraft:shears"); + public static final Key BRUSH = Key.of("minecraft:brush"); + public static final Key BOWL = Key.of("minecraft:bowl"); + public static final Key COMPASS = Key.of("minecraft:compass"); + public static final Key GLASS_BOTTLE = Key.of("minecraft:glass_bottle"); + public static final Key LIGHT = Key.of("minecraft:light"); + public static final Key GLOWSTONE = Key.of("minecraft:glowstone"); + public static final Key SADDLE = Key.of("minecraft:saddle"); + public static final Key HARNESS = Key.of("minecraft:harness"); + public static final Key WHITE_DYE = Key.of("minecraft:white_dye"); + public static final Key ORANGE_DYE = Key.of("minecraft:orange_dye"); + public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye"); + public static final Key LIGHT_BLUE_DYE = Key.of("minecraft:light_blue_dye"); + public static final Key YELLOW_DYE = Key.of("minecraft:yellow_dye"); + public static final Key LIME_DYE = Key.of("minecraft:lime_dye"); + public static final Key PINK_DYE = Key.of("minecraft:pink_dye"); + public static final Key GRAY_DYE = Key.of("minecraft:gray_dye"); + public static final Key LIGHT_GRAY_DYE = Key.of("minecraft:light_gray_dye"); + public static final Key CYAN_DYE = Key.of("minecraft:cyan_dye"); + public static final Key PURPLE_DYE = Key.of("minecraft:purple_dye"); + public static final Key BLUE_DYE = Key.of("minecraft:blue_dye"); + public static final Key BROWN_DYE = Key.of("minecraft:brown_dye"); + public static final Key GREEN_DYE = Key.of("minecraft:green_dye"); + public static final Key RED_DYE = Key.of("minecraft:red_dye"); + public static final Key BLACK_DYE = Key.of("minecraft:black_dye"); + public static final Key FIREWORK_STAR = Key.of("minecraft:firework_star"); public static final Key[] AXES = new Key[] { WOODEN_AXE, STONE_AXE, IRON_AXE, GOLDEN_AXE, DIAMOND_AXE, NETHERITE_AXE 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 d88e2d5d3..e1c18038d 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 @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.equipment.Equipment; -import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.ModernItemModel; @@ -18,14 +18,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.function.Function; public interface ItemManager extends Manageable, ModelGenerator { - void registerDataType(Function> factory, String... alias); - - Function> getDataType(String key); - Map equipments(); ConfigParser[] parsers(); @@ -59,10 +54,6 @@ public interface ItemManager extends Manageable, ModelGenerator { Collection items(); - Key itemId(T itemStack); - - Key customItemId(T itemStack); - ExternalItemSource getExternalItemSource(String name); boolean registerExternalItemSource(ExternalItemSource externalItemSource); @@ -87,11 +78,16 @@ public interface ItemManager extends Manageable, ModelGenerator { boolean addCustomItem(CustomItem customItem); - List tagToItems(Key tag); + default List itemIdsByTag(Key tag) { + List items = new ArrayList<>(); + items.addAll(vanillaItemIdsByTag(tag)); + items.addAll(customItemIdsByTag(tag)); + return items; + } - List tagToVanillaItems(Key tag); + List vanillaItemIdsByTag(Key tag); - List tagToCustomItems(Key tag); + List customItemIdsByTag(Key tag); int fuelTime(T itemStack); @@ -114,4 +110,6 @@ public interface ItemManager extends Manageable, ModelGenerator { UniqueIdItem uniqueEmptyItem(); Item applyTrim(Item base, Item addition, Item template, Key pattern); + + Item build(DatapackRecipeResult result); } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 9295d1745..f8d5ad125 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -25,12 +25,12 @@ import java.util.stream.Collectors; public class ItemSettings { int fuelTime; Set tags = Set.of(); - boolean canRepair = true; + Tristate canRepair = Tristate.UNDEFINED; List anvilRepairItems = List.of(); boolean renameable = true; boolean canPlaceRelatedVanillaBlock = false; ProjectileMeta projectileMeta; - boolean dyeable = true; + Tristate dyeable = Tristate.UNDEFINED; Helmet helmet = null; FoodData foodData = null; Key consumeReplacement = null; @@ -41,6 +41,10 @@ public class ItemSettings { boolean respectRepairableComponent = false; @Nullable ItemEquipment equipment; + @Nullable + Color dyeColor; + @Nullable + Color fireworkColor; private ItemSettings() {} @@ -99,6 +103,8 @@ public class ItemSettings { newSettings.canEnchant = settings.canEnchant; newSettings.compostProbability = settings.compostProbability; newSettings.respectRepairableComponent = settings.respectRepairableComponent; + newSettings.dyeColor = settings.dyeColor; + newSettings.fireworkColor = settings.fireworkColor; return newSettings; } @@ -122,7 +128,7 @@ public class ItemSettings { return canPlaceRelatedVanillaBlock; } - public boolean canRepair() { + public Tristate canRepair() { return canRepair; } @@ -138,7 +144,7 @@ public class ItemSettings { return tags; } - public boolean dyeable() { + public Tristate dyeable() { return dyeable; } @@ -179,6 +185,16 @@ public class ItemSettings { return equipment; } + @Nullable + public Color dyeColor() { + return this.dyeColor; + } + + @Nullable + public Color fireworkColor() { + return this.fireworkColor; + } + public List invulnerable() { return invulnerable; } @@ -187,6 +203,16 @@ public class ItemSettings { return compostProbability; } + public ItemSettings fireworkColor(Color color) { + this.fireworkColor = color; + return this; + } + + public ItemSettings dyeColor(Color color) { + this.dyeColor = color; + return this; + } + public ItemSettings repairItems(List items) { this.anvilRepairItems = items; return this; @@ -207,7 +233,7 @@ public class ItemSettings { return this; } - public ItemSettings canRepair(boolean canRepair) { + public ItemSettings canRepair(Tristate canRepair) { this.canRepair = canRepair; return this; } @@ -252,7 +278,7 @@ public class ItemSettings { return this; } - public ItemSettings dyeable(boolean bool) { + public ItemSettings dyeable(Tristate bool) { this.dyeable = bool; return this; } @@ -290,7 +316,7 @@ public class ItemSettings { static { registerFactory("repairable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "repairable"); - return settings -> settings.canRepair(bool); + return settings -> settings.canRepair(bool ? Tristate.TRUE : Tristate.FALSE); })); registerFactory("enchantable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "enchantable"); @@ -325,7 +351,13 @@ public class ItemSettings { })); registerFactory("tags", (value -> { List tags = MiscUtils.getAsStringList(value); - return settings -> settings.tags(tags.stream().map(Key::of).collect(Collectors.toSet())); + return settings -> settings.tags(tags.stream().map(it -> { + if (it.charAt(0) == '#') { + return Key.of(it.substring(1)); + } else { + return Key.of(it); + } + }).collect(Collectors.toSet())); })); registerFactory("equippable", (value -> { Map args = MiscUtils.castToMap(value, false); @@ -384,12 +416,26 @@ public class ItemSettings { })); registerFactory("dyeable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "dyeable"); - return settings -> settings.dyeable(bool); + return settings -> settings.dyeable(bool ? Tristate.TRUE : Tristate.FALSE); })); registerFactory("respect-repairable-component", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "respect-repairable-component"); return settings -> settings.respectRepairableComponent(bool); })); + registerFactory("dye-color", (value -> { + if (value instanceof Integer i) { + return settings -> settings.dyeColor(Color.fromDecimal(i)); + } else { + return settings -> settings.dyeColor(Color.fromVector3f(MiscUtils.getAsVector3f(value, "dye-color"))); + } + })); + registerFactory("firework-color", (value -> { + if (value instanceof Integer i) { + return settings -> settings.fireworkColor(Color.fromDecimal(i)); + } else { + return settings -> settings.fireworkColor(Color.fromVector3f(MiscUtils.getAsVector3f(value, "firework-color"))); + } + })); registerFactory("food", (value -> { Map args = MiscUtils.castToMap(value, false); FoodData data = new FoodData( diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/data/FireworkExplosion.java b/core/src/main/java/net/momirealms/craftengine/core/item/data/FireworkExplosion.java index bad976667..279f51799 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/data/FireworkExplosion.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/data/FireworkExplosion.java @@ -8,6 +8,12 @@ import java.util.Map; public record FireworkExplosion(Shape shape, IntList colors, IntList fadeColors, boolean hasTrail, boolean hasTwinkle) { + public static final FireworkExplosion DEFAULT = new FireworkExplosion(Shape.SMALL_BALL, IntList.of(), IntList.of(), false, false); + + public FireworkExplosion withFadeColors(@NotNull final IntList fadeColors) { + return new FireworkExplosion(this.shape, this.colors, fadeColors, this.hasTrail, this.hasTwinkle); + } + public enum Shape { SMALL_BALL(0, "small_ball"), LARGE_BALL(1, "large_ball"), diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentsModifier.java similarity index 59% rename from core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentModifier.java rename to core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentsModifier.java index 63628171d..33f4bb98f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ArgumentsModifier.java @@ -3,7 +3,11 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.plugin.context.text.TextProvider; +import net.momirealms.craftengine.core.plugin.context.text.TextProviders; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.StringTag; @@ -12,17 +16,22 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -public class ArgumentModifier implements ItemDataModifier { +public class ArgumentsModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); public static final String ARGUMENTS_TAG = "craftengine:arguments"; private final Map arguments; - public ArgumentModifier(Map arguments) { + public ArgumentsModifier(Map arguments) { this.arguments = arguments; } + public Map arguments() { + return arguments; + } + @Override - public String name() { - return "arguments"; + public Key type() { + return ItemDataModifiers.ARGUMENTS; } @Override @@ -44,4 +53,17 @@ public class ArgumentModifier implements ItemDataModifier { } return item; } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "arguments"); + Map arguments = new HashMap<>(); + for (Map.Entry entry : data.entrySet()) { + arguments.put(entry.getKey(), TextProviders.fromString(entry.getValue().toString())); + } + return new ArgumentsModifier<>(arguments); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java index 1bc71cff2..fc1ed038b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/AttributeModifiersModifier.java @@ -6,19 +6,16 @@ import net.momirealms.craftengine.core.attribute.Attributes1_21; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.UUIDUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.ListTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.Nullable; -import java.nio.charset.StandardCharsets; import java.util.*; -public class AttributeModifiersModifier implements ItemDataModifier { +public class AttributeModifiersModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); public static final Map CONVERTOR = new HashMap<>(); static { @@ -91,100 +88,101 @@ public class AttributeModifiersModifier implements ItemDataModifier { return CONVERTOR.getOrDefault(attributeName, attributeName); } - private final List modifiers; + private final List modifiers; - public AttributeModifiersModifier(List modifiers) { + public AttributeModifiersModifier(List modifiers) { this.modifiers = modifiers; } - public List modifiers() { + public List modifiers() { return this.modifiers; } @Override - public String name() { - return "attribute-modifiers"; + public Key type() { + return ItemDataModifiers.ATTRIBUTE_MODIFIERS; } - private static Object previous; - @Override public Item apply(Item item, ItemBuildContext context) { - if (VersionHelper.isOrAbove1_21_5()) { - ListTag modifiers = new ListTag(); - for (AttributeModifier modifier : this.modifiers) { - CompoundTag modifierTag = new CompoundTag(); - modifierTag.putString("type", modifier.type()); - modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); - modifierTag.putString("id", modifier.id().toString()); - modifierTag.putDouble("amount", modifier.amount()); - modifierTag.putString("operation", modifier.operation().id()); - AttributeModifier.Display display = modifier.display(); - if (VersionHelper.isOrAbove1_21_6() && display != null) { - CompoundTag displayTag = new CompoundTag(); - AttributeModifier.Display.Type displayType = display.type(); - displayTag.putString("type", displayType.name().toLowerCase(Locale.ENGLISH)); - if (displayType == AttributeModifier.Display.Type.OVERRIDE) { - displayTag.put("value", AdventureHelper.componentToTag(AdventureHelper.miniMessage().deserialize(display.value(), context.tagResolvers()))); - } - modifierTag.put("display", displayTag); - } - modifiers.add(modifierTag); - } - item.setNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, modifiers); - } else if (VersionHelper.isOrAbove1_20_5()) { - CompoundTag compoundTag = (CompoundTag) Optional.ofNullable(item.getSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS)).orElseGet(CompoundTag::new); - ListTag modifiers = new ListTag(); - compoundTag.put("modifiers", modifiers); - for (AttributeModifier modifier : this.modifiers) { - CompoundTag modifierTag = new CompoundTag(); - modifierTag.putString("type", modifier.type()); - modifierTag.putString("slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); - if (VersionHelper.isOrAbove1_21()) { - modifierTag.putString("id", modifier.id().toString()); - } else { - modifierTag.putIntArray("uuid", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8)))); - modifierTag.putString("name", modifier.id().toString()); - } - modifierTag.putDouble("amount", modifier.amount()); - modifierTag.putString("operation", modifier.operation().id()); - modifiers.add(modifierTag); - } - item.setNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, compoundTag); - } else { - ListTag listTag = new ListTag(); - for (AttributeModifier modifier : this.modifiers) { - CompoundTag modifierTag = new CompoundTag(); - modifierTag.putString("AttributeName", modifier.type()); - modifierTag.putString("Name", modifier.id().toString()); - modifierTag.putString("Slot", modifier.slot().name().toLowerCase(Locale.ENGLISH)); - modifierTag.putInt("Operation", modifier.operation().ordinal()); - modifierTag.putDouble("Amount", modifier.amount()); - modifierTag.putIntArray("UUID", UUIDUtils.uuidToIntArray(UUID.nameUUIDFromBytes(modifier.id().toString().getBytes(StandardCharsets.UTF_8)))); - listTag.add(modifierTag); - } - item.setTag(listTag, "AttributeModifiers"); - } - return item; + return item.attributeModifiers(this.modifiers.stream().map(it -> it.toAttributeModifier(context)).toList()); } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS); - if (previous != null) { - networkData.put(ComponentKeys.ATTRIBUTE_MODIFIERS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.ATTRIBUTE_MODIFIERS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("AttributeModifiers"); - if (previous != null) { - networkData.put("AttributeModifiers", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("AttributeModifiers", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.ATTRIBUTE_MODIFIERS; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"AttributeModifiers"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "AttributeModifiers"; + } + + public record PreModifier(String type, + AttributeModifier.Slot slot, + Key id, + NumberProvider amount, + AttributeModifier.Operation operation, + AttributeModifiersModifier.PreModifier.@Nullable PreDisplay display) { + + public PreModifier(String type, AttributeModifier.Slot slot, Key id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) { + this.amount = amount; + this.type = type; + this.slot = slot; + this.id = id; + this.operation = operation; + this.display = display; + } + + public AttributeModifier toAttributeModifier(ItemBuildContext context) { + return new AttributeModifier(type, slot, id, amount.getDouble(context), operation, display == null ? null : display.toDisplay(context)); + } + + public record PreDisplay(AttributeModifier.Display.Type type, String value) { + + public AttributeModifier.Display toDisplay(ItemBuildContext context) { + return new AttributeModifier.Display(type, AdventureHelper.miniMessage().deserialize(value, context.tagResolvers())); } } - return item; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + MutableInt mutableInt = new MutableInt(0); + List attributeModifiers = ResourceConfigUtils.parseConfigAsList(arg, (map) -> { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type"); + Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type)); + AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH)); + Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> { + mutableInt.add(1); + return Key.of("craftengine", "modifier_" + mutableInt.intValue()); + }); + NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount")); + AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf( + ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH) + ); + PreModifier.PreDisplay display = null; + if (VersionHelper.isOrAbove1_21_6() && map.containsKey("display")) { + Map displayMap = MiscUtils.castToMap(map.get("display"), false); + AttributeModifier.Display.Type displayType = AttributeModifier.Display.Type.valueOf(ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("type"), "warning.config.item.data.attribute_modifiers.display.missing_type").toUpperCase(Locale.ENGLISH)); + if (displayType == AttributeModifier.Display.Type.OVERRIDE) { + String miniMessageValue = ResourceConfigUtils.requireNonEmptyStringOrThrow(displayMap.get("value"), "warning.config.item.data.attribute_modifiers.display.missing_value"); + display = new PreModifier.PreDisplay(displayType, miniMessageValue); + } else { + display = new PreModifier.PreDisplay(displayType, null); + } + } + return new PreModifier(nativeType.value(), slot, id, + amount, operation, display); + }); + return new AttributeModifiersModifier<>(attributeModifiers); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java rename to core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index 3dc223ca3..7e822a08d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -1,14 +1,12 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.gson.JsonElement; -import net.momirealms.craftengine.core.item.ComponentKeys; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Pair; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -16,11 +14,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -public class ComponentModifier implements ItemDataModifier { +public class ComponentsModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final List> arguments; private CompoundTag customData = null; - public ComponentModifier(Map arguments) { + public ComponentsModifier(Map arguments) { List> pairs = new ArrayList<>(arguments.size()); for (Map.Entry entry : arguments.entrySet()) { Key key = Key.of(entry.getKey()); @@ -33,7 +32,7 @@ public class ComponentModifier implements ItemDataModifier { this.arguments = pairs; } - public List> arguments() { + public List> components() { return arguments; } @@ -49,8 +48,8 @@ public class ComponentModifier implements ItemDataModifier { } @Override - public String name() { - return "components"; + public Key type() { + return ItemDataModifiers.COMPONENTS; } @Override @@ -84,4 +83,13 @@ public class ComponentModifier implements ItemDataModifier { } return item; } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "components"); + return new ComponentsModifier<>(data); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomModelDataModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomModelDataModifier.java index f76bb1c78..9d1c68671 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomModelDataModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomModelDataModifier.java @@ -3,21 +3,26 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; -public class CustomModelDataModifier implements ItemDataModifier { +public class CustomModelDataModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final int argument; public CustomModelDataModifier(int argument) { this.argument = argument; } + public int customModelData() { + return this.argument; + } + @Override - public String name() { - return "custom-model-data"; + public Key type() { + return ItemDataModifiers.CUSTOM_MODEL_DATA; } @Override @@ -27,22 +32,26 @@ public class CustomModelDataModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.CUSTOM_MODEL_DATA); - if (previous != null) { - networkData.put(ComponentKeys.CUSTOM_MODEL_DATA.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.CUSTOM_MODEL_DATA.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("CustomModelData"); - if (previous != null) { - networkData.put("CustomModelData", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("CustomModelData", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.CUSTOM_MODEL_DATA; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"CustomModelData"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "CustomModelData"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + int customModelData = ResourceConfigUtils.getAsInt(arg, "custom-model-data"); + return new CustomModelDataModifier<>(customModelData); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java index ecbbbe33a..8989c2225 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java @@ -3,14 +3,14 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; -public class CustomNameModifier implements ItemDataModifier { +public class CustomNameModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final String argument; public CustomNameModifier(String argument) { @@ -25,9 +25,13 @@ public class CustomNameModifier implements ItemDataModifier { } } + public String customName() { + return argument; + } + @Override - public String name() { - return "custom-name"; + public Key type() { + return ItemDataModifiers.CUSTOM_NAME; } @Override @@ -37,22 +41,26 @@ public class CustomNameModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.CUSTOM_NAME); - if (previous != null) { - networkData.put(ComponentKeys.CUSTOM_NAME.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.CUSTOM_NAME.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "Name"); - if (previous != null) { - networkData.put("display.Name", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.Name", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.CUSTOM_NAME; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"display", "Name"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "display.Name"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + String name = arg.toString(); + return new CustomNameModifier<>(name); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java index a93bbf284..2ce36e797 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java @@ -3,46 +3,60 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.util.Color; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; -public class DyedColorModifier implements ItemDataModifier { - private final int color; +public class DyedColorModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + private final Color color; - public DyedColorModifier(int color) { + public DyedColorModifier(Color color) { this.color = color; } + public Color dyedColor() { + return color; + } + @Override - public String name() { - return "dyed-color"; + public Key type() { + return ItemDataModifiers.DYED_COLOR; } @Override public Item apply(Item item, ItemBuildContext context) { - item.dyedColor(this.color); - return item; + return item.dyedColor(this.color); } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.DYED_COLOR); - if (previous != null) { - networkData.put(ComponentKeys.DYED_COLOR.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.DYED_COLOR; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"display", "color"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "display.color"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + if (arg instanceof Integer integer) { + return new DyedColorModifier<>(Color.fromDecimal(integer)); } else { - networkData.put(ComponentKeys.DYED_COLOR.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "color"); - if (previous != null) { - networkData.put("display.color", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.color", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + Vector3f vector3f = MiscUtils.getAsVector3f(arg, "dyed-color"); + return new DyedColorModifier<>(Color.fromVector3f(vector3f)); } } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java deleted file mode 100644 index 65392a1b8..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java +++ /dev/null @@ -1,66 +0,0 @@ -package net.momirealms.craftengine.core.item.modifier; - -import net.momirealms.craftengine.core.item.ComponentKeys; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class DynamicLoreModifier implements ItemDataModifier { - public static final String CONTEXT_TAG_KEY = "craftengine:display_context"; - private final Map> displayContexts; - private final String defaultContext; - - public DynamicLoreModifier(Map> displayContexts) { - this.defaultContext = displayContexts.keySet().iterator().next(); - this.displayContexts = displayContexts; - } - - public Map> displayContexts() { - return Collections.unmodifiableMap(this.displayContexts); - } - - @Override - public String name() { - return "dynamic-lore"; - } - - @Override - public Item apply(Item item, ItemBuildContext context) { - String displayContext = Optional.ofNullable(item.getJavaTag(CONTEXT_TAG_KEY)).orElse(this.defaultContext).toString(); - List lore = this.displayContexts.get(displayContext); - if (lore == null) { - lore = this.displayContexts.get(this.defaultContext); - } - item.loreComponent(lore.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); - return item; - } - - @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.LORE); - if (previous != null) { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "Lore"); - if (previous != null) { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } - return item; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentModifier.java deleted file mode 100644 index 9168de84a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentModifier.java +++ /dev/null @@ -1,70 +0,0 @@ -package net.momirealms.craftengine.core.item.modifier; - -import net.momirealms.craftengine.core.item.*; -import net.momirealms.craftengine.core.item.data.Enchantment; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; - -import java.util.List; - -public class EnchantmentModifier implements ItemDataModifier { - private final List enchantments; - - public EnchantmentModifier(List enchantments) { - this.enchantments = enchantments; - } - - @Override - public String name() { - return "enchantment"; - } - - @Override - public Item apply(Item item, ItemBuildContext context) { - if (item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { - item.setStoredEnchantments(this.enchantments); - } else { - item.setEnchantments(this.enchantments); - } - return item; - } - - @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.STORED_ENCHANTMENTS); - if (previous != null) { - networkData.put(ComponentKeys.STORED_ENCHANTMENTS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.STORED_ENCHANTMENTS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("StoredEnchantments"); - if (previous != null) { - networkData.put("StoredEnchantments", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("StoredEnchantments", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } - } else { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.ENCHANTMENTS); - if (previous != null) { - networkData.put(ComponentKeys.ENCHANTMENTS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.ENCHANTMENTS.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("Enchantments"); - if (previous != null) { - networkData.put("Enchantments", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("Enchantments", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } - } - return item; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentsModifier.java new file mode 100644 index 000000000..8dbb7a591 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EnchantmentsModifier.java @@ -0,0 +1,68 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.*; +import net.momirealms.craftengine.core.item.data.Enchantment; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class EnchantmentsModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + private final List enchantments; + + public EnchantmentsModifier(List enchantments) { + this.enchantments = enchantments; + } + + public List enchantments() { + return enchantments; + } + + @Override + public Key type() { + return ItemDataModifiers.ENCHANTMENTS; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + if (item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { + return item.setStoredEnchantments(this.enchantments); + } else { + return item.setEnchantments(this.enchantments); + } + } + + @Override + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK) ? ComponentKeys.STORED_ENCHANTMENTS : ComponentKeys.ENCHANTMENTS; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK) ? new Object[]{"StoredEnchantments"} : new Object[]{"Enchantments"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return item.vanillaId().equals(ItemKeys.ENCHANTED_BOOK) ? "StoredEnchantments" : "Enchantments"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "enchantments"); + List enchantments = new ArrayList<>(); + for (Map.Entry e : data.entrySet()) { + if (e.getValue() instanceof Number number) { + enchantments.add(new Enchantment(Key.of(e.getKey()), number.intValue())); + } + } + return new EnchantmentsModifier<>(enchantments); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableAssetIdModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableAssetIdModifier.java index 48630e358..d6cb67ac6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableAssetIdModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableAssetIdModifier.java @@ -3,24 +3,26 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.Nullable; import java.util.Optional; -public class EquippableAssetIdModifier implements ItemDataModifier { +public class EquippableAssetIdModifier implements SimpleNetworkItemDataModifier { private final Key assetId; public EquippableAssetIdModifier(Key assetsId) { this.assetId = assetsId; } + public Key assetId() { + return assetId; + } + @Override - public String name() { - return "equippable-asset-id"; + public Key type() { + return ItemDataModifiers.EQUIPPABLE_ASSET_ID; } @Override @@ -39,13 +41,7 @@ public class EquippableAssetIdModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.EQUIPPABLE); - if (previous != null) { - networkData.put(ComponentKeys.EQUIPPABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.EQUIPPABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - return item; + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.EQUIPPABLE; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableModifier.java index 9851c1141..6570920da 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/EquippableModifier.java @@ -1,24 +1,49 @@ package net.momirealms.craftengine.core.item.modifier; +import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.setting.EquipmentData; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; -public class EquippableModifier implements ItemDataModifier { +import java.util.Map; + +public class EquippableModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final EquipmentData data; public EquippableModifier(EquipmentData data) { this.data = data; } + public EquipmentData data() { + return data; + } + @Override - public String name() { - return "equippable"; + public Key type() { + return ItemDataModifiers.EQUIPPABLE; } @Override public Item apply(Item item, ItemBuildContext context) { - item.equippable(this.data); - return item; + return item.equippable(this.data); + } + + @Override + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.EQUIPPABLE; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "equippable"); + return new EquippableModifier<>(EquipmentData.fromMap(data)); + } } } 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 index 3acc923b8..a424feb2a 100644 --- 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 @@ -3,9 +3,17 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ExternalItemSource; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Locale; +import java.util.Map; public class ExternalModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final String id; private final ExternalItemSource provider; @@ -14,9 +22,17 @@ public class ExternalModifier implements ItemDataModifier { this.provider = provider; } + public String id() { + return id; + } + + public ExternalItemSource source() { + return provider; + } + @Override - public String name() { - return "external"; + public Key type() { + return ItemDataModifiers.EXTERNAL; } @SuppressWarnings("unchecked") @@ -31,4 +47,17 @@ public class ExternalModifier implements ItemDataModifier { item.merge(anotherWrapped); return item; } + + public static class Factory implements ItemDataModifierFactory { + + @SuppressWarnings("unchecked") + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "external"); + String plugin = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(data, "plugin", "source"), "warning.config.item.data.external.missing_source"); + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(data.get("id"), "warning.config.item.data.external.missing_id"); + ExternalItemSource provider = (ExternalItemSource) CraftEngine.instance().itemManager().getExternalItemSource(plugin.toLowerCase(Locale.ENGLISH)); + return new ExternalModifier<>(id, ResourceConfigUtils.requireNonNullOrThrow(provider, () -> new LocalizedResourceConfigException("warning.config.item.data.external.invalid_source", plugin))); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/FoodModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/FoodModifier.java index 4ab82b684..42fcb7f65 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/FoodModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/FoodModifier.java @@ -3,13 +3,15 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; import java.util.Map; -public class FoodModifier implements ItemDataModifier { +public class FoodModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final int nutrition; private final float saturation; private final boolean canAlwaysEat; @@ -20,9 +22,21 @@ public class FoodModifier implements ItemDataModifier { this.saturation = saturation; } + public boolean canAlwaysEat() { + return canAlwaysEat; + } + + public int nutrition() { + return nutrition; + } + + public float saturation() { + return saturation; + } + @Override - public String name() { - return "food"; + public Key type() { + return ItemDataModifiers.FOOD; } @Override @@ -36,13 +50,18 @@ public class FoodModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.FOOD); - if (previous != null) { - networkData.put(ComponentKeys.FOOD.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.FOOD.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.FOOD; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "food"); + int nutrition = ResourceConfigUtils.getAsInt(data.get("nutrition"), "nutrition"); + float saturation = ResourceConfigUtils.getAsFloat(data.get("saturation"), "saturation"); + return new FoodModifier<>(nutrition, saturation, ResourceConfigUtils.getAsBoolean(data.getOrDefault("can-always-eat", false), "can-always-eat")); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/HideTooltipModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/HideTooltipModifier.java index 20c2bab7c..e2b229923 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/HideTooltipModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/HideTooltipModifier.java @@ -1,10 +1,7 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.common.collect.ImmutableMap; -import net.momirealms.craftengine.core.item.ComponentKeys; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; @@ -19,7 +16,19 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public class HideTooltipModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); public static final Map TO_LEGACY; + public static final List COMPONENTS = List.of( + ComponentKeys.UNBREAKABLE, + ComponentKeys.ENCHANTMENTS, + ComponentKeys.STORED_ENCHANTMENTS, + ComponentKeys.CAN_PLACE_ON, + ComponentKeys.CAN_BREAK, + ComponentKeys.ATTRIBUTE_MODIFIERS, + ComponentKeys.DYED_COLOR, + ComponentKeys.TRIM, + ComponentKeys.JUKEBOX_PLAYABLE + ); static { ImmutableMap.Builder builder = ImmutableMap.builder(); builder.put(ComponentKeys.ENCHANTMENTS, 1); @@ -52,13 +61,24 @@ public class HideTooltipModifier implements ItemDataModifier { if (components.isEmpty()) { this.applier = new DummyApplier<>(); } else if (components.size() == 1) { - this.applier = new SemiModernApplier<>(components.getFirst()); + if (COMPONENTS.contains(components.getFirst())) { + this.applier = new SemiModernApplier<>(components.getFirst()); + } else { + this.applier = new DummyApplier<>(); + } } else { List> appliers = new ArrayList<>(); for (Key key : components) { + if (!COMPONENTS.contains(key)) continue; appliers.add(new SemiModernApplier<>(key)); } - this.applier = new CompoundApplier<>(appliers); + if (appliers.isEmpty()) { + this.applier = new DummyApplier<>(); + } else if (appliers.size() == 1) { + this.applier = appliers.getFirst(); + } else { + this.applier = new CompoundApplier<>(appliers); + } } } else { this.applier = new LegacyApplier<>(components); @@ -66,7 +86,7 @@ public class HideTooltipModifier implements ItemDataModifier { } public List components() { - return components; + return this.components; } @Override @@ -105,8 +125,8 @@ public class HideTooltipModifier implements ItemDataModifier { } @Override - public String name() { - return "hide-tooltip"; + public Key type() { + return ItemDataModifiers.HIDE_TOOLTIP; } public interface Applier { @@ -134,10 +154,6 @@ public class HideTooltipModifier implements ItemDataModifier { if (previous instanceof CompoundTag compoundTag) { compoundTag.putBoolean("show_in_tooltip", false); item.setNBTComponent(this.component, compoundTag); - } else { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putBoolean("show_in_tooltip", false); - item.setNBTComponent(this.component, compoundTag); } } } @@ -215,4 +231,13 @@ public class HideTooltipModifier implements ItemDataModifier { } } } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + List components = MiscUtils.getAsStringList(arg).stream().map(Key::of).toList(); + return new HideTooltipModifier<>(components); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/IdModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/IdModifier.java index 690236b87..a262ac971 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/IdModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/IdModifier.java @@ -12,9 +12,13 @@ public class IdModifier implements ItemDataModifier { this.argument = argument; } + public Key identifier() { + return argument; + } + @Override - public String name() { - return "id"; + public Key type() { + return ItemDataModifiers.ID; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifier.java index 2ffe87f59..6a39cf797 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifier.java @@ -2,11 +2,12 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.sparrow.nbt.CompoundTag; public interface ItemDataModifier { - String name(); + Key type(); Item apply(Item item, ItemBuildContext context); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java new file mode 100644 index 000000000..f120401e8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java @@ -0,0 +1,88 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.item.modifier.lore.DynamicLoreModifier; +import net.momirealms.craftengine.core.item.modifier.lore.LoreModifier; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; +import net.momirealms.craftengine.core.util.VersionHelper; + +public final class ItemDataModifiers { + private ItemDataModifiers() {} + + public static final Key ITEM_MODEL = Key.of("craftengine:item-model"); + public static final Key ID = Key.of("craftengine:id"); + public static final Key HIDE_TOOLTIP = Key.of("craftengine:hide-tooltip"); + public static final Key FOOD = Key.of("craftengine:food"); + public static final Key EXTERNAL = Key.of("craftengine:external"); + public static final Key EQUIPPABLE = Key.of("craftengine:equippable"); + public static final Key EQUIPPABLE_ASSET_ID = Key.of("craftengine:equippable-asset-id"); + public static final Key ENCHANTMENT = Key.of("craftengine:enchantment"); + public static final Key ENCHANTMENTS = Key.of("craftengine:enchantments"); + public static final Key DYED_COLOR = Key.of("craftengine:dyed-color"); + public static final Key DISPLAY_NAME = Key.of("craftengine:display-name"); + public static final Key CUSTOM_NAME = Key.of("craftengine:custom-name"); + public static final Key CUSTOM_MODEL_DATA = Key.of("craftengine:custom-model-data"); + public static final Key COMPONENTS = Key.of("craftengine:components"); + public static final Key ATTRIBUTE_MODIFIERS = Key.of("craftengine:attribute-modifiers"); + public static final Key ATTRIBUTES = Key.of("craftengine:attributes"); + public static final Key ARGUMENTS = Key.of("craftengine:arguments"); + public static final Key ITEM_NAME = Key.of("craftengine:item-name"); + public static final Key JUKEBOX_PLAYABLE = Key.of("craftengine:jukebox-playable"); + public static final Key REMOVE_COMPONENTS = Key.of("craftengine:remove-components"); + public static final Key TAGS = Key.of("craftengine:tags"); + public static final Key NBT = Key.of("craftengine:nbt"); + public static final Key TOOLTIP_STYLE = Key.of("craftengine:tooltip-style"); + public static final Key TRIM = Key.of("craftengine:trim"); + public static final Key LORE = Key.of("craftengine:lore"); + public static final Key UNBREAKABLE = Key.of("craftengine:unbreakable"); + public static final Key DYNAMIC_LORE = Key.of("craftengine:dynamic-lore"); + + public static void register(Key key, ItemDataModifierFactory factory) { + ((WritableRegistry>) BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY) + .register(ResourceKey.create(Registries.ITEM_DATA_MODIFIER_FACTORY.location(), key), factory); + } + + public static void init() {} + + static { + register(EXTERNAL, ExternalModifier.FACTORY); + register(LORE, LoreModifier.FACTORY); + register(DYNAMIC_LORE, DynamicLoreModifier.FACTORY); + register(DYED_COLOR, DyedColorModifier.FACTORY); + register(TAGS, TagsModifier.FACTORY); + register(NBT, TagsModifier.FACTORY); + register(ATTRIBUTE_MODIFIERS, AttributeModifiersModifier.FACTORY); + register(ATTRIBUTES, AttributeModifiersModifier.FACTORY); + register(CUSTOM_MODEL_DATA, CustomModelDataModifier.FACTORY); + register(UNBREAKABLE, UnbreakableModifier.FACTORY); + register(ENCHANTMENT, EnchantmentsModifier.FACTORY); + register(ENCHANTMENTS, EnchantmentsModifier.FACTORY); + register(TRIM, TrimModifier.FACTORY); + register(HIDE_TOOLTIP, HideTooltipModifier.FACTORY); + register(ARGUMENTS, ArgumentsModifier.FACTORY); + if (VersionHelper.isOrAbove1_20_5()) { + register(CUSTOM_NAME, CustomNameModifier.FACTORY); + register(ITEM_NAME, ItemNameModifier.FACTORY); + register(DISPLAY_NAME, ItemNameModifier.FACTORY); + register(COMPONENTS, ComponentsModifier.FACTORY); + register(REMOVE_COMPONENTS, RemoveComponentModifier.FACTORY); + register(FOOD, FoodModifier.FACTORY); + } else { + register(CUSTOM_NAME, CustomNameModifier.FACTORY); + register(ITEM_NAME, CustomNameModifier.FACTORY); + register(DISPLAY_NAME, CustomNameModifier.FACTORY); + } + if (VersionHelper.isOrAbove1_21()) { + register(JUKEBOX_PLAYABLE, JukeboxSongModifier.FACTORY); + } + if (VersionHelper.isOrAbove1_21_2()) { + register(TOOLTIP_STYLE, TooltipStyleModifier.FACTORY); + register(ITEM_MODEL, ItemModelModifier.FACTORY); + register(EQUIPPABLE, EquippableModifier.FACTORY); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemModelModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemModelModifier.java index 319cacc13..c83be84d6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemModelModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemModelModifier.java @@ -3,37 +3,43 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.Nullable; -public class ItemModelModifier implements ItemDataModifier { +public class ItemModelModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final Key data; public ItemModelModifier(Key data) { this.data = data; } + public Key data() { + return data; + } + @Override - public String name() { - return "item-model"; + public Key type() { + return ItemDataModifiers.ITEM_MODEL; } @Override public Item apply(Item item, ItemBuildContext context) { - item.itemModel(this.data.toString()); - return item; + return item.itemModel(this.data.asString()); } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.ITEM_MODEL); - if (previous != null) { - networkData.put(ComponentKeys.ITEM_MODEL.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.ITEM_MODEL.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.ITEM_MODEL; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + String id = arg.toString(); + return new ItemModelModifier<>(Key.of(id)); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java index 63cfac93f..20728b979 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java @@ -3,22 +3,26 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; -public class ItemNameModifier implements ItemDataModifier { +public class ItemNameModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final String argument; public ItemNameModifier(String argument) { this.argument = argument; } + public String itemName() { + return argument; + } + @Override - public String name() { - return "item-name"; + public Key type() { + return ItemDataModifiers.ITEM_NAME; } @Override @@ -28,22 +32,26 @@ public class ItemNameModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.ITEM_NAME); - if (previous != null) { - networkData.put(ComponentKeys.ITEM_NAME.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.ITEM_NAME.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "Name"); - if (previous != null) { - networkData.put("display.Name", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.Name", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.ITEM_NAME; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"display", "Name"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "display.Name"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + String name = arg.toString(); + return new ItemNameModifier<>(name); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/JukeboxSongModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/JukeboxSongModifier.java index 6ccd1ec12..7702865f6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/JukeboxSongModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/JukeboxSongModifier.java @@ -2,18 +2,25 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; +import net.momirealms.craftengine.core.util.Key; public class JukeboxSongModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final JukeboxPlayable song; public JukeboxSongModifier(JukeboxPlayable song) { this.song = song; } + public JukeboxPlayable song() { + return song; + } + @Override - public String name() { - return "jukebox-playable"; + public Key type() { + return ItemDataModifiers.JUKEBOX_PLAYABLE; } @Override @@ -21,4 +28,13 @@ public class JukeboxSongModifier implements ItemDataModifier { item.jukeboxSong(this.song); return item; } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + String song = arg.toString(); + return new JukeboxSongModifier<>(new JukeboxPlayable(song, true)); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java deleted file mode 100644 index 6c5c88a59..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java +++ /dev/null @@ -1,65 +0,0 @@ -package net.momirealms.craftengine.core.item.modifier; - -import net.momirealms.craftengine.core.item.ComponentKeys; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; - -import java.util.ArrayList; -import java.util.List; - -public class LoreModifier implements ItemDataModifier { - private final List argument; - - public LoreModifier(List argument) { - if (Config.addNonItalicTag()) { - List processed = new ArrayList<>(argument.size()); - for (String arg : argument) { - if (arg.startsWith("")) { - processed.add(arg); - } else { - processed.add("" + arg); - } - } - this.argument = processed; - } else { - this.argument = argument; - } - } - - @Override - public String name() { - return "lore"; - } - - @Override - public Item apply(Item item, ItemBuildContext context) { - item.loreComponent(this.argument.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); - return item; - } - - @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.LORE); - if (previous != null) { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("display", "Lore"); - if (previous != null) { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } - return item; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/RemoveComponentModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/RemoveComponentModifier.java index 324db069b..a7ffd1c0b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/RemoveComponentModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/RemoveComponentModifier.java @@ -2,7 +2,10 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -10,19 +13,20 @@ import java.util.Collections; import java.util.List; public class RemoveComponentModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final List arguments; public RemoveComponentModifier(List arguments) { this.arguments = arguments; } - public List arguments() { + public List components() { return Collections.unmodifiableList(this.arguments); } @Override - public String name() { - return "remove-components"; + public Key type() { + return ItemDataModifiers.REMOVE_COMPONENTS; } @Override @@ -43,4 +47,13 @@ public class RemoveComponentModifier implements ItemDataModifier { } return item; } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + List data = MiscUtils.getAsStringList(arg); + return new RemoveComponentModifier<>(data); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/SimpleNetworkItemDataModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/SimpleNetworkItemDataModifier.java new file mode 100644 index 000000000..2d5664ff0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/SimpleNetworkItemDataModifier.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.Nullable; + +public interface SimpleNetworkItemDataModifier extends ItemDataModifier { + + @Override + default Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { + if (VersionHelper.COMPONENT_RELEASE) { + Key componentType= componentType(item, context); + if (componentType != null) { + Tag previous = item.getSparrowNBTComponent(componentType); + if (previous != null) { + networkData.put(componentType.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + } else { + networkData.put(componentType.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + } + } else { + Object[] path = nbtPath(item, context); + if (path != null) { + Tag previous = item.getTag(path); + if (previous != null) { + networkData.put(nbtPathString(item, context), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + } else { + networkData.put(nbtPathString(item, context), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + } + } + return item; + } + + @Nullable + default Key componentType(Item item, ItemBuildContext context) { + return null; + } + + @Nullable + default Object[] nbtPath(Item item, ItemBuildContext context) { + return null; + } + + default String nbtPathString(Item item, ItemBuildContext context) { + Object[] path = nbtPath(item, context); + if (path != null && path.length > 0) { + StringBuilder builder = new StringBuilder(); + for (Object object : path) { + builder.append(object.toString()); + } + return builder.toString(); + } + return ""; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TagsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TagsModifier.java index b264c9c33..6995c87a2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TagsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TagsModifier.java @@ -1,10 +1,7 @@ package net.momirealms.craftengine.core.item.modifier; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.TypeUtils; +import net.momirealms.craftengine.core.item.*; +import net.momirealms.craftengine.core.util.*; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -12,19 +9,20 @@ import java.util.LinkedHashMap; import java.util.Map; public class TagsModifier implements ItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final Map arguments; public TagsModifier(Map arguments) { this.arguments = mapToMap(arguments); } - public Map arguments() { + public Map tags() { return arguments; } @Override - public String name() { - return "tags"; + public Key type() { + return ItemDataModifiers.TAGS; } @Override @@ -40,12 +38,21 @@ public class TagsModifier implements ItemDataModifier { // TODO NOT PERFECT @Override public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - for (Map.Entry entry : this.arguments.entrySet()) { - Tag previous = item.getTag(entry.getKey()); + if (VersionHelper.isOrAbove1_20_5()) { + Tag previous = item.getSparrowNBTComponent(ComponentKeys.CUSTOM_DATA); if (previous != null) { - networkData.put(entry.getKey(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + networkData.put(ComponentKeys.CUSTOM_DATA.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); } else { - networkData.put(entry.getKey(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + networkData.put(ComponentKeys.CUSTOM_DATA.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + } else { + for (Map.Entry entry : this.arguments.entrySet()) { + Tag previous = item.getTag(entry.getKey()); + if (previous != null) { + networkData.put(entry.getKey(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + } else { + networkData.put(entry.getKey(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } } } return item; @@ -124,4 +131,12 @@ public class TagsModifier implements ItemDataModifier { private record ParsedValue(boolean success, Object result) { static final ParsedValue FAILURE = new ParsedValue(false, null); } + + public static class Factory implements ItemDataModifierFactory { + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "nbt"); + return new TagsModifier<>(data); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TooltipStyleModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TooltipStyleModifier.java index ac3a38114..573c484f5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TooltipStyleModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TooltipStyleModifier.java @@ -3,21 +3,25 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.Nullable; -public class TooltipStyleModifier implements ItemDataModifier { +public class TooltipStyleModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final Key argument; public TooltipStyleModifier(Key argument) { this.argument = argument; } + public Key tooltipStyle() { + return this.argument; + } + @Override - public String name() { - return "tooltip-style"; + public Key type() { + return ItemDataModifiers.TOOLTIP_STYLE; } @Override @@ -27,13 +31,16 @@ public class TooltipStyleModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.TOOLTIP_STYLE); - if (previous != null) { - networkData.put(ComponentKeys.TOOLTIP_STYLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.TOOLTIP_STYLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.TOOLTIP_STYLE; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + String id = arg.toString(); + return new TooltipStyleModifier<>(Key.of(id)); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TrimModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TrimModifier.java index 3a5a32a56..82c7a537e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TrimModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/TrimModifier.java @@ -3,14 +3,17 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; -public class TrimModifier implements ItemDataModifier { +import java.util.Locale; +import java.util.Map; + +public class TrimModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final Key material; private final Key pattern; @@ -19,34 +22,47 @@ public class TrimModifier implements ItemDataModifier { this.pattern = pattern; } + public Key material() { + return material; + } + + public Key pattern() { + return pattern; + } + @Override - public String name() { - return "trim"; + public Key type() { + return ItemDataModifiers.TRIM; } @Override public Item apply(Item item, ItemBuildContext context) { - item.trim(new Trim(this.pattern, this.material)); - return item; + return item.trim(new Trim(this.pattern, this.material)); } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.TRIM); - if (previous != null) { - networkData.put(ComponentKeys.TRIM.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.TRIM.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("Trim"); - if (previous != null) { - networkData.put("Trim", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("Trim", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.TRIM; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"Trim"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "Trim"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + Map data = ResourceConfigUtils.getAsMap(arg, "trim"); + String material = data.get("material").toString().toLowerCase(Locale.ENGLISH); + String pattern = data.get("pattern").toString().toLowerCase(Locale.ENGLISH); + return new TrimModifier<>(Key.of(material), Key.of(pattern)); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/UnbreakableModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/UnbreakableModifier.java index b1be281a0..e2799c35e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/UnbreakableModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/UnbreakableModifier.java @@ -3,21 +3,26 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; -public class UnbreakableModifier implements ItemDataModifier { +public class UnbreakableModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); private final boolean argument; public UnbreakableModifier(boolean argument) { this.argument = argument; } + public boolean unbreakable() { + return argument; + } + @Override - public String name() { - return "unbreakable"; + public Key type() { + return ItemDataModifiers.UNBREAKABLE; } @Override @@ -27,22 +32,26 @@ public class UnbreakableModifier implements ItemDataModifier { } @Override - public Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { - if (VersionHelper.isOrAbove1_20_5()) { - Tag previous = item.getSparrowNBTComponent(ComponentKeys.UNBREAKABLE); - if (previous != null) { - networkData.put(ComponentKeys.UNBREAKABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put(ComponentKeys.UNBREAKABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } - } else { - Tag previous = item.getTag("Unbreakable"); - if (previous != null) { - networkData.put("Unbreakable", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); - } else { - networkData.put("Unbreakable", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); - } + public @Nullable Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.UNBREAKABLE; + } + + @Override + public @Nullable Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"Unbreakable"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "Unbreakable"; + } + + public static class Factory implements ItemDataModifierFactory { + + @Override + public ItemDataModifier create(Object arg) { + boolean value = ResourceConfigUtils.getAsBoolean(arg, "unbreakable"); + return new UnbreakableModifier<>(value); } - return item; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java new file mode 100644 index 000000000..8f60c4cca --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/DynamicLoreModifier.java @@ -0,0 +1,73 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import net.momirealms.craftengine.core.item.ComponentKeys; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; +import net.momirealms.craftengine.core.item.modifier.SimpleNetworkItemDataModifier; +import net.momirealms.craftengine.core.util.Key; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +public final class DynamicLoreModifier implements SimpleNetworkItemDataModifier { + public static final Factory FACTORY = new Factory<>(); + public static final String CONTEXT_TAG_KEY = "craftengine:display_context"; + private final Map> displayContexts; + private final LoreModifier defaultModifier; + + public DynamicLoreModifier(Map> displayContexts) { + this.displayContexts = displayContexts; + this.defaultModifier = displayContexts.values().iterator().next(); + } + + public Map> displayContexts() { + return displayContexts; + } + + @Override + public Key type() { + return ItemDataModifiers.DYNAMIC_LORE; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + String displayContext = Optional.ofNullable(item.getJavaTag(CONTEXT_TAG_KEY)).orElse(this.defaultModifier).toString(); + LoreModifier lore = this.displayContexts.get(displayContext); + if (lore == null) { + lore = this.defaultModifier; + } + return lore.apply(item, context); + } + + @Override + public Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.LORE; + } + + @Override + public Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"display", "Lore"}; + } + + @Override + public String nbtPathString(Item item, ItemBuildContext context) { + return "display.Lore"; + } + + public static class Factory implements ItemDataModifierFactory { + @Override + public ItemDataModifier create(Object arg) { + Map> dynamicLore = new LinkedHashMap<>(); + if (arg instanceof Map map) { + for (Map.Entry entry : map.entrySet()) { + dynamicLore.put(entry.getKey().toString(), LoreModifier.createLoreModifier(entry.getValue())); + } + } + return new DynamicLoreModifier<>(dynamicLore); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java new file mode 100644 index 000000000..0b6b8ce34 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.TriFunction; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +// todo 可以考虑未来添加条件系统 +public record LoreModification(Operation operation, boolean split, String[] content) { + + public Stream apply(Stream lore, ItemBuildContext context) { + return this.operation.function.apply(lore, context, this); + } + + public Stream parseAsStream(ItemBuildContext context) { + Stream parsed = Arrays.stream(this.content).map(string -> AdventureHelper.miniMessage().deserialize(string, context.tagResolvers())); + return this.split ? parsed.map(AdventureHelper::splitLines).flatMap(List::stream) : parsed; + } + + public List parseAsList(ItemBuildContext context) { + return this.parseAsStream(context).collect(Collectors.toList()); + } + + public enum Operation { + APPEND((s, c, modification) -> Stream.concat(s, modification.parseAsStream(c))), + PREPEND((s, c, modification) -> Stream.concat(modification.parseAsStream(c), s)); + + private final TriFunction, ItemBuildContext, LoreModification, Stream> function; + + Operation(TriFunction, ItemBuildContext, LoreModification, Stream> function) { + this.function = function; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java new file mode 100644 index 000000000..7e2c4df28 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModificationHolder.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import org.jetbrains.annotations.NotNull; + +public record LoreModificationHolder(LoreModification modification, int priority) implements Comparable { + + @Override + public int compareTo(@NotNull LoreModificationHolder o) { + return Integer.compare(priority, o.priority); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java new file mode 100644 index 000000000..6e5f3411a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java @@ -0,0 +1,160 @@ +package net.momirealms.craftengine.core.item.modifier.lore; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.item.ComponentKeys; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; +import net.momirealms.craftengine.core.item.modifier.SimpleNetworkItemDataModifier; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Stream; + +public sealed interface LoreModifier extends SimpleNetworkItemDataModifier + permits LoreModifier.EmptyLoreModifier, LoreModifier.CompositeLoreModifier, LoreModifier.DoubleLoreModifier, LoreModifier.SingleLoreModifier { + Factory FACTORY = new Factory<>(); + + @Override + default Key type() { + return ItemDataModifiers.LORE; + } + + @Override + @Nullable + default Key componentType(Item item, ItemBuildContext context) { + return ComponentKeys.LORE; + } + + @Override + @Nullable + default Object[] nbtPath(Item item, ItemBuildContext context) { + return new Object[]{"display", "Lore"}; + } + + @Override + default String nbtPathString(Item item, ItemBuildContext context) { + return "display.Lore"; + } + + List lore(); + + class Factory implements ItemDataModifierFactory { + @Override + public ItemDataModifier create(Object arg) { + return createLoreModifier(arg); + } + } + + static LoreModifier createLoreModifier(Object arg) { + List rawLoreData = MiscUtils.getAsList(arg, Object.class); + String[] rawLore = new String[rawLoreData.size()]; + label_all_string_check: { + for (int i = 0; i < rawLore.length; i++) { + Object o = rawLoreData.get(i); + if (o instanceof Map) { + break label_all_string_check; + } else { + rawLore[i] = o.toString(); + } + } + return new SingleLoreModifier<>(new LoreModification(LoreModification.Operation.APPEND, false, rawLore)); + } + + List modifications = new ArrayList<>(rawLoreData.size() + 1); + int lastPriority = 0; + for (Object o : rawLoreData) { + if (o instanceof Map complexLore) { + String[] content = MiscUtils.getAsStringArray(complexLore.get("content")); + LoreModification.Operation operation = ResourceConfigUtils.getAsEnum(Optional.ofNullable(complexLore.get("operation")).map(String::valueOf).orElse(null), LoreModification.Operation.class, LoreModification.Operation.APPEND); + lastPriority = Optional.ofNullable(complexLore.get("priority")).map(it -> ResourceConfigUtils.getAsInt(it, "priority")).orElse(lastPriority); + boolean split = ResourceConfigUtils.getAsBoolean(complexLore.get("split-lines"), "split-lines"); + modifications.add(new LoreModificationHolder(new LoreModification(operation, split, content), lastPriority)); + } + } + modifications.sort(LoreModificationHolder::compareTo); + return switch (modifications.size()) { + case 0 -> new EmptyLoreModifier<>(); + case 1 -> new SingleLoreModifier<>(modifications.get(0).modification()); + case 2 -> new DoubleLoreModifier<>(modifications.get(0).modification(), modifications.get(1).modification()); + default -> new CompositeLoreModifier<>(modifications.stream().map(LoreModificationHolder::modification).toArray(LoreModification[]::new)); + }; + } + + non-sealed class EmptyLoreModifier implements LoreModifier { + + @Override + public Item apply(Item item, ItemBuildContext context) { + return item; + } + + @Override + public List lore() { + return List.of(); + } + } + + non-sealed class SingleLoreModifier implements LoreModifier { + private final LoreModification modification; + + public SingleLoreModifier(LoreModification modification) { + this.modification = modification; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(this.modification.parseAsList(context)); + return item; + } + + @Override + public List lore() { + return List.of(modification); + } + } + + non-sealed class DoubleLoreModifier implements LoreModifier { + private final LoreModification modification1; + private final LoreModification modification2; + + public DoubleLoreModifier(LoreModification m1, LoreModification m2) { + this.modification1 = m1; + this.modification2 = m2; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(this.modification2.apply(this.modification1.apply(Stream.empty(), context), context).toList()); + return item; + } + + @Override + public List lore() { + return List.of(modification1, modification2); + } + } + + non-sealed class CompositeLoreModifier implements LoreModifier { + private final LoreModification[] modifications; + + public CompositeLoreModifier(LoreModification... modifications) { + this.modifications = modifications; + } + + @Override + public Item apply(Item item, ItemBuildContext context) { + item.loreComponent(Arrays.stream(this.modifications).reduce(Stream.empty(), (stream, modification) -> modification.apply(stream, context), Stream::concat).toList()); + return item; + } + + @Override + public List lore() { + return Arrays.asList(modifications); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java index 9b71c93ce..c0634f467 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -10,7 +11,7 @@ public abstract class AbstractGroupedRecipe implements FixedResultRecipe { protected final CustomRecipeResult result; protected AbstractGroupedRecipe(Key id, String group, CustomRecipeResult result) { - this.group = group; + this.group = group == null ? "" : group; this.id = id; this.result = result; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java deleted file mode 100644 index 42f10cfae..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; - -import java.util.*; - -public abstract class AbstractRecipeFactory implements RecipeFactory { - - protected List ingredients(Map arguments) { - return MiscUtils.getAsStringList(getIngredientOrThrow(arguments)); - } - - protected Map ingredientMap(Map arguments) { - return MiscUtils.castToMap(getIngredientOrThrow(arguments), true); - } - - protected Set ingredientHolders(Map arguments) { - Set holders = new HashSet<>(); - for (String item : ingredients(arguments)) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - return holders; - } - - protected Object getIngredientOrThrow(Map arguments) { - Object ingredient = ResourceConfigUtils.get(arguments, "ingredient", "ingredients"); - if (ingredient == null) { - throw new LocalizedResourceConfigException("warning.config.recipe.missing_ingredient"); - } - return ingredient; - } - - protected CookingRecipeCategory cookingRecipeCategory(Map arguments) { - CookingRecipeCategory recipeCategory; - try { - recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; - } catch (IllegalArgumentException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.cooking.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CookingRecipeCategory.values())); - } - return recipeCategory; - } - - protected CraftingRecipeCategory craftingRecipeCategory(Map arguments) { - CraftingRecipeCategory recipeCategory; - try { - recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; - } catch (IllegalArgumentException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.crafting.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CraftingRecipeCategory.values())); - } - return recipeCategory; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index b39dbb002..796c79e9f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -1,11 +1,6 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20_5; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_21_2; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_21_5; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -14,24 +9,20 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; -import net.momirealms.craftengine.core.util.VersionHelper; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.*; public abstract class AbstractRecipeManager implements RecipeManager { - protected final VanillaRecipeReader recipeReader; - protected final Map>> byType = new HashMap<>(); + protected final Map>> byType = new HashMap<>(); protected final Map> byId = new HashMap<>(); protected final Map>> byResult = new HashMap<>(); protected final Map>> byIngredient = new HashMap<>(); protected final Set dataPackRecipes = new HashSet<>(); protected final RecipeParser recipeParser; - protected boolean isReloading; public AbstractRecipeManager() { - this.recipeReader = initVanillaRecipeReader(); this.recipeParser = new RecipeParser(); } @@ -40,18 +31,6 @@ public abstract class AbstractRecipeManager implements RecipeManager { return this.recipeParser; } - private VanillaRecipeReader initVanillaRecipeReader() { - if (VersionHelper.isOrAbove1_21_5()) { - return new VanillaRecipeReader1_21_5(); - } else if (VersionHelper.isOrAbove1_21_2()) { - return new VanillaRecipeReader1_21_2(); - } else if (VersionHelper.isOrAbove1_20_5()) { - return new VanillaRecipeReader1_20_5(); - } else { - return new VanillaRecipeReader1_20(); - } - } - @Override public void unload() { this.dataPackRecipes.clear(); @@ -67,44 +46,37 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Override public boolean isDataPackRecipe(Key key) { - if (this.isReloading) return false; return this.dataPackRecipes.contains(key); } @Override public boolean isCustomRecipe(Key key) { - if (this.isReloading) return false; return this.byId.containsKey(key); } @Override public Optional> recipeById(Key key) { - if (this.isReloading) return Optional.empty(); return Optional.ofNullable(this.byId.get(key)); } @Override - public List> recipesByType(Key type) { - if (this.isReloading) return List.of(); + public List> recipesByType(RecipeType type) { return this.byType.getOrDefault(type, List.of()); } @Override public List> recipeByResult(Key result) { - if (this.isReloading) return List.of(); return this.byResult.getOrDefault(result, List.of()); } @Override public List> recipeByIngredient(Key ingredient) { - if (this.isReloading) return List.of(); return this.byIngredient.getOrDefault(ingredient, List.of()); } @Nullable @Override - public Recipe recipeByInput(Key type, RecipeInput input) { - if (this.isReloading) return null; + public Recipe recipeByInput(RecipeType type, RecipeInput input) { List> recipes = this.byType.get(type); if (recipes == null) return null; for (Recipe recipe : recipes) { @@ -117,8 +89,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Nullable @Override - public Recipe recipeByInput(Key type, RecipeInput input, Key lastRecipe) { - if (this.isReloading) return null; + public Recipe recipeByInput(RecipeType type, RecipeInput input, Key lastRecipe) { if (lastRecipe != null) { Recipe last = this.byId.get(lastRecipe); if (last != null && last.matches(input)) { @@ -128,7 +99,8 @@ public abstract class AbstractRecipeManager implements RecipeManager { return recipeByInput(type, input); } - protected void registerInternalRecipe(Key id, Recipe recipe) { + protected boolean registerInternalRecipe(Key id, Recipe recipe) { + if (this.byId.containsKey(id)) return false; this.byType.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe); this.byId.put(id, recipe); if (recipe instanceof FixedResultRecipe fixedResult) { @@ -143,6 +115,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } } } + return true; } public class RecipeParser implements ConfigParser { @@ -164,10 +137,9 @@ public abstract class AbstractRecipeManager implements RecipeManager { if (AbstractRecipeManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.recipe.duplicate", path, id); } - Recipe recipe = RecipeTypes.fromMap(id, section); + Recipe recipe = RecipeSerializers.fromMap(id, section); try { registerInternalRecipe(id, recipe); - registerPlatformRecipe(id, recipe); } catch (LocalizedResourceConfigException e) { throw e; } catch (Exception e) { @@ -176,7 +148,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } } - protected abstract void unregisterPlatformRecipe(Key key, boolean isBrewingRecipe); + protected abstract void unregisterPlatformRecipeMainThread(Key key, boolean isBrewingRecipe); - protected abstract void registerPlatformRecipe(Key key, Recipe recipe); + protected abstract void registerPlatformRecipeMainThread(Recipe recipe); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java new file mode 100644 index 000000000..ddb4b0e4c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java @@ -0,0 +1,122 @@ +package net.momirealms.craftengine.core.item.recipe; + +import net.momirealms.craftengine.core.item.CloneableConstantItem; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemManager; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_20; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_20_5; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_21_2; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.item.recipe.result.PostProcessor; +import net.momirealms.craftengine.core.item.recipe.result.PostProcessors; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public abstract class AbstractRecipeSerializer> implements RecipeSerializer { + protected static final VanillaRecipeReader VANILLA_RECIPE_HELPER = + VersionHelper.isOrAbove1_21_2() ? + new VanillaRecipeReader1_21_2() : + VersionHelper.isOrAbove1_20_5() ? + new VanillaRecipeReader1_20_5() : + new VanillaRecipeReader1_20(); + + protected Ingredient singleInputIngredient(Map arguments) { + List ingredients = MiscUtils.getAsStringList(getIngredientOrThrow(arguments)); + return toIngredient(ingredients); + } + + // 不确定的类型 + protected Object getIngredientOrThrow(Map arguments) { + Object ingredient = ResourceConfigUtils.get(arguments, "ingredient", "ingredients"); + if (ingredient == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.missing_ingredient"); + } + return ingredient; + } + + protected CookingRecipeCategory cookingRecipeCategory(Map arguments) { + CookingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.cooking.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CookingRecipeCategory.values())); + } + return recipeCategory; + } + + protected CraftingRecipeCategory craftingRecipeCategory(Map arguments) { + CraftingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.crafting.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CraftingRecipeCategory.values())); + } + return recipeCategory; + } + + @SuppressWarnings({"unchecked"}) + protected CustomRecipeResult parseResult(Map arguments) { + Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); + if (resultMap == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); + } + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); + int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); + List> processors = ResourceConfigUtils.parseConfigAsList(resultMap.get("post-processors"), PostProcessors::fromMap); + return (CustomRecipeResult) new CustomRecipeResult<>( + CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)), + count, + processors.isEmpty() ? null : processors.toArray(new PostProcessor[0]) + ); + } + + @SuppressWarnings("unchecked") + protected CustomRecipeResult parseResult(DatapackRecipeResult recipeResult) { + Item result = (Item) CraftEngine.instance().itemManager().build(recipeResult); + return new CustomRecipeResult<>(CloneableConstantItem.of(result), recipeResult.count(), null); + } + + @Nullable + protected Ingredient toIngredient(String item) { + return toIngredient(List.of(item)); + } + + @Nullable + protected Ingredient toIngredient(List items) { + Set itemIds = new HashSet<>(); + Set minecraftItemIds = new HashSet<>(); + ItemManager itemManager = CraftEngine.instance().itemManager(); + for (String item : items) { + if (item.charAt(0) == '#') itemIds.addAll(itemManager.itemIdsByTag(Key.of(item.substring(1)))); + else { + Key itemId = Key.of(item); + if (itemManager.getBuildableItem(itemId).isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_ingredient", item); + } + itemIds.add(UniqueKey.create(itemId)); + } + } + boolean hasCustomItem = false; + for (UniqueKey holder : itemIds) { + Optional> optionalCustomItem = itemManager.getCustomItem(holder.key()); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + if (customItem.isVanillaItem()) { + minecraftItemIds.add(holder); + } else { + minecraftItemIds.add(UniqueKey.create(customItem.material())); + hasCustomItem = true; + } + } else { + minecraftItemIds.add(holder); + } + } + return itemIds.isEmpty() ? null : Ingredient.of(itemIds, minecraftItemIds, hasCustomItem); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java index 127d5a6b4..eaff6e37a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java @@ -1,35 +1,55 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomBlastingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomBlastingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); } @Override - public @NotNull Key type() { - return RecipeTypes.BLASTING; + public @NotNull Key serializerType() { + return RecipeSerializers.BLASTING; } - public static class Factory extends AbstractRecipeFactory { + @Override + public RecipeType type() { + return RecipeType.BLASTING; + } + + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomBlastingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomBlastingRecipe readMap(Key id, Map arguments) { + return new CustomBlastingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomBlastingRecipe readJson(Key id, JsonObject json) { + return new CustomBlastingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java index 1b622acfc..38e349063 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java @@ -1,8 +1,10 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.BrewingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -14,7 +16,7 @@ import java.util.List; import java.util.Map; public class CustomBrewingRecipe implements FixedResultRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final Ingredient container; private final Ingredient ingredient; @@ -56,8 +58,13 @@ public class CustomBrewingRecipe implements FixedResultRecipe { } @Override - public @NotNull Key type() { - return RecipeTypes.BREWING; + public @NotNull Key serializerType() { + return RecipeSerializers.BREWING; + } + + @Override + public RecipeType type() { + return RecipeType.BREWING; } @NotNull @@ -76,10 +83,10 @@ public class CustomBrewingRecipe implements FixedResultRecipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomBrewingRecipe readMap(Key id, Map arguments) { List container = MiscUtils.getAsStringList(arguments.get("container")); if (container.isEmpty()) { throw new LocalizedResourceConfigException("warning.config.recipe.brewing.missing_container"); @@ -93,5 +100,10 @@ public class CustomBrewingRecipe implements FixedResultRecipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(ingredient), "warning.config.recipe.brewing.missing_ingredient"), parseResult(arguments)); } + + @Override + public CustomBrewingRecipe readJson(Key id, JsonObject json) { + throw new UnsupportedOperationException(); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java index 38d7e5aff..9522de1fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java @@ -1,35 +1,55 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomCampfireRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomCampfireRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); } @Override - public @NotNull Key type() { - return RecipeTypes.CAMPFIRE_COOKING; + public @NotNull Key serializerType() { + return RecipeSerializers.CAMPFIRE_COOKING; } - public static class Factory extends AbstractRecipeFactory { + @Override + public RecipeType type() { + return RecipeType.CAMPFIRE_COOKING; + } + + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomCampfireRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomCampfireRecipe readMap(Key id, Map arguments) { + return new CustomCampfireRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomCampfireRecipe readJson(Key id, JsonObject json) { + return new CustomCampfireRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java index 423470eb1..78009eab6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import java.util.List; @@ -20,7 +21,7 @@ public abstract class CustomCookingRecipe extends AbstractGroupedRecipe { float experience, CustomRecipeResult result) { super(id, group, result); - this.category = category; + this.category = category == null ? CookingRecipeCategory.MISC : category; this.ingredient = ingredient; this.experience = experience; this.cookingTime = cookingTime; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java index 866a2db07..be75eede6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item.recipe; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe { @@ -7,10 +8,15 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe protected CustomCraftingTableRecipe(Key id, CraftingRecipeCategory category, String group, CustomRecipeResult result) { super(id, group, result); - this.category = category; + this.category = category == null ? CraftingRecipeCategory.MISC : category; } public CraftingRecipeCategory category() { return category; } + + @Override + public RecipeType type() { + return RecipeType.CRAFTING; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java deleted file mode 100644 index ea35480b2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java +++ /dev/null @@ -1,74 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.item.BuildableItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.registry.Registries; -import net.momirealms.craftengine.core.registry.WritableRegistry; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceKey; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -@SuppressWarnings("unchecked") -public record CustomRecipeResult(BuildableItem item, int count, PostProcessor[] postProcessors) { - - public T buildItemStack(ItemBuildContext context) { - Item builtItem = this.item.buildItem(context, count); - if (this.postProcessors != null) { - for (PostProcessor postProcessor : this.postProcessors) { - builtItem = postProcessor.process(builtItem, context); - } - } - return builtItem.getItem(); - } - - static { - registerPostProcessorType(Key.of("apply_data"), args -> { - List> modifiers = new ArrayList<>(); - for (Map.Entry entry : args.entrySet()) { - Optional.ofNullable(CraftEngine.instance().itemManager().getDataType(entry.getKey())).ifPresent(it -> { - modifiers.add(it.apply(entry.getValue())); - }); - } - return new ApplyItemDataProcessor<>(modifiers.toArray(new ItemDataModifier[0])); - }); - } - - public static void registerPostProcessorType(Key id, PostProcessor.Type type) { - ((WritableRegistry>) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE).register(ResourceKey.create(Registries.RECIPE_POST_PROCESSOR_TYPE.location(), id), type); - } - - @FunctionalInterface - public interface PostProcessor { - - Item process(Item item, ItemBuildContext context); - - interface Type { - - PostProcessor create(Map args); - } - } - - public static class ApplyItemDataProcessor implements PostProcessor { - private final ItemDataModifier[] modifiers; - - public ApplyItemDataProcessor(ItemDataModifier[] modifiers) { - this.modifiers = modifiers; - } - - @Override - public Item process(Item item, ItemBuildContext context) { - for (ItemDataModifier modifier : this.modifiers) { - item.apply(modifier, context); - } - return item; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index 2350a975b..806beee76 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -1,18 +1,20 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.common.collect.Maps; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.UniqueKey; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; import java.util.*; public class CustomShapedRecipe extends CustomCraftingTableRecipe { - public static final Factory FACTORY = new Factory>(); + public static final Serializer SERIALIZER = new Serializer>(); private final ParsedPattern parsedPattern; private final Pattern pattern; @@ -38,8 +40,8 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } @Override - public @NotNull Key type() { - return RecipeTypes.SHAPED; + public @NotNull Key serializerType() { + return RecipeSerializers.SHAPED; } public Pattern pattern() { @@ -133,11 +135,11 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { + public CustomShapedRecipe readMap(Key id, Map arguments) { List pattern = MiscUtils.getAsStringList(arguments.get("pattern")); if (pattern.isEmpty()) { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.missing_pattern"); @@ -146,30 +148,37 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_pattern", pattern.toString()); } Object ingredientObj = getIngredientOrThrow(arguments); - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; Map> ingredients = new HashMap<>(); - for (Map.Entry entry : MiscUtils.castToMap(ingredientObj, false).entrySet()) { + for (Map.Entry entry : ResourceConfigUtils.getAsMap(ingredientObj, "ingredient").entrySet()) { String key = entry.getKey(); if (key.length() != 1) { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_symbol", key); } char ch = key.charAt(0); List items = MiscUtils.getAsStringList(entry.getValue()); - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.put(ch, Ingredient.of(holders)); + ingredients.put(ch, toIngredient(items)); } - return new CustomShapedRecipe(id, craftingRecipeCategory(arguments), group, new Pattern<>(pattern.toArray(new String[0]), ingredients), parseResult(arguments)); + return new CustomShapedRecipe(id, + craftingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + new Pattern<>(pattern.toArray(new String[0]), ingredients), + parseResult(arguments) + ); + } + + @Override + public CustomShapedRecipe readJson(Key id, JsonObject json) { + Map> ingredients = Maps.transformValues(VANILLA_RECIPE_HELPER.shapedIngredientMap(json.getAsJsonObject("key")), this::toIngredient); + return new CustomShapedRecipe<>(id, + VANILLA_RECIPE_HELPER.craftingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))) + ); } private boolean validatePattern(List pattern) { - String first = pattern.get(0); + String first = pattern.getFirst(); int length = first.length(); for (String s : pattern) { if (s.length() != length) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index 5ff9494e0..16637d5bd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -1,17 +1,19 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; public class CustomShapelessRecipe extends CustomCraftingTableRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final List> ingredients; private final PlacementInfo placementInfo; @@ -47,65 +49,49 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { } @Override - public @NotNull Key type() { - return RecipeTypes.SHAPELESS; + public @NotNull Key serializerType() { + return RecipeSerializers.SHAPELESS; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; + public CustomShapelessRecipe readMap(Key id, Map arguments) { List> ingredients = new ArrayList<>(); Object ingredientsObject = getIngredientOrThrow(arguments); if (ingredientsObject instanceof Map map) { for (Map.Entry entry : (MiscUtils.castToMap(map, false)).entrySet()) { - List items = MiscUtils.getAsStringList(entry.getValue()); - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.add(Ingredient.of(holders)); + if (entry.getValue() == null) continue; + ingredients.add(toIngredient(MiscUtils.getAsStringList(entry.getValue()))); } } else if (ingredientsObject instanceof List list) { for (Object obj : list) { if (obj instanceof List inner) { - Set holders = new HashSet<>(); - for (String item : MiscUtils.getAsStringList(inner)) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(MiscUtils.getAsStringList(inner))); } else { String item = obj.toString(); - Set holders = new HashSet<>(); - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(item)); } } } else { - String item = ingredientsObject.toString(); - Set holders = new HashSet<>(); - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(ingredientsObject.toString())); } - return new CustomShapelessRecipe(id, craftingRecipeCategory(arguments), group, ingredients, parseResult(arguments)); + return new CustomShapelessRecipe(id, + craftingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + ingredients, + parseResult(arguments)); + } + + @Override + public CustomShapelessRecipe readJson(Key id, JsonObject json) { + return new CustomShapelessRecipe<>(id, + VANILLA_RECIPE_HELPER.craftingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList(), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java index 8d7c2be96..7cadcc45c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java @@ -1,35 +1,55 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomSmeltingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomSmeltingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); } @Override - public @NotNull Key type() { - return RecipeTypes.SMELTING; + public @NotNull Key serializerType() { + return RecipeSerializers.SMELTING; } - public static class Factory extends AbstractRecipeFactory { + @Override + public RecipeType type() { + return RecipeType.SMELTING; + } + + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomSmeltingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomSmeltingRecipe readMap(Key id, Map arguments) { + return new CustomSmeltingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomSmeltingRecipe readJson(Key id, JsonObject json) { + return new CustomSmeltingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } 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 ae26068cd..c73ac1b6e 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 @@ -1,9 +1,11 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; @@ -16,9 +18,10 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class CustomSmithingTransformRecipe implements FixedResultRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final CustomRecipeResult result; private final Ingredient base; @@ -28,7 +31,7 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { private final List processors; public CustomSmithingTransformRecipe(Key id, - @Nullable Ingredient base, + @NotNull Ingredient base, @Nullable Ingredient template, @Nullable Ingredient addition, CustomRecipeResult result, @@ -78,8 +81,13 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { } @Override - public @NotNull Key type() { - return RecipeTypes.SMITHING_TRANSFORM; + public @NotNull Key serializerType() { + return RecipeSerializers.SMITHING_TRANSFORM; + } + + @Override + public RecipeType type() { + return RecipeType.SMITHING; } @Override @@ -113,7 +121,7 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { return this.result; } - @Nullable + @NotNull public Ingredient base() { return this.base; } @@ -129,26 +137,37 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - if (base.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.missing_base"); - } - List addition = MiscUtils.getAsStringList(arguments.get("addition")); List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); @SuppressWarnings("unchecked") List> processors = (List>) arguments.getOrDefault("post-processors", List.of()); - return new CustomSmithingTransformRecipe<>( - id, - toIngredient(base), toIngredient(template),toIngredient(addition), parseResult(arguments), + return new CustomSmithingTransformRecipe<>(id, + ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_transform.missing_base"), + toIngredient(template), + toIngredient(addition), + parseResult(arguments), mergeComponents, ItemDataProcessors.fromMapList(processors) ); } + + @Override + public CustomSmithingTransformRecipe readJson(Key id, JsonObject json) { + return new CustomSmithingTransformRecipe<>(id, + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), + parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), + true, + null + ); + } } public static class ItemDataProcessors { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index 993fb1a31..51c2a5491 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -1,11 +1,11 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -16,9 +16,10 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class CustomSmithingTrimRecipe implements Recipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final Ingredient base; private final Ingredient template; @@ -60,14 +61,7 @@ public class CustomSmithingTrimRecipe implements Recipe { } private boolean checkIngredient(Ingredient ingredient, UniqueIdItem item) { - if (ingredient != null) { - if (item == null || item.isEmpty()) { - return false; - } - return ingredient.test(item); - } else { - return item == null || item.isEmpty(); - } + return ingredient.test(item); } @Override @@ -80,8 +74,13 @@ public class CustomSmithingTrimRecipe implements Recipe { } @Override - public @NotNull Key type() { - return RecipeTypes.SMITHING_TRIM; + public @NotNull Key serializerType() { + return RecipeSerializers.SMITHING_TRIM; + } + + @Override + public RecipeType type() { + return RecipeType.SMITHING; } @Override @@ -89,17 +88,17 @@ public class CustomSmithingTrimRecipe implements Recipe { return this.id; } - @Nullable + @NotNull public Ingredient base() { return this.base; } - @Nullable + @NotNull public Ingredient template() { return template; } - @Nullable + @NotNull public Ingredient addition() { return addition; } @@ -110,22 +109,13 @@ public class CustomSmithingTrimRecipe implements Recipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - if (base.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_base"); - } - List addition = MiscUtils.getAsStringList(arguments.get("addition")); - if (addition.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_addition"); - } List template = MiscUtils.getAsStringList(arguments.get("template-type")); - if (template.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_template_type"); - } + List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_trim.missing_base"), @@ -133,5 +123,15 @@ public class CustomSmithingTrimRecipe implements Recipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(addition), "warning.config.recipe.smithing_trim.missing_addition"), pattern); } + + @Override + public CustomSmithingTrimRecipe readJson(Key id, JsonObject json) { + return new CustomSmithingTrimRecipe<>(id, + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template")))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition")))), + VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null + ); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java index 83939ac29..eb0bca8f5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java @@ -1,35 +1,55 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomSmokingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomSmokingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); } @Override - public @NotNull Key type() { - return RecipeTypes.SMOKING; + public @NotNull Key serializerType() { + return RecipeSerializers.SMOKING; } - public static class Factory extends AbstractRecipeFactory { + @Override + public RecipeType type() { + return RecipeType.SMOKING; + } + + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomSmokingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomSmokingRecipe readMap(Key id, Map arguments) { + return new CustomSmokingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomSmokingRecipe readJson(Key id, JsonObject json) { + return new CustomSmokingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java index 95e91ef77..0476b96d7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java @@ -1,17 +1,17 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; -import java.util.Set; public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); protected final Ingredient ingredient; public CustomStoneCuttingRecipe(Key id, String group, Ingredient ingredient, CustomRecipeResult result) { @@ -27,26 +27,36 @@ public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { @Override public List> ingredientsInUse() { - return List.of(ingredient); + return List.of(this.ingredient); } @Override - public @NotNull Key type() { - return RecipeTypes.STONECUTTING; + public @NotNull Key serializerType() { + return RecipeSerializers.STONECUTTING; + } + + @Override + public RecipeType type() { + return RecipeType.STONECUTTING; } public Ingredient ingredient() { - return ingredient; + return this.ingredient; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { + public CustomStoneCuttingRecipe readMap(Key id, Map arguments) { String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - Set holders = ingredientHolders(arguments); - return new CustomStoneCuttingRecipe<>(id, group, Ingredient.of(holders), parseResult(arguments)); + return new CustomStoneCuttingRecipe<>(id, group, singleInputIngredient(arguments), parseResult(arguments)); + } + + @Override + public CustomStoneCuttingRecipe readJson(Key id, JsonObject json) { + String group = VANILLA_RECIPE_HELPER.readGroup(json); + return new CustomStoneCuttingRecipe<>(id, group, toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), parseResult(VANILLA_RECIPE_HELPER.stoneCuttingResult(json))); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java new file mode 100644 index 000000000..4f2f388e4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.item.recipe; + +import com.google.gson.JsonObject; + +public record DatapackRecipeResult(String id, int count, JsonObject components) { + + public boolean isCustom() { + return components != null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/FixedResultRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/FixedResultRecipe.java index 6a17c507c..757d37533 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/FixedResultRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/FixedResultRecipe.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; +import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; public interface FixedResultRecipe extends Recipe { @@ -9,6 +10,8 @@ public interface FixedResultRecipe extends Recipe { CustomRecipeResult result(); + + @Override default T assemble(RecipeInput input, ItemBuildContext context) { return this.result(context); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java index db368e605..6d2f1ce02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java @@ -6,10 +6,17 @@ import java.util.*; import java.util.function.Predicate; public class Ingredient implements Predicate>, StackedContents.IngredientInfo { + // 自定义物品与原版物品混合的列表 private final List items; + // 自定义物品原版材质与原版物品混合的列表 + private final List vanillaItems; + // ingredient里是否含有自定义物品 + private final boolean hasCustomItem; - public Ingredient(List items) { - this.items = items; + private Ingredient(List items, List vanillaItems, boolean hasCustomItem) { + this.items = List.copyOf(items); + this.vanillaItems = List.copyOf(vanillaItems); + this.hasCustomItem = hasCustomItem; } public static boolean isInstance(Optional> optionalIngredient, UniqueIdItem stack) { @@ -17,12 +24,12 @@ public class Ingredient implements Predicate>, StackedContent .orElseGet(stack::isEmpty); } - public static Ingredient of(List items) { - return new Ingredient<>(items); + public static Ingredient of(Set items, Set minecraftItems, boolean hasCustomItem) { + return new Ingredient<>(new ArrayList<>(items), new ArrayList<>(minecraftItems), hasCustomItem); } - public static Ingredient of(Set items) { - return new Ingredient<>(new ArrayList<>(items)); + public boolean hasCustomItem() { + return hasCustomItem; } @Override @@ -39,6 +46,10 @@ public class Ingredient implements Predicate>, StackedContent return this.items; } + public List minecraftItems() { + return vanillaItems; + } + @Override public String toString() { StringJoiner joiner = new StringJoiner(", "); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java index a07812c06..db3d2f675 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java @@ -16,7 +16,13 @@ public interface Recipe { List> ingredientsInUse(); @NotNull - Key type(); + Key serializerType(); + + RecipeType type(); Key id(); + + default boolean showNotification() { + return true; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java deleted file mode 100644 index 563b8ca1c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public interface RecipeFactory { - - Recipe create(Key id, Map arguments); - - @SuppressWarnings({"unchecked", "rawtypes"}) - default CustomRecipeResult parseResult(Map arguments) { - Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); - if (resultMap == null) { - throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); - } - String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); - int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); - return new CustomRecipeResult( - CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow( - () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)), - count, - null - ); - } - - default Ingredient toIngredient(List items) { - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - return holders.isEmpty() ? null : Ingredient.of(holders); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java index c1fdcdbc3..fbe916176 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeManager.java @@ -19,15 +19,15 @@ public interface RecipeManager extends Manageable { Optional> recipeById(Key id); - List> recipesByType(Key type); + List> recipesByType(RecipeType type); List> recipeByResult(Key result); List> recipeByIngredient(Key ingredient); @Nullable - Recipe recipeByInput(Key type, RecipeInput input); + Recipe recipeByInput(RecipeType type, RecipeInput input); @Nullable - Recipe recipeByInput(Key type, RecipeInput input, @Nullable Key lastRecipe); + Recipe recipeByInput(RecipeType type, RecipeInput input, @Nullable Key lastRecipe); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java new file mode 100644 index 000000000..f307bbb45 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.item.recipe; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Map; + +public interface RecipeSerializer> { + + R readMap(Key id, Map arguments); + + R readJson(Key id, JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java similarity index 55% rename from core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java rename to core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java index 603c76840..0d3fe7b12 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java @@ -10,7 +10,7 @@ import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; -public class RecipeTypes { +public final class RecipeSerializers { public static final Key SHAPED = Key.of("minecraft:shaped"); public static final Key SHAPELESS = Key.of("minecraft:shapeless"); public static final Key SMELTING = Key.of("minecraft:smelting"); @@ -23,31 +23,34 @@ public class RecipeTypes { public static final Key BREWING = Key.of("minecraft:brewing"); static { - register(SHAPED, CustomShapedRecipe.FACTORY); - register(SHAPELESS, CustomShapelessRecipe.FACTORY); - register(SMELTING, CustomSmeltingRecipe.FACTORY); - register(SMOKING, CustomSmokingRecipe.FACTORY); - register(BLASTING, CustomBlastingRecipe.FACTORY); - register(CAMPFIRE_COOKING, CustomCampfireRecipe.FACTORY); - register(STONECUTTING, CustomStoneCuttingRecipe.FACTORY); - register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.FACTORY); - register(SMITHING_TRIM, CustomSmithingTrimRecipe.FACTORY); - register(BREWING, CustomBrewingRecipe.FACTORY); + register(SHAPED, CustomShapedRecipe.SERIALIZER); + register(Key.of("crafting_shaped"), CustomShapedRecipe.SERIALIZER); + register(SHAPELESS, CustomShapelessRecipe.SERIALIZER); + register(Key.of("crafting_shapeless"), CustomShapelessRecipe.SERIALIZER); + register(SMELTING, CustomSmeltingRecipe.SERIALIZER); + register(SMOKING, CustomSmokingRecipe.SERIALIZER); + register(BLASTING, CustomBlastingRecipe.SERIALIZER); + register(CAMPFIRE_COOKING, CustomCampfireRecipe.SERIALIZER); + register(STONECUTTING, CustomStoneCuttingRecipe.SERIALIZER); + register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.SERIALIZER); + register(SMITHING_TRIM, CustomSmithingTrimRecipe.SERIALIZER); + register(BREWING, CustomBrewingRecipe.SERIALIZER); } - public static void register(Key key, RecipeFactory factory) { - ((WritableRegistry>) BuiltInRegistries.RECIPE_FACTORY) - .register(ResourceKey.create(Registries.RECIPE_FACTORY.location(), key), factory); + @SuppressWarnings({"unchecked", "rawtypes"}) + public static > void register(Key key, RecipeSerializer serializer) { + WritableRegistry> registry = (WritableRegistry) BuiltInRegistries.RECIPE_SERIALIZER; + registry.register(ResourceKey.create(Registries.RECIPE_FACTORY.location(), key), serializer); } @SuppressWarnings("unchecked") - public static Recipe fromMap(Key id, Map map) { + public static > Recipe fromMap(Key id, Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.recipe.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); - RecipeFactory factory = (RecipeFactory) BuiltInRegistries.RECIPE_FACTORY.getValue(key); + RecipeSerializer factory = (RecipeSerializer) BuiltInRegistries.RECIPE_SERIALIZER.getValue(key); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.recipe.invalid_type", type); } - return factory.create(id, map); + return factory.readMap(id, map); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeType.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeType.java new file mode 100644 index 000000000..1d86dafd7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeType.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.core.item.recipe; + +public enum RecipeType { + CRAFTING("crafting"), + SMELTING("smelting"), + BLASTING("blasting"), + SMOKING("smoking"), + CAMPFIRE_COOKING("campfire_cooking"), + STONECUTTING("stonecutting"), + BREWING("brewing"), + SMITHING("smithing"); + + private final String id; + + RecipeType(String id) { + this.id = id; + } + + public String id() { + return id; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java index 3166c9d00..7e60308d1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java @@ -8,9 +8,13 @@ public class UniqueIdItem { private final Item rawItem; private final UniqueKey uniqueId; - public UniqueIdItem(@NotNull UniqueKey uniqueId, @NotNull Item rawItem) { - this.uniqueId = uniqueId; + private UniqueIdItem(@NotNull Item rawItem) { this.rawItem = rawItem; + this.uniqueId = rawItem.recipeIngredientId(); + } + + public static UniqueIdItem of(Item rawItem) { + return new UniqueIdItem<>(rawItem); } @NotNull @@ -28,7 +32,7 @@ public class UniqueIdItem { } public boolean isEmpty() { - return this.uniqueId == UniqueKey.AIR; + return this.uniqueId == null; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java index e07cac354..c08a45f86 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java @@ -2,12 +2,14 @@ package net.momirealms.craftengine.core.item.recipe.input; import net.momirealms.craftengine.core.item.recipe.RecipeFinder; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; -public final class CraftingInput implements RecipeInput { +public final class CraftingInput implements RecipeInput, Iterable> { private final int width; private final int height; private final List> items; @@ -99,4 +101,9 @@ public final class CraftingInput implements RecipeInput { public UniqueIdItem getItem(int index) { return this.items.get(index); } + + @Override + public @NotNull Iterator> iterator() { + return this.items.iterator(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java index ec1757f31..1ab863933 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.item.recipe.input; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public final class SmithingInput implements RecipeInput { private final UniqueIdItem base; @@ -10,8 +9,8 @@ public final class SmithingInput implements RecipeInput { private final UniqueIdItem addition; public SmithingInput(@NotNull UniqueIdItem base, - @Nullable UniqueIdItem template, - @Nullable UniqueIdItem addition) { + @NotNull UniqueIdItem template, + @NotNull UniqueIdItem addition) { this.base = base; this.template = template; this.addition = addition; @@ -22,12 +21,12 @@ public final class SmithingInput implements RecipeInput { return base; } - @Nullable + @NotNull public UniqueIdItem template() { return template; } - @Nullable + @NotNull public UniqueIdItem addition() { return addition; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader.java new file mode 100644 index 000000000..3f5e432d9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader.java @@ -0,0 +1,47 @@ +package net.momirealms.craftengine.core.item.recipe.reader; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public interface VanillaRecipeReader { + + @NotNull DatapackRecipeResult cookingResult(JsonElement object); + + @NotNull DatapackRecipeResult craftingResult(JsonObject object); + + @NotNull DatapackRecipeResult smithingResult(JsonObject object); + + List> shapelessIngredients(JsonArray json); + + Map> shapedIngredientMap(JsonObject json); + + @NotNull List ingredientList(JsonArray array); + + String[] craftingShapedPattern(JsonObject object); + + @Nullable + String readGroup(JsonObject object); + + @NotNull + CraftingRecipeCategory craftingCategory(JsonObject object); + + @NotNull + CookingRecipeCategory cookingCategory(JsonObject object); + + float cookingExperience(JsonObject object); + + int cookingTime(JsonObject object); + + @NotNull DatapackRecipeResult stoneCuttingResult(JsonObject json); + + List singleIngredient(JsonElement json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java new file mode 100644 index 000000000..4a34fd17d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java @@ -0,0 +1,147 @@ +package net.momirealms.craftengine.core.item.recipe.reader; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class VanillaRecipeReader1_20 implements VanillaRecipeReader { + + @Override + public @NotNull DatapackRecipeResult cookingResult(JsonElement object) { + return new DatapackRecipeResult(object.getAsString(), 1, null); + } + + @Override + public @NotNull DatapackRecipeResult craftingResult(JsonObject object) { + String item = object.get("item").getAsString(); + int count = object.has("count") ? object.get("count").getAsInt() : 1; + return new DatapackRecipeResult(item, count, null); + } + + @Override + public @NotNull DatapackRecipeResult smithingResult(JsonObject object) { + String item = object.get("item").getAsString(); + return new DatapackRecipeResult(item, 1, null); + } + + @Override + public List> shapelessIngredients(JsonArray json) { + List> ingredients = new ArrayList<>(); + for (JsonElement element : json) { + if (element.isJsonObject()) { + JsonObject jsonObject = element.getAsJsonObject(); + if (jsonObject.has("item")) { + ingredients.add(List.of(jsonObject.get("item").getAsString())); + } else if (jsonObject.has("tag")) { + ingredients.add(List.of("#" + jsonObject.get("tag").getAsString())); + } + } else if (element.isJsonArray()) { + List ingredient = ingredientList((JsonArray) element); + ingredients.add(ingredient); + } + } + return ingredients; + } + + @Override + public Map> shapedIngredientMap(JsonObject json) { + Map> ingredients = new HashMap<>(); + for (Map.Entry entry : json.entrySet()) { + char c = entry.getKey().charAt(0); + if (entry.getValue().isJsonObject()) { + JsonObject argument = entry.getValue().getAsJsonObject(); + if (argument.has("item")) { + ingredients.put(c, List.of(argument.get("item").getAsString())); + } else if (argument.has("tag")) { + ingredients.put(c, List.of("#" + argument.get("tag").getAsString())); + } + } else if (entry.getValue().isJsonArray()) { + List items = ingredientList((JsonArray) entry.getValue()); + ingredients.put(c, items); + } + } + return ingredients; + } + + @Override + public @NotNull List ingredientList(JsonArray array) { + List items = new ArrayList<>(); + for (JsonElement element : array) { + if (element.isJsonObject()) { + JsonObject argument = element.getAsJsonObject(); + if (argument.has("item")) { + items.add(argument.get("item").getAsString()); + } else if (argument.has("tag")) { + items.add("#" + argument.get("tag").getAsString()); + } + } + } + return items; + } + + @Override + public String[] craftingShapedPattern(JsonObject object) { + JsonArray pattern = object.getAsJsonArray("pattern"); + List patternList = new ArrayList<>(); + for (JsonElement element : pattern) { + patternList.add(element.getAsString()); + } + return patternList.toArray(new String[0]); + } + + @Override + public @Nullable String readGroup(JsonObject object) { + return object.has("group") ? object.get("group").getAsString() : null; + } + + @Override + public @NotNull CraftingRecipeCategory craftingCategory(JsonObject object) { + return object.has("category") ? CraftingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : CraftingRecipeCategory.MISC; + } + + @Override + public @NotNull CookingRecipeCategory cookingCategory(JsonObject object) { + return object.has("category") ? CookingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : CookingRecipeCategory.MISC; + } + + @Override + public float cookingExperience(JsonObject object) { + return object.has("experience") ? object.get("experience").getAsFloat() : 0; + } + + @Override + public int cookingTime(JsonObject object) { + return object.has("cookingtime") ? object.get("cookingtime").getAsInt() : 200; + } + + @Override + public @NotNull DatapackRecipeResult stoneCuttingResult(JsonObject json) { + int count = json.has("count") ? json.get("count").getAsInt() : 1; + String result = json.get("result").getAsString(); + return new DatapackRecipeResult(result, count, null); + } + + @Override + public List singleIngredient(JsonElement json) { + List ingredients = new ArrayList<>(); + if (json.isJsonObject()) { + JsonObject argument = json.getAsJsonObject(); + if (argument.has("item")) { + ingredients.add(argument.get("item").getAsString()); + } else if (argument.has("tag")) { + ingredients.add("#" + argument.get("tag").getAsString()); + } + } else if (json.isJsonArray()) { + List items = ingredientList((JsonArray) json); + ingredients.addAll(items); + } + return ingredients; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java new file mode 100644 index 000000000..806766772 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.item.recipe.reader; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import org.jetbrains.annotations.NotNull; + +public class VanillaRecipeReader1_20_5 extends VanillaRecipeReader1_20 { + + @Override + public @NotNull DatapackRecipeResult craftingResult(JsonObject object) { + String item = object.get("id").getAsString(); + JsonObject components = object.has("components") ? object.getAsJsonObject("components") : null; + int count = object.has("count") ? object.get("count").getAsInt() : 1; + return new DatapackRecipeResult(item, count, components); + } + + @NotNull + @Override + public DatapackRecipeResult cookingResult(JsonElement object) { + return craftingResult(object.getAsJsonObject()); + } + + @NotNull + @Override + public DatapackRecipeResult stoneCuttingResult(JsonObject json) { + return craftingResult(json.getAsJsonObject("result")); + } + + @Override + public @NotNull DatapackRecipeResult smithingResult(JsonObject object) { + return craftingResult(object.getAsJsonObject()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java rename to core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java index eafec4a5a..bacb8037d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; +package net.momirealms.craftengine.core.item.recipe.reader; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -12,7 +12,7 @@ import java.util.Map; public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { @Override - protected List readSingleIngredient(JsonElement json) { + public List singleIngredient(JsonElement json) { if (json.isJsonPrimitive()) { return List.of(json.getAsString()); } else { @@ -26,7 +26,7 @@ public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { } @Override - protected Map> readShapedIngredientMap(JsonObject json) { + public Map> shapedIngredientMap(JsonObject json) { Map> ingredients = new HashMap<>(); for (Map.Entry entry : json.entrySet()) { char c = entry.getKey().charAt(0); @@ -44,7 +44,7 @@ public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { } @Override - protected List> readShapelessIngredients(JsonArray json) { + public List> shapelessIngredients(JsonArray json) { List> ingredients = new ArrayList<>(); for (JsonElement element : json) { if (element.isJsonPrimitive()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/ApplyItemDataPostProcessor.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/ApplyItemDataPostProcessor.java new file mode 100644 index 000000000..3ace66275 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/ApplyItemDataPostProcessor.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.item.recipe.result; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; + +public class ApplyItemDataPostProcessor implements PostProcessor { + private final ItemDataModifier[] modifiers; + + public ApplyItemDataPostProcessor(ItemDataModifier[] modifiers) { + this.modifiers = modifiers; + } + + @Override + public Item process(Item item, ItemBuildContext context) { + for (ItemDataModifier modifier : this.modifiers) { + item.apply(modifier, context); + } + return item; + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/CustomRecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/CustomRecipeResult.java new file mode 100644 index 000000000..26e21d102 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/CustomRecipeResult.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.core.item.recipe.result; + +import net.momirealms.craftengine.core.item.BuildableItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; + +public record CustomRecipeResult(BuildableItem item, int count, PostProcessor[] postProcessors) { + + public T buildItemStack(ItemBuildContext context) { + return buildItem(context).getItem(); + } + + public Item buildItem(ItemBuildContext context) { + Item builtItem = this.item.buildItem(context, this.count); + if (this.postProcessors != null) { + for (PostProcessor postProcessor : this.postProcessors) { + builtItem = postProcessor.process(builtItem, context); + } + } + return builtItem; + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessor.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessor.java new file mode 100644 index 000000000..7e8eb5c2f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessor.java @@ -0,0 +1,16 @@ +package net.momirealms.craftengine.core.item.recipe.result; + +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; + +import java.util.Map; + +public interface PostProcessor { + + Item process(Item item, ItemBuildContext context); + + interface Type { + + PostProcessor create(Map args); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java new file mode 100644 index 000000000..b091523f5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/result/PostProcessors.java @@ -0,0 +1,47 @@ +package net.momirealms.craftengine.core.item.recipe.result; + +import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@SuppressWarnings("unchecked") +public class PostProcessors { + public static final Key APPLY_DATA = Key.of("craftengine:apply_data"); + + static { + registerPostProcessorType(APPLY_DATA, args -> { + List> modifiers = new ArrayList<>(); + Map data = ResourceConfigUtils.getAsMap(args.get("data"), "data"); + for (Map.Entry entry : data.entrySet()) { + Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), Key.DEFAULT_NAMESPACE))) + .ifPresent(factory -> modifiers.add(factory.create(entry.getValue()))); + } + return new ApplyItemDataPostProcessor<>(modifiers.toArray(new ItemDataModifier[0])); + }); + } + + public static PostProcessor fromMap(Map map) { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.recipe.result.post_processor.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + PostProcessor.Type processor = (PostProcessor.Type) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE.getValue(key); + if (processor == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.result.post_processor.invalid_type", type); + } + return processor.create(map); + } + + public static void registerPostProcessorType(Key id, PostProcessor.Type type) { + ((WritableRegistry>) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE) + .register(ResourceKey.create(Registries.RECIPE_POST_PROCESSOR_TYPE.location(), id), type); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java deleted file mode 100644 index a00730bb0..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import com.google.gson.JsonObject; - -public record RecipeResult(String id, int count, JsonObject components) { - - public boolean isCustom() { - return components != null; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java deleted file mode 100644 index a78b6bb0c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaBlastingRecipe extends VanillaCookingRecipe { - - public VanillaBlastingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { - super(category, group, result, ingredient, experience, cookingTime); - } - - @Override - public Key type() { - return RecipeTypes.BLASTING; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java deleted file mode 100644 index aa12d79db..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaCampfireRecipe extends VanillaCookingRecipe { - - public VanillaCampfireRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { - super(category, group, result, ingredient, experience, cookingTime); - } - - @Override - public Key type() { - return RecipeTypes.CAMPFIRE_COOKING; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java deleted file mode 100644 index 93d91f943..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; - -import java.util.List; - -public abstract class VanillaCookingRecipe extends VanillaGroupedRecipe { - protected final List ingredient; - protected final CookingRecipeCategory category; - protected final float experience; - protected final int cookingTime; - - protected VanillaCookingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { - super(group, result); - this.ingredient = ingredient; - this.experience = experience; - this.cookingTime = cookingTime; - this.category = category; - } - - public CookingRecipeCategory category() { - return category; - } - - public List ingredient() { - return ingredient; - } - - public float experience() { - return experience; - } - - public int cookingTime() { - return cookingTime; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java deleted file mode 100644 index d8cd3bdb5..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; - -public abstract class VanillaCraftingRecipe extends VanillaGroupedRecipe { - protected final CraftingRecipeCategory category; - - protected VanillaCraftingRecipe(CraftingRecipeCategory category, String group, RecipeResult result) { - super(group, result); - this.category = category; - } - - public CraftingRecipeCategory category() { - return category; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java deleted file mode 100644 index 99d4e9aed..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -public abstract class VanillaGroupedRecipe implements VanillaRecipe { - protected final String group; - protected final RecipeResult result; - - protected VanillaGroupedRecipe(String group, RecipeResult result) { - this.group = group; - this.result = result; - } - - public String group() { - return group; - } - - public RecipeResult result() { - return result; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipe.java deleted file mode 100644 index a58cc6523..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipe.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.util.Key; - -public interface VanillaRecipe { - - Key type(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java deleted file mode 100644 index 343d9b06a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import com.google.gson.JsonObject; - -public interface VanillaRecipeReader { - - VanillaShapedRecipe readShaped(JsonObject json); - - VanillaShapelessRecipe readShapeless(JsonObject json); - - VanillaBlastingRecipe readBlasting(JsonObject json); - - VanillaSmeltingRecipe readSmelting(JsonObject json); - - VanillaSmokingRecipe readSmoking(JsonObject json); - - VanillaCampfireRecipe readCampfire(JsonObject json); - - VanillaStoneCuttingRecipe readStoneCutting(JsonObject json); - - VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json); - - VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java deleted file mode 100644 index 753590f5c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; -import java.util.Map; - -public class VanillaShapedRecipe extends VanillaCraftingRecipe { - private final String[] pattern; - private final Map> key; - - public VanillaShapedRecipe(CraftingRecipeCategory category, - String group, - Map> key, - String[] pattern, - RecipeResult result) { - super(category, group, result); - this.key = key; - this.pattern = pattern; - } - - public Map> ingredients() { - return key; - } - - public String[] pattern() { - return pattern; - } - - @Override - public Key type() { - return RecipeTypes.SHAPED; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java deleted file mode 100644 index 2e06b2988..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaShapelessRecipe extends VanillaCraftingRecipe { - private final List> ingredients; - - public VanillaShapelessRecipe(CraftingRecipeCategory category, String group, List> ingredients, RecipeResult result) { - super(category, group, result); - this.ingredients = ingredients; - } - - public List> ingredients() { - return ingredients; - } - - @Override - public Key type() { - return RecipeTypes.SHAPELESS; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java deleted file mode 100644 index ebbed4b21..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaSmeltingRecipe extends VanillaCookingRecipe { - - public VanillaSmeltingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { - super(category, group, result, ingredient, experience, cookingTime); - } - - @Override - public Key type() { - return RecipeTypes.SMELTING; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java deleted file mode 100644 index fef4ffbda..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaSmithingTransformRecipe implements VanillaRecipe { - private final RecipeResult result; - private final List base; - private final List template; - private final List addition; - - public VanillaSmithingTransformRecipe(List base, List template, List addition, RecipeResult result) { - this.result = result; - this.base = base; - this.template = template; - this.addition = addition; - } - - @Override - public Key type() { - return RecipeTypes.SMITHING_TRANSFORM; - } - - public RecipeResult result() { - return result; - } - - public List base() { - return base; - } - - public List template() { - return template; - } - - public List addition() { - return addition; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTrimRecipe.java deleted file mode 100644 index 4b6bee14d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTrimRecipe.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class VanillaSmithingTrimRecipe implements VanillaRecipe { - @Nullable // 1.21.5 - private final String pattern; - - private final List base; - private final List template; - private final List addition; - - public VanillaSmithingTrimRecipe(List base, List template, List addition, @Nullable String pattern) { - this.base = base; - this.template = template; - this.addition = addition; - this.pattern = pattern; - } - - @Override - public Key type() { - return RecipeTypes.SMITHING_TRIM; - } - - public List base() { - return base; - } - - public List template() { - return template; - } - - public List addition() { - return addition; - } - - @Nullable - public String pattern() { - return pattern; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java deleted file mode 100644 index 736a3fd5e..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaSmokingRecipe extends VanillaCookingRecipe { - - public VanillaSmokingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { - super(category, group, result, ingredient, experience, cookingTime); - } - - @Override - public Key type() { - return RecipeTypes.SMOKING; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java deleted file mode 100644 index 864557c68..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import net.momirealms.craftengine.core.item.recipe.RecipeTypes; -import net.momirealms.craftengine.core.util.Key; - -import java.util.List; - -public class VanillaStoneCuttingRecipe extends VanillaGroupedRecipe { - private final List ingredient; - - public VanillaStoneCuttingRecipe(String group, RecipeResult result, List ingredient) { - super(group, result); - this.ingredient = ingredient; - } - - public List ingredient() { - return ingredient; - } - - @Override - public Key type() { - return RecipeTypes.STONECUTTING; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java deleted file mode 100644 index 9422071a2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public abstract class AbstractRecipeReader implements VanillaRecipeReader { - - protected String[] readPattern(JsonObject object) { - JsonArray pattern = object.getAsJsonArray("pattern"); - List patternList = new ArrayList<>(); - for (JsonElement element : pattern) { - patternList.add(element.getAsString()); - } - return patternList.toArray(new String[0]); - } - - @Nullable - protected String readGroup(JsonObject object) { - return object.has("group") ? object.get("group").getAsString() : null; - } - - @Nullable - protected CraftingRecipeCategory readCraftingCategory(JsonObject object) { - return object.has("category") ? CraftingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : null; - } - - @Nullable - protected CookingRecipeCategory readCookingCategory(JsonObject object) { - return object.has("category") ? CookingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : null; - } - - protected float readExperience(JsonObject object) { - return object.has("experience") ? object.get("experience").getAsFloat() : 0; - } - - protected int readCookingTime(JsonObject object) { - return object.has("cookingtime") ? object.get("cookingtime").getAsInt() : 200; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java deleted file mode 100644 index d45c8e5a2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java +++ /dev/null @@ -1,206 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.*; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class VanillaRecipeReader1_20 extends AbstractRecipeReader { - - @Override - public VanillaShapedRecipe readShaped(JsonObject json) { - return new VanillaShapedRecipe( - readCraftingCategory(json), - readGroup(json), - readShapedIngredientMap(json.getAsJsonObject("key")), - readPattern(json), - readCraftingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaShapelessRecipe readShapeless(JsonObject json) { - return new VanillaShapelessRecipe( - readCraftingCategory(json), - readGroup(json), - readShapelessIngredients(json.getAsJsonArray("ingredients")), - readCraftingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaBlastingRecipe readBlasting(JsonObject json) { - return new VanillaBlastingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaSmeltingRecipe readSmelting(JsonObject json) { - return new VanillaSmeltingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaSmokingRecipe readSmoking(JsonObject json) { - return new VanillaSmokingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaCampfireRecipe readCampfire(JsonObject json) { - return new VanillaCampfireRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaStoneCuttingRecipe readStoneCutting(JsonObject json) { - return new VanillaStoneCuttingRecipe( - readGroup(json), - readStoneCuttingResult(json), - readSingleIngredient(json.get("ingredient")) - ); - } - - @Override - public VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json) { - return new VanillaSmithingTransformRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - readSmithingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json) { - return new VanillaSmithingTrimRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - null - ); - } - - protected List readSingleIngredient(JsonElement json) { - List ingredients = new ArrayList<>(); - if (json.isJsonObject()) { - JsonObject argument = json.getAsJsonObject(); - if (argument.has("item")) { - ingredients.add(argument.get("item").getAsString()); - } else if (argument.has("tag")) { - ingredients.add("#" + argument.get("tag").getAsString()); - } - } else if (json.isJsonArray()) { - List items = readIngredientList((JsonArray) json); - ingredients.addAll(items); - } - return ingredients; - } - - @NotNull - protected RecipeResult readStoneCuttingResult(JsonObject json) { - int count = json.has("count") ? json.get("count").getAsInt() : 1; - String result = json.get("result").getAsString(); - return new RecipeResult(result, count, null); - } - - @NotNull - protected RecipeResult readCookingResult(JsonElement object) { - return new RecipeResult(object.getAsString(), 1, null); - } - - @NotNull - protected RecipeResult readCraftingResult(JsonObject object) { - String item = object.get("item").getAsString(); - int count = object.has("count") ? object.get("count").getAsInt() : 1; - return new RecipeResult(item, count, null); - } - - @NotNull - protected RecipeResult readSmithingResult(JsonObject object) { - String item = object.get("item").getAsString(); - return new RecipeResult(item, 1, null); - } - - protected List> readShapelessIngredients(JsonArray json) { - List> ingredients = new ArrayList<>(); - for (JsonElement element : json) { - if (element.isJsonObject()) { - JsonObject jsonObject = element.getAsJsonObject(); - if (jsonObject.has("item")) { - ingredients.add(List.of(jsonObject.get("item").getAsString())); - } else if (jsonObject.has("tag")) { - ingredients.add(List.of("#" + jsonObject.get("tag").getAsString())); - } - } else if (element.isJsonArray()) { - List ingredient = readIngredientList((JsonArray) element); - ingredients.add(ingredient); - } - } - return ingredients; - } - - protected Map> readShapedIngredientMap(JsonObject json) { - Map> ingredients = new HashMap<>(); - for (Map.Entry entry : json.entrySet()) { - char c = entry.getKey().charAt(0); - if (entry.getValue().isJsonObject()) { - JsonObject argument = entry.getValue().getAsJsonObject(); - if (argument.has("item")) { - ingredients.put(c, List.of(argument.get("item").getAsString())); - } else if (argument.has("tag")) { - ingredients.put(c, List.of("#" + argument.get("tag").getAsString())); - } - } else if (entry.getValue().isJsonArray()) { - List items = readIngredientList((JsonArray) entry.getValue()); - ingredients.put(c, items); - } - } - return ingredients; - } - - protected @NotNull List readIngredientList(JsonArray array) { - List items = new ArrayList<>(); - for (JsonElement element : array) { - if (element.isJsonObject()) { - JsonObject argument = element.getAsJsonObject(); - if (argument.has("item")) { - items.add(argument.get("item").getAsString()); - } else if (argument.has("tag")) { - items.add("#" + argument.get("tag").getAsString()); - } - } - } - return items; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java deleted file mode 100644 index 3bd399f96..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.RecipeResult; -import org.jetbrains.annotations.NotNull; - -public class VanillaRecipeReader1_20_5 extends VanillaRecipeReader1_20 { - - @Override - protected @NotNull RecipeResult readCraftingResult(JsonObject object) { - String item = object.get("id").getAsString(); - JsonObject components = object.has("components") ? object.getAsJsonObject("components") : null; - int count = object.has("count") ? object.get("count").getAsInt() : 1; - return new RecipeResult(item, count, components); - } - - @NotNull - @Override - protected RecipeResult readCookingResult(JsonElement object) { - return readCraftingResult(object.getAsJsonObject()); - } - - @NotNull - @Override - protected RecipeResult readStoneCuttingResult(JsonObject json) { - return readCraftingResult(json.getAsJsonObject("result")); - } - - @Override - protected @NotNull RecipeResult readSmithingResult(JsonObject object) { - return readCraftingResult(object.getAsJsonObject()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java deleted file mode 100644 index 49634d90e..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaSmithingTrimRecipe; - -public class VanillaRecipeReader1_21_5 extends VanillaRecipeReader1_21_2 { - - @Override - public VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json) { - return new VanillaSmithingTrimRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - json.get("pattern").getAsString() - ); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 395f6af8e..478d3e5bd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -497,6 +497,11 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/table_lamp.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/wooden_chair.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/bench.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ceiling.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_ground.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/models/item/custom/flower_basket_wall.json"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket.png"); + plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/flower_basket_2d.png"); // tooltip plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_background.png"); plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/gui/sprites/tooltip/topaz_background.png.mcmeta"); @@ -586,11 +591,7 @@ public abstract class AbstractPackManager implements PackManager { } } } catch (LocalizedException e) { - if (e instanceof LocalizedResourceConfigException exception) { - exception.setPath(cached.filePath()); - exception.setId(cached.prefix() + "." + key); - } - TranslationManager.instance().log(e.node(), e.arguments()); + printWarningRecursively(e, cached.filePath(), cached.prefix() + "." + key); } catch (Exception e) { this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "." + key + "'. Please find the cause according to the stacktrace or seek developer help. Additional info: " + GsonHelper.get().toJson(configEntry.getValue()), e); } @@ -602,6 +603,19 @@ public abstract class AbstractPackManager implements PackManager { } } + private void printWarningRecursively(LocalizedException e, Path path, String prefix) { + for (Throwable t : e.getSuppressed()) { + if (t instanceof LocalizedException suppressed) { + printWarningRecursively(suppressed, path, prefix); + } + } + if (e instanceof LocalizedResourceConfigException exception) { + exception.setPath(path); + exception.setId(prefix); + } + TranslationManager.instance().log(e.node(), e.arguments()); + } + private void processConfigEntry(Map.Entry entry, Path path, Pack pack, BiConsumer callback) { if (entry.getValue() instanceof Map typeSections0) { String key = entry.getKey(); @@ -667,6 +681,10 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().info("Validated resource pack in " + (time3 - time2) + "ms"); Path finalPath = resourcePackPath(); Files.createDirectories(finalPath.getParent()); + if (!VersionHelper.PREMIUM) { + Config.instance().setObf(false); + this.plugin.logger().warn("Resource pack obfuscation requires Premium Edition."); + } try { this.zipGenerator.accept(generatedPackPath, finalPath); } catch (Exception e) { @@ -1551,15 +1569,24 @@ public abstract class AbstractPackManager implements PackManager { private void generateClientLang(Path generatedPackPath) { for (Map.Entry entry : this.plugin.translationManager().clientLangData().entrySet()) { - JsonObject json = new JsonObject(); - for (Map.Entry pair : entry.getValue().translations.entrySet()) { - json.addProperty(pair.getKey(), pair.getValue()); - } Path langPath = generatedPackPath .resolve("assets") .resolve("minecraft") .resolve("lang") .resolve(entry.getKey() + ".json"); + JsonObject json; + if (Files.exists(langPath)) { + try { + json = GsonHelper.readJsonFile(langPath).getAsJsonObject(); + } catch (Exception e) { + json = new JsonObject(); + } + } else { + json = new JsonObject(); + } + for (Map.Entry pair : entry.getValue().translations.entrySet()) { + json.addProperty(pair.getKey(), pair.getValue()); + } try { Files.createDirectories(langPath.getParent()); } catch (IOException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java index 4e580df18..1d6c48e33 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java @@ -20,7 +20,7 @@ public final class ResourceLocation { } public static boolean isValidNamespace(String namespace) { - for(int i = 0; i < namespace.length(); ++i) { + for (int i = 0; i < namespace.length(); ++i) { if (!validNamespaceChar(namespace.charAt(i))) { return false; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java index 849123084..c6547ac9d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java @@ -55,7 +55,7 @@ public interface Revision { @Override public boolean matches(MinecraftVersion min, MinecraftVersion max) { - return this.minVersion.isAtOrBelow(max); + return this.minVersion.isAtOrBelow(max) && min.isBelow(this.minVersion); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java index 83b15f39b..f38d30c1f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java @@ -34,4 +34,6 @@ public interface CompatibilityManager { String parse(Player player1, Player player2, String text); int getPlayerProtocolVersion(UUID uuid); + + void executeMMSkill(String skill, float power, Player player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 425de32f9..8439a43d6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -20,10 +20,7 @@ import net.momirealms.craftengine.core.plugin.PluginProperties; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MinecraftVersion; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.InjectionTarget; import net.momirealms.craftengine.core.world.chunk.storage.CompressionMethod; @@ -254,8 +251,11 @@ public class Config { resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false); resource_pack$generate_mod_assets = config.getBoolean("resource-pack.generate-mod-assets", false); resource_pack$remove_tinted_leaves_particle = config.getBoolean("resource-pack.remove-tinted-leaves-particle", true); - resource_pack$supported_version$min = getVersion(config.get("resource-pack.supported-version.min", "1.20").toString()); + resource_pack$supported_version$min = getVersion(config.get("resource-pack.supported-version.min", "SERVER").toString()); resource_pack$supported_version$max = getVersion(config.get("resource-pack.supported-version.max", "LATEST").toString()); + if (resource_pack$supported_version$min.isAbove(resource_pack$supported_version$max)) { + resource_pack$supported_version$min = resource_pack$supported_version$max; + } resource_pack$merge_external_folders = config.getStringList("resource-pack.merge-external-folders"); resource_pack$merge_external_zips = config.getStringList("resource-pack.merge-external-zip-files"); resource_pack$exclude_file_extensions = new HashSet<>(config.getStringList("resource-pack.exclude-file-extensions")); @@ -409,6 +409,9 @@ public class Config { if (version.equalsIgnoreCase("LATEST")) { return new MinecraftVersion(PluginProperties.getValue("latest-version")); } + if (version.equalsIgnoreCase("SERVER")) { + return VersionHelper.MINECRAFT_VERSION; + } return MinecraftVersion.parse(version); } @@ -832,6 +835,10 @@ public class Config { return instance.recipe$ingredient_sources; } + public void setObf(boolean enable) { + this.resource_pack$protection$obfuscation$enable = enable; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java index 89af9a7b0..d3b7dc037 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CommonParameterProvider.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +//TODO 需要为random指定id public class CommonParameterProvider implements AdditionalParameterProvider { private double lastRandom = -1; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java index e843a235c..ae534b927 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java @@ -72,6 +72,7 @@ public class EventFunctions { register(CommonFunctions.SPAWN_FURNITURE, new SpawnFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REMOVE_FURNITURE, new RemoveFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REPLACE_FURNITURE, new ReplaceFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.MYTHIC_MOBS_SKILL, new MythicMobsSkillFunction.FactoryImpl<>(EventConditions::fromMap)); } public static void register(Key key, FunctionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index f81794979..bede6e946 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java @@ -30,4 +30,5 @@ public final class CommonFunctions { public static final Key SPAWN_FURNITURE = Key.of("craftengine:spawn_furniture"); public static final Key REMOVE_FURNITURE = Key.of("craftengine:remove_furniture"); public static final Key REPLACE_FURNITURE = Key.of("craftengine:replace_furniture"); + public static final Key MYTHIC_MOBS_SKILL = Key.of("craftengine:mythic_mobs_skill"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java new file mode 100644 index 000000000..031ed08da --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; + +public class MythicMobsSkillFunction extends AbstractConditionalFunction { + private final String skill; + private final float power; + + public MythicMobsSkillFunction(String skill, float power, List> predicates) { + super(predicates); + this.skill = skill; + this.power = power; + } + + @Override + protected void runInternal(CTX ctx) { + ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> { + CraftEngine.instance().compatibilityManager().executeMMSkill(this.skill, this.power, it); + }); + } + + @Override + public Key type() { + return CommonFunctions.MYTHIC_MOBS_SKILL; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map args) { + String skill = ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("skill"), "warning.config.function.mythic_mobs_skill.missing_skill"); + float power = ResourceConfigUtils.getAsFloat(args.getOrDefault("power", 1.0), "power"); + return new MythicMobsSkillFunction<>(skill, power, getPredicates(args)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java index bc65826b4..2ab73db03 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java @@ -38,7 +38,7 @@ public class ParticleFunction extends AbstractConditionalFu })), ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER); registerParticleData(map -> new ColorData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES); registerParticleData(map -> new JavaTypeData( ResourceConfigUtils.getAsFloat(map.get("charge"), "charge")), @@ -47,12 +47,12 @@ public class ParticleFunction extends AbstractConditionalFu ResourceConfigUtils.getAsInt(map.get("shriek"), "shriek")), ParticleTypes.SHRIEK); registerParticleData(map -> new DustData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), ParticleTypes.DUST); registerParticleData(map -> new DustTransitionData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), ParticleTypes.DUST_COLOR_TRANSITION); registerParticleData(map -> new ItemStackData( @@ -75,7 +75,7 @@ public class ParticleFunction extends AbstractConditionalFu NumberProviders.fromObject(map.getOrDefault("target-x", 0)), NumberProviders.fromObject(map.getOrDefault("target-y", 0)), NumberProviders.fromObject(map.getOrDefault("target-z", 0)), - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), NumberProviders.fromObject(map.getOrDefault("duration", 10))), ParticleTypes.TRAIL); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java index b4662a547..d636f5268 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/number/FixedNumberProvider.java @@ -31,6 +31,10 @@ public class FixedNumberProvider implements NumberProvider { return NumberProviders.FIXED; } + public static FixedNumberProvider of(final double value) { + return new FixedNumberProvider(value); + } + public static class FactoryImpl implements NumberProviderFactory { @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java index 8b3974115..b3e20fab4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -20,6 +20,7 @@ public class FurnitureParameterProvider implements ChainParameterProvider furniture.position().z()); CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, furniture -> furniture.position().xRot()); CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, furniture -> furniture.position().yRot()); + CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Furniture::position); } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index e51058d2f..fd5b95839 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -392,24 +392,24 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { if (index >= recipes.size()) return; if (depth > MAX_RECIPE_DEPTH) return; Recipe recipe = recipes.get(index); - Key recipeType = recipe.type(); - if (recipeType == RecipeTypes.SHAPELESS || recipeType == RecipeTypes.SHAPED) { + Key recipeType = recipe.serializerType(); + if (recipeType == RecipeSerializers.SHAPELESS || recipeType == RecipeSerializers.SHAPED) { openCraftingRecipePage(player, (CustomCraftingTableRecipe) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage); return; } - if (recipeType == RecipeTypes.BLASTING || recipeType == RecipeTypes.CAMPFIRE_COOKING || recipeType == RecipeTypes.SMOKING || recipeType == RecipeTypes.SMELTING) { + if (recipeType == RecipeSerializers.BLASTING || recipeType == RecipeSerializers.CAMPFIRE_COOKING || recipeType == RecipeSerializers.SMOKING || recipeType == RecipeSerializers.SMELTING) { openCookingRecipePage(player, (CustomCookingRecipe) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage); return; } - if (recipeType == RecipeTypes.STONECUTTING) { + if (recipeType == RecipeSerializers.STONECUTTING) { openStoneCuttingRecipePage(player, (CustomStoneCuttingRecipe) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage); return; } - if (recipeType == RecipeTypes.SMITHING_TRANSFORM) { + if (recipeType == RecipeSerializers.SMITHING_TRANSFORM) { openSmithingTransformRecipePage(player, (CustomSmithingTransformRecipe) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage); return; } - if (recipeType == RecipeTypes.BREWING) { + if (recipeType == RecipeSerializers.BREWING) { openBrewingRecipePage(player, (CustomBrewingRecipe) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage); return; } @@ -1041,11 +1041,11 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { })); String title; - if (recipe.type() == RecipeTypes.SMELTING) { + if (recipe.serializerType() == RecipeSerializers.SMELTING) { title = Constants.RECIPE_SMELTING_TITLE; - } else if (recipe.type() == RecipeTypes.BLASTING) { + } else if (recipe.serializerType() == RecipeSerializers.BLASTING) { title = Constants.RECIPE_BLASTING_TITLE; - } else if (recipe.type() == RecipeTypes.CAMPFIRE_COOKING) { + } else if (recipe.serializerType() == RecipeSerializers.CAMPFIRE_COOKING) { title = Constants.RECIPE_CAMPFIRE_TITLE; } else { title = Constants.RECIPE_SMOKING_TITLE; @@ -1154,7 +1154,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { })); char start = 'A'; - if (recipe.type() == RecipeTypes.SHAPED) { + if (recipe.serializerType() == RecipeSerializers.SHAPED) { String[] pattern = ((CustomShapedRecipe) recipe).pattern().pattern(); for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index 9ff316fcf..042413ba9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -27,6 +27,8 @@ public interface NetWorkUser { void sendPacket(Object packet, boolean immediately); + void sendPacket(Object packet, boolean immediately, Runnable sendListener); + void sendCustomPayload(Key channel, byte[] data); void kick(Component message); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java index e24a4ba29..808dd56eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java @@ -25,14 +25,22 @@ public interface NetworkManager extends Manageable { Player[] onlineUsers(); default void sendPacket(@NotNull NetWorkUser player, Object packet) { - sendPacket(player, packet, false); + sendPacket(player, packet, false, null); } - void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately); + default void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately) { + sendPacket(player, packet, immediately, null); + } + + void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately, Runnable sendListener); default void sendPackets(@NotNull NetWorkUser player, List packet) { - sendPackets(player, packet, false); + sendPackets(player, packet, false, null); } - void sendPackets(@NotNull NetWorkUser player, List packet, boolean immediately); + default void sendPackets(@NotNull NetWorkUser player, List packet, boolean immediately) { + sendPackets(player, packet, immediately, null); + } + + void sendPackets(@NotNull NetWorkUser player, List packet, boolean immediately, Runnable sendListener); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index a88ef798b..482b2e1d5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -4,14 +4,16 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; -import net.momirealms.craftengine.core.item.recipe.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeFactory; +import net.momirealms.craftengine.core.item.recipe.Recipe; +import net.momirealms.craftengine.core.item.recipe.RecipeSerializer; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipe; import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; import net.momirealms.craftengine.core.item.recipe.network.modern.display.slot.SlotDisplay; +import net.momirealms.craftengine.core.item.recipe.result.PostProcessor; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory; import net.momirealms.craftengine.core.loot.function.ApplyBonusCountFunction; @@ -42,6 +44,7 @@ import net.momirealms.craftengine.core.util.ResourceKey; public class BuiltInRegistries { public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK); public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY); + public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY); public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY); public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY); public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY); @@ -61,7 +64,7 @@ public class BuiltInRegistries { public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); - public static final Registry> RECIPE_FACTORY = createConstantBoundRegistry(Registries.RECIPE_FACTORY); + public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); @@ -75,7 +78,7 @@ public class BuiltInRegistries { public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE); public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE); public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE); - public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); + public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); private static Registry createConstantBoundRegistry(ResourceKey> key) { return new ConstantBoundRegistry<>(key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 382277302..78e68ec0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -4,14 +4,16 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; +import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; -import net.momirealms.craftengine.core.item.recipe.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeFactory; +import net.momirealms.craftengine.core.item.recipe.Recipe; +import net.momirealms.craftengine.core.item.recipe.RecipeSerializer; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipe; import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; import net.momirealms.craftengine.core.item.recipe.network.modern.display.slot.SlotDisplay; +import net.momirealms.craftengine.core.item.recipe.result.PostProcessor; import net.momirealms.craftengine.core.loot.LootContext; import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory; import net.momirealms.craftengine.core.loot.function.ApplyBonusCountFunction; @@ -43,6 +45,7 @@ import net.momirealms.craftengine.core.util.ResourceKey; public class Registries { public static final Key ROOT_REGISTRY = Key.withDefaultNamespace("root"); public static final ResourceKey> BLOCK = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block")); + public static final ResourceKey>> ITEM_DATA_MODIFIER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_data_modifier_factory")); public static final ResourceKey> PROPERTY_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("property_factory")); public static final ResourceKey> BLOCK_BEHAVIOR_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_behavior_factory")); public static final ResourceKey> ITEM_BEHAVIOR_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_behavior_factory")); @@ -63,7 +66,7 @@ public class Registries { public static final ResourceKey> CONDITION_PROPERTY_READER = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("condition_property_reader")); public static final ResourceKey> SELECT_PROPERTY_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_factory")); public static final ResourceKey> SELECT_PROPERTY_READER = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_reader")); - public static final ResourceKey>> RECIPE_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_factory")); + public static final ResourceKey>>> RECIPE_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_factory")); public static final ResourceKey> FORMULA_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("formula_factory")); public static final ResourceKey>> PATH_MATCHER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory")); public static final ResourceKey> RESOLUTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory")); @@ -77,5 +80,5 @@ public class Registries { public static final ResourceKey> SLOT_DISPLAY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("slot_display_type")); public static final ResourceKey> RECIPE_DISPLAY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_display_type")); public static final ResourceKey> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type")); - public static final ResourceKey>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); + public static final ResourceKey>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index eaa7df2ba..a0892da26 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -19,6 +19,7 @@ public abstract class AbstractSoundManager implements SoundManager { protected final Map songs = new HashMap<>(); protected final SoundParser soundParser; protected final SongParser songParser; + protected final Map customSoundsInRegistry = new HashMap<>(); public AbstractSoundManager(CraftEngine plugin) { this.plugin = plugin; @@ -46,6 +47,8 @@ public abstract class AbstractSoundManager implements SoundManager { @Override public void runDelayedSyncTasks() { if (!VersionHelper.isOrAbove1_21()) return; + // 问题是会踢客户端 + // this.registerSounds(this.byId.keySet()); this.registerSongs(this.songs); } @@ -60,6 +63,8 @@ public abstract class AbstractSoundManager implements SoundManager { protected abstract void registerSongs(Map songs); + protected abstract void registerSounds(Collection sounds); + public class SongParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"jukebox_songs", "jukebox_song", "jukebox-songs", "jukebox-song"}; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index 3ed1f6048..e988d1f68 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -5,7 +5,9 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentIteratorType; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -15,7 +17,7 @@ import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.adventure.NBTComponentSerializer; import net.momirealms.sparrow.nbt.adventure.NBTSerializerOptions; -import java.util.Map; +import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -29,6 +31,21 @@ public class AdventureHelper { private final MiniMessage miniMessageCustom; private final GsonComponentSerializer gsonComponentSerializer; private final NBTComponentSerializer nbtComponentSerializer; + private static final TextReplacementConfig REPLACE_LF = TextReplacementConfig.builder().matchLiteral("\n").replacement(Component.newline()).build(); + /** + * This iterator slices a component into individual parts that + *
    + *
  • Can be used individually without style loss
  • + *
  • Can be concatenated to form the original component, given that children are dropped
  • + *
+ * Any {@link net.kyori.adventure.text.ComponentIteratorFlag}s are ignored. + */ + private static final ComponentIteratorType SLICER = (component, deque, flags) -> { + final List children = component.children(); + for (int i = children.size() - 1; i >= 0; i--) { + deque.addFirst(children.get(i).applyFallbackStyle(component.style())); + } + }; private AdventureHelper() { this.miniMessage = MiniMessage.builder().build(); @@ -209,6 +226,24 @@ public class AdventureHelper { return getNBT().deserialize(tag); } + public static List splitLines(Component component) { + List result = new ArrayList<>(1); + Component line = Component.empty(); + for (Iterator it = component.replaceText(REPLACE_LF).iterator(SLICER); it.hasNext(); ) { + Component child = it.next().children(Collections.emptyList()); + if (child instanceof TextComponent text && text.content().equals(Component.newline().content())) { + result.add(line.compact()); + line = Component.empty(); + } else { + line = line.append(child); + } + } + if (Component.IS_NOT_EMPTY.test(line)) { + result.add(line.compact()); + } + return result; + } + /** * Checks if a character is a legacy color code. * diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java index 0e47529c1..6884afedd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java @@ -1,52 +1,95 @@ package net.momirealms.craftengine.core.util; +import org.joml.Vector3f; + import java.util.Arrays; public class Color { + private static final int BIT_MASK = 0xff; private static final byte DEFAULT_ALPHA = (byte) 255; - private final byte r; - private final byte g; - private final byte b; - private final byte a; + private final int color; - public Color(byte r, byte g, byte b, byte a) { - this.b = b; - this.g = g; - this.r = r; - this.a = a; + public Color(int color) { + this.color = color; } - public Color(byte r, byte g, byte b) { - this(r, g, b, DEFAULT_ALPHA); + public Color(int r, int g, int b) { + this(DEFAULT_ALPHA, r, g, b); } - public int toDecimal() { - return DEFAULT_ALPHA << 24 | (r << 16) | (g << 8) | b; + public Color(int a, int r, int g, int b) { + this(toDecimal(a, r, g, b)); } - public static Color fromString(String[] strings) { + public int color() { + return color; + } + + public static int toDecimal(int a, int r, int g, int b) { + return a << 24 | r << 16 | g << 8 | b; + } + + public static int toDecimal(int r, int g, int b) { + return DEFAULT_ALPHA << 24 | r << 16 | g << 8 | b; + } + + public static Color fromDecimal(int decimal) { + return new Color(decimal); + } + + public static Color fromVector3f(Vector3f vec) { + return new Color(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vec.x) << 16 | MCUtils.fastFloor(vec.y) << 8 | MCUtils.fastFloor(vec.z)); + } + + public static int opaque(int color) { + return color | -16777216; + } + + public static int transparent(int color) { + return color & 16777215; + } + + public static int alpha(int color) { + return color >>> 24; + } + + public static int red(int color) { + return color >> 16 & BIT_MASK; + } + + public static int green(int color) { + return color >> 8 & BIT_MASK; + } + + public static int blue(int color) { + return color & BIT_MASK; + } + + public static Color fromStrings(String[] strings) { if (strings.length == 3) { - return new Color(Byte.parseByte(strings[0]), Byte.parseByte(strings[1]), Byte.parseByte(strings[2])); + // rgb + return fromDecimal(toDecimal(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2]))); } else if (strings.length == 4) { - return new Color(Byte.parseByte(strings[0]), Byte.parseByte(strings[1]), Byte.parseByte(strings[2]), Byte.parseByte(strings[3])); + // argb + return fromDecimal(toDecimal(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2]), Integer.parseInt(strings[3]))); } else { throw new IllegalArgumentException("Invalid color format: " + Arrays.toString(strings)); } } - public byte a() { - return a; + public int a() { + return alpha(color); } - public byte b() { - return b; + public int b() { + return blue(color); } - public byte g() { - return g; + public int g() { + return green(color); } - public byte r() { - return r; + public int r() { + return red(color); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ExceptionCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/ExceptionCollector.java index 868df2a03..f7a678862 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ExceptionCollector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ExceptionCollector.java @@ -19,5 +19,10 @@ public class ExceptionCollector { throw this.result; } } + + public void addAndThrow(T throwable) throws T { + this.add(throwable); + this.throwIfPresent(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index f3b1bf95c..54195dff8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -51,6 +51,20 @@ public class MiscUtils { return list; } + public static String[] getAsStringArray(Object o) { + if (o instanceof List list) { + String[] array = new String[list.size()]; + for (int i = 0; i < array.length; i++) { + array[i] = list.get(i).toString(); + } + return array; + } else if (o != null) { + return new String[]{o.toString()}; + } else { + return new String[0]; + } + } + @SuppressWarnings("unchecked") public static List getAsList(Object o, Class clazz) { if (o instanceof List list) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java index 4ac327b00..fa7b61ce5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ReflectionUtils.java @@ -74,7 +74,7 @@ public class ReflectionUtils { } } - @NotNull + @Nullable public static Field getDeclaredField(@NotNull Class clazz, @NotNull String... possibleNames) { List possibleNameList = Arrays.asList(possibleNames); for (Field field : clazz.getDeclaredFields()) { @@ -82,7 +82,7 @@ public class ReflectionUtils { return field; } } - throw new RuntimeException("Class " + clazz.getName() + " does not contain a field with possible names " + Arrays.toString(possibleNames)); + return null; } @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java index b6a40aa27..d0d69ebc7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ResourceConfigUtils.java @@ -5,10 +5,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -20,6 +17,17 @@ public final class ResourceConfigUtils { return raw != null ? function.apply(raw) : defaultValue; } + public static > E getAsEnum(Object o, Class clazz, E defaultValue) { + if (o == null) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + return defaultValue; + } + } + public static T requireNonNullOrThrow(T obj, String node) { if (obj == null) throw new LocalizedResourceConfigException(node); @@ -196,4 +204,12 @@ public final class ResourceConfigUtils { } } } + + @SuppressWarnings("unchecked") + public static Map getAsMap(Object obj, String option) { + if (obj instanceof Map map) { + return (Map) map; + } + throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/UniqueKey.java b/core/src/main/java/net/momirealms/craftengine/core/util/UniqueKey.java index 1a4591535..a9e023a57 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/UniqueKey.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/UniqueKey.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.util; -import net.momirealms.craftengine.core.item.ItemKeys; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -8,7 +7,6 @@ import java.util.Map; public final class UniqueKey { private static final Map CACHE = new HashMap<>(4096, 0.5f); - public static final UniqueKey AIR = UniqueKey.create(ItemKeys.AIR); private final Key key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java index 12e001c79..0c003a3eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java @@ -7,7 +7,9 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; public class VersionHelper { + public static final boolean PREMIUM = true; public static final MinecraftVersion MINECRAFT_VERSION; + public static final boolean COMPONENT_RELEASE; private static final int version; private static final int majorVersion; private static final int minorVersion; @@ -70,6 +72,8 @@ public class VersionHelper { majorVersion = major; minorVersion = minor; + COMPONENT_RELEASE = v1_20_5; + mojmap = checkMojMap(); folia = checkFolia(); paper = checkPaper(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java index 5c83b592b..d6a746cbd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java @@ -13,6 +13,8 @@ public interface WorldManager extends Manageable { CEWorld getWorld(UUID uuid); + CEWorld[] getWorlds(); + void loadWorld(World world); void loadWorld(CEWorld world); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index 45b0a52b8..fb00cc0dc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -113,7 +113,7 @@ public class CEChunk { return this.sections[index]; } - @Nullable + @NotNull public CESection sectionById(int sectionId) { return this.sections[sectionIndex(sectionId)]; } diff --git a/gradle.properties b/gradle.properties index ef51839f9..7399a1fc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.60.3 -config_version=42 -lang_version=22 +project_version=0.0.60.11 +config_version=43 +lang_version=23 project_group=net.momirealms latest_supported_version=1.21.8 @@ -40,7 +40,7 @@ commons_io_version=2.18.0 commons_imaging_version=1.0.0-alpha6 commons_lang3_version=3.17.0 sparrow_nbt_version=0.9.4 -sparrow_util_version=0.50.6 +sparrow_util_version=0.50.8 fastutil_version=8.5.15 netty_version=4.1.121.Final joml_version=1.10.8 @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.18 -nms_helper_version=1.0.37 +nms_helper_version=1.0.52 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23