Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/client/java/com/tcm/MineTale/MineTaleDataGen.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package com.tcm.MineTale;

import com.tcm.MineTale.datagen.ModBlockTagProvider;
import com.tcm.MineTale.datagen.ModLangProvider;
import com.tcm.MineTale.datagen.ModModelProvider;
import com.tcm.MineTale.datagen.ModRecipeProvider;
import com.tcm.MineTale.datagen.*;

import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;

public class MineTaleDataGen implements DataGeneratorEntrypoint {

/**
* Initialize a data pack and register the mod's data providers.
* Initialize a data pack and register the mod's data providers for data generation.
*
* Creates a data pack from the given Fabric data generator and adds the language
* and model providers so they will run during data generation.
* Registers language, model, recipe, block tag, and loot table providers so they
* will run as part of the Fabric data generation pack created from the given generator.
*
* @param fabricDataGenerator the Fabric data generator used to create the data pack
*/
Expand All @@ -26,5 +23,6 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
pack.addProvider(ModModelProvider::new);
pack.addProvider(ModRecipeProvider::new);
pack.addProvider(ModBlockTagProvider::new);
pack.addProvider(ModLootTableProvider::new);
}
}
17 changes: 14 additions & 3 deletions src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tcm.MineTale.datagen;

import com.jcraft.jorbis.Block;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove wrong com.jcraft.jorbis.Block import.

com.jcraft.jorbis is an Ogg Vorbis audio decoder library; its Block class is a DSP processing block and has nothing to do with Minecraft blocks. This import is unused in this file and should be removed.

🐛 Proposed fix
-import com.jcraft.jorbis.Block;
 import com.tcm.MineTale.registry.ModBlocks;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import com.jcraft.jorbis.Block;
import com.tcm.MineTale.registry.ModBlocks;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java` at line 3,
Remove the incorrect and unused import com.jcraft.jorbis.Block from the
ModBlockTagProvider file (the import at the top referencing Block) — it is
unrelated to Minecraft; delete that line and then re-run import
organizing/formatting to ensure only relevant imports remain in the
ModBlockTagProvider class.

import com.tcm.MineTale.registry.ModBlocks;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
Expand All @@ -20,14 +21,24 @@ public ModBlockTagProvider(FabricDataOutput output, CompletableFuture<HolderLook
}

/**
* Populates the BlockTags.LOGS tag with this mod's log blocks.
* Populate block tags with this mod's blocks.
*
* Registers each mod-defined log block so they are included in the game's LOGS tag mapping.
* Adds mod-defined blocks to relevant vanilla block tags (for example
* MINEABLE_WITH_PICKAXE, MINEABLE_WITH_AXE, and LOGS) so they are included
* in the game's tag mappings.
*
* @param provider a registry lookup provider used to resolve holders during tag population
* @param provider registry lookup provider used to resolve holders during tag population
*/
@Override
protected void addTags(HolderLookup.Provider provider) {
valueLookupBuilder(BlockTags.MINEABLE_WITH_PICKAXE)
.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)
.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2)
.add(ModBlocks.ARMORERS_WORKBENCH_BLOCK);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ARMORERS_WORKBENCH_BLOCK should be in MINEABLE_WITH_AXE, not MINEABLE_WITH_PICKAXE.

Per the ModBlocks snippet, ARMORERS_WORKBENCH_BLOCK is registered with BlockBehaviour.Properties.of().sound(SoundType.WOOD) — the same wood properties as CAMPFIRE_WORKBENCH_BLOCK and WORKBENCH_WORKBENCH_BLOCK, both of which are correctly placed in MINEABLE_WITH_AXE. Placing a wood-type block under the pickaxe tag is inconsistent and will give the wrong mining-speed bonus.

🐛 Proposed fix
 valueLookupBuilder(BlockTags.MINEABLE_WITH_PICKAXE)
         .add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)
-        .add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2)
-        .add(ModBlocks.ARMORERS_WORKBENCH_BLOCK);
+        .add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2);
 valueLookupBuilder(BlockTags.MINEABLE_WITH_AXE)
         .add(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK)
-        .add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK);
+        .add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK)
+        .add(ModBlocks.ARMORERS_WORKBENCH_BLOCK);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.add(ModBlocks.ARMORERS_WORKBENCH_BLOCK);
valueLookupBuilder(BlockTags.MINEABLE_WITH_PICKAXE)
.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)
.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2);
valueLookupBuilder(BlockTags.MINEABLE_WITH_AXE)
.add(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK)
.add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK)
.add(ModBlocks.ARMORERS_WORKBENCH_BLOCK);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java` at line
35, ARMORERS_WORKBENCH_BLOCK is incorrectly added to the pickaxe tag; update
ModBlockTagProvider so ARMORERS_WORKBENCH_BLOCK is added to the
MINEABLE_WITH_AXE tag instead of MINEABLE_WITH_PICKAXE: locate the
.add(ModBlocks.ARMORERS_WORKBENCH_BLOCK) call in the block tag registration (the
same area where CAMPFIRE_WORKBENCH_BLOCK and WORKBENCH_WORKBENCH_BLOCK are
placed), remove it from the MINEABLE_WITH_PICKAXE group and add it to the
MINEABLE_WITH_AXE group to match its wood SoundType properties.

