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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"

// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"

}

Expand Down
14 changes: 7 additions & 7 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.jvmargs=-Xmx4G
org.gradle.parallel=true

# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
Expand All @@ -8,13 +8,13 @@ org.gradle.configuration-cache=false
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.11
loader_version=0.18.2
loom_version=1.14-SNAPSHOT
loader_version=0.18.4
loom_version=1.15-SNAPSHOT

# Fabric API
fabric_api_version=0.141.2+1.21.11
Comment on lines +11 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Fabric loader version exists
echo "=== Checking Fabric Loader 0.18.4 ==="
curl -s "https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml" | grep -o '<version>[^<]*</version>' | grep "0.18.4" || echo "Version 0.18.4 not found in metadata"

# Verify Fabric API version exists for MC 1.21.11
echo "=== Checking Fabric API 0.141.2+1.21.11 ==="
curl -s "https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api/maven-metadata.xml" | grep -o '<version>[^<]*</version>' | grep "0.141.2+1.21.11" || echo "Version 0.141.2+1.21.11 not found in metadata"

# Check for fabric-loom releases
echo "=== Checking Fabric Loom versions ==="
curl -s "https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml" | grep -o '<version>[^<]*</version>' | tail -20

Repository: CodeMonkeysMods/MineTale

Length of output: 919


Consider replacing loom_version=1.15-SNAPSHOT with a stable release.

All versions are available in Maven repositories:

  • loader_version=0.18.4
  • fabric_api_version=0.141.2+1.21.11
  • loom_version=1.15-SNAPSHOT exists, but stable versions 1.15.1, 1.15.2, and 1.15.3 are also available

Using the SNAPSHOT version introduces build instability unnecessarily when stable releases are available. Pin to 1.15.3 (or the latest stable release appropriate for your build).

🤖 Prompt for AI Agents
In `@gradle.properties` around lines 11 - 15, Replace the unstable SNAPSHOT value
for loom_version with a stable release: update the property loom_version from
"1.15-SNAPSHOT" to "1.15.3" (or another chosen stable 1.15.x), keeping
loader_version and fabric_api_version as-is; ensure the change is made where the
gradle property loom_version is defined so builds use the pinned stable loom
release.


# Mod Properties
mod_version=1.0.0
maven_group=com.tcm
archives_base_name=MineTale

# Dependencies
fabric_version=0.139.4+1.21.11
archives_base_name=MineTale
9 changes: 8 additions & 1 deletion src/client/java/com/tcm/MineTale/MineTaleClient.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.tcm.MineTale;

import com.tcm.MineTale.block.workbenches.screen.FurnaceWorkbenchScreen;
import com.tcm.MineTale.registry.ModMenuTypes;

import net.fabricmc.api.ClientModInitializer;
import net.minecraft.client.gui.screens.MenuScreens;

