From 6ce5977c27f806d71613cd94b0c4e4612001499a Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 15:43:27 +0800 Subject: [PATCH 01/15] fix(storage): fix storage bugs --- .../storage/HyperdimensionStorageStationBlock.java | 2 +- .../dubhe/anvilcraft/client/event/ClientEventListener.java | 2 ++ .../client/gui/screen/CategorySettingsScreen.java | 5 +++++ .../dev/dubhe/anvilcraft/saved/setting/PlayerSettings.java | 4 ++++ .../anvilcraft/saved/storage/category/AndCategory.java | 2 +- .../storage/category/CraftingBookCategoryCategory.java | 2 +- .../saved/storage/category/CreativeModeTabCategory.java | 2 +- .../anvilcraft/saved/storage/category/FilterCategory.java | 3 ++- .../saved/storage/category/HasComponentCategory.java | 3 ++- .../dubhe/anvilcraft/saved/storage/category/ICategory.java | 7 ++++++- .../saved/storage/category/NamespaceCategory.java | 3 ++- .../anvilcraft/saved/storage/category/OrCategory.java | 3 ++- 12 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java index 19f772e6e5..eb1006ccdd 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java @@ -127,7 +127,7 @@ public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, if (id != null) { Storages.get().remove(id); } - } else { + } else if (player.hasInfiniteMaterials()) { // 非空容器:掉落含 STORAGE 引用的容器物品(与 tooltip 声明一致, // 拾取后放回仍可访问其存储内容),对所有模式生效。 LootParams.Builder builder = new LootParams.Builder(serverLevel) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java index 7dec798b3c..58d546f223 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java @@ -21,6 +21,7 @@ import dev.dubhe.anvilcraft.network.DragonRodStopDevourPacket; import dev.dubhe.anvilcraft.network.OpenHammerAnvilPacket; import dev.dubhe.anvilcraft.network.UsePillBoxPacket; +import dev.dubhe.anvilcraft.saved.setting.PlayerSettings; import dev.dubhe.anvilcraft.util.BlockHighlightUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -76,6 +77,7 @@ public static void onClientPlayerDisconnect(ClientPlayerNetworkEvent.LoggingOut SoundHelper.INSTANCE.clear(); StorageTerminalClientStub.clear(); TerminalRemoteOverlay.setHovering(ItemStack.EMPTY); + PlayerSettings.clearClient(); } @SubscribeEvent diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/CategorySettingsScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/CategorySettingsScreen.java index 6f1731cbdf..5a91a87306 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/CategorySettingsScreen.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/CategorySettingsScreen.java @@ -100,6 +100,11 @@ public int size() { @Override public void setHead(int head) { CategorySettingsScreen.this.alternateHead = head; + + if (CategorySettingsScreen.this.addCategory != null) { + CategorySettingsScreen.this.addCategory.active = head == 0; + CategorySettingsScreen.this.addCategory.visible = head == 0; + } } }; private int alternateHead = 0; diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/setting/PlayerSettings.java b/src/main/java/dev/dubhe/anvilcraft/saved/setting/PlayerSettings.java index ed381c8205..583ce960d3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/setting/PlayerSettings.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/setting/PlayerSettings.java @@ -43,6 +43,10 @@ public static PlayerSetting getSetting(HolderLookup.Provider registries, UUID id return PlayerSettings.get().settings.computeIfAbsent(id, ignored -> new PlayerSetting(registries)); } + public static void clearClient() { + PlayerSettings.CLIENT_COPY.settings.clear(); + } + @Override protected void registerDataFixers() { } diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/AndCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/AndCategory.java index 0ca8217413..bd41335acb 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/AndCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/AndCategory.java @@ -54,7 +54,7 @@ public static class Type implements ICategory.Type { ItemStack.CODEC .fieldOf("icon") .forGetter(AndCategory::icon), - ComponentSerialization.CODEC + ICategory.NAME_CODEC .fieldOf("name") .forGetter(AndCategory::name), ICategory.CODEC diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CraftingBookCategoryCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CraftingBookCategoryCategory.java index 99ead586a3..b4e6b781b2 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CraftingBookCategoryCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CraftingBookCategoryCategory.java @@ -77,7 +77,7 @@ public static class Type implements ICategory.Type ItemStack.CODEC .fieldOf("icon") .forGetter(CraftingBookCategoryCategory::icon), - ComponentSerialization.CODEC + ICategory.NAME_CODEC .fieldOf("name") .forGetter(CraftingBookCategoryCategory::name), CraftingBookCategory.CODEC diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CreativeModeTabCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CreativeModeTabCategory.java index f84a0c3fb5..9cc6f14890 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CreativeModeTabCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/CreativeModeTabCategory.java @@ -51,7 +51,7 @@ public static class Type implements ICategory.Type { ItemStack.CODEC .fieldOf("icon") .forGetter(CreativeModeTabCategory::icon), - ComponentSerialization.CODEC + ICategory.NAME_CODEC .fieldOf("name") .forGetter(CreativeModeTabCategory::name), ResourceKey.codec(Registries.CREATIVE_MODE_TAB) diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/FilterCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/FilterCategory.java index 8444b79e1e..da3cd95716 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/FilterCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/FilterCategory.java @@ -59,7 +59,8 @@ public static class Type implements ICategory.Type { ItemStack.CODEC .fieldOf("icon") .forGetter(FilterCategory::icon), - ComponentSerialization.CODEC + + ICategory.NAME_CODEC .fieldOf("name") .forGetter(FilterCategory::name), FilterContent.CODEC diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/HasComponentCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/HasComponentCategory.java index 5e657caff4..f3579703dc 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/HasComponentCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/HasComponentCategory.java @@ -206,7 +206,8 @@ public int hashCode() { public static class Type implements ICategory.Type { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( ItemStack.CODEC.fieldOf("icon").forGetter(HasComponentCategory::icon), - ComponentSerialization.CODEC.fieldOf("name").forGetter(HasComponentCategory::name), + + ICategory.NAME_CODEC.fieldOf("name").forGetter(HasComponentCategory::name), DataComponentPredicate.CODEC.listOf().optionalFieldOf("predicates", List.of()).forGetter(HasComponentCategory::predicates), DataComponentType.CODEC.listOf().optionalFieldOf("presence", List.of()).forGetter(HasComponentCategory::presence), MatchType.CODEC.optionalFieldOf("match_type", MatchType.OR).forGetter(HasComponentCategory::match) diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java index 584160ae5f..dcedf0057e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.saved.storage.category; +import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; @@ -13,6 +14,7 @@ import net.minecraft.core.HolderLookup; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.RegistryFileCodec; @@ -22,6 +24,7 @@ import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.function.Predicate; public interface ICategory extends Predicate { @@ -55,6 +58,8 @@ public DataResult encode(ICategory input, DynamicOps ops, T prefix) { StreamCodec STREAM_CODEC = ByteBufCodecs.registry(ModRegistryKeys.CATEGORY_TYPE) .dispatch(ICategory::getType, Type::streamCodec); StreamCodec> LIST_STREAM_CODEC = ICategory.STREAM_CODEC.apply(ByteBufCodecs.list()); + Codec NAME_CODEC = Codec.either(ComponentSerialization.CODEC, ComponentSerialization.flatCodec(Integer.MAX_VALUE)) + .xmap(either -> either.map(Function.identity(), Function.identity()), Either::left); ItemStack icon(); @@ -71,4 +76,4 @@ static Component constructName(ResourceLocation suffix) { interface Type extends ISerializer { } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/NamespaceCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/NamespaceCategory.java index a51a90a6a9..dabe650916 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/NamespaceCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/NamespaceCategory.java @@ -64,7 +64,8 @@ public static class Type implements ICategory.Type { ItemStack.CODEC .fieldOf("icon") .forGetter(NamespaceCategory::icon), - ComponentSerialization.CODEC + + ICategory.NAME_CODEC .fieldOf("name") .forGetter(NamespaceCategory::name), Codec.STRING diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/OrCategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/OrCategory.java index 2825e7e5b2..40563af7e5 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/OrCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/OrCategory.java @@ -54,7 +54,8 @@ public static class Type implements ICategory.Type { ItemStack.CODEC .fieldOf("icon") .forGetter(OrCategory::icon), - ComponentSerialization.CODEC + + ICategory.NAME_CODEC .fieldOf("name") .forGetter(OrCategory::name), ICategory.CODEC From ee13381aa92382b16e99c10fffa6a88d2a051e8e Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 16:04:57 +0800 Subject: [PATCH 02/15] fix(storage): fix storage bugs --- .../dubhe/anvilcraft/client/event/ClientEventListener.java | 2 ++ .../dev/dubhe/anvilcraft/client/rpc/SettingClientStub.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java index 58d546f223..af3285f8c8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java @@ -8,6 +8,7 @@ import dev.dubhe.anvilcraft.client.gui.screen.StorageScreen; import dev.dubhe.anvilcraft.client.gui.tooltip.FilterContentHoverWindow; import dev.dubhe.anvilcraft.client.init.ModKeyMappings; +import dev.dubhe.anvilcraft.client.rpc.SettingClientStub; import dev.dubhe.anvilcraft.client.rpc.StorageTerminalClientStub; import dev.dubhe.anvilcraft.client.support.AmuletSelectorSupport; import dev.dubhe.anvilcraft.client.support.StructureDiskPreviewSupport; @@ -78,6 +79,7 @@ public static void onClientPlayerDisconnect(ClientPlayerNetworkEvent.LoggingOut StorageTerminalClientStub.clear(); TerminalRemoteOverlay.setHovering(ItemStack.EMPTY); PlayerSettings.clearClient(); + SettingClientStub.clearCache(); } @SubscribeEvent diff --git a/src/main/java/dev/dubhe/anvilcraft/client/rpc/SettingClientStub.java b/src/main/java/dev/dubhe/anvilcraft/client/rpc/SettingClientStub.java index ba142a80bc..3a68e1f3f0 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/rpc/SettingClientStub.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/rpc/SettingClientStub.java @@ -181,6 +181,10 @@ private static synchronized void clearFallback(UUID playerId) { return setting; } + public static void clearCache() { + SettingClientStub.cachedSetting = null; + } + private static void cache(UUID playerId, @Nullable PlayerSetting setting) { SettingClientStub.cachedPlayerId = playerId; SettingClientStub.cachedSetting = setting; From 737fa98685768a7dab2a57b628c3782def40df98 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 17:56:10 +0800 Subject: [PATCH 03/15] fix(jei): fix jei universal support --- .../client/event/ClientEventListener.java | 2 + .../client/rpc/TerminalJeiStorageCache.java | 96 ++++++++ .../integration/jei/AnvilCraftJeiPlugin.java | 62 +---- .../TerminalRecipeTransferHandler.java | 218 ------------------ .../BasicRecipeTransferHandlerMixin.java | 217 +++++++++++++++++ .../anvilcraft/rpc/StorageServerStub.java | 45 +++- src/main/resources/anvilcraft.mixins.json | 1 + 7 files changed, 364 insertions(+), 277 deletions(-) create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java delete mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/handlers/TerminalRecipeTransferHandler.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java index af3285f8c8..20f042b9c1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/event/ClientEventListener.java @@ -10,6 +10,7 @@ import dev.dubhe.anvilcraft.client.init.ModKeyMappings; import dev.dubhe.anvilcraft.client.rpc.SettingClientStub; import dev.dubhe.anvilcraft.client.rpc.StorageTerminalClientStub; +import dev.dubhe.anvilcraft.client.rpc.TerminalJeiStorageCache; import dev.dubhe.anvilcraft.client.support.AmuletSelectorSupport; import dev.dubhe.anvilcraft.client.support.StructureDiskPreviewSupport; import dev.dubhe.anvilcraft.client.support.TerminalRemoteOverlay; @@ -77,6 +78,7 @@ public static void onRenderBlockOverlay(RenderBlockScreenEffectEvent event) { public static void onClientPlayerDisconnect(ClientPlayerNetworkEvent.LoggingOut event) { SoundHelper.INSTANCE.clear(); StorageTerminalClientStub.clear(); + TerminalJeiStorageCache.clear(); TerminalRemoteOverlay.setHovering(ItemStack.EMPTY); PlayerSettings.clearClient(); SettingClientStub.clearCache(); diff --git a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java new file mode 100644 index 0000000000..98f2c3bbb6 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java @@ -0,0 +1,96 @@ +package dev.dubhe.anvilcraft.client.rpc; + +import dev.anvilcraft.lib.v2.rpc.RPC; +import dev.anvilcraft.lib.v2.rpc.RpcTarget; +import dev.dubhe.anvilcraft.init.item.ModComponents; +import dev.dubhe.anvilcraft.init.item.ModItems; +import dev.dubhe.anvilcraft.item.property.component.TerminalBinding; +import dev.dubhe.anvilcraft.rpc.StorageServerStub; +import net.minecraft.client.Minecraft; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * 客户端缓存玩家绑定存储站的物品代表列表(含数量), + * 供 JEI 转移的检查阶段判断存储站是否满足配方需求。 + */ +public final class TerminalJeiStorageCache { + private static final Map> CACHE = new HashMap<>(); + private static final Map>> PENDING = new HashMap<>(); + + private TerminalJeiStorageCache() { + } + + /** 玩家是否持有指向任意存储站的绑定终端。 */ + public static @Nullable UUID boundStorage(Player player) { + for (ItemStack stack : player.getInventory().items) { + UUID id = TerminalJeiStorageCache.storageOf(stack); + if (id != null) { + return id; + } + } + for (ItemStack stack : player.getInventory().offhand) { + UUID id = TerminalJeiStorageCache.storageOf(stack); + if (id != null) { + return id; + } + } + return null; + } + + /** 获取缓存的存储站物品列表(可能为 null 表示尚未加载)。 */ + public static @Nullable List get(UUID storageId) { + return TerminalJeiStorageCache.CACHE.get(storageId); + } + + /** 异步加载存储站物品列表到缓存;若已在加载则复用进行中的 future。 */ + public static CompletableFuture> ensure(UUID storageId) { + synchronized (TerminalJeiStorageCache.class) { + if (TerminalJeiStorageCache.CACHE.containsKey(storageId)) { + return CompletableFuture.completedFuture(TerminalJeiStorageCache.CACHE.get(storageId)); + } + CompletableFuture> pending = TerminalJeiStorageCache.PENDING.get(storageId); + if (pending != null) { + return pending; + } + Player player = Minecraft.getInstance().player; + if (player == null) { + return CompletableFuture.completedFuture(List.of()); + } + pending = RPC.invoke( + RpcTarget.server(), + StorageServerStub::getStorageItems, + player.getGameProfile().getId(), + storageId + ).thenApply(items -> { + TerminalJeiStorageCache.CACHE.put(storageId, items); + return items; + }).whenComplete((items, error) -> TerminalJeiStorageCache.PENDING.remove(storageId)); + TerminalJeiStorageCache.PENDING.put(storageId, pending); + return pending; + } + } + + public static void clear() { + TerminalJeiStorageCache.CACHE.clear(); + TerminalJeiStorageCache.PENDING.clear(); + } + + private static @Nullable UUID storageOf(ItemStack stack) { + if (!stack.is(ModItems.HYPERDIMENSION_TERMINAL)) { + return null; + } + TerminalBinding binding = stack.get(ModComponents.TERMINAL_BINDING); + if (binding == null || binding.id().isEmpty()) { + return null; + } + return binding.id().get(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java index 4b00122e0f..b5816ba822 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java @@ -10,6 +10,7 @@ import dev.dubhe.anvilcraft.client.gui.screen.ItemDetectorScreen; import dev.dubhe.anvilcraft.client.gui.screen.JewelCraftingScreen; import dev.dubhe.anvilcraft.client.gui.screen.StorageScreen; +import dev.dubhe.anvilcraft.init.ModMenuTypes; import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.integration.jei.category.AnvilCollisionCraftCategory; import dev.dubhe.anvilcraft.integration.jei.category.BeaconConversionCategory; @@ -47,7 +48,6 @@ import dev.dubhe.anvilcraft.integration.jei.category.multiblock.MultiBlockConversionCategory; import dev.dubhe.anvilcraft.integration.jei.category.multiblock.MultiBlockCraftingCategory; import dev.dubhe.anvilcraft.integration.jei.handlers.GhostIngredientHandler; -import dev.dubhe.anvilcraft.integration.jei.handlers.TerminalRecipeTransferHandler; import dev.dubhe.anvilcraft.integration.jei.recipe.BeaconConversionRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.DecayRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.HyperdimensionStorageStationUpgradeRecipe; @@ -93,9 +93,6 @@ import mezz.jei.api.helpers.IGuiHelper; import mezz.jei.api.helpers.IJeiHelpers; import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.recipe.transfer.IRecipeTransferHandler; -import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper; -import mezz.jei.api.recipe.transfer.IRecipeTransferInfo; import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.IRecipeCatalystRegistration; import mezz.jei.api.registration.IRecipeCategoryRegistration; @@ -107,7 +104,6 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.Rect2i; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Recipe; @@ -330,63 +326,13 @@ public void registerCategories(IRecipeCategoryRegistration registration) { @Override public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) { - // 携带已绑定终端时,JEI "+" 快速合成可从终端连接的存储站补库 - IRecipeTransferHandlerHelper helper = registration.getTransferHelper(); - - // 皇家锻造台(SMITHING) - registerStorageAware( - registration, - helper, + // 终端存储站补库由 BasicRecipeTransferHandlerMixin 统一注入 JEI 转移流程处理 + registration.addRecipeTransferHandler( RoyalSmithingMenu.class, + ModMenuTypes.ROYAL_SMITHING.get(), RecipeTypes.SMITHING, 0, 3, 4, 36 ); - - // 工作台 3x3:合成格 1-9,玩家物品栏 10-45(9 是合成格,不能作为物品栏起点) - registerStorageAware( - registration, - helper, - net.minecraft.world.inventory.CraftingMenu.class, - RecipeTypes.CRAFTING, - 1, 9, 10, 36 - ); - // 背包 2x2:合成格 1-4,玩家物品栏 9-44 - registerStorageAware( - registration, - helper, - net.minecraft.world.inventory.InventoryMenu.class, - RecipeTypes.CRAFTING, - 1, 4, 9, 36 - ); - } - - private static > void registerStorageAware( - IRecipeTransferRegistration registration, - IRecipeTransferHandlerHelper helper, - Class containerClass, - RecipeType> recipeType, - int recipeSlotStart, - int recipeSlotCount, - int inventorySlotStart, - @SuppressWarnings("SameParameterValue") - int inventorySlotCount - ) { - IRecipeTransferInfo> info = - helper.createBasicRecipeTransferInfo( - containerClass, - null, - recipeType, - recipeSlotStart, - recipeSlotCount, - inventorySlotStart, - inventorySlotCount - ); - IRecipeTransferHandler> delegate = - helper.createUnregisteredRecipeTransferHandler(info); - registration.addRecipeTransferHandler( - new TerminalRecipeTransferHandler<>(containerClass, recipeType, delegate), - recipeType - ); } @Override diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/handlers/TerminalRecipeTransferHandler.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/handlers/TerminalRecipeTransferHandler.java deleted file mode 100644 index 3ca8f609ed..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/handlers/TerminalRecipeTransferHandler.java +++ /dev/null @@ -1,218 +0,0 @@ -package dev.dubhe.anvilcraft.integration.jei.handlers; - -import dev.dubhe.anvilcraft.client.rpc.StorageTerminalClientStub; -import dev.dubhe.anvilcraft.init.item.ModComponents; -import dev.dubhe.anvilcraft.init.item.ModItems; -import dev.dubhe.anvilcraft.item.property.component.TerminalBinding; -import mezz.jei.api.gui.ingredient.IRecipeSlotView; -import mezz.jei.api.gui.ingredient.IRecipeSlotsView; -import mezz.jei.api.recipe.RecipeIngredientRole; -import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.recipe.transfer.IRecipeTransferError; -import mezz.jei.api.recipe.transfer.IRecipeTransferHandler; -import net.minecraft.client.Minecraft; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.item.crafting.RecipeHolder; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import javax.annotation.Nullable; - -/** - * 携带已绑定终端时,JEI "+" 快速合成会把终端连接的存储站视为可用库存: - * 点击传输时先从存储站取出背包缺少的物品(异步),补库完成后在主线程再次执行 - * 默认传输逻辑填入合成格。检查阶段不取物,避免打开界面就消耗存储站物品。 - */ -public class TerminalRecipeTransferHandler> - implements IRecipeTransferHandler> { - private final Class containerClass; - private final RecipeType> recipeType; - private final IRecipeTransferHandler> delegate; - - public TerminalRecipeTransferHandler( - Class containerClass, - RecipeType> recipeType, - IRecipeTransferHandler> delegate - ) { - this.containerClass = containerClass; - this.recipeType = recipeType; - this.delegate = delegate; - } - - @Override - public Class getContainerClass() { - return this.containerClass; - } - - @Override - public Optional> getMenuType() { - return Optional.empty(); - } - - @Override - public RecipeType> getRecipeType() { - return this.recipeType; - } - - @Override - public @Nullable IRecipeTransferError transferRecipe( - AbstractContainerMenu container, - RecipeHolder recipe, - IRecipeSlotsView recipeSlots, - Player player, - boolean maxTransfer, - boolean doTransfer - ) { - UUID storageId = TerminalRecipeTransferHandler.boundTerminalStorage(player); - if (storageId == null || !player.level().isClientSide()) { - // 未携带终端或非客户端:走 JEI 默认传输逻辑 - return this.delegate.transferRecipe(container, recipe, recipeSlots, player, maxTransfer, doTransfer); - } - if (!doTransfer) { - // 检查阶段:存储站物品视为可用,直接返回成功让 "+" 可用;不实际取物 - return null; - } - // 传输阶段:计算背包与容器(含合成格)缺少的物品(Shift 时按多组需求量)。 - // 合成格中已有的物品会在默认传输时被清出放回背包,故应计入“已有”避免重复补库。 - List missing = TerminalRecipeTransferHandler.collectMissing( - container, - player, - recipeSlots, - maxTransfer - ); - if (missing.isEmpty()) { - // 背包已足够:直接走默认传输 - return this.delegate.transferRecipe(container, recipe, recipeSlots, player, maxTransfer, true); - } - // 异步补库,完成后在主线程再次执行默认传输填充合成格 - StorageTerminalClientStub.withdrawToInventory(storageId, missing).thenAccept(changed -> Minecraft.getInstance().execute(() -> - this.delegate.transferRecipe(container, recipe, recipeSlots, player, maxTransfer, true) - )); - // 首次调用返回成功,实际填充由补库完成后的回调执行 - return null; - } - - @Nullable - private static UUID boundTerminalStorage(Player player) { - // 终端可能在主手、副手或背包任意槽位中 - for (ItemStack stack : player.getInventory().items) { - UUID id = TerminalRecipeTransferHandler.storageOf(stack); - if (id != null) { - return id; - } - } - for (ItemStack stack : player.getInventory().offhand) { - UUID id = TerminalRecipeTransferHandler.storageOf(stack); - if (id != null) { - return id; - } - } - return null; - } - - @Nullable - private static UUID storageOf(ItemStack stack) { - if (!stack.is(ModItems.HYPERDIMENSION_TERMINAL)) { - return null; - } - TerminalBinding binding = stack.get(ModComponents.TERMINAL_BINDING); - if (binding == null || binding.id().isEmpty()) { - return null; - } - return binding.id().get(); - } - - /** - * 计算容器(背包 + 合成格)缺少的物品:按配方输入槽聚合每种物品的总需求量与槽数, - * 扣除整个容器中已有的同种物品(含合成格,因为合成格中的物品在默认传输时会被 - * 清出放回背包)。普通传输每种按一份(配方用量)计;Shift(maxTransfer)按 - * 槽数 × 每组数量计(合成格每格放满一组),服务端按背包剩余空间实际限制提取量。 - */ - private static List collectMissing( - AbstractContainerMenu container, - Player player, - IRecipeSlotsView recipeSlots, - boolean maxTransfer - ) { - // 聚合配方所需:每种物品的总用量与出现槽数 - List needs = new ArrayList<>(); - List slotCounts = new ArrayList<>(); - for (IRecipeSlotView slotView : recipeSlots.getSlotViews(RecipeIngredientRole.INPUT)) { - List variants = slotView.getItemStacks().toList(); - if (variants.isEmpty()) { - continue; - } - ItemStack representative = variants.getFirst().copy(); - if (representative.getCount() <= 0) { - representative.setCount(1); - } - int idx = TerminalRecipeTransferHandler.findNeed(needs, representative); - if (idx < 0) { - needs.add(representative.copy()); - slotCounts.add(1); - } else { - needs.get(idx).grow(representative.getCount()); - slotCounts.set(idx, slotCounts.get(idx) + 1); - } - } - // 扣除容器已有(背包 + 合成格 + 鼠标指针),得到缺口 - List missing = new ArrayList<>(); - for (int i = 0; i < needs.size(); i++) { - ItemStack need = needs.get(i); - int required; - if (maxTransfer) { - // Shift:每格放满一组,目标 = 槽数 × 每组数量 - required = need.getMaxStackSize() * slotCounts.get(i); - } else { - required = need.getCount(); - } - int have = TerminalRecipeTransferHandler.countInContainer(container, player, need); - int deficit = Math.max(0, required - have); - if (deficit > 0) { - ItemStack m = need.copy(); - m.setCount(deficit); - missing.add(m); - } - } - return missing; - } - - private static int findNeed(List needs, ItemStack add) { - for (int i = 0; i < needs.size(); i++) { - if (ItemStack.isSameItemSameComponents(needs.get(i), add)) { - return i; - } - } - return -1; - } - - /** - * 统计整个容器(含合成格)与鼠标指针中同种物品的总量。 - * 合成格中的物品在默认传输时会被清出放回背包,因此必须计入“已有”以免重复补库。 - */ - private static int countInContainer(AbstractContainerMenu container, Player player, ItemStack resource) { - int count = 0; - for (Slot slot : container.slots) { - if (slot.isFake()) { - continue; - } - ItemStack stack = slot.getItem(); - if (!stack.isEmpty() && ItemStack.isSameItemSameComponents(stack, resource)) { - count += stack.getCount(); - } - } - // 鼠标指针上的同种物品也算作已有(避免捏着物品时重复补库) - ItemStack carried = container.getCarried(); - if (!carried.isEmpty() && ItemStack.isSameItemSameComponents(carried, resource)) { - count += carried.getCount(); - } - return count; - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java new file mode 100644 index 0000000000..e73d75bc69 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java @@ -0,0 +1,217 @@ +package dev.dubhe.anvilcraft.mixin; + +import dev.dubhe.anvilcraft.client.rpc.StorageTerminalClientStub; +import dev.dubhe.anvilcraft.client.rpc.TerminalJeiStorageCache; +import mezz.jei.api.gui.ingredient.IRecipeSlotView; +import mezz.jei.api.gui.ingredient.IRecipeSlotsView; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.recipe.transfer.IRecipeTransferError; +import mezz.jei.library.transfer.BasicRecipeTransferHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * 把终端的 JEI 转移配方逻辑注入 JEI 的 {@link BasicRecipeTransferHandler#transferRecipe}: + *
    + *
  • 检查阶段(doTransfer=false):持有绑定终端时直接返回成功,使 "+" 按钮可用 + * (存储站物品视为可用),不实际取物。
  • + *
  • 传输阶段(doTransfer=true):先从存储站补足背包缺少的配方物品(异步), + * 补库完成后重试原传输逻辑把物品填入合成格。
  • + *
+ * 不注入 {@code getInventoryState}:其声明的"虚拟空槽"无法让服务端实际取到物品。 + */ +@Mixin(BasicRecipeTransferHandler.class) +public abstract class BasicRecipeTransferHandlerMixin { + @Shadow + public abstract @Nullable IRecipeTransferError transferRecipe( + C container, + R recipe, + IRecipeSlotsView recipeSlotsView, + Player player, + boolean maxTransfer, + boolean doTransfer + ); + + @Unique + private static final ThreadLocal ANVILCRAFT_RESTOCKING = ThreadLocal.withInitial(() -> false); + + @Inject(method = "transferRecipe", at = @At("HEAD"), cancellable = true) + private void anvilcraft$restockOrAllow( + C container, + R recipe, + IRecipeSlotsView recipeSlotsView, + Player player, + boolean maxTransfer, + boolean doTransfer, + CallbackInfoReturnable cir + ) { + if (!player.level().isClientSide() || ANVILCRAFT_RESTOCKING.get()) { + return; + } + UUID storageId = TerminalJeiStorageCache.boundStorage(player); + if (storageId == null) { + return; + } + if (!doTransfer) { + // 检查阶段:仅当背包 + 存储站满足配方需求时才视为可用;否则走原方法(报缺少材料)。 + // 存储站物品列表为异步缓存,首次可能未就绪——此时走原方法(+ 暂不可用), + // 缓存加载完成后 JEI 刷新即可正确判断。 + TerminalJeiStorageCache.ensure(storageId); + List storageItems = TerminalJeiStorageCache.get(storageId); + if (storageItems != null + && BasicRecipeTransferHandlerMixin.anvilcraft$containerSatisfies(container, storageItems, recipeSlotsView)) { + cir.setReturnValue(null); + } + return; + } + // 传输阶段:先补足背包缺少的配方物品 + List missing = BasicRecipeTransferHandlerMixin.anvilcraft$collectMissing( + container, + recipeSlotsView, + maxTransfer + ); + if (missing.isEmpty()) { + return; // 背包已足够,走原传输逻辑 + } + ANVILCRAFT_RESTOCKING.set(true); + StorageTerminalClientStub.withdrawToInventory(storageId, missing).thenAccept(changed -> + Minecraft.getInstance().execute(() -> { + try { + // 补库完成:重试原传输逻辑(背包已有物品,getInventoryState 自然看到并生成真实转移) + this.transferRecipe(container, recipe, recipeSlotsView, player, maxTransfer, true); + } finally { + ANVILCRAFT_RESTOCKING.set(false); + } + }) + ); + cir.setReturnValue(null); + } + + @Unique + private static List anvilcraft$collectMissing( + AbstractContainerMenu container, + IRecipeSlotsView recipeSlots, + boolean maxTransfer + ) { + List needs = new ArrayList<>(); + List slotCounts = new ArrayList<>(); + for (IRecipeSlotView slotView : recipeSlots.getSlotViews(RecipeIngredientRole.INPUT)) { + List variants = slotView.getItemStacks().toList(); + if (variants.isEmpty()) { + continue; + } + ItemStack representative = variants.getFirst().copy(); + if (representative.getCount() <= 0) { + representative.setCount(1); + } + int idx = BasicRecipeTransferHandlerMixin.anvilcraft$findNeed(needs, representative); + if (idx < 0) { + needs.add(representative.copy()); + slotCounts.add(1); + } else { + needs.get(idx).grow(representative.getCount()); + slotCounts.set(idx, slotCounts.get(idx) + 1); + } + } + List missing = new ArrayList<>(); + for (int i = 0; i < needs.size(); i++) { + ItemStack need = needs.get(i); + int required = maxTransfer + ? need.getMaxStackSize() * slotCounts.get(i) + : need.getCount(); + int have = BasicRecipeTransferHandlerMixin.anvilcraft$countInContainer(container, need); + int deficit = Math.max(0, required - have); + if (deficit > 0) { + ItemStack m = need.copy(); + m.setCount(deficit); + missing.add(m); + } + } + return missing; + } + + @Unique + private static int anvilcraft$findNeed(List needs, ItemStack add) { + for (int i = 0; i < needs.size(); i++) { + if (ItemStack.isSameItemSameComponents(needs.get(i), add)) { + return i; + } + } + return -1; + } + + /** + * 检查阶段:判断背包 + 合成格 + 存储站是否满足配方所有输入需求。 + */ + @Unique + private static boolean anvilcraft$containerSatisfies( + AbstractContainerMenu container, + List storageItems, + IRecipeSlotsView recipeSlots + ) { + List needs = new ArrayList<>(); + for (IRecipeSlotView slotView : recipeSlots.getSlotViews(RecipeIngredientRole.INPUT)) { + List variants = slotView.getItemStacks().toList(); + if (variants.isEmpty()) { + continue; + } + ItemStack representative = variants.getFirst().copy(); + if (representative.getCount() <= 0) { + representative.setCount(1); + } + int idx = BasicRecipeTransferHandlerMixin.anvilcraft$findNeed(needs, representative); + if (idx < 0) { + needs.add(representative.copy()); + } else { + needs.get(idx).grow(representative.getCount()); + } + } + for (ItemStack need : needs) { + int have = BasicRecipeTransferHandlerMixin.anvilcraft$countInContainer(container, need); + // 存储站可提供量(每种物品的缓存数量) + for (ItemStack storageItem : storageItems) { + if (ItemStack.isSameItemSameComponents(storageItem, need)) { + have += storageItem.getCount(); + break; + } + } + if (have < need.getCount()) { + return false; + } + } + return true; + } + + @Unique + private static int anvilcraft$countInContainer(AbstractContainerMenu container, ItemStack resource) { + int count = 0; + for (Slot slot : container.slots) { + if (slot.isFake()) { + continue; + } + ItemStack stack = slot.getItem(); + if (!stack.isEmpty() && ItemStack.isSameItemSameComponents(stack, resource)) { + count += stack.getCount(); + } + } + ItemStack carried = container.getCarried(); + if (!carried.isEmpty() && ItemStack.isSameItemSameComponents(carried, resource)) { + count += carried.getCount(); + } + return count; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java index bd27dea6ef..04d9e953f3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java +++ b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java @@ -586,7 +586,8 @@ public static InteractionResult terminalInsert( public static boolean terminalWithdrawToInventory( UUID playerId, UUID storageId, - @CallableParam(clazz = StorageServerStub.class, field = "ITEM_STACK_LIST_STREAM_CODEC") List needs + @CallableParam(clazz = StorageServerStub.class, field = "ITEM_STACK_LIST_STREAM_CODEC") + List needs ) { ServerPlayer player = StorageServerStub.getServerPlayer(playerId); if (!StorageServerStub.ownsBoundTerminal(player, storageId)) { @@ -639,6 +640,48 @@ public static boolean terminalWithdrawToInventory( return changed; } + /** + * JEI 快速合成检查:返回玩家绑定存储站中每种物品的代表及数量(数量为存储站内总量, + * 受 ItemStack 上限限制,用于检查阶段判断存储站是否满足配方需求)。 + */ + @CallableParam(clazz = StorageServerStub.class, field = "ITEM_STACK_LIST_STREAM_CODEC") + @RemoteCallable(validator = TerminalAccessValidator.class) + public static List getStorageItems(UUID playerId, UUID storageId) { + ServerPlayer player = StorageServerStub.getServerPlayer(playerId); + if (!StorageServerStub.ownsBoundTerminal(player, storageId)) { + return List.of(); + } + Optional storageOp = Storages.get().get(storageId, HyperdimensionStorage.class); + if (storageOp.isEmpty()) { + return List.of(); + } + UnlimitedItemStacksResourceHandler items = storageOp.get().getItems(); + List result = new ArrayList<>(); + for (int slot = 0; slot < items.size(); slot++) { + long amount = items.getAmountAsLong(slot); + if (amount <= 0) { + continue; + } + ItemStack stack = items.getUnlimitedStackInSlot(slot).toStack().copyWithCount(1); + if (stack.isEmpty()) { + continue; + } + boolean merged = false; + for (ItemStack existing : result) { + if (ItemStack.isSameItemSameComponents(existing, stack)) { + int total = existing.getCount() + (int) Math.min(amount, Integer.MAX_VALUE - existing.getCount()); + existing.setCount(Math.min(total, stack.getMaxStackSize())); + merged = true; + break; + } + } + if (!merged) { + result.add(stack.copyWithCount((int) Math.min(amount, stack.getMaxStackSize()))); + } + } + return result; + } + private static int countInInventory(Inventory inventory, ItemStack resource) { int count = 0; for (int i = 0; i < inventory.getContainerSize(); i++) { diff --git a/src/main/resources/anvilcraft.mixins.json b/src/main/resources/anvilcraft.mixins.json index e69c7089fd..9b2e568d30 100644 --- a/src/main/resources/anvilcraft.mixins.json +++ b/src/main/resources/anvilcraft.mixins.json @@ -94,6 +94,7 @@ ], "client": [ "AbstractContainerScreenMixin", + "BasicRecipeTransferHandlerMixin", "BlockStateBaseMixin", "ClientLevelMixin", "ClientPacketListenerMixin", From f0f2889aaf2f0796b7ef186081d498b7d8324174 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 18:03:09 +0800 Subject: [PATCH 04/15] fix(screen): fix slot highlight over items --- .../client/gui/screen/StorageScreen.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageScreen.java index 6963193601..5960cf3d0a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageScreen.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageScreen.java @@ -420,10 +420,6 @@ private void renderStorageContents(GuiGraphics graphics, int mouseX, int mouseY) int slot = this.displayOrder.getInt(orderIndex); UnlimitedItemStack stack = this.getDisplayedStack(slot); - // 高亮先画在底层,物品图标盖过高亮,tooltip最后绘制 - if (hovered) { - AbstractContainerScreen.renderSlotHighlight(graphics, x, y, 0); - } if (!stack.isEmpty()) { ItemStack itemStack = stack.toStack(); graphics.renderItem(itemStack, x, y); @@ -436,6 +432,9 @@ private void renderStorageContents(GuiGraphics graphics, int mouseX, int mouseY) y ); } + if (hovered) { + AbstractContainerScreen.renderSlotHighlight(graphics, x, y, 0); + } if (hovered && this.carried.isEmpty() && !stack.isEmpty()) { List tooltipLines = new ArrayList<>(stack.toStack().getTooltipLines( Item.TooltipContext.of(this.minecraft.level), @@ -527,14 +526,13 @@ private void renderInventorySlot(GuiGraphics graphics, Inventory inv, int slot, stack = this.getQuickCraftPreviewStack(slot); graphics.fill(x, y, x + 16, y + 16, -2130706433); } - // 高亮先画在底层,物品图标盖过高亮,tooltip最后绘制 - if (hovered) { - AbstractContainerScreen.renderSlotHighlight(graphics, x, y, 0); - } if (!stack.isEmpty()) { graphics.renderItem(stack, x, y); graphics.renderItemDecorations(this.font, stack, x, y); } + if (hovered) { + AbstractContainerScreen.renderSlotHighlight(graphics, x, y, 0); + } if (hovered && this.carried.isEmpty() && !stack.isEmpty()) { graphics.renderTooltip(this.font, stack, mouseX, mouseY); } From c9c6540ee478edd06b7f23121d4b5ed4a4542330 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 18:12:42 +0800 Subject: [PATCH 05/15] chore(anno): add non-functional annotation to mark function --- .../container/storage/HyperdimensionStorageStationBlock.java | 2 +- .../dev/dubhe/anvilcraft/mixin/ComponentSerializationMixin.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java index eb1006ccdd..5b93fc8f14 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java @@ -129,7 +129,7 @@ public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, } } else if (player.hasInfiniteMaterials()) { // 非空容器:掉落含 STORAGE 引用的容器物品(与 tooltip 声明一致, - // 拾取后放回仍可访问其存储内容),对所有模式生效。 + // 拾取后放回仍可访问其存储内容),对创造模式生效(生存/冒险模式破坏子部件后会正常触发主部件掉落)。 LootParams.Builder builder = new LootParams.Builder(serverLevel) .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(mainPos)) .withParameter(LootContextParams.TOOL, player.getMainHandItem()) diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/ComponentSerializationMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/ComponentSerializationMixin.java index f92bb808c8..1d49d2fbf4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/ComponentSerializationMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/ComponentSerializationMixin.java @@ -9,6 +9,7 @@ @Mixin(ComponentSerialization.class) public class ComponentSerializationMixin { + // ComponentSerialization 中所有的 StreamCodec 都引用了 CODEC 作为原型,无需也无法注入 @ModifyVariable(method = "createCodec", at = @At("STORE")) private static ComponentContents.Type[] register(ComponentContents.Type[] original) { ComponentContents.Type[] newTypes = new ComponentContents.Type[original.length + 1]; From d7ca50f2692fe4be09cf98ab0aaf24a42fedd567 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 20:56:32 +0800 Subject: [PATCH 06/15] feat: add storage command, fix 3x3x3 crate turn into large crate, fix tooltip --- .../assets/anvilcraft/lang/en_ud.json | 29 ++- .../assets/anvilcraft/lang/en_us.json | 29 ++- .../impl/ShulkerContainerTooltipProvider.java | 29 +-- .../block/container/storage/CrateBlock.java | 70 ++++++- .../HyperdimensionStorageStationBlock.java | 19 ++ .../container/storage/LargeCrateBlock.java | 20 ++ .../storage/ShulkerContainerBlock.java | 19 ++ .../entity/storage/StorageBlockEntity.java | 29 +++ .../anvilcraft/command/MultiphaseCommand.java | 94 --------- .../anvilcraft/command/StorageCommand.java | 196 ++++++++++++++++++ .../anvilcraft/data/lang/CommandLang.java | 27 ++- .../data/lang/ScreenTooltipLang.java | 11 +- .../anvilcraft/init/block/ModBlocks.java | 10 +- .../anvilcraft/init/command/ModCommands.java | 4 +- 14 files changed, 431 insertions(+), 155 deletions(-) delete mode 100644 src/main/java/dev/dubhe/anvilcraft/command/MultiphaseCommand.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/command/StorageCommand.java diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 51558eca68..add79469a9 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -725,11 +725,6 @@ "category.anvilcraft.unstackable": "sɯǝʇI ǝꞁqɐʞɔɐʇsu∩", "command.anvilcraft.multiBlock.multi_block_pos": " sᴉ sod ʇɹɐd uᴉɐW", "command.anvilcraft.multiBlock.not_multi_block": "ʞɔoꞁq-ᴉʇꞁnɯ ɐ ʇou sᴉ ʞɔoꞁq sᴉɥ⟘", - "command.anvilcraft.multiphase.add.full": "sǝɔɐds ʇuǝɯʇuɐɥɔuǝ ɹnoɟ sɐɥ ʎpɐǝɹꞁɐ ɯǝʇᴉ sᴉɥ⟘", - "command.anvilcraft.multiphase.add.success": "%2$s :ꞁɐʇoʇ ⸵(s)ǝɔɐds ʇuǝɯʇuɐɥɔuǝ %1$s pǝppⱯ", - "command.anvilcraft.multiphase.info.phase": "%4$s :sʇuǝɯʇuɐɥɔuǝ '%3$s :ʇsoɔ ɹᴉɐdǝɹ '%2$s [%1$s]", - "command.anvilcraft.multiphase.info.summary": "%2$s :ǝʌᴉʇɔɐ '%1$s :sǝɔɐds ǝsɐɥdᴉʇꞁnW", - "command.anvilcraft.multiphase.no_item": "ǝsɐɥdᴉʇꞁnW ǝʌɐɥ ʇou sǝop ɯǝʇᴉ puɐɥ-uᴉɐɯ ǝɥ⟘", "command.anvilcraft.overseer.entry": "%3$s :ʞɔᴉʇ ɯopuɐɹ '%2$s˙ʌꞀ@[%1$s]", "command.anvilcraft.overseer.head": "%s uᴉ sɹǝǝsɹǝʌo ꞁꞁⱯ", "command.anvilcraft.overseer.invalid_dimension": "pᴉꞁɐʌuᴉ sᴉ uoᴉsuǝɯᴉᗡ", @@ -741,6 +736,19 @@ "command.anvilcraft.powergrid.info.total_consume": "%s :ǝɯnsuoɔ ɹǝʍod ꞁɐʇo⟘", "command.anvilcraft.powergrid.info.total_generate": "%s :ǝʇɐɹǝuǝᵷ ɹǝʍod ꞁɐʇo⟘", "command.anvilcraft.powergrid.info.transmitter": "(%5$s :ǝᵷuɐᴚ) %4$s '%3$s '%2$s ʇɐ %1$s", + "command.anvilcraft.storage.bind.success": "%2$s oʇ ǝᵷɐɹoʇs %1$s punoᗺ", + "command.anvilcraft.storage.info.item": "%s ɟo oɟuᴉ ǝᵷɐɹoʇS", + "command.anvilcraft.storage.info.none": "ǝuou", + "command.anvilcraft.storage.info.ref": "%2$s :ᗡI '%1$s :ǝdʎ⟘", + "command.anvilcraft.storage.info.terminal": "%s :ǝᵷɐɹoʇs punoᗺ", + "command.anvilcraft.storage.invalid_id": "ᗡI ǝᵷɐɹoʇs pᴉꞁɐʌuI", + "command.anvilcraft.storage.invalid_type": "ǝdʎʇ ǝᵷɐɹoʇs pᴉꞁɐʌuI", + "command.anvilcraft.storage.list.entry": "%2$s :ᗡI '%1$s :ǝdʎ⟘", + "command.anvilcraft.storage.list.head": ":(%1$s) sǝᵷɐɹoʇS", + "command.anvilcraft.storage.no_hand_item": "puɐɥ uᴉɐɯ uᴉ ɯǝʇᴉ oN", + "command.anvilcraft.storage.no_storage": "ǝᵷɐɹoʇs ɐ ǝɔuǝɹǝɟǝɹ ʇou sǝop ɯǝʇᴉ pꞁǝɥ ǝɥ⟘", + "command.anvilcraft.storage.type_mismatch": "%2$s ǝdʎʇ pǝʇsǝnbǝɹ ɥɔʇɐɯ ʇou sǝop %1$s ǝdʎʇ ǝᵷɐɹoʇs ɯǝʇᴉ pꞁǝH", + "command.anvilcraft.storage.unbind.success": "ǝᵷɐɹoʇs punoqu∩", "command.anvilcraft.universe.no_id": "puɐɥ uo sɯǝʇᴉ uᴉ ᗡI puᴉɟ ʇouuɐɔ puɐ pǝpᴉʌoɹd ᗡI oN", "component_content.anvilcraft.mod_name.ageratum": "ɯnʇɐɹǝᵷⱯ", "component_content.anvilcraft.mod_name.anvilcraft": "ʇɟɐɹƆꞁᴉʌuⱯ", @@ -2075,8 +2083,8 @@ "tooltip.anvilcraft.large_cauldron.inputs": ":sɯǝʇI ʇnduI", "tooltip.anvilcraft.large_cauldron.outputs": ":sɯǝʇI ʇndʇnO", "tooltip.anvilcraft.large_cauldron.will_process": "%s ʎq pǝssǝɔoɹd ǝq ꞁꞁᴉʍ ", - "tooltip.anvilcraft.large_crate.0": "puɐ ɹossǝɹdɯoɔɹǝʌO ǝɔɐdS ⥝ ʇɔǝɾuI", - "tooltip.anvilcraft.large_crate.1": "ɟo doʇ ǝɥʇ oʇuᴉ sʞɔoꞁᗺ ǝʇᴉɹǝɥʇǝN 9", + "tooltip.anvilcraft.large_crate.0": "puɐ ɹossǝɹdɯoɔɹǝʌO ǝɔɐdS x⥝ ʇɔǝɾuI", + "tooltip.anvilcraft.large_crate.1": "ɟo doʇ ǝɥʇ oʇuᴉ sʞɔoꞁᗺ ǝʇᴉɹǝɥʇǝN x9", "tooltip.anvilcraft.large_crate.2": "ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS oʇ ʇᴉ ǝpɐɹᵷdn oʇ ǝʇɐɹƆ ǝᵷɹɐꞀ ɐ", "tooltip.anvilcraft.large_crate.3": "ǝꞁqᴉsɹǝʌǝɹɹᴉ sᴉ ssǝɔoɹd sᴉɥ⟘", "tooltip.anvilcraft.load_monitor.jade.load": "%s :pɐoꞀ", @@ -2125,10 +2133,13 @@ "tooltip.anvilcraft.shulker_container.2": "ǝꞁqᴉsɹǝʌǝɹɹᴉ sᴉ ssǝɔoɹd sᴉɥ⟘", "tooltip.anvilcraft.shulker_container.3": "ꞁꞁᴉʍ ɹossǝɹdɯoɔɹǝʌO ǝɔɐdS pǝʇɔǝɾuᴉ ɥɔɐƎ", "tooltip.anvilcraft.shulker_container.4": "ǝzᴉs xɐɯ ɐ ɥʇᴉʍ 'ǝzᴉs xɐɯ sʇᴉ ǝꞁqnop", - "tooltip.anvilcraft.shulker_container.5": "(sǝpɐɹᵷdn ɹnoɟ oʇ dn ˙ǝ˙ᴉ) 9ㄥϛ8߈0⥝ ɟo ʇᴉɯᴉꞁ", + "tooltip.anvilcraft.shulker_container.5": "(sǝpɐɹᵷdn ߈ oʇ dn ˙ǝ˙ᴉ) 9ㄥϛ8߈0⥝ ɟo ʇᴉɯᴉꞁ", "tooltip.anvilcraft.shulker_container.6": "%s :sǝɯᴉʇ pǝʇɔǝɾuᴉ ʎꞁʇuǝɹɹnƆ", "tooltip.anvilcraft.shulker_container.6.waiting": "ᵷuᴉɔuʎs ɹoɟ ᵷuᴉʇᴉɐM", - "tooltip.anvilcraft.shulker_container.hyperdimension": "ǝꞁqᴉsɹǝʌǝɹɹᴉ sᴉ ssǝɔoɹd sᴉɥ⟘ ˙uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH ɐ oʇuᴉ ʇᴉ ǝpɐɹᵷdn oʇ doʇ ǝɥʇ ɯoɹɟ ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS pǝxɐɯ ǝɥʇ oʇuo ǝqnɔɹǝdʎH x9⥝ puɐ ꞁɐʇsʎɹƆ ʎʇᴉɹɐꞁnᵷuᴉS x⥝ doɹᗡ", + "tooltip.anvilcraft.shulker_container.hyperdimension.0": "puɐ ꞁɐʇsʎɹƆ ʎʇᴉɹɐꞁnᵷuᴉS x⥝ ʇɔǝɾuI", + "tooltip.anvilcraft.shulker_container.hyperdimension.1": "ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS ɐ ɟo doʇ ǝɥʇ oʇuᴉ ǝqnɔɹǝdʎH x9⥝", + "tooltip.anvilcraft.shulker_container.hyperdimension.2": "uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH ɐ oʇuᴉ ʇᴉ ǝpɐɹᵷdn oʇ", + "tooltip.anvilcraft.shulker_container.hyperdimension.3": "ǝꞁqᴉsɹǝʌǝɹɹᴉ sᴉ ssǝɔoɹd sᴉɥ⟘", "tooltip.anvilcraft.smart_block_placer.jade.blueprint_name": "%s :ʇuᴉɹdǝnꞁᗺ", "tooltip.anvilcraft.smart_block_placer.jade.missing.skip": "dᴉʞS", "tooltip.anvilcraft.smart_block_placer.jade.missing.stop": "doʇS", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index f5d8e37cb4..df08ee0752 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -725,11 +725,6 @@ "category.anvilcraft.unstackable": "Unstackable Items", "command.anvilcraft.multiBlock.multi_block_pos": "Main part pos is ", "command.anvilcraft.multiBlock.not_multi_block": "This block is not a multi-block", - "command.anvilcraft.multiphase.add.full": "This item already has four enchantment spaces", - "command.anvilcraft.multiphase.add.success": "Added %1$s enchantment space(s); total: %2$s", - "command.anvilcraft.multiphase.info.phase": "[%1$s] %2$s, repair cost: %3$s, enchantments: %4$s", - "command.anvilcraft.multiphase.info.summary": "Multiphase spaces: %1$s, active: %2$s", - "command.anvilcraft.multiphase.no_item": "The main-hand item does not have Multiphase", "command.anvilcraft.overseer.entry": "[%1$s]@Lv.%2$s, random tick: %3$s", "command.anvilcraft.overseer.head": "All overseers in %s", "command.anvilcraft.overseer.invalid_dimension": "Dimension is invalid", @@ -741,6 +736,19 @@ "command.anvilcraft.powergrid.info.total_consume": "Total power consume: %s", "command.anvilcraft.powergrid.info.total_generate": "Total power generate: %s", "command.anvilcraft.powergrid.info.transmitter": "%1$s at %2$s, %3$s, %4$s (Range: %5$s)", + "command.anvilcraft.storage.bind.success": "Bound %1$s storage to %2$s", + "command.anvilcraft.storage.info.item": "Storage info of %s", + "command.anvilcraft.storage.info.none": "none", + "command.anvilcraft.storage.info.ref": "Type: %1$s, ID: %2$s", + "command.anvilcraft.storage.info.terminal": "Bound storage: %s", + "command.anvilcraft.storage.invalid_id": "Invalid storage ID", + "command.anvilcraft.storage.invalid_type": "Invalid storage type", + "command.anvilcraft.storage.list.entry": "Type: %1$s, ID: %2$s", + "command.anvilcraft.storage.list.head": "Storages (%1$s):", + "command.anvilcraft.storage.no_hand_item": "No item in main hand", + "command.anvilcraft.storage.no_storage": "The held item does not reference a storage", + "command.anvilcraft.storage.type_mismatch": "Held item storage type %1$s does not match requested type %2$s", + "command.anvilcraft.storage.unbind.success": "Unbound storage", "command.anvilcraft.universe.no_id": "No ID provided and cannot find ID in items on hand", "component_content.anvilcraft.mod_name.ageratum": "Ageratum", "component_content.anvilcraft.mod_name.anvilcraft": "AnvilCraft", @@ -2075,8 +2083,8 @@ "tooltip.anvilcraft.large_cauldron.inputs": "Input Items:", "tooltip.anvilcraft.large_cauldron.outputs": "Output Items:", "tooltip.anvilcraft.large_cauldron.will_process": " will be processed by %s", - "tooltip.anvilcraft.large_crate.0": "Inject 1 Space Overcompressor and", - "tooltip.anvilcraft.large_crate.1": "6 Netherite Blocks into the top of", + "tooltip.anvilcraft.large_crate.0": "Inject 1x Space Overcompressor and", + "tooltip.anvilcraft.large_crate.1": "6x Netherite Blocks into the top of", "tooltip.anvilcraft.large_crate.2": "a Large Crate to upgrade it to Shulker Container", "tooltip.anvilcraft.large_crate.3": "This process is irreversible", "tooltip.anvilcraft.load_monitor.jade.load": "Load: %s", @@ -2125,10 +2133,13 @@ "tooltip.anvilcraft.shulker_container.2": "This process is irreversible", "tooltip.anvilcraft.shulker_container.3": "Each injected Space Overcompressor will", "tooltip.anvilcraft.shulker_container.4": "double its max size, with a max size", - "tooltip.anvilcraft.shulker_container.5": "limit of 1048576 (i.e. up to four upgrades)", + "tooltip.anvilcraft.shulker_container.5": "limit of 1048576 (i.e. up to 4 upgrades)", "tooltip.anvilcraft.shulker_container.6": "Currently injected times: %s", "tooltip.anvilcraft.shulker_container.6.waiting": "Waiting for syncing", - "tooltip.anvilcraft.shulker_container.hyperdimension": "Drop 1x Singularity Crystal and 16x Hypercube onto the maxed Shulker Container from the top to upgrade it into a Hyperdimension Storage Station. This process is irreversible", + "tooltip.anvilcraft.shulker_container.hyperdimension.0": "Inject 1x Singularity Crystal and", + "tooltip.anvilcraft.shulker_container.hyperdimension.1": "16x Hypercube into the top of a Shulker Container", + "tooltip.anvilcraft.shulker_container.hyperdimension.2": "to upgrade it into a Hyperdimension Storage Station", + "tooltip.anvilcraft.shulker_container.hyperdimension.3": "This process is irreversible", "tooltip.anvilcraft.smart_block_placer.jade.blueprint_name": "Blueprint: %s", "tooltip.anvilcraft.smart_block_placer.jade.missing.skip": "Skip", "tooltip.anvilcraft.smart_block_placer.jade.missing.stop": "Stop", diff --git a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/ShulkerContainerTooltipProvider.java b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/ShulkerContainerTooltipProvider.java index 8a2b27433b..4a0071cedc 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/ShulkerContainerTooltipProvider.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/impl/ShulkerContainerTooltipProvider.java @@ -53,18 +53,23 @@ public List tooltip(BlockEntity value) { ? Component.translatable("tooltip.anvilcraft.shulker_container.6.waiting") : this.upgradeCount; ImmutableList.Builder builder = ImmutableList.builder(); - builder.add( - Component.translatable("tooltip.anvilcraft.shulker_container.0"), - Component.translatable("tooltip.anvilcraft.shulker_container.1"), - Component.translatable("tooltip.anvilcraft.shulker_container.2"), - Component.translatable("tooltip.anvilcraft.shulker_container.3"), - Component.translatable("tooltip.anvilcraft.shulker_container.4"), - Component.translatable("tooltip.anvilcraft.shulker_container.5"), - Component.translatable("tooltip.anvilcraft.shulker_container.6", upgradeCount) - ); - // 升级已满(空间大小达到上限)时,追加向超维存储站升级的提示 - if (this.upgradeCount >= ShulkerContainerTooltipProvider.UPGRADES_TO_MAX) { - builder.add(Component.translatable("tooltip.anvilcraft.shulker_container.hyperdimension")); + if (this.upgradeCount < ShulkerContainerTooltipProvider.UPGRADES_TO_MAX) { + builder.add( + Component.translatable("tooltip.anvilcraft.shulker_container.0"), + Component.translatable("tooltip.anvilcraft.shulker_container.1"), + Component.translatable("tooltip.anvilcraft.shulker_container.2"), + Component.translatable("tooltip.anvilcraft.shulker_container.3"), + Component.translatable("tooltip.anvilcraft.shulker_container.4"), + Component.translatable("tooltip.anvilcraft.shulker_container.5"), + Component.translatable("tooltip.anvilcraft.shulker_container.6", upgradeCount) + ); + } else { + builder.add( + Component.translatable("tooltip.anvilcraft.shulker_container.hyperdimension.0"), + Component.translatable("tooltip.anvilcraft.shulker_container.hyperdimension.1"), + Component.translatable("tooltip.anvilcraft.shulker_container.hyperdimension.2"), + Component.translatable("tooltip.anvilcraft.shulker_container.hyperdimension.3") + ); } return builder.build(); } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/CrateBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/CrateBlock.java index 1f33f06402..b012dd85ce 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/CrateBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/CrateBlock.java @@ -17,7 +17,6 @@ import dev.dubhe.anvilcraft.saved.storage.StorageType; import dev.dubhe.anvilcraft.saved.storage.Storages; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; @@ -26,11 +25,14 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; import net.neoforged.api.distmarker.Dist; import java.util.ArrayList; @@ -94,7 +96,7 @@ protected ItemInteractionResult useItemOn( if (player.isSpectator()) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; if (player.isShiftKeyDown() && itemStack.is(ModBlocks.LARGE_CRATE.asItem())) { if (level.isClientSide()) return ItemInteractionResult.sidedSuccess(true); - return CrateBlock.mergeIntoLargeCrate(level, pos, hitResult.getDirection(), itemStack, player); + return CrateBlock.mergeIntoLargeCrate(level, pos, itemStack, player); } if (player instanceof ServerPlayer) { return ItemInteractionResult.sidedSuccess(false); @@ -107,14 +109,29 @@ protected ItemInteractionResult useItemOn( return super.useItemOn(itemStack, state, level, pos, player, hand, hitResult); } + @Override + public ItemStack getCloneItemStack( + BlockState state, + HitResult target, + LevelReader level, + BlockPos pos, + Player player + ) { + ItemStack stack = super.getCloneItemStack(state, target, level, pos, player); + if (level instanceof Level realLevel) { + StorageBlockEntity.applyPickStorageId(stack, realLevel, pos, state, StorageType.CRATE); + } + return stack; + } + private static ItemInteractionResult mergeIntoLargeCrate( Level level, BlockPos center, - Direction clickedFace, ItemStack largeCrateStack, Player player ) { - BlockPos origin = CrateBlock.getLargeCrateOrigin(center, clickedFace); + BlockPos origin = CrateBlock.findLargeCrateOrigin(level, center); + if (origin == null) return ItemInteractionResult.FAIL; List crates = new ArrayList<>(); for (Cube3x3PartHalf part : Cube3x3PartHalf.values()) { BlockPos pos = origin.offset(part.getOffset()); @@ -156,18 +173,51 @@ private static ItemInteractionResult mergeIntoLargeCrate( } LargeCrateBlock largeCrate = ModBlocks.LARGE_CRATE.get(); BlockState defaultState = largeCrate.defaultBlockState(); + // 先移除 27 个旧箱子:逐 part 放置会被多方块 updateShape 判定为结构不完整而破坏( + // 未放置的邻居还是普通箱子)。参照多方块升级做法:全部置为 AIR 后放置主方块(不带 + // 邻居更新),再通过 setPlacedBy 铺开其余 part(此时邻居为已放置的同类 part)。 + // 被替换的 27 个普通箱子以物品形式掉落到原位置附近返还给玩家。 for (Cube3x3PartHalf part : Cube3x3PartHalf.values()) { - level.setBlockAndUpdate(origin.offset(part.getOffset()), defaultState.setValue(LargeCrateBlock.HALF, part)); + BlockPos pos = origin.offset(part.getOffset()); + Block.popResource(level, pos, new ItemStack(ModBlocks.CRATE.get())); + level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_NONE); } + level.setBlock( + origin, + defaultState.setValue(LargeCrateBlock.HALF, Cube3x3PartHalf.BOTTOM_CENTER), + Block.UPDATE_CLIENTS + ); + BlockState placedState = level.getBlockState(origin); + placedState.getBlock().setPlacedBy(level, origin, placedState, null, ItemStack.EMPTY); if (level.getBlockEntity(origin) instanceof StorageBlockEntity storage) storage.setId(target.getId()); if (!player.hasInfiniteMaterials()) largeCrateStack.shrink(1); return ItemInteractionResult.sidedSuccess(false); } - private static BlockPos getLargeCrateOrigin(BlockPos center, Direction clickedFace) { - return switch (clickedFace.getAxis()) { - case X, Z -> center.relative(clickedFace.getOpposite()).below(); - case Y -> center.offset(0, clickedFace == Direction.UP ? -2 : 0, 0); - }; + /** + * 从被点击的箱子出发,扫描其周围可能的 3x3x3 大箱子区域,返回能使全部 + * 27 个 part 都是箱子的底层中心位置;找不到则返回 null。 + */ + private static @javax.annotation.Nullable BlockPos findLargeCrateOrigin(Level level, BlockPos center) { + Cube3x3PartHalf[] parts = Cube3x3PartHalf.values(); + for (int ox = -1; ox <= 1; ox++) { + for (int oy = 0; oy <= 2; oy++) { + for (int oz = -1; oz <= 1; oz++) { + BlockPos candidate = center.offset(ox, oy - 2, oz); + boolean matched = true; + for (Cube3x3PartHalf part : parts) { + BlockPos pos = candidate.offset(part.getOffset()); + if (!(level.getBlockEntity(pos) instanceof CrateBlockEntity)) { + matched = false; + break; + } + } + if (matched) { + return candidate; + } + } + } + } + return null; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java index 5b93fc8f14..d4c6ed0269 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java @@ -5,6 +5,7 @@ import dev.anvilcraft.lib.v2.util.ShapeUtil; import dev.dubhe.anvilcraft.api.hammer.IHammerRemovable; import dev.dubhe.anvilcraft.block.entity.storage.HyperdimensionStorageStationBlockEntity; +import dev.dubhe.anvilcraft.block.entity.storage.StorageBlockEntity; import dev.dubhe.anvilcraft.block.multipart.MultiPartBlockEntity; import dev.dubhe.anvilcraft.block.multipart.SimpleMultiPartBlock; import dev.dubhe.anvilcraft.block.state.Cube3x3PartHalf; @@ -14,6 +15,7 @@ import dev.dubhe.anvilcraft.init.item.ModItems; import dev.dubhe.anvilcraft.item.HyperdimensionTerminalItem; import dev.dubhe.anvilcraft.item.property.component.TerminalBinding; +import dev.dubhe.anvilcraft.saved.storage.StorageType; import dev.dubhe.anvilcraft.saved.storage.Storages; import net.minecraft.advancements.critereon.StatePropertiesPredicate; import net.minecraft.core.BlockPos; @@ -28,6 +30,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -44,6 +47,7 @@ import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; @@ -92,6 +96,21 @@ protected void createBlockStateDefinition(StateDefinition.Builder> UPDATE_OFFSET = ImmutableMap.of( Direction.DOWN, ImmutableList.of( diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/storage/StorageBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/storage/StorageBlockEntity.java index 2ab78046ed..77c1b59630 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/storage/StorageBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/storage/StorageBlockEntity.java @@ -1,11 +1,13 @@ package dev.dubhe.anvilcraft.block.entity.storage; import dev.dubhe.anvilcraft.api.itemhandler.unlimited.UnlimitedItemStacksResourceHandler; +import dev.dubhe.anvilcraft.block.multipart.AbstractMultiPartBlock; import dev.dubhe.anvilcraft.init.item.ModComponents; import dev.dubhe.anvilcraft.item.property.component.StorageRef; import dev.dubhe.anvilcraft.saved.storage.StorageType; import dev.dubhe.anvilcraft.saved.storage.Storages; import lombok.Getter; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponentMap; @@ -125,4 +127,31 @@ public void dropContents(Level level, BlockPos pos) { }); } } + + /** + * 中键复制仓储方块时,把(多方块)主方块的存储 ID 写入复制物品的 STORAGE 组件。 + * 仅在客户端且按住 Ctrl 时生效;普通中键保持原样(不带存储 ID)。 + * + * @param stack 复制出的物品 + * @param level 所在世界 + * @param pos 被点击的方块位置(可能是多方块的 part) + * @param state 被点击的方块状态 + * @param type 该仓储方块的存储类型 + */ + public static void applyPickStorageId(ItemStack stack, Level level, BlockPos pos, BlockState state, StorageType type) { + if (!level.isClientSide() || !Screen.hasControlDown()) { + return; + } + BlockPos mainPos = pos; + if (state.getBlock() instanceof AbstractMultiPartBlock multipart) { + mainPos = multipart.getMainPartPos(pos, state); + } + BlockEntity blockEntity = level.getBlockEntity(mainPos); + if (blockEntity instanceof StorageBlockEntity storage) { + UUID id = storage.getId(); + if (id != null) { + stack.set(ModComponents.STORAGE, new StorageRef(type, id)); + } + } + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/command/MultiphaseCommand.java b/src/main/java/dev/dubhe/anvilcraft/command/MultiphaseCommand.java deleted file mode 100644 index 69f0acfe71..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/command/MultiphaseCommand.java +++ /dev/null @@ -1,94 +0,0 @@ -package dev.dubhe.anvilcraft.command; - -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import dev.dubhe.anvilcraft.init.item.ModComponents; -import dev.dubhe.anvilcraft.item.property.component.Multiphase; -import dev.dubhe.anvilcraft.util.CommandUtil; -import net.minecraft.ChatFormatting; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.world.item.ItemStack; - -import static net.minecraft.commands.Commands.argument; -import static net.minecraft.commands.Commands.literal; - -public class MultiphaseCommand { - private static final SimpleCommandExceptionType ERROR_NO_MULTIPHASE = new SimpleCommandExceptionType( - Component.translatable("command.anvilcraft.multiphase.no_item") - ); - private static final SimpleCommandExceptionType ERROR_MAX_PHASES = new SimpleCommandExceptionType( - Component.translatable("command.anvilcraft.multiphase.add.full") - ); - - public static void registerCommand(LiteralArgumentBuilder parent) { - parent.then( - literal("multiphase") - .requires(source -> source.hasPermission(2)) - .executes(MultiphaseCommand::showInfo) - .then(literal("info").executes(MultiphaseCommand::showInfo)) - .then( - literal("add") - .executes(context -> addPhases(context, 1)) - .then( - argument("count", IntegerArgumentType.integer(1, Multiphase.MAX_PHASE_COUNT)) - .executes(context -> addPhases( - context, - IntegerArgumentType.getInteger(context, "count") - )) - ) - ) - ); - } - - private static int showInfo(CommandContext context) throws CommandSyntaxException { - ItemStack stack = getMultiphaseStack(context.getSource()); - Multiphase multiphase = stack.get(ModComponents.MULTIPHASE).capture(stack); - stack.set(ModComponents.MULTIPHASE, multiphase); - - MutableComponent message = Component.translatable( - "command.anvilcraft.multiphase.info.summary", - multiphase.phases().size(), - multiphase.activePhase() + 1 - ).withStyle(ChatFormatting.LIGHT_PURPLE); - for (int i = 0; i < multiphase.phases().size(); i++) { - Multiphase.Phase phase = multiphase.phases().get(i); - message.append(Component.literal("\n")).append(Component.translatable( - "command.anvilcraft.multiphase.info.phase", - i + 1, - multiphase.phaseDisplayName(i), - phase.repairCost(), - phase.enchantments() - )); - } - return CommandUtil.sendSuccess(context.getSource(), () -> message); - } - - private static int addPhases(CommandContext context, int requested) throws CommandSyntaxException { - ItemStack stack = getMultiphaseStack(context.getSource()); - int added = 0; - for (int i = 0; i < requested; i++) { - Multiphase multiphase = stack.get(ModComponents.MULTIPHASE); - if (multiphase == null || !multiphase.addPhase(stack)) break; - added++; - } - if (added == 0) throw ERROR_MAX_PHASES.create(); - int phaseCount = stack.get(ModComponents.MULTIPHASE).phases().size(); - return CommandUtil.sendSuccess( - context.getSource(), - "command.anvilcraft.multiphase.add.success", - added, - phaseCount - ); - } - - private static ItemStack getMultiphaseStack(CommandSourceStack source) throws CommandSyntaxException { - ItemStack stack = source.getPlayerOrException().getMainHandItem(); - if (!stack.has(ModComponents.MULTIPHASE)) throw ERROR_NO_MULTIPHASE.create(); - return stack; - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/command/StorageCommand.java b/src/main/java/dev/dubhe/anvilcraft/command/StorageCommand.java new file mode 100644 index 0000000000..60e1fab1a7 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/command/StorageCommand.java @@ -0,0 +1,196 @@ +package dev.dubhe.anvilcraft.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import dev.dubhe.anvilcraft.init.item.ModComponents; +import dev.dubhe.anvilcraft.init.item.ModItems; +import dev.dubhe.anvilcraft.item.property.component.StorageRef; +import dev.dubhe.anvilcraft.item.property.component.TerminalBinding; +import dev.dubhe.anvilcraft.saved.storage.BaseStorage; +import dev.dubhe.anvilcraft.saved.storage.StorageType; +import dev.dubhe.anvilcraft.saved.storage.Storages; +import dev.dubhe.anvilcraft.util.CommandUtil; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import javax.annotation.Nullable; + +import static net.minecraft.commands.Commands.argument; +import static net.minecraft.commands.Commands.literal; + +/** + * 存储信息与绑定命令:显示仓储方块/超维终端的存储 ID 与类型, + * 将对应类型与手持的仓储方块/超维终端绑定。 + */ +public class StorageCommand { + private static final SimpleCommandExceptionType ERROR_NO_HAND_ITEM = new SimpleCommandExceptionType( + Component.translatable("command.anvilcraft.storage.no_hand_item") + ); + private static final SimpleCommandExceptionType ERROR_NO_STORAGE = new SimpleCommandExceptionType( + Component.translatable("command.anvilcraft.storage.no_storage") + ); + private static final SimpleCommandExceptionType ERROR_INVALID_TYPE = new SimpleCommandExceptionType( + Component.translatable("command.anvilcraft.storage.invalid_type") + ); + private static final SimpleCommandExceptionType ERROR_INVALID_ID = new SimpleCommandExceptionType( + Component.translatable("command.anvilcraft.storage.invalid_id") + ); + + public static void registerCommand(LiteralArgumentBuilder parent) { + parent.then( + literal("storage") + .requires(source -> source.hasPermission(2)) + .executes(StorageCommand::storageInfo) + .then(literal("info").executes(StorageCommand::storageInfo)) + .then( + literal("list") + .executes(StorageCommand::storageList) + .then( + argument("type", StringArgumentType.word()) + .executes(StorageCommand::storageListFiltered) + ) + ) + .then( + literal("bind") + .then( + argument("id", StringArgumentType.word()) + .executes(StorageCommand::storageBind) + ) + ) + .then(literal("unbind").executes(StorageCommand::storageUnbind)) + ); + } + + /** 显示手持物品的存储信息(仓储方块 BlockItem 的 StorageRef,或超维终端的绑定)。 */ + private static int storageInfo(CommandContext context) throws CommandSyntaxException { + ItemStack stack = context.getSource().getPlayerOrException().getMainHandItem(); + if (stack.isEmpty()) throw ERROR_NO_HAND_ITEM.create(); + + MutableComponent message = Component.translatable( + "command.anvilcraft.storage.info.item", + stack.getHoverName() + ).withStyle(ChatFormatting.LIGHT_PURPLE); + + if (stack.is(ModItems.HYPERDIMENSION_TERMINAL)) { + TerminalBinding binding = stack.getOrDefault(ModComponents.TERMINAL_BINDING, TerminalBinding.EMPTY); + message.append(Component.literal("\n")).append(Component.translatable( + "command.anvilcraft.storage.info.terminal", + binding.id().map(id -> Component.literal(id.toString())) + .orElseGet(() -> Component.translatable("command.anvilcraft.storage.info.none")) + )); + } else { + StorageRef ref = stack.get(ModComponents.STORAGE); + if (ref == null) { + throw ERROR_NO_STORAGE.create(); + } + message.append(Component.literal("\n")).append(Component.translatable( + "command.anvilcraft.storage.info.ref", + Component.literal(ref.type().getSerializedName()), + ref.id().map(id -> Component.literal(id.toString())) + .orElseGet(() -> Component.translatable("command.anvilcraft.storage.info.none")) + )); + } + return CommandUtil.sendSuccess(context.getSource(), () -> message); + } + + /** 列出所有存储(可选按类型过滤)。 */ + private static int storageList(CommandContext context) { + return StorageCommand.storageListFiltered(context, null); + } + + private static int storageListFiltered(CommandContext context) throws CommandSyntaxException { + String typeName = StringArgumentType.getString(context, "type"); + StorageType type = parseType(typeName); + if (type == null) throw ERROR_INVALID_TYPE.create(); + return StorageCommand.storageListFiltered(context, type); + } + + private static int storageListFiltered(CommandContext context, @Nullable StorageType filter) { + Map> storages = Storages.get().getStorages(); + MutableComponent message = Component.translatable( + "command.anvilcraft.storage.list.head", + storages.size() + ).withStyle(ChatFormatting.LIGHT_PURPLE); + for (Map.Entry> entry : storages.entrySet()) { + StorageType type = StorageType.find(entry.getValue()); + if (filter != null && type != filter) continue; + message.append(Component.literal("\n")).append(Component.translatable( + "command.anvilcraft.storage.list.entry", + Component.literal(type.getSerializedName()), + Component.literal(entry.getKey().toString()) + )); + } + return CommandUtil.sendSuccess(context.getSource(), () -> message); + } + + /** 将手持的仓储方块/超维终端绑定到指定存储 id(类型从手持物品推断)。 */ + private static int storageBind(CommandContext context) throws CommandSyntaxException { + UUID id = parseUuid(StringArgumentType.getString(context, "id")); + if (id == null) throw ERROR_INVALID_ID.create(); + ServerPlayer player = context.getSource().getPlayerOrException(); + ItemStack stack = player.getMainHandItem(); + if (stack.isEmpty()) throw ERROR_NO_HAND_ITEM.create(); + + StorageType type; + if (stack.is(ModItems.HYPERDIMENSION_TERMINAL)) { + // 终端绑定到存储 id(类型仅用于显示) + type = StorageType.HYPERDIMENSION; + stack.set(ModComponents.TERMINAL_BINDING, new TerminalBinding(Optional.of(id))); + } else { + StorageRef existing = stack.get(ModComponents.STORAGE); + if (existing == null) throw ERROR_NO_STORAGE.create(); + type = existing.type(); + stack.set(ModComponents.STORAGE, new StorageRef(type, id)); + } + player.getInventory().setChanged(); + player.inventoryMenu.broadcastChanges(); + return CommandUtil.sendSuccess( + context.getSource(), + "command.anvilcraft.storage.bind.success", + Component.literal(type.getSerializedName()), + Component.literal(id.toString()) + ); + } + + /** 清除手持超维终端的绑定。 */ + private static int storageUnbind(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + ItemStack stack = player.getMainHandItem(); + if (stack.isEmpty()) throw ERROR_NO_HAND_ITEM.create(); + if (stack.is(ModItems.HYPERDIMENSION_TERMINAL)) { + stack.remove(ModComponents.TERMINAL_BINDING); + } else { + StorageRef ref = stack.get(ModComponents.STORAGE); + if (ref == null) throw ERROR_NO_STORAGE.create(); + stack.set(ModComponents.STORAGE, new StorageRef(ref.type())); + } + player.getInventory().setChanged(); + player.inventoryMenu.broadcastChanges(); + return CommandUtil.sendSuccess(context.getSource(), "command.anvilcraft.storage.unbind.success"); + } + + private static @Nullable StorageType parseType(String name) { + for (StorageType type : StorageType.values()) { + if (type.getSerializedName().equalsIgnoreCase(name)) return type; + } + return null; + } + + private static @Nullable UUID parseUuid(String value) { + try { + return UUID.fromString(value); + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java index 80caf0b3d6..4947517f5b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java @@ -13,20 +13,19 @@ public static void init(RegistrumLangProvider provider) { provider.add("command.anvilcraft.powergrid.info.transmitter", "%1$s at %2$s, %3$s, %4$s (Range: %5$s)"); provider.add("command.anvilcraft.powergrid.info.not_found", "No power grid found at position %1$s, %2$s, %3$s"); - provider.add("command.anvilcraft.multiphase.no_item", "The main-hand item does not have Multiphase"); - provider.add( - "command.anvilcraft.multiphase.info.summary", - "Multiphase spaces: %1$s, active: %2$s" - ); - provider.add( - "command.anvilcraft.multiphase.info.phase", - "[%1$s] %2$s, repair cost: %3$s, enchantments: %4$s" - ); - provider.add( - "command.anvilcraft.multiphase.add.success", - "Added %1$s enchantment space(s); total: %2$s" - ); - provider.add("command.anvilcraft.multiphase.add.full", "This item already has four enchantment spaces"); + provider.add("command.anvilcraft.storage.no_hand_item", "No item in main hand"); + provider.add("command.anvilcraft.storage.no_storage", "The held item does not reference a storage"); + provider.add("command.anvilcraft.storage.invalid_type", "Invalid storage type"); + provider.add("command.anvilcraft.storage.invalid_id", "Invalid storage ID"); + provider.add("command.anvilcraft.storage.type_mismatch", "Held item storage type %1$s does not match requested type %2$s"); + provider.add("command.anvilcraft.storage.info.item", "Storage info of %s"); + provider.add("command.anvilcraft.storage.info.none", "none"); + provider.add("command.anvilcraft.storage.info.terminal", "Bound storage: %s"); + provider.add("command.anvilcraft.storage.info.ref", "Type: %1$s, ID: %2$s"); + provider.add("command.anvilcraft.storage.list.head", "Storages (%1$s):"); + provider.add("command.anvilcraft.storage.list.entry", "Type: %1$s, ID: %2$s"); + provider.add("command.anvilcraft.storage.bind.success", "Bound %1$s storage to %2$s"); + provider.add("command.anvilcraft.storage.unbind.success", "Unbound storage"); provider.add("command.anvilcraft.universe.no_id", "No ID provided and cannot find ID in items on hand"); diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenTooltipLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenTooltipLang.java index 1f1137d341..b507680748 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenTooltipLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenTooltipLang.java @@ -21,8 +21,8 @@ public static void init(RegistrumLangProvider provider) { provider.add("screen.anvilcraft.range_no_overlap", "The working ranges of two identical blocks must not overlap"); provider.add("screen.anvilcraft.range_overlap", "No other identical block is allowed within the %1$s×%1$s range."); - provider.add("tooltip.anvilcraft.large_crate.0", "Inject 1 Space Overcompressor and"); - provider.add("tooltip.anvilcraft.large_crate.1", "6 Netherite Blocks into the top of"); + provider.add("tooltip.anvilcraft.large_crate.0", "Inject 1x Space Overcompressor and"); + provider.add("tooltip.anvilcraft.large_crate.1", "6x Netherite Blocks into the top of"); provider.add("tooltip.anvilcraft.large_crate.2", "a Large Crate to upgrade it to Shulker Container"); provider.add("tooltip.anvilcraft.large_crate.3", "This process is irreversible"); provider.add("tooltip.anvilcraft.shulker_container.0", "Inject more Space Overcompressors into"); @@ -30,9 +30,12 @@ public static void init(RegistrumLangProvider provider) { provider.add("tooltip.anvilcraft.shulker_container.2", "This process is irreversible"); provider.add("tooltip.anvilcraft.shulker_container.3", "Each injected Space Overcompressor will"); provider.add("tooltip.anvilcraft.shulker_container.4", "double its max size, with a max size"); - provider.add("tooltip.anvilcraft.shulker_container.5", "limit of 1048576 (i.e. up to four upgrades)"); + provider.add("tooltip.anvilcraft.shulker_container.5", "limit of 1048576 (i.e. up to 4 upgrades)"); provider.add("tooltip.anvilcraft.shulker_container.6", "Currently injected times: %s"); provider.add("tooltip.anvilcraft.shulker_container.6.waiting", "Waiting for syncing"); - provider.add("tooltip.anvilcraft.shulker_container.hyperdimension", "Drop 1x Singularity Crystal and 16x Hypercube onto the maxed Shulker Container from the top to upgrade it into a Hyperdimension Storage Station. This process is irreversible"); + provider.add("tooltip.anvilcraft.shulker_container.hyperdimension.0", "Inject 1x Singularity Crystal and"); + provider.add("tooltip.anvilcraft.shulker_container.hyperdimension.1", "16x Hypercube into the top of a Shulker Container"); + provider.add("tooltip.anvilcraft.shulker_container.hyperdimension.2", "to upgrade it into a Hyperdimension Storage Station"); + provider.add("tooltip.anvilcraft.shulker_container.hyperdimension.3", "This process is irreversible"); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java index 216d00b16e..fb13412047 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java @@ -263,14 +263,17 @@ import net.minecraft.tags.TagKey; import net.minecraft.util.ColorRGBA; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.item.enchantment.LevelBasedValue; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.ColoredFallingBlock; @@ -1610,7 +1613,12 @@ public class ModBlocks { .noOcclusion() .isValidSpawn(ModBlocks::never) ) - .item(SimpleMultiPartBlockItem::new) + .item((block, properties) -> new SimpleMultiPartBlockItem<>(block, properties) { + @Override + public boolean doesSneakBypassUse(ItemStack stack, LevelReader level, BlockPos pos, Player player) { + return true; + } + }) .properties(properties -> properties .stacksTo(16) .component(ModComponents.STORAGE, StorageRef.largeCrate()) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/command/ModCommands.java b/src/main/java/dev/dubhe/anvilcraft/init/command/ModCommands.java index 5adb868812..a3b5bada49 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/command/ModCommands.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/command/ModCommands.java @@ -3,9 +3,9 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import dev.dubhe.anvilcraft.command.MultiBlockCommand; -import dev.dubhe.anvilcraft.command.MultiphaseCommand; import dev.dubhe.anvilcraft.command.OverseerCommand; import dev.dubhe.anvilcraft.command.PowergridCommand; +import dev.dubhe.anvilcraft.command.StorageCommand; import dev.dubhe.anvilcraft.command.TickSprintVoteCommand; import dev.dubhe.anvilcraft.init.ModInspections; import net.minecraft.commands.CommandSourceStack; @@ -17,7 +17,7 @@ public static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder root = literal("anvilcraft"); ModInspections.INSTANCE.registerCommand(root); PowergridCommand.registerCommand(root); - MultiphaseCommand.registerCommand(root); + StorageCommand.registerCommand(root); MultiBlockCommand.registerCommand(root); OverseerCommand.registerCommand(root); TickSprintVoteCommand.registerCommand(root); From 65334e3923bc4a85fc1697ae973f1dd100145e42 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 21:14:48 +0800 Subject: [PATCH 07/15] fix: fix curios texture ref --- .../data/anvilcraft/curios/slots/ionocraft_backpack.json | 2 +- .../anvilcraft/integration/curios/data/ModCuriosProvider.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generated/resources/data/anvilcraft/curios/slots/ionocraft_backpack.json b/src/generated/resources/data/anvilcraft/curios/slots/ionocraft_backpack.json index b2ab007cca..27261e3df9 100644 --- a/src/generated/resources/data/anvilcraft/curios/slots/ionocraft_backpack.json +++ b/src/generated/resources/data/anvilcraft/curios/slots/ionocraft_backpack.json @@ -1,4 +1,4 @@ { "add_cosmetic": true, - "icon": "anvilcraft:curios/empty_ionocraft_backpack_slot" + "icon": "anvilcraft:gui/curios/empty_ionocraft_backpack_slot" } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/curios/data/ModCuriosProvider.java b/src/main/java/dev/dubhe/anvilcraft/integration/curios/data/ModCuriosProvider.java index 6390afcb3b..b6c18bc52e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/curios/data/ModCuriosProvider.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/curios/data/ModCuriosProvider.java @@ -17,7 +17,7 @@ public ModCuriosProvider(PackOutput output, ExistingFileHelper fileHelper, Compl public void generate(HolderLookup.Provider registries, ExistingFileHelper fileHelper) { this.createSlot("ionocraft_backpack") .addCosmetic(true) - .icon(AnvilCraft.of("curios/empty_ionocraft_backpack_slot")); + .icon(AnvilCraft.of("gui/curios/empty_ionocraft_backpack_slot")); this.createEntities("ionocraft_backpack") .addPlayer() From 5623c31a22567c42cb6260c113a321be9ac997bc Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 21:23:13 +0800 Subject: [PATCH 08/15] fix: fix problems --- .../assets/anvilcraft/lang/en_ud.json | 1 - .../assets/anvilcraft/lang/en_us.json | 1 - .../client/rpc/TerminalJeiStorageCache.java | 14 ++++++++++++-- .../anvilcraft/data/lang/CommandLang.java | 1 - .../mixin/BasicRecipeTransferHandlerMixin.java | 18 ++++++++++++++---- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index add79469a9..ddbaed1f04 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -747,7 +747,6 @@ "command.anvilcraft.storage.list.head": ":(%1$s) sǝᵷɐɹoʇS", "command.anvilcraft.storage.no_hand_item": "puɐɥ uᴉɐɯ uᴉ ɯǝʇᴉ oN", "command.anvilcraft.storage.no_storage": "ǝᵷɐɹoʇs ɐ ǝɔuǝɹǝɟǝɹ ʇou sǝop ɯǝʇᴉ pꞁǝɥ ǝɥ⟘", - "command.anvilcraft.storage.type_mismatch": "%2$s ǝdʎʇ pǝʇsǝnbǝɹ ɥɔʇɐɯ ʇou sǝop %1$s ǝdʎʇ ǝᵷɐɹoʇs ɯǝʇᴉ pꞁǝH", "command.anvilcraft.storage.unbind.success": "ǝᵷɐɹoʇs punoqu∩", "command.anvilcraft.universe.no_id": "puɐɥ uo sɯǝʇᴉ uᴉ ᗡI puᴉɟ ʇouuɐɔ puɐ pǝpᴉʌoɹd ᗡI oN", "component_content.anvilcraft.mod_name.ageratum": "ɯnʇɐɹǝᵷⱯ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index df08ee0752..b6e4f70d6f 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -747,7 +747,6 @@ "command.anvilcraft.storage.list.head": "Storages (%1$s):", "command.anvilcraft.storage.no_hand_item": "No item in main hand", "command.anvilcraft.storage.no_storage": "The held item does not reference a storage", - "command.anvilcraft.storage.type_mismatch": "Held item storage type %1$s does not match requested type %2$s", "command.anvilcraft.storage.unbind.success": "Unbound storage", "command.anvilcraft.universe.no_id": "No ID provided and cannot find ID in items on hand", "component_content.anvilcraft.mod_name.ageratum": "Ageratum", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java index 98f2c3bbb6..7e01508fe8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java @@ -22,7 +22,11 @@ * 供 JEI 转移的检查阶段判断存储站是否满足配方需求。 */ public final class TerminalJeiStorageCache { + /** 缓存有效期(毫秒):存储站内容可能被其他玩家/自动化改动,定期刷新避免误判。 */ + private static final long TTL_MILLIS = 5_000L; + private static final Map> CACHE = new HashMap<>(); + private static final Map TIMESTAMPS = new HashMap<>(); private static final Map>> PENDING = new HashMap<>(); private TerminalJeiStorageCache() { @@ -45,15 +49,19 @@ private TerminalJeiStorageCache() { return null; } - /** 获取缓存的存储站物品列表(可能为 null 表示尚未加载)。 */ + /** 获取缓存的存储站物品列表(可能为 null 表示尚未加载或已过期)。 */ public static @Nullable List get(UUID storageId) { + Long timestamp = TerminalJeiStorageCache.TIMESTAMPS.get(storageId); + if (timestamp == null || System.currentTimeMillis() - timestamp > TerminalJeiStorageCache.TTL_MILLIS) { + return null; + } return TerminalJeiStorageCache.CACHE.get(storageId); } /** 异步加载存储站物品列表到缓存;若已在加载则复用进行中的 future。 */ public static CompletableFuture> ensure(UUID storageId) { synchronized (TerminalJeiStorageCache.class) { - if (TerminalJeiStorageCache.CACHE.containsKey(storageId)) { + if (TerminalJeiStorageCache.get(storageId) != null) { return CompletableFuture.completedFuture(TerminalJeiStorageCache.CACHE.get(storageId)); } CompletableFuture> pending = TerminalJeiStorageCache.PENDING.get(storageId); @@ -71,6 +79,7 @@ public static CompletableFuture> ensure(UUID storageId) { storageId ).thenApply(items -> { TerminalJeiStorageCache.CACHE.put(storageId, items); + TerminalJeiStorageCache.TIMESTAMPS.put(storageId, System.currentTimeMillis()); return items; }).whenComplete((items, error) -> TerminalJeiStorageCache.PENDING.remove(storageId)); TerminalJeiStorageCache.PENDING.put(storageId, pending); @@ -80,6 +89,7 @@ public static CompletableFuture> ensure(UUID storageId) { public static void clear() { TerminalJeiStorageCache.CACHE.clear(); + TerminalJeiStorageCache.TIMESTAMPS.clear(); TerminalJeiStorageCache.PENDING.clear(); } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java index 4947517f5b..4aba54733f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/CommandLang.java @@ -17,7 +17,6 @@ public static void init(RegistrumLangProvider provider) { provider.add("command.anvilcraft.storage.no_storage", "The held item does not reference a storage"); provider.add("command.anvilcraft.storage.invalid_type", "Invalid storage type"); provider.add("command.anvilcraft.storage.invalid_id", "Invalid storage ID"); - provider.add("command.anvilcraft.storage.type_mismatch", "Held item storage type %1$s does not match requested type %2$s"); provider.add("command.anvilcraft.storage.info.item", "Storage info of %s"); provider.add("command.anvilcraft.storage.info.none", "none"); provider.add("command.anvilcraft.storage.info.terminal", "Bound storage: %s"); diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java index e73d75bc69..065c202b38 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java @@ -88,9 +88,14 @@ public abstract class BasicRecipeTransferHandlerMixin + // whenComplete:RPC 异常完成(超时/断连)时同样清除标志位,避免 ThreadLocal 永久泄漏 + // 导致本会话内后续所有 transferRecipe 提前 return、终端补库静默失效。 + StorageTerminalClientStub.withdrawToInventory(storageId, missing).whenComplete((changed, error) -> Minecraft.getInstance().execute(() -> { try { + if (error != null) { + return; // 补库失败:不重试传输,由 JEI 原逻辑兜底 + } // 补库完成:重试原传输逻辑(背包已有物品,getInventoryState 自然看到并生成真实转移) this.transferRecipe(container, recipe, recipeSlotsView, player, maxTransfer, true); } finally { @@ -182,14 +187,19 @@ public abstract class BasicRecipeTransferHandlerMixin= need.getCount()) { + continue; // 背包/合成格已满足 + } + // 存储站缓存的数量被 clamp 到 maxStackSize(64),不能用于判断 >64 的需求; + // 存储站存在该物品即视为可补足(传输阶段会按实际缺口补库)。 + boolean inStorage = false; for (ItemStack storageItem : storageItems) { if (ItemStack.isSameItemSameComponents(storageItem, need)) { - have += storageItem.getCount(); + inStorage = true; break; } } - if (have < need.getCount()) { + if (!inStorage) { return false; } } From b67bb59db3812ccb6ea0f4360dc5e4a75f5b89dd Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 21:44:57 +0800 Subject: [PATCH 09/15] chore: update anvillib to 2.0.0+snapshot.511 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b3ea1015e4..a40fb29fb9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ minecraft = "1.21.1" neoForge = "21.1.219" -anvillib = "2.0.0+snapshot.509" +anvillib = "2.0.0+snapshot.511" ageratum = "0.0.1+build.111" jei = "19.32.0.358" From 3c9a6617117b15ab1dc7b6d3f2d32d1dabc4d5d9 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 21:53:32 +0800 Subject: [PATCH 10/15] fix: fix problems --- .../client/rpc/TerminalJeiStorageCache.java | 12 +++-- .../integration/jei/AnvilCraftJeiPlugin.java | 2 +- ...> JeiBasicRecipeTransferHandlerMixin.java} | 14 +++--- .../anvilcraft/rpc/StorageServerStub.java | 19 +++---- src/main/resources/anvilcraft.mixins.json | 50 +++++++++---------- 5 files changed, 50 insertions(+), 47 deletions(-) rename src/main/java/dev/dubhe/anvilcraft/mixin/{BasicRecipeTransferHandlerMixin.java => JeiBasicRecipeTransferHandlerMixin.java} (92%) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java index 7e01508fe8..ad7057274e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java @@ -11,11 +11,11 @@ import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; /** * 客户端缓存玩家绑定存储站的物品代表列表(含数量), @@ -23,11 +23,13 @@ */ public final class TerminalJeiStorageCache { /** 缓存有效期(毫秒):存储站内容可能被其他玩家/自动化改动,定期刷新避免误判。 */ - private static final long TTL_MILLIS = 5_000L; + private static final long TTL_MILLIS = 15_000L; - private static final Map> CACHE = new HashMap<>(); - private static final Map TIMESTAMPS = new HashMap<>(); - private static final Map>> PENDING = new HashMap<>(); + // ConcurrentHashMap:渲染线程(mixin 检查阶段 get)与 RPC 完成线程(thenApply 写入) + // 并发读写,普通 HashMap 有数据损坏风险。 + private static final Map> CACHE = new ConcurrentHashMap<>(); + private static final Map TIMESTAMPS = new ConcurrentHashMap<>(); + private static final Map>> PENDING = new ConcurrentHashMap<>(); private TerminalJeiStorageCache() { } diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java index b5816ba822..60937f9629 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java @@ -326,7 +326,7 @@ public void registerCategories(IRecipeCategoryRegistration registration) { @Override public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) { - // 终端存储站补库由 BasicRecipeTransferHandlerMixin 统一注入 JEI 转移流程处理 + // 终端存储站补库由 JeiBasicRecipeTransferHandlerMixin 统一注入 JEI 转移流程处理 registration.addRecipeTransferHandler( RoyalSmithingMenu.class, ModMenuTypes.ROYAL_SMITHING.get(), diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java similarity index 92% rename from src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java rename to src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java index 065c202b38..d5d77fbf1a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/BasicRecipeTransferHandlerMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java @@ -35,7 +35,7 @@ * 不注入 {@code getInventoryState}:其声明的"虚拟空槽"无法让服务端实际取到物品。 */ @Mixin(BasicRecipeTransferHandler.class) -public abstract class BasicRecipeTransferHandlerMixin { +public abstract class JeiBasicRecipeTransferHandlerMixin { @Shadow public abstract @Nullable IRecipeTransferError transferRecipe( C container, @@ -73,13 +73,13 @@ public abstract class BasicRecipeTransferHandlerMixin storageItems = TerminalJeiStorageCache.get(storageId); if (storageItems != null - && BasicRecipeTransferHandlerMixin.anvilcraft$containerSatisfies(container, storageItems, recipeSlotsView)) { + && JeiBasicRecipeTransferHandlerMixin.anvilcraft$containerSatisfies(container, storageItems, recipeSlotsView)) { cir.setReturnValue(null); } return; } // 传输阶段:先补足背包缺少的配方物品 - List missing = BasicRecipeTransferHandlerMixin.anvilcraft$collectMissing( + List missing = JeiBasicRecipeTransferHandlerMixin.anvilcraft$collectMissing( container, recipeSlotsView, maxTransfer @@ -123,7 +123,7 @@ public abstract class BasicRecipeTransferHandlerMixin 0) { ItemStack m = need.copy(); @@ -178,7 +178,7 @@ public abstract class BasicRecipeTransferHandlerMixin= need.getCount()) { continue; // 背包/合成格已满足 } diff --git a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java index 04d9e953f3..556dae3fb1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java +++ b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java @@ -641,9 +641,12 @@ public static boolean terminalWithdrawToInventory( } /** - * JEI 快速合成检查:返回玩家绑定存储站中每种物品的代表及数量(数量为存储站内总量, - * 受 ItemStack 上限限制,用于检查阶段判断存储站是否满足配方需求)。 + * JEI 快速合成检查:返回玩家绑定存储站中每种物品的一份代表(去重), + * 供客户端缓存判断存储站是否拥有配方所需物品。限制返回条目数,避免超大 + * 存储站在每次刷新时全量扫描造成服务端尖峰。 */ + private static final int MAX_STORAGE_ITEMS = 512; + @CallableParam(clazz = StorageServerStub.class, field = "ITEM_STACK_LIST_STREAM_CODEC") @RemoteCallable(validator = TerminalAccessValidator.class) public static List getStorageItems(UUID playerId, UUID storageId) { @@ -657,7 +660,7 @@ public static List getStorageItems(UUID playerId, UUID storageId) { } UnlimitedItemStacksResourceHandler items = storageOp.get().getItems(); List result = new ArrayList<>(); - for (int slot = 0; slot < items.size(); slot++) { + for (int slot = 0; slot < items.size() && result.size() < StorageServerStub.MAX_STORAGE_ITEMS; slot++) { long amount = items.getAmountAsLong(slot); if (amount <= 0) { continue; @@ -666,17 +669,15 @@ public static List getStorageItems(UUID playerId, UUID storageId) { if (stack.isEmpty()) { continue; } - boolean merged = false; + boolean duplicate = false; for (ItemStack existing : result) { if (ItemStack.isSameItemSameComponents(existing, stack)) { - int total = existing.getCount() + (int) Math.min(amount, Integer.MAX_VALUE - existing.getCount()); - existing.setCount(Math.min(total, stack.getMaxStackSize())); - merged = true; + duplicate = true; break; } } - if (!merged) { - result.add(stack.copyWithCount((int) Math.min(amount, stack.getMaxStackSize()))); + if (!duplicate) { + result.add(stack); } } return result; diff --git a/src/main/resources/anvilcraft.mixins.json b/src/main/resources/anvilcraft.mixins.json index 9b2e568d30..1e4aaa6a64 100644 --- a/src/main/resources/anvilcraft.mixins.json +++ b/src/main/resources/anvilcraft.mixins.json @@ -93,31 +93,31 @@ "tsmm.SetBlockCommandMixin" ], "client": [ - "AbstractContainerScreenMixin", - "BasicRecipeTransferHandlerMixin", - "BlockStateBaseMixin", - "ClientLevelMixin", - "ClientPacketListenerMixin", - "CreativeModeInventoryScreenMixin", - "GameRendererMixin", - "GuiGraphicsMixin", - "ItemFrameRendererMixin", - "ItemInHandRendererMixin", - "ItemOverridesMixin", - "LevelRendererMixin", - "LocalPlayerMixin", - "MinecraftClientMixin", - "MouseHandlerMixin", - "ParticleEngineMixin", - "PostChainAccessor", - "SectionCompilerMixin", - "SoundEngineMixin", - "accessor.MultiPlayerGameModeAccessor", - "accessor.ScreenAccessor", - "compat.EmbBlockOcclusionCacheMixin", - "compat.EmbChunkBuilderMeshingTaskMixin", - "compat.SodiumBlockOcclusionCacheMixin", - "compat.SodiumChunkBuilderMeshingTaskMixin" + "AbstractContainerScreenMixin", + "BlockStateBaseMixin", + "ClientLevelMixin", + "ClientPacketListenerMixin", + "CreativeModeInventoryScreenMixin", + "GameRendererMixin", + "GuiGraphicsMixin", + "ItemFrameRendererMixin", + "ItemInHandRendererMixin", + "ItemOverridesMixin", + "JeiBasicRecipeTransferHandlerMixin", + "LevelRendererMixin", + "LocalPlayerMixin", + "MinecraftClientMixin", + "MouseHandlerMixin", + "ParticleEngineMixin", + "PostChainAccessor", + "SectionCompilerMixin", + "SoundEngineMixin", + "accessor.MultiPlayerGameModeAccessor", + "accessor.ScreenAccessor", + "compat.EmbBlockOcclusionCacheMixin", + "compat.EmbChunkBuilderMeshingTaskMixin", + "compat.SodiumBlockOcclusionCacheMixin", + "compat.SodiumChunkBuilderMeshingTaskMixin" ], "plugin": "dev.dubhe.anvilcraft.mixin.plugin.AnvilCraftMixinPlugin", "injectors": { From d9aa16de5d76c7dc2a00ef19a35e524cda2e9b5f Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Mon, 17 Aug 2026 22:02:20 +0800 Subject: [PATCH 11/15] fix(category): fix category name encoding --- .../dubhe/anvilcraft/saved/storage/category/ICategory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java index dcedf0057e..efffadbdba 100644 --- a/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/saved/storage/category/ICategory.java @@ -58,8 +58,8 @@ public DataResult encode(ICategory input, DynamicOps ops, T prefix) { StreamCodec STREAM_CODEC = ByteBufCodecs.registry(ModRegistryKeys.CATEGORY_TYPE) .dispatch(ICategory::getType, Type::streamCodec); StreamCodec> LIST_STREAM_CODEC = ICategory.STREAM_CODEC.apply(ByteBufCodecs.list()); - Codec NAME_CODEC = Codec.either(ComponentSerialization.CODEC, ComponentSerialization.flatCodec(Integer.MAX_VALUE)) - .xmap(either -> either.map(Function.identity(), Function.identity()), Either::left); + Codec NAME_CODEC = Codec.either(ComponentSerialization.flatCodec(Integer.MAX_VALUE), ComponentSerialization.CODEC) + .xmap(either -> either.map(Function.identity(), Function.identity()), Either::right); ItemStack icon(); From 6b39ed01852b32b592d327d271257464e4106df9 Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Mon, 17 Aug 2026 22:28:31 +0800 Subject: [PATCH 12/15] =?UTF-8?q?feat(jei):=20=E6=B7=BB=E5=8A=A0=E8=B6=85?= =?UTF-8?q?=E7=BB=B4=E5=AD=98=E5=82=A8=E7=AB=99=E5=8D=87=E7=BA=A7=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=8F=8A=E6=96=87=E5=AD=97=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增“requires_expansion”字段,显示所需4个空间超压缩机扩展 - HyperdimensionStorageStationUpgradeCategory类中添加对应显示组件与黄金样式 - 新增输入槽位展示4个空间超压缩机物品提示 - 更新语言文件 en_us.json 和 en_ud.json,添加新字段与对应描述 - Jeilang数据文件中加入新文本项,支持新功能显示 - UI界面中绘制新增槽位,完善交互体验 --- src/generated/resources/assets/anvilcraft/lang/en_ud.json | 1 + src/generated/resources/assets/anvilcraft/lang/en_us.json | 1 + src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java | 4 ++++ .../HyperdimensionStorageStationUpgradeCategory.java | 8 ++++++++ 4 files changed, 14 insertions(+) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index ddbaed1f04..3b70f1f751 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -851,6 +851,7 @@ "gui.anvilcraft.category.fluid_mixing": "uoᴉʇɔɐǝᴚ pᴉnꞁℲ xǝꞁdɯoƆ", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "ǝpɐɹᵷd∩ uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "ɹǝuᴉɐʇuoɔ ɹǝʞꞁnɥs ǝɥʇ oʇuo sɯǝʇᴉ ǝsǝɥʇ doɹᗡ", + "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion": "suoᴉsuɐdxƎ ɹossǝɹdɯoɔ-ɹǝʌO ǝɔɐdS ߈ sǝɹᴉnbǝᴚ", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "ɹǝuᴉɐʇuoɔ ǝɥʇ ǝpɐɹᵷdn oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇS", "gui.anvilcraft.category.item_compress": "ssǝɹdɯoƆ ɯǝʇI", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "sǝpoꞁdxǝ ǝsᴉʍɹǝɥʇo ⸵ɹoʇᴉɔɐdɐɔɹǝdnS ɐ ǝɔnpoɹd oʇ ǝɔuɐɥ% c0ϛ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index b6e4f70d6f..69109e9a65 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -851,6 +851,7 @@ "gui.anvilcraft.category.fluid_mixing": "Complex Fluid Reaction", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "Hyperdimension Storage Station Upgrade", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "Drop these items onto the shulker container", + "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion": "Requires 4 Space Over-compressor Expansions", "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "Strike with an anvil to upgrade the container", "gui.anvilcraft.category.item_compress": "Item Compress", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "50% chance to produce a Supercapacitor; otherwise explodes", diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java index 817ccc0026..614bd2bb04 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java @@ -90,6 +90,10 @@ public static void init(RegistrumLangProvider provider) { provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade", "Hyperdimension Storage Station Upgrade"); provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top", "Drop these items onto the shulker container"); provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike", "Strike with an anvil to upgrade the container"); + provider.add( + "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion", + "Requires 4 Space Over-compressor Expansions" + ); provider.add("gui.anvilcraft.category.decay", "Decay"); provider.add("gui.anvilcraft.category.decay.random_tick", "The block at center will decay on a random tick"); diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java index c342d708bb..2d226e7742 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java @@ -40,6 +40,7 @@ public class HyperdimensionStorageStationUpgradeCategory implements IRecipeCateg private final Component title; private final Component dropOnTopTooltip; private final Component strikeTooltip; + private final Component requiresExpansion; public HyperdimensionStorageStationUpgradeCategory(IGuiHelper helper) { this.icon = helper.createDrawableItemStack(ModBlocks.HYPERDIMENSION_STORAGE_STATION.asStack()); @@ -54,6 +55,9 @@ public HyperdimensionStorageStationUpgradeCategory(IGuiHelper helper) { this.strikeTooltip = Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike") .withStyle(ChatFormatting.GOLD); + this.requiresExpansion = + Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion") + .withStyle(ChatFormatting.GOLD); } @Override @@ -102,6 +106,9 @@ public void setRecipe(IRecipeLayoutBuilder builder, HyperdimensionStorageStation builder.addSlot(RecipeIngredientRole.OUTPUT, 128, 8) .addItemStack(ModBlocks.HYPERDIMENSION_STORAGE_STATION.asStack()) .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(strikeTooltip)); + builder.addSlot(RecipeIngredientRole.INPUT, 31, 41) + .addItemStack(ModBlocks.SPACE_OVERCOMPRESSOR.asStack(4)) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(requiresExpansion)); } @Override @@ -141,6 +148,7 @@ public void draw( slotDefault.draw(guiGraphics, 8, 7); slotDefault.draw(guiGraphics, 30, 7); slotDefault.draw(guiGraphics, 127, 7); + slotDefault.draw(guiGraphics, 30, 40); } public static void registerRecipes(IRecipeRegistration registration) { From a23914933df89f2c3b4d238ac1a1c33d2bec9e11 Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Tue, 18 Aug 2026 12:28:49 +0800 Subject: [PATCH 13/15] =?UTF-8?q?refactor(jei):=20=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E5=AE=B9=E5=99=A8=E5=8D=87=E7=BA=A7=E7=B1=BB=E5=88=AB=EF=BC=8C?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=97=A7=E5=8D=87=E7=BA=A7=E7=B1=BB=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 ShulkerContainerUpgrade 和 HyperdimensionStorageStationUpgrade 相关类别及配方类 - 新增 ContainerUpgradeCategory 类,支持箱子升级与存储站升级两种类型 - 修改 AnvilCraftJeiPlugin,使用 ContainerUpgrade 取代旧的升级类别和配方 - 更新本地化语言文件,替换对应类别的文本条目 - 精简 JEI 注册流程,整合升级类别为统一处理 --- .../assets/anvilcraft/lang/en_ud.json | 11 +- .../assets/anvilcraft/lang/en_us.json | 11 +- .../dubhe/anvilcraft/data/lang/JeiLang.java | 12 +- .../integration/jei/AnvilCraftJeiPlugin.java | 21 +- .../category/ContainerUpgradeCategory.java | 215 ++++++++++++++++++ ...imensionStorageStationUpgradeCategory.java | 167 -------------- .../ShulkerContainerUpgradeCategory.java | 161 ------------- .../jei/recipe/ContainerUpgradeRecipe.java | 18 ++ ...rdimensionStorageStationUpgradeRecipe.java | 9 - .../recipe/ShulkerContainerUpgradeRecipe.java | 9 - 10 files changed, 252 insertions(+), 382 deletions(-) create mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ContainerUpgradeCategory.java delete mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java delete mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ShulkerContainerUpgradeCategory.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ContainerUpgradeRecipe.java delete mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/HyperdimensionStorageStationUpgradeRecipe.java delete mode 100644 src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ShulkerContainerUpgradeRecipe.java diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 3b70f1f751..ad4bdfb7fb 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -839,6 +839,10 @@ "gui.anvilcraft.category.charger_charging.power_consume": "%s :ǝɯnsuoƆ ɹǝʍoԀ", "gui.anvilcraft.category.charger_charging.power_produce": "%s :ǝɔnpoɹԀ ɹǝʍoԀ", "gui.anvilcraft.category.charger_charging.time": "puoɔǝs %s :ǝɯᴉ⟘", + "gui.anvilcraft.category.container_upgrade": "ǝpɐɹᵷd∩ ɹǝuᴉɐʇuoƆ", + "gui.anvilcraft.category.container_upgrade.drop_on_top": "ɹǝuᴉɐʇuoɔ ǝɥʇ oʇuo sɯǝʇᴉ ǝsǝɥʇ doɹᗡ", + "gui.anvilcraft.category.container_upgrade.requires_expansion": "suoᴉsuɐdxƎ ɹossǝɹdɯoɔ-ɹǝʌO ǝɔɐdS ߈ sǝɹᴉnbǝᴚ", + "gui.anvilcraft.category.container_upgrade.strike": "ɹǝuᴉɐʇuoɔ ǝɥʇ ǝpɐɹᵷdn oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇS", "gui.anvilcraft.category.decay": "ʎɐɔǝᗡ", "gui.anvilcraft.category.decay.around": "uʍoɥs sɐ ɹǝʇuǝɔ ǝɥʇ punoɹɐ sʞɔoꞁq ǝsǝɥʇ ǝɔɐꞁԀ", "gui.anvilcraft.category.decay.center": "ɹǝʇuǝɔ ǝɥʇ ʇɐ ʞɔoꞁq sᴉɥʇ ʇnԀ", @@ -849,10 +853,6 @@ "gui.anvilcraft.category.fast_cooking.consume_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɯnsuoƆ", "gui.anvilcraft.category.fast_cooking.produce_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɔnpoɹԀ", "gui.anvilcraft.category.fluid_mixing": "uoᴉʇɔɐǝᴚ pᴉnꞁℲ xǝꞁdɯoƆ", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "ǝpɐɹᵷd∩ uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "ɹǝuᴉɐʇuoɔ ɹǝʞꞁnɥs ǝɥʇ oʇuo sɯǝʇᴉ ǝsǝɥʇ doɹᗡ", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion": "suoᴉsuɐdxƎ ɹossǝɹdɯoɔ-ɹǝʌO ǝɔɐdS ߈ sǝɹᴉnbǝᴚ", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "ɹǝuᴉɐʇuoɔ ǝɥʇ ǝpɐɹᵷdn oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇS", "gui.anvilcraft.category.item_compress": "ssǝɹdɯoƆ ɯǝʇI", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "sǝpoꞁdxǝ ǝsᴉʍɹǝɥʇo ⸵ɹoʇᴉɔɐdɐɔɹǝdnS ɐ ǝɔnpoɹd oʇ ǝɔuɐɥ% c0ϛ", "gui.anvilcraft.category.item_compress.supercapacitor.resin": "ɹǝdǝǝɹƆ pǝᵷɹɐɥɔ-ᵷuᴉuʇɥᵷᴉꞁ ɐ ᵷuᴉuᴉɐʇuoɔ ʞɔoꞁq uᴉsǝᴚ", @@ -888,9 +888,6 @@ "gui.anvilcraft.category.portal_conversion": "ꞁɐʇɹoԀ oʇuI sꞁꞁɐℲ ʞɔoꞁᗺ", "gui.anvilcraft.category.portal_conversion.fall_through": "%s ɥᵷnoɹɥʇ ꞁꞁɐɟ uǝɥʍ pǝʇɹǝʌuoƆ", "gui.anvilcraft.category.procedural_process": "ᵷuᴉssǝɔoɹԀ ǝɔuǝnbǝS ʞɔoꞁᗺ", - "gui.anvilcraft.category.shulker_container_upgrade": "ǝpɐɹᵷd∩ ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS", - "gui.anvilcraft.category.shulker_container_upgrade.drop_on_top": "ǝʇɐɹɔ ǝᵷɹɐꞁ ǝɥʇ oʇuo sɯǝʇᴉ ǝsǝɥʇ doɹᗡ", - "gui.anvilcraft.category.shulker_container_upgrade.strike": "ǝʇɐɹɔ ǝɥʇ ǝpɐɹᵷdn oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇS", "gui.anvilcraft.category.solid_liquid": "uoᴉʇɔɐǝᴚ pᴉnꞁℲ-pᴉꞁoS", "gui.anvilcraft.category.solid_liquid.consume_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɯnsuoƆ", "gui.anvilcraft.category.solid_liquid.produce_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɔnpoɹԀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 69109e9a65..a850a0b537 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -839,6 +839,10 @@ "gui.anvilcraft.category.charger_charging.power_consume": "Power Consume: %s", "gui.anvilcraft.category.charger_charging.power_produce": "Power Produce: %s", "gui.anvilcraft.category.charger_charging.time": "Time: %s second", + "gui.anvilcraft.category.container_upgrade": "Container Upgrade", + "gui.anvilcraft.category.container_upgrade.drop_on_top": "Drop these items onto the container", + "gui.anvilcraft.category.container_upgrade.requires_expansion": "Requires 4 Space Over-compressor Expansions", + "gui.anvilcraft.category.container_upgrade.strike": "Strike with an anvil to upgrade the container", "gui.anvilcraft.category.decay": "Decay", "gui.anvilcraft.category.decay.around": "Place these blocks around the center as shown", "gui.anvilcraft.category.decay.center": "Put this block at the center", @@ -849,10 +853,6 @@ "gui.anvilcraft.category.fast_cooking.consume_fluid": "Consume: %1$d mB of %2$s", "gui.anvilcraft.category.fast_cooking.produce_fluid": "Produce: %1$d mB of %2$s", "gui.anvilcraft.category.fluid_mixing": "Complex Fluid Reaction", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "Hyperdimension Storage Station Upgrade", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "Drop these items onto the shulker container", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion": "Requires 4 Space Over-compressor Expansions", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "Strike with an anvil to upgrade the container", "gui.anvilcraft.category.item_compress": "Item Compress", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "50% chance to produce a Supercapacitor; otherwise explodes", "gui.anvilcraft.category.item_compress.supercapacitor.resin": "Resin block containing a lightning-charged Creeper", @@ -888,9 +888,6 @@ "gui.anvilcraft.category.portal_conversion": "Block Falls Into Portal", "gui.anvilcraft.category.portal_conversion.fall_through": "Converted when fall through %s", "gui.anvilcraft.category.procedural_process": "Block Sequence Processing", - "gui.anvilcraft.category.shulker_container_upgrade": "Shulker Container Upgrade", - "gui.anvilcraft.category.shulker_container_upgrade.drop_on_top": "Drop these items onto the large crate", - "gui.anvilcraft.category.shulker_container_upgrade.strike": "Strike with an anvil to upgrade the crate", "gui.anvilcraft.category.solid_liquid": "Solid-Fluid Reaction", "gui.anvilcraft.category.solid_liquid.consume_fluid": "Consume: %1$d mB of %2$s", "gui.anvilcraft.category.solid_liquid.produce_fluid": "Produce: %1$d mB of %2$s", diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java index 614bd2bb04..2de50855af 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/JeiLang.java @@ -83,15 +83,11 @@ public static void init(RegistrumLangProvider provider) { provider.add("gui.anvilcraft.category.beacon_conversion.activate", "Use this item to activate beacon"); provider.add("gui.anvilcraft.category.beacon_conversion.beacon_base", "Use this block as beacon base"); - provider.add("gui.anvilcraft.category.shulker_container_upgrade", "Shulker Container Upgrade"); - provider.add("gui.anvilcraft.category.shulker_container_upgrade.drop_on_top", "Drop these items onto the large crate"); - provider.add("gui.anvilcraft.category.shulker_container_upgrade.strike", "Strike with an anvil to upgrade the crate"); - - provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade", "Hyperdimension Storage Station Upgrade"); - provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top", "Drop these items onto the shulker container"); - provider.add("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike", "Strike with an anvil to upgrade the container"); + provider.add("gui.anvilcraft.category.container_upgrade", "Container Upgrade"); + provider.add("gui.anvilcraft.category.container_upgrade.drop_on_top", "Drop these items onto the container"); + provider.add("gui.anvilcraft.category.container_upgrade.strike", "Strike with an anvil to upgrade the container"); provider.add( - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion", + "gui.anvilcraft.category.container_upgrade.requires_expansion", "Requires 4 Space Over-compressor Expansions" ); diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java index 60937f9629..155527a401 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java @@ -15,17 +15,16 @@ import dev.dubhe.anvilcraft.integration.jei.category.AnvilCollisionCraftCategory; import dev.dubhe.anvilcraft.integration.jei.category.BeaconConversionCategory; import dev.dubhe.anvilcraft.integration.jei.category.ChargerChargingCategory; +import dev.dubhe.anvilcraft.integration.jei.category.ContainerUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.DecayCategory; import dev.dubhe.anvilcraft.integration.jei.category.EnergyWeaponCategory; import dev.dubhe.anvilcraft.integration.jei.category.FluidMixingCategory; -import dev.dubhe.anvilcraft.integration.jei.category.HyperdimensionStorageStationUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.JewelCraftingCategory; import dev.dubhe.anvilcraft.integration.jei.category.MineralFountainCategory; import dev.dubhe.anvilcraft.integration.jei.category.MobTransformCategory; import dev.dubhe.anvilcraft.integration.jei.category.MultipleToOneSmithingCategory; import dev.dubhe.anvilcraft.integration.jei.category.PortalConversionCategory; import dev.dubhe.anvilcraft.integration.jei.category.ProceduralProcessCategory; -import dev.dubhe.anvilcraft.integration.jei.category.ShulkerContainerUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.anvil.BlockCompressCategory; import dev.dubhe.anvilcraft.integration.jei.category.anvil.BlockCrushCategory; import dev.dubhe.anvilcraft.integration.jei.category.anvil.BlockSmearCategory; @@ -49,12 +48,11 @@ import dev.dubhe.anvilcraft.integration.jei.category.multiblock.MultiBlockCraftingCategory; import dev.dubhe.anvilcraft.integration.jei.handlers.GhostIngredientHandler; import dev.dubhe.anvilcraft.integration.jei.recipe.BeaconConversionRecipe; +import dev.dubhe.anvilcraft.integration.jei.recipe.ContainerUpgradeRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.DecayRecipe; -import dev.dubhe.anvilcraft.integration.jei.recipe.HyperdimensionStorageStationUpgradeRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.MeshRecipeGroup; import dev.dubhe.anvilcraft.integration.jei.recipe.MineralFountainJeiRecipe; import dev.dubhe.anvilcraft.integration.jei.recipe.MobTransformJeiRecipe; -import dev.dubhe.anvilcraft.integration.jei.recipe.ShulkerContainerUpgradeRecipe; import dev.dubhe.anvilcraft.inventory.RoyalSmithingMenu; import dev.dubhe.anvilcraft.recipe.CanningFoodRecipe; import dev.dubhe.anvilcraft.recipe.ChargerChargingRecipe; @@ -135,10 +133,8 @@ public class AnvilCraftJeiPlugin implements IModPlugin { public static final RecipeType BEACON_CONVERSION = createRecipeType("beacon_conversion", BeaconConversionRecipe.class); public static final RecipeType DECAY = createRecipeType("decay", DecayRecipe.class); - public static final RecipeType SHULKER_CONTAINER_UPGRADE = - createRecipeType("shulker_container_upgrade", ShulkerContainerUpgradeRecipe.class); - public static final RecipeType HYPERDIMENSION_STORAGE_STATION_UPGRADE = - createRecipeType("hyperdimension_storage_station_upgrade", HyperdimensionStorageStationUpgradeRecipe.class); + public static final RecipeType CONTAINER_UPGRADE = + createRecipeType("container_upgrade", ContainerUpgradeRecipe.class); public static final RecipeType> BLOCK_COMPRESS = createRecipeHolderType("block_compress"); @@ -231,8 +227,7 @@ public void registerRecipes(IRecipeRegistration registration) { ProceduralProcessCategory.registerRecipes(registration); EnergyWeaponCategory.registerRecipes(registration); MineralFountainCategory.registerRecipes(registration); - ShulkerContainerUpgradeCategory.registerRecipes(registration); - HyperdimensionStorageStationUpgradeCategory.registerRecipes(registration); + ContainerUpgradeCategory.registerRecipes(registration); } @Override @@ -268,8 +263,7 @@ public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { ProceduralProcessCategory.registerRecipeCatalysts(registration); EnergyWeaponCategory.registerRecipeCatalysts(registration); MineralFountainCategory.registerRecipeCatalysts(registration); - ShulkerContainerUpgradeCategory.registerRecipeCatalysts(registration); - HyperdimensionStorageStationUpgradeCategory.registerRecipeCatalysts(registration); + ContainerUpgradeCategory.registerRecipeCatalysts(registration); registration.addRecipeCatalyst(new ItemStack(ModBlocks.BATCH_CRAFTER), RecipeTypes.CRAFTING); @@ -320,8 +314,7 @@ public void registerCategories(IRecipeCategoryRegistration registration) { registration.addRecipeCategories(new ProceduralProcessCategory(guiHelper)); registration.addRecipeCategories(new EnergyWeaponCategory(guiHelper)); registration.addRecipeCategories(new MineralFountainCategory(guiHelper)); - registration.addRecipeCategories(new ShulkerContainerUpgradeCategory(guiHelper)); - registration.addRecipeCategories(new HyperdimensionStorageStationUpgradeCategory(guiHelper)); + registration.addRecipeCategories(new ContainerUpgradeCategory(guiHelper)); } @Override diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ContainerUpgradeCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ContainerUpgradeCategory.java new file mode 100644 index 0000000000..0cf483b3c3 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ContainerUpgradeCategory.java @@ -0,0 +1,215 @@ +package dev.dubhe.anvilcraft.integration.jei.category; + +import dev.dubhe.anvilcraft.block.container.storage.LargeCrateBlock; +import dev.dubhe.anvilcraft.block.container.storage.ShulkerContainerBlock; +import dev.dubhe.anvilcraft.block.state.Cube3x3PartHalf; +import dev.dubhe.anvilcraft.block.state.OpenedCube3x3PartHalf; +import dev.dubhe.anvilcraft.client.support.RenderSupport; +import dev.dubhe.anvilcraft.init.block.ModBlocks; +import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin; +import dev.dubhe.anvilcraft.integration.jei.recipe.ContainerUpgradeRecipe; +import dev.dubhe.anvilcraft.integration.jei.util.JeiBlockIngredientUtil; +import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; +import mezz.jei.api.gui.ITickTimer; +import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; +import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IRecipeSlotsView; +import mezz.jei.api.gui.widgets.IRecipeExtrasBuilder; +import mezz.jei.api.helpers.IGuiHelper; +import mezz.jei.api.recipe.IFocusGroup; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.recipe.RecipeType; +import mezz.jei.api.recipe.category.IRecipeCategory; +import mezz.jei.api.registration.IRecipeCatalystRegistration; +import mezz.jei.api.registration.IRecipeRegistration; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import org.jetbrains.annotations.Nullable; + +public class ContainerUpgradeCategory implements IRecipeCategory { + public static final int WIDTH = 162; + public static final int HEIGHT = 64; + public static final int NETHERITE_BLOCK_COUNT = 6; + public static final int HYPERCUBE_COUNT = 16; + + private final IDrawable icon; + private final IDrawable slotDefault; + private final IDrawable arrowIn; + private final IDrawable arrowOutputFromBelow; + private final ITickTimer timer; + private final Component title; + private final Component dropOnTopTooltip; + private final Component strikeTooltip; + private final Component requiresExpansion; + + public ContainerUpgradeCategory(IGuiHelper helper) { + this.icon = helper.createDrawableItemStack(ModBlocks.SHULKER_CONTAINER.asStack()); + this.slotDefault = JeiRenderHelper.getSlotDefault(helper); + this.arrowIn = JeiRenderHelper.getArrowInput(helper); + this.arrowOutputFromBelow = JeiRenderHelper.getArrowOutputFromBelow(helper); + this.timer = helper.createTickTimer(30, 60, true); + this.title = Component.translatable("gui.anvilcraft.category.container_upgrade"); + this.dropOnTopTooltip = + Component.translatable("gui.anvilcraft.category.container_upgrade.drop_on_top") + .withStyle(ChatFormatting.GOLD); + this.strikeTooltip = + Component.translatable("gui.anvilcraft.category.container_upgrade.strike") + .withStyle(ChatFormatting.GOLD); + this.requiresExpansion = + Component.translatable("gui.anvilcraft.category.container_upgrade.requires_expansion") + .withStyle(ChatFormatting.GOLD); + } + + @Override + public RecipeType getRecipeType() { + return AnvilCraftJeiPlugin.CONTAINER_UPGRADE; + } + + @Override + public Component getTitle() { + return title; + } + + @Override + public int getWidth() { + return WIDTH; + } + + @Override + public int getHeight() { + return HEIGHT; + } + + @Override + public @Nullable IDrawable getIcon() { + return icon; + } + + @Override + public void setRecipe( + IRecipeLayoutBuilder builder, ContainerUpgradeRecipe recipe, IFocusGroup focuses) { + switch (recipe.type()) { + case CRATE_TO_CONTAINER -> { + builder.addSlot(RecipeIngredientRole.INPUT, 9, 8) + .addItemStack(ModBlocks.SPACE_OVERCOMPRESSOR.asStack()) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); + builder.addSlot(RecipeIngredientRole.INPUT, 31, 8) + .addItemStack(new ItemStack(Blocks.NETHERITE_BLOCK, NETHERITE_BLOCK_COUNT)) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); + JeiBlockIngredientUtil.addSlot( + builder, + RecipeIngredientRole.INPUT, + "crate", + 69, + 32, + 25, + 25, + ModBlocks.LARGE_CRATE.get() + ); + builder.addSlot(RecipeIngredientRole.OUTPUT, 128, 8) + .addItemStack(ModBlocks.SHULKER_CONTAINER.asStack()) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(strikeTooltip)); + } + case CONTAINER_TO_STATION -> { + builder.addSlot(RecipeIngredientRole.INPUT, 9, 8) + .addItemStack(ModBlocks.SINGULARITY_CRYSTAL.asStack()) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); + builder.addSlot(RecipeIngredientRole.INPUT, 31, 8) + .addItemStack(new ItemStack(ModBlocks.HYPERCUBE, HYPERCUBE_COUNT)) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); + JeiBlockIngredientUtil.addSlot( + builder, + RecipeIngredientRole.INPUT, + "container", + 69, + 32, + 25, + 25, + ModBlocks.SHULKER_CONTAINER.get() + ); + builder.addSlot(RecipeIngredientRole.OUTPUT, 128, 8) + .addItemStack(ModBlocks.HYPERDIMENSION_STORAGE_STATION.asStack()) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(strikeTooltip)); + builder.addSlot(RecipeIngredientRole.INPUT, 31, 41) + .addItemStack(ModBlocks.SPACE_OVERCOMPRESSOR.asStack(4)) + .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(requiresExpansion)); + } + default -> {} + } + } + + @Override + public void createRecipeExtras( + IRecipeExtrasBuilder builder, ContainerUpgradeRecipe recipe, IFocusGroup focuses) { + JeiBlockIngredientUtil.suppressHoverOverlays(builder); + } + + @Override + public void draw( + ContainerUpgradeRecipe recipe, + IRecipeSlotsView recipeSlotsView, + GuiGraphics guiGraphics, + double mouseX, + double mouseY) { + float anvilYOffset = JeiRenderHelper.getAnvilAnimationOffset(timer); + RenderSupport.renderBlock( + guiGraphics, + Blocks.ANVIL.defaultBlockState(), + 81, + 22 + anvilYOffset, + 20, + 12, + RenderSupport.SINGLE_BLOCK); + + switch (recipe.type()) { + case CRATE_TO_CONTAINER -> RenderSupport.renderBlock( + guiGraphics, + ModBlocks.LARGE_CRATE.getDefaultState().setValue(LargeCrateBlock.HALF, Cube3x3PartHalf.MID_CENTER), + 81, + 44, + 10, + 8, + RenderSupport.SINGLE_BLOCK); + case CONTAINER_TO_STATION -> { + RenderSupport.renderBlock( + guiGraphics, + ModBlocks.SHULKER_CONTAINER.getDefaultState() + .setValue(ShulkerContainerBlock.HALF, OpenedCube3x3PartHalf.MID_CENTER), + 81, + 44, + 10, + 8, + RenderSupport.SINGLE_BLOCK); + slotDefault.draw(guiGraphics, 30, 40); + } + default -> {} + } + + arrowIn.draw(guiGraphics, 52, 30); + arrowOutputFromBelow.draw(guiGraphics, 94, 29); + + slotDefault.draw(guiGraphics, 8, 7); + slotDefault.draw(guiGraphics, 30, 7); + slotDefault.draw(guiGraphics, 127, 7); + } + + public static void registerRecipes(IRecipeRegistration registration) { + registration.addRecipes( + AnvilCraftJeiPlugin.CONTAINER_UPGRADE, + ContainerUpgradeRecipe.getAllRecipes() + ); + } + + public static void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { + AnvilCraftJeiPlugin.addAnvilProcessingCatalysts(registration, AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(ModBlocks.LARGE_CRATE.asStack(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(ModBlocks.SPACE_OVERCOMPRESSOR.asStack(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(Blocks.NETHERITE_BLOCK.asItem().getDefaultInstance(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(ModBlocks.SHULKER_CONTAINER.asStack(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(ModBlocks.SINGULARITY_CRYSTAL.asStack(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + registration.addRecipeCatalyst(ModBlocks.HYPERCUBE.asStack(), AnvilCraftJeiPlugin.CONTAINER_UPGRADE); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java deleted file mode 100644 index 2d226e7742..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/HyperdimensionStorageStationUpgradeCategory.java +++ /dev/null @@ -1,167 +0,0 @@ -package dev.dubhe.anvilcraft.integration.jei.category; - -import dev.dubhe.anvilcraft.block.container.storage.ShulkerContainerBlock; -import dev.dubhe.anvilcraft.block.state.OpenedCube3x3PartHalf; -import dev.dubhe.anvilcraft.client.support.RenderSupport; -import dev.dubhe.anvilcraft.init.block.ModBlocks; -import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin; -import dev.dubhe.anvilcraft.integration.jei.recipe.HyperdimensionStorageStationUpgradeRecipe; -import dev.dubhe.anvilcraft.integration.jei.util.JeiBlockIngredientUtil; -import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; -import mezz.jei.api.gui.ITickTimer; -import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; -import mezz.jei.api.gui.drawable.IDrawable; -import mezz.jei.api.gui.ingredient.IRecipeSlotsView; -import mezz.jei.api.gui.widgets.IRecipeExtrasBuilder; -import mezz.jei.api.helpers.IGuiHelper; -import mezz.jei.api.recipe.IFocusGroup; -import mezz.jei.api.recipe.RecipeIngredientRole; -import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.recipe.category.IRecipeCategory; -import mezz.jei.api.registration.IRecipeCatalystRegistration; -import mezz.jei.api.registration.IRecipeRegistration; -import net.minecraft.ChatFormatting; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Blocks; -import org.jetbrains.annotations.Nullable; - -public class HyperdimensionStorageStationUpgradeCategory implements IRecipeCategory { - public static final int WIDTH = 162; - public static final int HEIGHT = 64; - public static final int HYPERCUBE_COUNT = 16; - - private final IDrawable icon; - private final IDrawable slotDefault; - private final IDrawable arrowIn; - private final IDrawable arrowOutputFromBelow; - private final ITickTimer timer; - private final Component title; - private final Component dropOnTopTooltip; - private final Component strikeTooltip; - private final Component requiresExpansion; - - public HyperdimensionStorageStationUpgradeCategory(IGuiHelper helper) { - this.icon = helper.createDrawableItemStack(ModBlocks.HYPERDIMENSION_STORAGE_STATION.asStack()); - this.slotDefault = JeiRenderHelper.getSlotDefault(helper); - this.arrowIn = JeiRenderHelper.getArrowInput(helper); - this.arrowOutputFromBelow = JeiRenderHelper.getArrowOutputFromBelow(helper); - this.timer = helper.createTickTimer(30, 60, true); - this.title = Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade"); - this.dropOnTopTooltip = - Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top") - .withStyle(ChatFormatting.GOLD); - this.strikeTooltip = - Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike") - .withStyle(ChatFormatting.GOLD); - this.requiresExpansion = - Component.translatable("gui.anvilcraft.category.hyperdimension_storage_station_upgrade.requires_expansion") - .withStyle(ChatFormatting.GOLD); - } - - @Override - public RecipeType getRecipeType() { - return AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE; - } - - @Override - public Component getTitle() { - return title; - } - - @Override - public int getWidth() { - return WIDTH; - } - - @Override - public int getHeight() { - return HEIGHT; - } - - @Override - public @Nullable IDrawable getIcon() { - return icon; - } - - @Override - public void setRecipe(IRecipeLayoutBuilder builder, HyperdimensionStorageStationUpgradeRecipe recipe, IFocusGroup focuses) { - builder.addSlot(RecipeIngredientRole.INPUT, 9, 8) - .addItemStack(ModBlocks.SINGULARITY_CRYSTAL.asStack()) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); - builder.addSlot(RecipeIngredientRole.INPUT, 31, 8) - .addItemStack(new ItemStack(ModBlocks.HYPERCUBE, HYPERCUBE_COUNT)) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); - JeiBlockIngredientUtil.addSlot( - builder, - RecipeIngredientRole.INPUT, - "container", - 69, - 32, - 25, - 25, - ModBlocks.SHULKER_CONTAINER.get() - ); - builder.addSlot(RecipeIngredientRole.OUTPUT, 128, 8) - .addItemStack(ModBlocks.HYPERDIMENSION_STORAGE_STATION.asStack()) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(strikeTooltip)); - builder.addSlot(RecipeIngredientRole.INPUT, 31, 41) - .addItemStack(ModBlocks.SPACE_OVERCOMPRESSOR.asStack(4)) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(requiresExpansion)); - } - - @Override - public void createRecipeExtras(IRecipeExtrasBuilder builder, HyperdimensionStorageStationUpgradeRecipe recipe, IFocusGroup focuses) { - JeiBlockIngredientUtil.suppressHoverOverlays(builder); - } - - @Override - public void draw( - HyperdimensionStorageStationUpgradeRecipe recipe, - IRecipeSlotsView recipeSlotsView, - GuiGraphics guiGraphics, - double mouseX, - double mouseY - ) { - float anvilYOffset = JeiRenderHelper.getAnvilAnimationOffset(timer); - RenderSupport.renderBlock( - guiGraphics, - Blocks.ANVIL.defaultBlockState(), - 81, - 22 + anvilYOffset, - 20, - 12, - RenderSupport.SINGLE_BLOCK); - RenderSupport.renderBlock( - guiGraphics, - ModBlocks.SHULKER_CONTAINER.getDefaultState().setValue(ShulkerContainerBlock.HALF, OpenedCube3x3PartHalf.MID_CENTER), - 81, - 44, - 10, - 8, - RenderSupport.SINGLE_BLOCK); - - arrowIn.draw(guiGraphics, 52, 30); - arrowOutputFromBelow.draw(guiGraphics, 94, 29); - - slotDefault.draw(guiGraphics, 8, 7); - slotDefault.draw(guiGraphics, 30, 7); - slotDefault.draw(guiGraphics, 127, 7); - slotDefault.draw(guiGraphics, 30, 40); - } - - public static void registerRecipes(IRecipeRegistration registration) { - registration.addRecipes( - AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE, - HyperdimensionStorageStationUpgradeRecipe.getAllRecipes() - ); - } - - public static void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { - AnvilCraftJeiPlugin.addAnvilProcessingCatalysts(registration, AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE); - registration.addRecipeCatalyst(ModBlocks.SHULKER_CONTAINER.asStack(), AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE); - registration.addRecipeCatalyst(ModBlocks.SINGULARITY_CRYSTAL.asStack(), AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE); - registration.addRecipeCatalyst(ModBlocks.HYPERCUBE.asStack(), AnvilCraftJeiPlugin.HYPERDIMENSION_STORAGE_STATION_UPGRADE); - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ShulkerContainerUpgradeCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ShulkerContainerUpgradeCategory.java deleted file mode 100644 index 146d3527dc..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/ShulkerContainerUpgradeCategory.java +++ /dev/null @@ -1,161 +0,0 @@ -package dev.dubhe.anvilcraft.integration.jei.category; - -import dev.dubhe.anvilcraft.block.container.storage.LargeCrateBlock; -import dev.dubhe.anvilcraft.block.state.Cube3x3PartHalf; -import dev.dubhe.anvilcraft.client.support.RenderSupport; -import dev.dubhe.anvilcraft.init.block.ModBlocks; -import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin; -import dev.dubhe.anvilcraft.integration.jei.recipe.ShulkerContainerUpgradeRecipe; -import dev.dubhe.anvilcraft.integration.jei.util.JeiBlockIngredientUtil; -import dev.dubhe.anvilcraft.integration.jei.util.JeiRenderHelper; -import mezz.jei.api.gui.ITickTimer; -import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; -import mezz.jei.api.gui.drawable.IDrawable; -import mezz.jei.api.gui.ingredient.IRecipeSlotsView; -import mezz.jei.api.gui.widgets.IRecipeExtrasBuilder; -import mezz.jei.api.helpers.IGuiHelper; -import mezz.jei.api.recipe.IFocusGroup; -import mezz.jei.api.recipe.RecipeIngredientRole; -import mezz.jei.api.recipe.RecipeType; -import mezz.jei.api.recipe.category.IRecipeCategory; -import mezz.jei.api.registration.IRecipeCatalystRegistration; -import mezz.jei.api.registration.IRecipeRegistration; -import net.minecraft.ChatFormatting; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Blocks; -import org.jetbrains.annotations.Nullable; - -public class ShulkerContainerUpgradeCategory implements IRecipeCategory { - public static final int WIDTH = 162; - public static final int HEIGHT = 64; - public static final int NETHERITE_BLOCK_COUNT = 6; - - private final IDrawable icon; - private final IDrawable slotDefault; - private final IDrawable arrowIn; - private final IDrawable arrowOutputFromBelow; - private final ITickTimer timer; - private final Component title; - private final Component dropOnTopTooltip; - private final Component strikeTooltip; - - public ShulkerContainerUpgradeCategory(IGuiHelper helper) { - this.icon = helper.createDrawableItemStack(ModBlocks.SHULKER_CONTAINER.asStack()); - this.slotDefault = JeiRenderHelper.getSlotDefault(helper); - this.arrowIn = JeiRenderHelper.getArrowInput(helper); - this.arrowOutputFromBelow = JeiRenderHelper.getArrowOutputFromBelow(helper); - this.timer = helper.createTickTimer(30, 60, true); - this.title = Component.translatable("gui.anvilcraft.category.shulker_container_upgrade"); - this.dropOnTopTooltip = - Component.translatable("gui.anvilcraft.category.shulker_container_upgrade.drop_on_top") - .withStyle(ChatFormatting.GOLD); - this.strikeTooltip = - Component.translatable("gui.anvilcraft.category.shulker_container_upgrade.strike") - .withStyle(ChatFormatting.GOLD); - } - - @Override - public RecipeType getRecipeType() { - return AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE; - } - - @Override - public Component getTitle() { - return title; - } - - @Override - public int getWidth() { - return WIDTH; - } - - @Override - public int getHeight() { - return HEIGHT; - } - - @Override - public @Nullable IDrawable getIcon() { - return icon; - } - - @Override - public void setRecipe( - IRecipeLayoutBuilder builder, ShulkerContainerUpgradeRecipe recipe, IFocusGroup focuses) { - builder.addSlot(RecipeIngredientRole.INPUT, 9, 8) - .addItemStack(ModBlocks.SPACE_OVERCOMPRESSOR.asStack()) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); - builder.addSlot(RecipeIngredientRole.INPUT, 31, 8) - .addItemStack(new ItemStack(Blocks.NETHERITE_BLOCK, NETHERITE_BLOCK_COUNT)) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(dropOnTopTooltip)); - JeiBlockIngredientUtil.addSlot( - builder, - RecipeIngredientRole.INPUT, - "crate", - 69, - 32, - 25, - 25, - ModBlocks.LARGE_CRATE.get() - ); - builder.addSlot(RecipeIngredientRole.OUTPUT, 128, 8) - .addItemStack(ModBlocks.SHULKER_CONTAINER.asStack()) - .addRichTooltipCallback((recipeSlotView, tooltip) -> tooltip.add(strikeTooltip)); - } - - @Override - public void createRecipeExtras( - IRecipeExtrasBuilder builder, ShulkerContainerUpgradeRecipe recipe, IFocusGroup focuses) { - JeiBlockIngredientUtil.suppressHoverOverlays(builder); - } - - @Override - public void draw( - ShulkerContainerUpgradeRecipe recipe, - IRecipeSlotsView recipeSlotsView, - GuiGraphics guiGraphics, - double mouseX, - double mouseY) { - float anvilYOffset = JeiRenderHelper.getAnvilAnimationOffset(timer); - RenderSupport.renderBlock( - guiGraphics, - Blocks.ANVIL.defaultBlockState(), - 81, - 22 + anvilYOffset, - 20, - 12, - RenderSupport.SINGLE_BLOCK); - RenderSupport.renderBlock( - guiGraphics, - ModBlocks.LARGE_CRATE.getDefaultState().setValue(LargeCrateBlock.HALF, Cube3x3PartHalf.MID_CENTER), - 81, - 44, - 10, - 8, - RenderSupport.SINGLE_BLOCK); - - arrowIn.draw(guiGraphics, 52, 30); - arrowOutputFromBelow.draw(guiGraphics, 94, 29); - - slotDefault.draw(guiGraphics, 8, 7); - slotDefault.draw(guiGraphics, 30, 7); - slotDefault.draw(guiGraphics, 127, 7); - } - - public static void registerRecipes(IRecipeRegistration registration) { - registration.addRecipes( - AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE, - ShulkerContainerUpgradeRecipe.getAllRecipes() - ); - } - - public static void registerRecipeCatalysts(IRecipeCatalystRegistration registration) { - AnvilCraftJeiPlugin.addAnvilProcessingCatalysts(registration, AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE); - registration.addRecipeCatalyst(ModBlocks.LARGE_CRATE.asStack(), AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE); - registration.addRecipeCatalyst(ModBlocks.SPACE_OVERCOMPRESSOR.asStack(), AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE); - registration.addRecipeCatalyst(Items.NETHERITE_BLOCK.getDefaultInstance(), AnvilCraftJeiPlugin.SHULKER_CONTAINER_UPGRADE); - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ContainerUpgradeRecipe.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ContainerUpgradeRecipe.java new file mode 100644 index 0000000000..42865913e5 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ContainerUpgradeRecipe.java @@ -0,0 +1,18 @@ +package dev.dubhe.anvilcraft.integration.jei.recipe; + +import com.google.common.collect.ImmutableList; + +public record ContainerUpgradeRecipe(Type type) { + + public enum Type { + CRATE_TO_CONTAINER, + CONTAINER_TO_STATION + } + + public static ImmutableList getAllRecipes() { + return ImmutableList.of( + new ContainerUpgradeRecipe(Type.CRATE_TO_CONTAINER), + new ContainerUpgradeRecipe(Type.CONTAINER_TO_STATION) + ); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/HyperdimensionStorageStationUpgradeRecipe.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/HyperdimensionStorageStationUpgradeRecipe.java deleted file mode 100644 index 1f686e1e73..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/HyperdimensionStorageStationUpgradeRecipe.java +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dubhe.anvilcraft.integration.jei.recipe; - -import com.google.common.collect.ImmutableList; - -public record HyperdimensionStorageStationUpgradeRecipe() { - public static ImmutableList getAllRecipes() { - return ImmutableList.of(new HyperdimensionStorageStationUpgradeRecipe()); - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ShulkerContainerUpgradeRecipe.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ShulkerContainerUpgradeRecipe.java deleted file mode 100644 index 9c8e1c5312..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/recipe/ShulkerContainerUpgradeRecipe.java +++ /dev/null @@ -1,9 +0,0 @@ -package dev.dubhe.anvilcraft.integration.jei.recipe; - -import com.google.common.collect.ImmutableList; - -public record ShulkerContainerUpgradeRecipe() { - public static ImmutableList getAllRecipes() { - return ImmutableList.of(new ShulkerContainerUpgradeRecipe()); - } -} \ No newline at end of file From 5b72c154326f7aad259f4c12a84ba99180bf1775 Mon Sep 17 00:00:00 2001 From: QiuShui1012 Date: Tue, 18 Aug 2026 12:35:04 +0800 Subject: [PATCH 14/15] fix: fix problems --- .../HyperdimensionStorageStationBlock.java | 9 +++++--- .../client/rpc/TerminalJeiStorageCache.java | 15 +++++++++++++ .../JeiBasicRecipeTransferHandlerMixin.java | 22 +++++++++---------- .../anvilcraft/rpc/StorageServerStub.java | 13 ++++++++++- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java index d4c6ed0269..fd3b6e6c41 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/container/storage/HyperdimensionStorageStationBlock.java @@ -146,9 +146,12 @@ public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, if (id != null) { Storages.get().remove(id); } - } else if (player.hasInfiniteMaterials()) { - // 非空容器:掉落含 STORAGE 引用的容器物品(与 tooltip 声明一致, - // 拾取后放回仍可访问其存储内容),对创造模式生效(生存/冒险模式破坏子部件后会正常触发主部件掉落)。 + } + // 手动生成含 STORAGE 引用的容器物品掉落(与 tooltip 声明一致,拾取后放回仍可访问其存储内容): + // 创造模式破坏从不产生原版战利品掉落;破坏子部件时其余 part 经 updateShape 逐个塌缩为空气, + // 不走产生掉落的破坏路径,且战利品表仅匹配主部件状态,因此这两种情况都不会自然掉落。 + // 生存/冒险模式直接破坏主部件时由原版战利品表兜底,此处跳过以免重复掉落。 + if (player.hasInfiniteMaterials() || !pos.equals(mainPos)) { LootParams.Builder builder = new LootParams.Builder(serverLevel) .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(mainPos)) .withParameter(LootContextParams.TOOL, player.getMainHandItem()) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java index ad7057274e..eb101823e3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/rpc/TerminalJeiStorageCache.java @@ -30,10 +30,24 @@ public final class TerminalJeiStorageCache { private static final Map> CACHE = new ConcurrentHashMap<>(); private static final Map TIMESTAMPS = new ConcurrentHashMap<>(); private static final Map>> PENDING = new ConcurrentHashMap<>(); + /** + * 客户端主线程的"JEI 补库中"标志,防止 transferRecipe 递归重入。 + * 断线时补库 RPC 可能永不完成(排队的 execute 任务被登出流程丢弃), + * 标志位会泄漏到下一次会话;由 {@link #clear()}(断线清理)统一复位。 + */ + private static final ThreadLocal RESTOCKING = ThreadLocal.withInitial(() -> false); private TerminalJeiStorageCache() { } + public static boolean isRestocking() { + return TerminalJeiStorageCache.RESTOCKING.get(); + } + + public static void setRestocking(boolean restocking) { + TerminalJeiStorageCache.RESTOCKING.set(restocking); + } + /** 玩家是否持有指向任意存储站的绑定终端。 */ public static @Nullable UUID boundStorage(Player player) { for (ItemStack stack : player.getInventory().items) { @@ -93,6 +107,7 @@ public static void clear() { TerminalJeiStorageCache.CACHE.clear(); TerminalJeiStorageCache.TIMESTAMPS.clear(); TerminalJeiStorageCache.PENDING.clear(); + TerminalJeiStorageCache.RESTOCKING.remove(); } private static @Nullable UUID storageOf(ItemStack stack) { diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java index d5d77fbf1a..74fc6d5d21 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiBasicRecipeTransferHandlerMixin.java @@ -12,7 +12,6 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -23,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import javax.annotation.Nullable; /** * 把终端的 JEI 转移配方逻辑注入 JEI 的 {@link BasicRecipeTransferHandler#transferRecipe}: @@ -46,9 +46,6 @@ public abstract class JeiBasicRecipeTransferHandlerMixin ANVILCRAFT_RESTOCKING = ThreadLocal.withInitial(() -> false); - @Inject(method = "transferRecipe", at = @At("HEAD"), cancellable = true) private void anvilcraft$restockOrAllow( C container, @@ -59,7 +56,7 @@ public abstract class JeiBasicRecipeTransferHandlerMixin cir ) { - if (!player.level().isClientSide() || ANVILCRAFT_RESTOCKING.get()) { + if (!player.level().isClientSide() || TerminalJeiStorageCache.isRestocking()) { return; } UUID storageId = TerminalJeiStorageCache.boundStorage(player); @@ -87,19 +84,20 @@ public abstract class JeiBasicRecipeTransferHandlerMixin TerminalJeiStorageCache.clear() 复位。 StorageTerminalClientStub.withdrawToInventory(storageId, missing).whenComplete((changed, error) -> Minecraft.getInstance().execute(() -> { try { - if (error != null) { - return; // 补库失败:不重试传输,由 JEI 原逻辑兜底 - } - // 补库完成:重试原传输逻辑(背包已有物品,getInventoryState 自然看到并生成真实转移) + // 无论补库成功与否都重试原传输逻辑:补库失败时背包可能已被部分补入 + // (服务端在超时/断线前可能已执行部分取出),原逻辑会按实际背包状态转移; + // 完全未补入时原逻辑返回"缺少材料"错误提示,而不是静默丢弃本次合成。 this.transferRecipe(container, recipe, recipeSlotsView, player, maxTransfer, true); } finally { - ANVILCRAFT_RESTOCKING.set(false); + TerminalJeiStorageCache.setRestocking(false); } }) ); diff --git a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java index 556dae3fb1..a58cff41f3 100644 --- a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java +++ b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java @@ -646,6 +646,13 @@ public static boolean terminalWithdrawToInventory( * 存储站在每次刷新时全量扫描造成服务端尖峰。 */ private static final int MAX_STORAGE_ITEMS = 512; + /** + * 单次扫描的最大槽位数。存储槽列表稀疏设计(槽位索引可能因历史删除产生空洞), + * 仅限制结果数会允许 512 种物品分散在极大槽位范围时深扫整个列表; + * 超过该深度后视为不再有效收集(缓存 15s 刷新,漏报仅影响 JEI "+" 可用性提示, + * 传输阶段仍由服务端按实际缺口校验)。 + */ + private static final int MAX_STORAGE_SCAN_SLOTS = 4096; @CallableParam(clazz = StorageServerStub.class, field = "ITEM_STACK_LIST_STREAM_CODEC") @RemoteCallable(validator = TerminalAccessValidator.class) @@ -660,7 +667,11 @@ public static List getStorageItems(UUID playerId, UUID storageId) { } UnlimitedItemStacksResourceHandler items = storageOp.get().getItems(); List result = new ArrayList<>(); - for (int slot = 0; slot < items.size() && result.size() < StorageServerStub.MAX_STORAGE_ITEMS; slot++) { + for (int slot = 0; + slot < items.size() + && slot < StorageServerStub.MAX_STORAGE_SCAN_SLOTS + && result.size() < StorageServerStub.MAX_STORAGE_ITEMS; + slot++) { long amount = items.getAmountAsLong(slot); if (amount <= 0) { continue; From 3d8e7c4c3dadd3f507bc4f895e61c9c4b5a007db Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Tue, 18 Aug 2026 12:48:55 +0800 Subject: [PATCH 15/15] =?UTF-8?q?refactor(jei):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84=E7=B1=BB=E5=88=AB=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=92=8C=E7=9B=B8=E5=85=B3=E8=AF=AD=E8=A8=80=E6=9D=A1?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了FluidMixingCategory和HyperdimensionStorageStationUpgradeCategory的导入 - 精简了en_us和en_ud语言文件中对应的类别描述 - 优化了fluid_mixing类别名称,删除了“Complex”修饰词 - 删除了hyperdimension_storage_station_upgrade类别及其子项的语言配置 --- src/generated/resources/assets/anvilcraft/lang/en_ud.json | 4 ---- src/generated/resources/assets/anvilcraft/lang/en_us.json | 4 ---- .../dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java | 3 --- 3 files changed, 11 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 861b527f61..bd649a6d85 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -852,11 +852,7 @@ "gui.anvilcraft.category.fast_cooking": "ᵷuᴉʞooƆ ʇsɐℲ", "gui.anvilcraft.category.fast_cooking.consume_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɯnsuoƆ", "gui.anvilcraft.category.fast_cooking.produce_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɔnpoɹԀ", - "gui.anvilcraft.category.fluid_mixing": "uoᴉʇɔɐǝᴚ pᴉnꞁℲ xǝꞁdɯoƆ", "gui.anvilcraft.category.fluid_mixing": "uoᴉʇɔɐǝᴚ pᴉnꞁℲ", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "ǝpɐɹᵷd∩ uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "ɹǝuᴉɐʇuoɔ ɹǝʞꞁnɥs ǝɥʇ oʇuo sɯǝʇᴉ ǝsǝɥʇ doɹᗡ", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "ɹǝuᴉɐʇuoɔ ǝɥʇ ǝpɐɹᵷdn oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇS", "gui.anvilcraft.category.item_compress": "ssǝɹdɯoƆ ɯǝʇI", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "sǝpoꞁdxǝ ǝsᴉʍɹǝɥʇo ⸵ɹoʇᴉɔɐdɐɔɹǝdnS ɐ ǝɔnpoɹd oʇ ǝɔuɐɥ% c0ϛ", "gui.anvilcraft.category.item_compress.supercapacitor.resin": "ɹǝdǝǝɹƆ pǝᵷɹɐɥɔ-ᵷuᴉuʇɥᵷᴉꞁ ɐ ᵷuᴉuᴉɐʇuoɔ ʞɔoꞁq uᴉsǝᴚ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 5c122275c6..e37e4f1147 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -852,11 +852,7 @@ "gui.anvilcraft.category.fast_cooking": "Fast Cooking", "gui.anvilcraft.category.fast_cooking.consume_fluid": "Consume: %1$d mB of %2$s", "gui.anvilcraft.category.fast_cooking.produce_fluid": "Produce: %1$d mB of %2$s", - "gui.anvilcraft.category.fluid_mixing": "Complex Fluid Reaction", "gui.anvilcraft.category.fluid_mixing": "Fluid Reaction", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade": "Hyperdimension Storage Station Upgrade", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.drop_on_top": "Drop these items onto the shulker container", - "gui.anvilcraft.category.hyperdimension_storage_station_upgrade.strike": "Strike with an anvil to upgrade the container", "gui.anvilcraft.category.item_compress": "Item Compress", "gui.anvilcraft.category.item_compress.supercapacitor.chance": "50% chance to produce a Supercapacitor; otherwise explodes", "gui.anvilcraft.category.item_compress.supercapacitor.resin": "Resin block containing a lightning-charged Creeper", diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java index d4244bcec7..f1b1dc4370 100644 --- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java +++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/AnvilCraftJeiPlugin.java @@ -18,16 +18,13 @@ import dev.dubhe.anvilcraft.integration.jei.category.ContainerUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.DecayCategory; import dev.dubhe.anvilcraft.integration.jei.category.EnergyWeaponCategory; -import dev.dubhe.anvilcraft.integration.jei.category.FluidMixingCategory; import dev.dubhe.anvilcraft.integration.jei.category.FluidReactionCategory; -import dev.dubhe.anvilcraft.integration.jei.category.HyperdimensionStorageStationUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.JewelCraftingCategory; import dev.dubhe.anvilcraft.integration.jei.category.MineralFountainCategory; import dev.dubhe.anvilcraft.integration.jei.category.MobTransformCategory; import dev.dubhe.anvilcraft.integration.jei.category.MultipleToOneSmithingCategory; import dev.dubhe.anvilcraft.integration.jei.category.PortalConversionCategory; import dev.dubhe.anvilcraft.integration.jei.category.ProceduralProcessCategory; -import dev.dubhe.anvilcraft.integration.jei.category.ShulkerContainerUpgradeCategory; import dev.dubhe.anvilcraft.integration.jei.category.SolidLiquidCategory; import dev.dubhe.anvilcraft.integration.jei.category.anvil.BlockCompressCategory; import dev.dubhe.anvilcraft.integration.jei.category.anvil.BlockCrushCategory;