valueLookupBuilder(BlockTags.MINEABLE_WITH_AXE)
.add(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK)
.add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK);

valueLookupBuilder(BlockTags.LOGS)
.add(ModBlocks.AMBER_LOG)
.add(ModBlocks.BAMBOO_LOG)
Expand Down
177 changes: 177 additions & 0 deletions src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.tcm.MineTale.datagen;

import com.tcm.MineTale.block.workbenches.AbstractWorkbench;
import com.tcm.MineTale.registry.ModBlocks;
import com.tcm.MineTale.registry.ModItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.advancements.criterion.StatePropertiesPredicate;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer;
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;

import java.util.concurrent.CompletableFuture;

public class ModLootTableProvider extends FabricBlockLootTableProvider {
/**
* Creates a ModLootTableProvider used to generate the mod's block loot tables.
*
* Initializes the provider with the data output target and a future registry lookup used to resolve game registries during loot table generation.
*
* @param dataOutput the data output target for generated data
* @param registryLookup a future that provides a HolderLookup.Provider for resolving registries needed while generating loot tables
*/
public ModLootTableProvider(FabricDataOutput dataOutput, CompletableFuture<HolderLookup.Provider> registryLookup) {
super(dataOutput, registryLookup);
}

/**
* Registers loot tables for the mod's workbench blocks.
*
* Each added loot table causes the block to drop itself (one item) only when the block's state
* matches the required DoubleBlockHalf (LOWER) and ChestType (LEFT or SINGLE) for that block,
* and the drop is subject to explosion survival/decay.
*/
@Override
public void generate() {
///Block Drops Itself
this.add(ModBlocks.ARMORERS_WORKBENCH_BLOCK,
LootTable.lootTable() // Use the static factory method to start the builder
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(ModBlocks.ARMORERS_WORKBENCH_BLOCK))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.ARMORERS_WORKBENCH_BLOCK)
.setProperties(StatePropertiesPredicate.Builder.properties()
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
.hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT)
)
)
.when(ExplosionCondition.survivesExplosion())
)
);

this.add(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK,
LootTable.lootTable() // Use the static factory method to start the builder
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK)
.setProperties(StatePropertiesPredicate.Builder.properties()
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
.hasProperty(AbstractWorkbench.TYPE, ChestType.SINGLE)
)
)
.when(ExplosionCondition.survivesExplosion())
)
);

this.add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK,
LootTable.lootTable() // Use the static factory method to start the builder
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.WORKBENCH_WORKBENCH_BLOCK)
.setProperties(StatePropertiesPredicate.Builder.properties()
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
.hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT)
)
Comment on lines +88 to +92
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find and examine AbstractWorkbench to understand HALF property registration
fd -e java "AbstractWorkbench" --type f -exec cat -n {} +