public class MineTaleClient implements ClientModInitializer {
/**
* Registers the screen factory for the furnace workbench menu so the client can create FurnaceWorkbenchScreen instances for that menu type.
*/
@Override
public void onInitializeClient() {
// This entrypoint is suitable for setting up client-specific logic, such as rendering.
MenuScreens.register(ModMenuTypes.FURNACE_WORKBENCH_MENU, FurnaceWorkbenchScreen::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.tcm.MineTale.block.workbenches.screen;

import com.tcm.MineTale.block.workbenches.menu.FurnaceWorkbenchMenu;

import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.network.chat.Component;

public class FurnaceWorkbenchScreen extends AbstractContainerScreen<FurnaceWorkbenchMenu> {
private static final Identifier TEXTURE =
// Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/furnace_workbench.png");
Identifier.withDefaultNamespace("textures/gui/container/furnace.png");
Comment on lines +3 to +15
Copy link
Copy Markdown
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:

#!/bin/bash
# Find the FurnaceWorkbenchScreen file
find . -name "FurnaceWorkbenchScreen.java" -type f

Repository: CodeMonkeysMods/MineTale

Length of output: 155


🏁 Script executed:

#!/bin/bash
# Search for furnace_workbench texture asset
find . -name "*furnace_workbench*" -type f 2>/dev/null

Repository: CodeMonkeysMods/MineTale

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for MineTale class with MOD_ID constant
find . -name "MineTale.java" -type f

Repository: CodeMonkeysMods/MineTale

Length of output: 114


🏁 Script executed:

#!/bin/bash
# Look at the FurnaceWorkbenchMenu to verify the layout
find . -name "FurnaceWorkbenchMenu.java" -type f

Repository: CodeMonkeysMods/MineTale

Length of output: 149


🏁 Script executed:

#!/bin/bash
cat -n ./src/client/java/com/tcm/MineTale/block/workbenches/screen/FurnaceWorkbenchScreen.java

Repository: CodeMonkeysMods/MineTale

Length of output: 1937


🏁 Script executed:

#!/bin/bash
cat -n ./src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java

Repository: CodeMonkeysMods/MineTale

Length of output: 6355


🏁 Script executed:

#!/bin/bash
cat -n ./src/main/java/com/tcm/MineTale/MineTale.java | head -50

Repository: CodeMonkeysMods/MineTale

Length of output: 1384


🏁 Script executed:

#!/bin/bash
# Double-check for texture assets in the correct asset directory structure
find . -path "*/assets/*/textures/gui/container/*furnace*" -type f 2>/dev/null

Repository: CodeMonkeysMods/MineTale

Length of output: 50


Custom GUI texture asset must be created to match the 2x2 output slot layout.

The menu defines a 2x2 output grid (slots at coordinates 116/134 × 21/39), but the vanilla furnace texture only has a single output slot. Either create the custom texture asset at src/main/resources/assets/minetale/textures/gui/container/furnace_workbench.png or use a different existing texture. The commented line should only be uncommented once the texture file is in place. Remove the unused import of RenderPipelines while you're at it.

🤖 Prompt for AI Agents
In
`@src/client/java/com/tcm/MineTale/block/workbenches/screen/FurnaceWorkbenchScreen.java`
around lines 3 - 15, The TEXTURE constant in FurnaceWorkbenchScreen points to
the vanilla single-output furnace texture but your menu implements a 2x2 output
grid; add a matching custom GUI texture named furnace_workbench.png in your
mod's resources, change TEXTURE back to use
Identifier.fromNamespaceAndPath(MineTale.MOD_ID,
"textures/gui/container/furnace_workbench.png") (uncomment that line) only after
the texture exists, and remove the unused import RenderPipelines; also ensure
the new texture layout places the four output slots at the menu's coordinates
(116/134 × 21/39).


/**
* Creates a new furnace workbench screen for the given menu, player inventory, and title.
*
* @param menu the container menu that provides slots and syncs state for this screen
* @param inventory the player's inventory to display and interact with
* @param title the title component shown at the top of the screen
*/
public FurnaceWorkbenchScreen(FurnaceWorkbenchMenu menu, Inventory inventory, Component title) {
super(menu, inventory, title);
}

/**
* Initializes the screen and centers the title horizontally by setting {@code titleLabelX}.
*/
@Override
protected void init() {
super.init();
this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2;
}

/**
* Draws the furnace workbench background texture onto the screen.
*
* @param guiGraphics the graphics context used for drawing
* @param f partial tick time used for interpolation
* @param i current mouse x position
* @param j current mouse y position
*/
protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
int k = this.leftPos;
int l = this.topPos;
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
}

/**
* Renders the furnace workbench screen, drawing its background, contents, and tooltips.
*
* @param graphics the graphics context used for rendering
* @param mouseX the current mouse X coordinate
* @param mouseY the current mouse Y coordinate
* @param delta the frame time delta (partial tick) used for animated rendering
*/
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
renderBackground(graphics, mouseX, mouseY, delta);
super.render(graphics, mouseX, mouseY, delta);
renderTooltip(graphics, mouseX, mouseY);
}
Comment on lines +1 to +64
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd 'FurnaceWorkbenchScreen.java'

Repository: CodeMonkeysMods/MineTale

Length of output: 151


🏁 Script executed:

rg -n --type=java 'FurnaceWorkbenchScreen' -C2

Repository: CodeMonkeysMods/MineTale

Length of output: 2288


🏁 Script executed:

# Also check for source set configuration files
fd -e 'gradle' -e 'pom.xml' | head -20

Repository: CodeMonkeysMods/MineTale

Length of output: 96


🏁 Script executed:

cat build.gradle

Repository: CodeMonkeysMods/MineTale

Length of output: 2416


🏁 Script executed:

# Check if there are any other references to FurnaceWorkbenchScreen outside client code
rg 'FurnaceWorkbenchScreen' --type=java -l

Repository: CodeMonkeysMods/MineTale

Length of output: 204


Move client-only screen to src/client/java (architectural violation with split source sets).

FurnaceWorkbenchScreen uses client-only APIs (AbstractContainerScreen, GuiGraphics, RenderPipelines) but is placed in src/main/java. The build.gradle declares separate sourceSets.main and sourceSets.client, so client code must live in src/client/java. The class is already referenced only from the client entrypoint (MineTaleClient.java), making it safe to move.

🧰 Tools
🪛 GitHub Actions: build

[error] 1-39: Compilation failed due to missing Minecraft GUI classes and unresolved symbols. Errors include: cannot find symbol for net.minecraft.client.gui.GuiGraphics, net.minecraft.client.gui.screens.inventory.AbstractContainerScreen, net.minecraft.client.renderer.RenderPipelines, and many dependent members (e.g., GuiGraphics, AbstractContainerScreen, RenderPipelines, title/width/font fields). This indicates missing or incompatible Minecraft/LibGraft/Mixin APIs in the project setup.

🤖 Prompt for AI Agents
In
`@src/main/java/com/tcm/MineTale/block/workbenches/screen/FurnaceWorkbenchScreen.java`
around lines 1 - 44, FurnaceWorkbenchScreen uses client-only APIs
(AbstractContainerScreen, GuiGraphics, RenderPipelines) but lives in main; move
the class file for FurnaceWorkbenchScreen into the client source set
(src/client/java) keeping the same package declaration and imports, so the
compiler will exclude it from server/runtime builds; ensure the existing
reference in MineTaleClient.java still points to the same package/class and
remove the file from src/main/java to avoid duplicate symbol errors.

}
8 changes: 8 additions & 0 deletions src/main/java/com/tcm/MineTale/MineTale.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.tcm.MineTale.registry.ModEntities;
import com.tcm.MineTale.registry.ModEntityDataSerializers;
import com.tcm.MineTale.registry.ModItems;
import com.tcm.MineTale.registry.ModMenuTypes;

public class MineTale implements ModInitializer {
public static final String MOD_ID = "minetale";
Expand All @@ -19,10 +20,17 @@ public class MineTale implements ModInitializer {
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

/**
* Initializes and registers the mod's game content and subsystems during Fabric startup.
*
* <p>Triggers initialization for blocks, block entities, menu types, entities, items, and entity
* data serializers so they are registered with the game before gameplay begins.</p>
*/
@Override
public void onInitialize() {
ModBlocks.initialize();
ModBlockEntities.initialize();
ModMenuTypes.initialize();
ModEntities.initialize();
ModItems.initialize();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
Expand All @@ -18,11 +21,15 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.*;
import net.minecraft.world.phys.BlockHitResult;

import org.jetbrains.annotations.Nullable;

import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;

import java.util.function.Supplier;

public abstract class AbstractWorkbench<E extends BlockEntity> extends BaseEntityBlock {
public abstract class AbstractWorkbench<E extends AbstractWorkbenchEntity> extends BaseEntityBlock {
public static final EnumProperty<Direction> FACING = HorizontalDirectionalBlock.FACING;
public static final EnumProperty<DoubleBlockHalf> HALF = BlockStateProperties.DOUBLE_BLOCK_HALF;
public static final EnumProperty<ChestType> TYPE = BlockStateProperties.CHEST_TYPE;
Expand Down Expand Up @@ -127,8 +134,98 @@ private boolean isCompatiblePart(BlockState current, BlockState neighbor) {
return true;
}

/**
* Registers the block state properties used by this block.
*
* @param builder the state builder to populate with this block's properties (`FACING`, `HALF`, `TYPE`)
*/
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, HALF, TYPE);
}

/**
* Create the block entity for the primary anchor of a multipart workbench.
*
* @param pos the position where the block entity would be created
* @param state the block state at the position
* @return the new block entity when this block is the primary anchor (lower half with TYPE `LEFT` or `SINGLE`), or `null` if this block is a secondary part
*/
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
// We only want a Block Entity at the 'primary' anchor point of the structure.
// For 1x1: HALF=LOWER, TYPE=SINGLE
// For 2x1: HALF=LOWER, TYPE=LEFT
// For 2x2: HALF=LOWER, TYPE=LEFT

boolean isLower = state.getValue(HALF) == DoubleBlockHalf.LOWER;
ChestType type = state.getValue(TYPE);

// If it's the RIGHT side of a wide block, or the UPPER half of a tall block, return null.
if (isLower && (type == ChestType.LEFT || type == ChestType.SINGLE)) {
return this.blockEntityType.get().create(pos, state);
}

return null;
}

/**
* Open the workbench menu for the block's master part when a player interacts without an item.
*
* @param state the block state at the clicked position
* @param level the level in which the interaction occurs
* @param pos the position of the block that was interacted with
* @param player the player performing the interaction
* @param hitResult details about the hit (hit position and face)
* @return {@code InteractionResult.CONSUME} if a menu was opened on the server, {@code InteractionResult.SUCCESS} on the client, {@code InteractionResult.PASS} otherwise
*/
@Override
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
if (level.isClientSide()) {
return InteractionResult.SUCCESS;
}

// 1. Find the Master Position (Bottom-Left)
BlockPos masterPos = getMasterPos(state, pos);
BlockEntity blockEntity = level.getBlockEntity(masterPos);

// 2. Check if the Master block has the MenuProvider trait
if (blockEntity instanceof MenuProvider menuProvider) {
// 3. Open the Screen (this triggers the ScreenHandler/Menu)
player.openMenu(menuProvider);
return InteractionResult.CONSUME;
}

return InteractionResult.PASS;
}

/**
* Compute the master (bottom-left) anchor position for this workbench block.
*
* The master is the block that serves as the primary anchor for multi-block
* behavior and block-entity placement: if this block is the upper half the
* master is one block below; if this block is the right-side part the master
* is one block to the left relative to the block's facing.
*
* @param state the block state used to determine HALF, TYPE, and FACING
* @param pos the current block position
* @return the position of the master (bottom-left) block for this workbench
*/
public BlockPos getMasterPos(BlockState state, BlockPos pos) {
BlockPos master = pos;
Direction facing = state.getValue(FACING);

// Move down if we are the upper half
if (state.getValue(HALF) == DoubleBlockHalf.UPPER) {
master = master.below();
}

// Move left if we are the right side (relative to facing)
if (state.getValue(TYPE) == ChestType.RIGHT) {
master = master.relative(facing.getCounterClockWise());
}

return master;
}
}
Loading