Repository: CodeMonkeysMods/MineTale

Length of output: 298


🏁 Script executed:

# Find WORKBENCH_WORKBENCH_BLOCK definition
fd -e java . | xargs rg -l "WORKBENCH_WORKBENCH_BLOCK" | head -20

Repository: CodeMonkeysMods/MineTale

Length of output: 535


🏁 Script executed:

# Check ModBlocks to see WORKBENCH_WORKBENCH_BLOCK registration
fd -e java "ModBlocks" --type f -exec cat -n {} +

Repository: CodeMonkeysMods/MineTale

Length of output: 298


🏁 Script executed:

# Find AbstractWorkbench file
fd "AbstractWorkbench" --type f

Repository: CodeMonkeysMods/MineTale

Length of output: 314


🏁 Script executed:

# Read ModBlocks.java to see WORKBENCH_WORKBENCH_BLOCK definition
cat src/main/java/com/tcm/MineTale/registry/ModBlocks.java

Repository: CodeMonkeysMods/MineTale

Length of output: 30000


🏁 Script executed:

# Check the size of ModLootTableProvider to read it properly
wc -l src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java

Repository: CodeMonkeysMods/MineTale

Length of output: 138


🏁 Script executed:

# Read AbstractWorkbench to understand HALF property registration
cat src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java

Repository: CodeMonkeysMods/MineTale

Length of output: 18822


🏁 Script executed:

# Read WorkbenchWorkbench to verify IS_TALL/IS_WIDE configuration
cat src/main/java/com/tcm/MineTale/block/workbenches/WorkbenchWorkbench.java

Repository: CodeMonkeysMods/MineTale

Length of output: 4014


🏁 Script executed:

# Read ModLootTableProvider fully
cat src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java

Repository: CodeMonkeysMods/MineTale

Length of output: 7984


Remove spurious HALF predicate from WORKBENCH_WORKBENCH_BLOCK — the condition is redundant but harmless.

While the HALF property is indeed registered on WORKBENCH_WORKBENCH_BLOCK (AbstractWorkbench always adds it regardless of IS_TALL), the condition checking for HALF = LOWER is redundant. Since IS_TALL = false, the block can only ever have HALF = LOWER and will never have an upper half. The predicate will work, but it adds unnecessary noise to the loot table. Simplify by keeping only the TYPE = LEFT condition, which is the only property that varies for a wide-only multi-block.

Suggested cleanup
         this.add(ModBlocks.WORKBENCH_WORKBENCH_BLOCK,
             LootTable.lootTable()
                 .withPool(LootPool.lootPool()
                     .setRolls(ConstantValue.exactly(1.0F))
                     .add(LootItem.lootTableItem(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
                     .when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.WORKBENCH_WORKBENCH_BLOCK)
                         .setProperties(StatePropertiesPredicate.Builder.properties()
-                            .hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
                             .hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT)
                         )
                     )
                     .when(ExplosionCondition.survivesExplosion())
                 )
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java` around
lines 73 - 77, In ModLootTableProvider, remove the redundant HALF property
predicate from the LootItemBlockStatePropertyCondition for
ModBlocks.WORKBENCH_WORKBENCH_BLOCK: delete the
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER) call in the
StatePropertiesPredicate.Builder so only the varying property
.hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT) remains; this keeps the
loot condition accurate and removes unnecessary noise while leaving
StatePropertiesPredicate.Builder and the WORKBENCH_WORKBENCH_BLOCK condition
intact.

)
.when(ExplosionCondition.survivesExplosion())
)
);

this.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1,
LootTable.lootTable() // Use the static factory method to start the builder
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)
.setProperties(StatePropertiesPredicate.Builder.properties()
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
.hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT)
)
)
.when(ExplosionCondition.survivesExplosion())
)
);

this.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2,
LootTable.lootTable() // Use the static factory method to start the builder
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1.0F))
.add(LootItem.lootTableItem(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2)
.setProperties(StatePropertiesPredicate.Builder.properties()
.hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER)
.hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT)
)
)
.when(ExplosionCondition.survivesExplosion())
)
);
}


/// For Ore Drops
/**
* Create a loot table builder that drops the specified item in multiple quantities with Silk Touch, Fortune bonus, and explosion decay applied.
*
* @param drop the source block used for Silk Touch dispatch and explosion-decay context
* @param item the item to drop from the ore
* @return a LootTable.Builder that drops `item` in a base count between 2 and 5, augmented by the Fortune enchantment, with Silk Touch handling and explosion decay applied
*/
public LootTable.Builder AverageOreDrops(Block drop, Item item) {
HolderLookup.RegistryLookup<Enchantment> impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT);
return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder<?>)
LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(UniformGenerator.between(2, 5))))
.apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE)))));
}

/**
* Creates a loot table builder for an ore that yields the specified item with Silk Touch and Fortune handling.
*
* The table gives a base drop count of exactly 1 (before Fortune), increases the count with Fortune, returns the
* ore block when mined with Silk Touch, and applies explosion decay to the drop.
*
* @param drop the ore block (returned when Silk Touch is used)
* @param item the item to drop when the ore is mined without Silk Touch
* @return a LootTable.Builder configured to drop the specified item with Fortune bonuses, Silk Touch dispatch,
* and explosion decay
*/
public LootTable.Builder SingleOreDrops(Block drop, Item item) {
HolderLookup.RegistryLookup<Enchantment> impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT);
return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder<?>)
LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(ConstantValue.exactly(1))))
.apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE)))));
}

/**
* Builds a loot table for a "light" ore block that supports Silk Touch, Fortune bonuses, and explosion decay.
*
* @param drop the ore block whose loot table is being created
* @param item the item to drop from the ore when not Silk Touched
* @return a LootTable.Builder that drops {@code item} in quantities of 1–2 (before Fortune), applies Fortune bonus,
* dispatches to Silk Touch drops when applicable, and respects explosion decay
*/
public LootTable.Builder LightOreDrops(Block drop, Item item) {
HolderLookup.RegistryLookup<Enchantment> impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT);
return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder<?>)
LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 2))))
.apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE)))));
}
}
16 changes: 14 additions & 2 deletions src/main/java/com/tcm/MineTale/MineTale.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tcm.MineTale;

import com.tcm.MineTale.util.ModLootTableModifiers;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
Expand Down Expand Up @@ -72,6 +73,8 @@ public void onInitialize() {

RecipeSynchronization.synchronizeRecipeSerializer(ModRecipes.FURNACE_SERIALIZER);

ModLootTableModifiers.modifyLootTables();

// Register the payload type and codec so the game knows how to handle it
PayloadTypeRegistry.playC2S().register(CraftRequestPayload.TYPE, CraftRequestPayload.CODEC);

Expand Down Expand Up @@ -129,10 +132,19 @@ public void onInitialize() {
});
});

LOGGER.info("Hello Fabric world!");
LOGGER.info("MineTale Loaded!");
}

private boolean hasIngredients(ServerPlayer player, WorkbenchRecipe recipe) {
/**
* Determines whether the player (and nearby pullable chests, if the workbench allows) collectively contain all item ingredients required by the given workbench recipe.
*
* This check requires the player's open container to be an AbstractWorkbenchContainerMenu; if it is not, the method returns `false`. It examines the player's non-equipment inventory and, when the workbench permits pulling, the contents of nearby inventories. Each recipe ingredient must be satisfied by a distinct matching item instance from those inventories.
*
* @param player the server player whose inventories are checked
* @param recipe the workbench recipe whose ingredient requirements are being validated
* @return `true` if all ingredients of the recipe can be satisfied from the player and allowed nearby inventories, `false` otherwise
*/
private boolean hasIngredients(ServerPlayer player, WorkbenchRecipe recipe) {
if (!(player.containerMenu instanceof AbstractWorkbenchContainerMenu menu)) return false;
AbstractWorkbenchEntity be = menu.getBlockEntity();

Expand Down
Loading