Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamped the check for side invisibility. #51

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Fellteros
Copy link

@Fellteros Fellteros commented Jan 25, 2025

Hi there,
recently I have been trying to find an easy way to make the sides of glass blocks invisible. Since I've taken some inspiration from your mod before, I thought I'd return the favor and provide you with my solution (see the changed files).

    protected boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction) {
        VoxelShape stateCullingShape = state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN);
        VoxelShape stateFromCullingShape = stateFrom.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN);
        return VoxelShapes.isSideCovered(stateCullingShape, stateFromCullingShape, direction);
    }

Here's how it works: We get two voxel shapes, from both blocks we are checking. Since there are no BlockView & BlockPos parameters, we use an instance of EmptyBlockView and BlockPos.ORIGIN. Then we use a method from the VoxelShapes class, that checks if two adjacent faces are covered. It's as simple as that.

It should work in all cases, if not, please notify me.

There's no need to credit me, though I would be grateful if you do.

@CLAassistant
Copy link

CLAassistant commented Jan 25, 2025

CLA assistant check
All committers have signed the CLA.

Copy link

coderabbitai bot commented Jan 25, 2025

Walkthrough

The pull request introduces significant refactoring to the glass-related block classes in the Wurst Client project. The primary changes involve modifying the inheritance structure, simplifying visibility logic, and streamlining method implementations across multiple glass block types. The modifications affect GlassSlabBlock, GlassStairsBlock, StainedGlassSlabBlock, StainedGlassStairsBlock, TintedGlassSlabBlock, TintedGlassStairsBlock, and their corresponding mixin classes. The changes focus on consolidating invisibility checks using VoxelShapes.isSideCovered, removing redundant methods, and creating a more unified approach to handling glass block interactions and rendering. The final modifier has been removed from certain classes to allow for subclassing, and the visibility-checking methods have been updated to enhance clarity and maintainability.

Possibly related PRs

Suggested reviewers

  • Alexander01998
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (9)
src/main/java/net/wurstclient/glass/GlassSlabBlock.java (1)

32-42: Consolidate multiple if checks into a unified approach.

Currently, the method checks each related block type individually. Consider a shared property or a single condition to handle all glass-like blocks. This avoids repetitive if statements and simplifies maintenance.

 public boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction)
 {
-    if(stateFrom.getBlock() == Blocks.GLASS)
-        return true;
-
-    if(stateFrom.getBlock() == this && isInvisible(state, stateFrom, direction))
-        return true;
-
-    if(stateFrom.getBlock() == MoGlassBlocks.GLASS_STAIRS && isInvisible(state, stateFrom, direction))
-        return true;
+    if (isGlassVariant(stateFrom.getBlock()) && isInvisible(state, stateFrom, direction))
+        return true;

     return super.isSideInvisible(state, stateFrom, direction);
 }

Add a helper method, isGlassVariant(Block block), to handle all glass-like blocks comprehensively.

src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (1)

39-40: Avoid code duplication when dealing with similarly typed blocks.

Calling isInvisible for slabs is valid, but multiple repeated checks for each block type can bloat the code. Consider a single method or shared helper to unify these checks.

src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (1)

33-39: Consider making the code more concise and consistent.

The spacing between conditions is inconsistent. Consider this more concise version:

-		if(stateFrom.getBlock() == this && isInvisible(state, stateFrom, direction))
-			return true;
-			
-		if(stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_STAIRS && isInvisible(state, stateFrom, direction))
-			return true;
-			
+		if(isInvisible(state, stateFrom, direction)
+			&& (stateFrom.getBlock() == this 
+				|| stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_STAIRS))
+			return true;
+
src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (1)

39-40: Consider merging these checks for maintainability.
The repeated pattern for checking GLASS_SLAB and then calling isInvisible may be consolidated with the checks in lines 42-43 to reduce duplication:

- if(stateFrom.getBlock() == MoGlassBlocks.GLASS_SLAB && isInvisible(state, stateFrom, direction))
-     return true;
- if(stateFrom.getBlock() == MoGlassBlocks.GLASS_STAIRS && isInvisible(state, stateFrom, direction))
-     return true;
+ if ((stateFrom.getBlock() == MoGlassBlocks.GLASS_SLAB
+       || stateFrom.getBlock() == MoGlassBlocks.GLASS_STAIRS)
+     && isInvisible(state, stateFrom, direction))
+ {
+     return true;
+ }
src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (1)

60-66: Geometry-based check is more robust.

Using getOutlineShape and VoxelShapes.isSideCovered is a cleaner, more scalable mechanism than type checks for slabs/stairs. Consider caching or reusing shapes if performance becomes an issue in heavily populated worlds.

src/main/java/net/wurstclient/glass/GlassStairsBlock.java (1)

32-42: Replace repetitive checks with a combined logic.

You have multiple checks for specific block types (GLASS_SLAB and this). Combining them into a single conditional block can reduce duplication and improve readability.

Consider applying this diff:

 public boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction)
 {
   if (stateFrom.getBlock() == Blocks.GLASS)
     return true;
 
-  if(stateFrom.getBlock() == MoGlassBlocks.GLASS_SLAB)
-    if(isInvisible(state, stateFrom, direction))
-      return true;
-
-  if(stateFrom.getBlock() == this)
-    if(isInvisible(state, stateFrom, direction))
-      return true;
+  if (stateFrom.getBlock() == MoGlassBlocks.GLASS_SLAB
+      || stateFrom.getBlock() == this)
+  {
+    if (isInvisible(state, stateFrom, direction))
+      return true;
+  }
 
   return super.isSideInvisible(state, stateFrom, direction);
 }
src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (2)

27-31: Immediate invisibility check for vanilla tinted glass.
Returning true straightaway for Blocks.TINTED_GLASS makes sense to handle any direct adjacency to vanilla tinted glass. If you anticipate adding more tinted variants, consider consolidating these checks into a shared helper method for easier maintainability.


32-37: Unify repeated checks for tinted glass blocks.
These conditions for MoGlassBlocks.TINTED_GLASS_SLAB, plus the check for this class, seem modular, but could be refactored to reduce repetition. For example, a single method could handle the invisibility logic for the entire tinted glass family.

src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (1)

47-53: Fix method parameter formatting and approve implementation.

The new geometric approach using VoxelShapes.isSideCovered is a more reliable way to check visibility. However, there's inconsistent indentation in the method parameters.

Apply this formatting fix:

-	private boolean isInvisible(BlockState state,
-								BlockState stateFrom, Direction direction)
+	private boolean isInvisible(BlockState state, BlockState stateFrom,
+		Direction direction)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24bc010 and 980c654.

📒 Files selected for processing (9)
  • src/main/java/net/wurstclient/glass/GlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/GlassStairsBlock.java (1 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (1 hunks)
  • src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (3 hunks)
  • src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (2 hunks)
🔇 Additional comments (37)
src/main/java/net/wurstclient/glass/GlassSlabBlock.java (3)

21-21: No issues with the new import.

The import of EmptyBlockView is necessary for shape rendering logic. Good use of the built-in singleton.


23-23: Removal of final is understandable but ensure consistency across the hierarchy.

Converting GlassSlabBlock from final to non-final signals subclass intent. Please verify that any subclasses correctly override the new isInvisible method.


46-49: Verify the usage of EmptyBlockView for shape calculation.

Using EmptyBlockView.INSTANCE and a fixed position (BlockPos.ORIGIN) may not account for position-specific shape differences. This is likely acceptable for glass side invisibility, but confirm that no block-level orientation or context is lost.

If necessary, consider using the actual BlockView and block coordinates if future features require more complex shape calculations.

src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (6)

18-18: Switching from StairsBlock to GlassStairsBlock looks good.

This aligns with the unified approach to handle glass rendering logic, reducing duplication across classes. Marking as approved.


25-25: Constructor call simplified.

Removing the previous block state parameter is consistent with inheriting from GlassStairsBlock; no issues observed.


36-36: Same-colored stained glass is correctly marked invisible.

Ensuring visibility checks on matching stained glass color is appropriate and matches the user expectation.


43-43: Verify reference to this instead of color matching.

Using blockFrom == this might be acceptable here, but if you intend to allow multiple block variants that share the same color or logic, comparing classes or color attributes might be more flexible.


48-48: No content change.

No new code or comment here.


56-56: Color getter is straightforward.

Method is concise and correctly returns the stored color.

src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (3)

Line range hint 19-25: LGTM! Clean inheritance structure.

The class properly extends GlassSlabBlock and maintains immutability with the final modifier. The constructor implementation is correct and minimal.


28-40: LGTM! Clear and logical visibility checks.

The implementation correctly handles all three cases:

  1. Full tinted glass blocks
  2. Tinted glass slabs
  3. Tinted glass stairs

The reuse of the parent's isInvisible method for complex shape checks is a good practice.


Line range hint 42-60: LGTM! Correct light and opacity handling.

The methods properly implement the expected behavior for tinted glass:

  • Maximum ambient occlusion light level
  • Non-transparent block
  • Correct opacity values for single and double slabs
src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (6)

11-11: Good use of the @Unique annotation.
This annotation helps ensure that the declared element doesn’t collide with existing members in the class being mixed into.


17-17: These imports align well with the introduced culling logic.
All four imports (BlockPos, VoxelShape, VoxelShapes, and EmptyBlockView) are necessary for the new shape-based side invisibility approach.

Also applies to: 19-19, 20-20, 21-21


28-28: Constructor parameter removal is appropriate.
Removing the unused MoGlass parameter keeps the constructor clean and clearly indicates that any dependency on MoGlass is no longer needed here.


42-43: Duplicate logic noted.
This is effectively the same logic as the slab check above, reinforcing the suggestion to unify these conditions.


49-50: Proper use of a dedicated private Mixin method.
Marking this helper as @Unique ensures it’s restricted to this Mixin and reduces collision risks.


52-54: 🛠️ Refactor suggestion

Verify behavior with partial or custom block shapes.
isSideCovered works well for ordinary blocks, but partial shapes or custom geometry might produce corner cases. Consider testing or adding fallback logic for blocks that don’t fully occupy their bounding box.

src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (4)

13-13: Use of @Unique annotation is correct.

This annotation helps prevent naming conflicts within Mixin classes and keeps the method scoped to this class only.


20-25: Imports align with new geometry-based logic.

Bringing in BlockPos, VoxelShape, VoxelShapes, and EmptyBlockView is consistent with the expanded usage of geometric checks for block invisibility.


49-54: Early return improves readability.

The quick return from isInvisible(...) is a neat approach, reducing nested if conditions and making it clear when the side is indeed invisible.


36-36: Verify removal of MoGlass dependency.

By removing the MoGlass parameter, ensure no external calls or references in other files depend on this constructor. This change may break code that calls the old constructor.

✅ Verification successful

Constructor modification is safe

The constructor change is safe as this is a Mixin class (annotated with @Mixin) that isn't directly instantiated. The constructor is private and the class is abstract, serving only as a template for runtime code injection into StainedGlassBlock.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if references to the old constructor still exist
ast-grep --pattern $'new StainedGlassBlockMixin($_, $$$)' 

Length of output: 58


Script:

#!/bin/bash
# Check if it's a Mixin class and look for any references
echo "=== Checking Mixin annotation ==="
rg "@Mixin" "src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java"

echo -e "\n=== Looking for any references to StainedGlassBlockMixin ==="
rg "StainedGlassBlockMixin" --type java

echo -e "\n=== Looking for potential constructor patterns ==="
ast-grep --pattern 'StainedGlassBlockMixin($$$)'

Length of output: 799

src/main/java/net/wurstclient/glass/GlassStairsBlock.java (4)

21-21: Looks good!

The new import for EmptyBlockView is appropriate for retrieving outline shapes without a full world context.


23-23: Class no longer marked final.

Making this class extendable is reasonable if you plan on letting other blocks or mods build upon its functionality. Just ensure that any subclassing remains aligned with the overall design.


25-25: Constructor changed from protected to package-private.

This is fine if your design requires allowing creation of GlassStairsBlock within other classes in the same package. Verify that external code needing this constructor is also in the same package.


48-53: Shape-based invisibility check looks correct.

Using EmptyBlockView.INSTANCE to retrieve outline shapes and then leveraging VoxelShapes.isSideCovered is a concise approach. However, confirm it gracefully handles special cases (e.g., tinted or stained glass with dynamic shapes).

Run the following script to locate references and verify that no unexpected shape overrides interfere with this logic:

✅ Verification successful

Shape-based invisibility check is verified as correct and handles all glass variants uniformly

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Searching codebase for references to shape overrides in tinted/stained glass
rg -A 5 "getOutlineShape"

Length of output: 3541

src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (3)

18-18: Extending GlassStairsBlock for consistency with other glass blocks.
This change appears intentional and aligns with the mod’s approach for glass-based blocks.


20-22: Constructor transition to use settings directly.
Calling the parent constructor with settings is the correct approach and ensures all relevant block properties are inherited properly.


40-40: No issues detected.
The additional blank line / spacing change does not impact functionality.

src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (4)

21-21: Reusing the glass invisibility logic.

Switching the parent class to GlassSlabBlock is a clear improvement that ensures consistent invisibility checks across all glass-related slabs. This also reduces code duplication.


49-49: Validate ambient occlusion settings.

Returning 1.0F forces full brightness, which may be intentional for glass blocks in certain rendering contexts. Double-check that this behavior is desired if you want a more translucent or softer lighting effect.


57-57: Color retrieval logic is correct.

Returning the stored color aligns with the Stainable interface requirements. No issues found here.


33-46: Consider consolidating similar block checks.

When verifying if sides are invisible, there are parallel checks for StainedGlassBlock, this, and StainedGlassStairsBlock. You could streamline the approach by grouping color checks and instance checks into a single condition to make the logic more concise. Also, confirm that the parent’s isInvisible method is thoroughly tested for edge cases (e.g., mixing tinted and non-tinted glass).

src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (4)

11-11: LGTM! Imports are properly aligned with the new implementation.

The new imports support the enhanced visibility checking approach using VoxelShapes.

Also applies to: 16-21


27-30: LGTM! Constructor signature simplified.

Good cleanup by removing the unused MoGlass parameter from the constructor.


Line range hint 33-45: LGTM! Improved visibility check logic.

The refactored implementation provides:

  • Consistent visibility checking for both slab and stairs
  • Clean separation of concerns with the extracted isInvisible method
  • Proper fallback to parent implementation

51-53: Verify the VoxelShape-based visibility check across different scenarios.

The new geometric approach using VoxelShapes.isSideCovered should be tested with various block configurations to ensure it works correctly in all cases.

Let's verify the implementation with the following script:

✅ Verification successful

✓ VoxelShape-based visibility check is consistently implemented

The implementation is correctly and uniformly applied across all glass block variants (regular, stained, tinted) and shapes (full blocks, stairs, slabs), ensuring consistent behavior throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases covering the new visibility check implementation
rg -l "test.*Invisible" --type java

# Look for any existing VoxelShapes.isSideCovered usage in tests
rg "VoxelShapes\.isSideCovered.*test" --type java

# Find all block classes that might need similar visibility checks
ast-grep --pattern 'class $_ extends TransparentBlock'

Length of output: 146


Script:

#!/bin/bash
# Search for glass-related block classes
rg -l "Glass.*Block" --type java

# Look for any VoxelShape usage in block classes
rg "VoxelShape" -A 3 --type java

# Search for similar visibility check implementations
rg "shouldDrawSide|getVisibility" --type java

Length of output: 7930

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (1)

47-57: Add documentation for the geometric visibility calculation.

While the implementation using VoxelShapes.isSideCovered is robust, consider adding a brief comment explaining how the geometric calculation determines side invisibility. This would help future maintainers understand the logic more quickly.

 @Unique
+// Determines if the side should be invisible by checking if the neighboring block's
+// outline shape fully covers the current block's face in the given direction
 private boolean isInvisible(BlockState state, BlockState stateFrom,
   Direction direction)
src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (1)

Line range hint 28-42: Clean implementation with good separation of concerns.

The refactored implementation is more maintainable and follows DRY principles by leveraging the parent class's isInvisible method.

Consider improving readability by extracting the block type checks into descriptive boolean variables:

 @Override
 @Environment(EnvType.CLIENT)
 public boolean isSideInvisible(BlockState state, BlockState stateFrom,
   Direction direction)
 {
-  if(stateFrom.getBlock() == Blocks.TINTED_GLASS)
+  boolean isTintedGlass = stateFrom.getBlock() == Blocks.TINTED_GLASS;
+  boolean isTintedGlassSlab = stateFrom.getBlock() == this;
+  boolean isTintedGlassStairs = stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_STAIRS;
+
+  if(isTintedGlass)
     return true;
 
-  if(stateFrom.getBlock() == this
+  if(isTintedGlassSlab
     && isInvisible(state, stateFrom, direction))
     return true;
 
-  if(stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_STAIRS
+  if(isTintedGlassStairs
     && isInvisible(state, stateFrom, direction))
     return true;
 
   return super.isSideInvisible(state, stateFrom, direction);
 }
src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (1)

33-41: Consider extracting block type checks for better readability.

The implementation effectively leverages the parent class's isInvisible method. However, the block type checks could be made more readable.

Consider this minor refactoring:

-		if(stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_SLAB
-			&& isInvisible(state, stateFrom, direction))
+		boolean isTintedGlassSlab = stateFrom.getBlock() == MoGlassBlocks.TINTED_GLASS_SLAB;
+		boolean isTintedGlassStairs = stateFrom.getBlock() == this;
+		
+		if(isTintedGlassSlab && isInvisible(state, stateFrom, direction))
			return true;
		
-		if(stateFrom.getBlock() == this
-			&& isInvisible(state, stateFrom, direction))
+		if(isTintedGlassStairs && isInvisible(state, stateFrom, direction))
			return true;
src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (1)

60-70: Add documentation for the geometric visibility check approach.

The implementation using VoxelShapes.isSideCovered is robust and efficient. Consider adding a brief comment explaining the geometric approach to help future maintainers understand the rationale.

 @Unique
+// Determines if a side should be invisible by checking if the adjacent block's shape
+// fully covers the current block's face in the given direction
 private boolean isInvisible(BlockState state, BlockState stateFrom,
   Direction direction)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 980c654 and 44ea779.

📒 Files selected for processing (9)
  • src/main/java/net/wurstclient/glass/GlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/GlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (3 hunks)
  • src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (25)
src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (3)

11-21: LGTM! Clean up of imports and constructor.

The removal of the unused MoGlass parameter and the addition of necessary VoxelShape-related imports improve code clarity.

Also applies to: 27-30


Line range hint 33-45: LGTM! Improved consistency in visibility checks.

The refactored method provides a unified approach to checking visibility for both slab and stairs blocks, making the code more maintainable.


Line range hint 33-57: Verify the visibility behavior with various block configurations.

The new implementation looks solid, but please ensure it's tested with various combinations of:

  • Different block orientations
  • Mixed glass types (tinted, stained, regular)
  • Complex stair configurations
  • Corner cases where multiple glass blocks meet

Would you like me to help create a comprehensive test suite for these scenarios?

src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (1)

Line range hint 19-24: LGTM! Good inheritance restructuring.

The change to extend GlassSlabBlock is a solid improvement that promotes code reuse and better represents the relationship between glass types.

src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (2)

18-23: LGTM! Clean inheritance restructuring.

The changes to extend GlassStairsBlock and simplify the constructor align well with the goal of revamping side invisibility checks. The package-private visibility is appropriate for this implementation.


Line range hint 44-56: LGTM! Correct ambient occlusion and transparency handling.

The implementations correctly maintain the expected behavior for tinted glass blocks.

src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (4)

11-22: LGTM! All new imports are necessary for the refactored visibility logic.


28-31: LGTM! Good simplification of the constructor.

The removal of the MoGlass parameter reduces coupling, making the class more maintainable.


39-48: LGTM! Clean and well-structured visibility checks.

The refactored logic handles both glass slabs and stairs consistently using the new isInvisible method.


51-61: LGTM! Excellent use of vanilla Minecraft's shape-based visibility system.

The new isInvisible method is a significant improvement:

  • Uses VoxelShapes.isSideCovered for robust shape-based visibility checks
  • Works correctly with all block shapes
  • Follows vanilla Minecraft's approach to side visibility

The @Unique annotation correctly prevents method conflicts in the mixin.

src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (3)

13-13: LGTM! New imports support the VoxelShape-based visibility checking.

The added imports are necessary and properly utilized for the new geometric-based approach to visibility checking.

Also applies to: 20-25


36-39: LGTM! Constructor simplified by removing unnecessary dependency.

The constructor has been appropriately simplified to only accept the essential Settings parameter, improving the class's independence.


Line range hint 42-58: LGTM! Clean refactoring of visibility checks.

The method maintains proper color matching while delegating the geometric visibility check to the new isInvisible method. The logic is clear and the fallback to super is correctly preserved.

src/main/java/net/wurstclient/glass/GlassStairsBlock.java (3)

23-25: LGTM! Well-structured inheritance changes.

The removal of final and constructor access changes support better encapsulation while enabling inheritance for specialized glass stair blocks within the package.


39-43: LGTM! Clean refactoring of visibility checks.

The visibility logic is now more maintainable by delegating to the new isInvisible method while preserving backward compatibility.


49-57: Verify edge cases in the new visibility implementation.

While the new implementation using VoxelShapes.isSideCovered is cleaner, please verify:

  1. That EmptyBlockView.INSTANCE provides accurate shapes for all possible block states
  2. Edge cases where block shapes might not align perfectly
  3. That null states/directions are handled appropriately

Consider adding null checks for the input parameters.

✅ Verification successful

Implementation verified as safe and consistent

The visibility check implementation follows the standard pattern used consistently across all glass-related blocks in the mod and matches vanilla Minecraft's approach. The use of EmptyBlockView.INSTANCE is appropriate for outline shape calculations in this context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of EmptyBlockView to understand common patterns
rg -A 2 "EmptyBlockView" --type java

# Search for test cases involving VoxelShapes.isSideCovered
rg -A 5 "isSideCovered.*direction" --type java

Length of output: 4481

src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (4)

18-18: LGTM! Good inheritance restructuring.

The change to extend GlassStairsBlock better represents the class hierarchy and promotes code reuse for glass-specific functionality while maintaining the stainable behavior.


25-27: LGTM! Constructor simplified appropriately.

The constructor has been streamlined while maintaining proper initialization of the color field.


Line range hint 52-63: LGTM! Ambient occlusion and color handling preserved.

The ambient occlusion and color functionality remain correctly implemented, ensuring proper rendering and staining behavior.


42-48: Verify behavior with different stair orientations.

The unified isInvisible check looks good, but please ensure it handles all stair orientation combinations correctly:

  • Stairs facing each other
  • Stairs at right angles
  • Inverted stairs
  • Corner stairs
✅ Verification successful

Glass visibility changes don't affect stair orientation behavior

The isInvisible check only handles glass-specific properties while all stair orientation logic is managed by the unchanged parent StairsBlock class. The changes are safe and won't affect how different stair orientations connect.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases covering stair orientations
rg -l "StainedGlassStairsBlock.*test" src/

Length of output: 44


Script:

#!/bin/bash
# Find parent class and related implementations
echo "=== Parent class implementation ==="
ast-grep --pattern 'class $_ extends $_ {
  $$$
  isInvisible($$$) {
    $$$
  }
  $$$
}'

echo "=== Related stair implementations ==="
rg -A 5 "extends.*Stairs.*Block" src/

echo "=== Block state handling ==="
rg -A 10 "getStateForPlacement|createBlockState" src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java

Length of output: 2101

src/main/java/net/wurstclient/glass/GlassSlabBlock.java (3)

23-23: LGTM! Good architectural change.

Removing the final modifier enables proper inheritance hierarchy for specialized glass slab blocks.


38-46: LGTM! Clean implementation of side invisibility checks.

The logic is now more maintainable with clear separation of concerns:

  1. Glass block check
  2. Same block type check
  3. Glass stairs check

49-57: Verify the behavior of EmptyBlockView usage.

The implementation uses EmptyBlockView.INSTANCE and BlockPos.ORIGIN for shape calculations. While this approach is more efficient than creating temporary objects, we should verify it works correctly in all scenarios.

Consider adding a comment explaining why EmptyBlockView is sufficient here, as it might not be immediately obvious to other developers that block state's outline shape doesn't need world context in this case.

src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (2)

21-22: LGTM! Good use of inheritance.

Extending GlassSlabBlock while implementing Stainable provides a clean separation between glass behavior and staining capability.


43-45: Verify color-specific invisibility behavior.

The implementation correctly handles invisibility checks for:

  1. Regular stained glass blocks
  2. Same colored stained glass slabs
  3. Same colored stained glass stairs

However, we should verify that the invisibility behavior works correctly when different colored glass blocks are adjacent.

Also applies to: 48-48

✅ Verification successful

Different colored glass blocks are correctly non-invisible to each other

The implementation in StainedGlassSlabBlock explicitly requires matching colors for invisibility behavior, which is the correct and expected behavior matching vanilla Minecraft's stained glass mechanics. The code properly checks color equality using getColor() == color when comparing with adjacent stained glass blocks, slabs, and stairs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests covering different colored glass block interactions
rg -l "StainedGlassSlabBlock.*getColor|isSideInvisible.*StainedGlass" src/test/

Length of output: 131


Script:

#!/bin/bash
# Find the implementation of isInvisible and color-related methods
ast-grep --pattern 'class StainedGlassSlabBlock {
  $$$
  isInvisible($$$) {
    $$$
  }
  $$$
}'

# Search for color-related methods and comparisons
rg "getColor|DyeColor" --type java

# Find test files in the repository
fd -e java -e kt test

Length of output: 3151


Script:

#!/bin/bash
# Check the test implementation
cat src/main/java/net/wurstclient/glass/test/MoGlassTestClient.java

# Also check the actual invisibility implementation in StainedGlassSlabBlock
cat src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java

Length of output: 12471

Copy link
Member

@Alexander01998 Alexander01998 left a comment

Choose a reason for hiding this comment

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

Hi @Fellteros, thanks for the PR!

Looking through the changed files, there seem to be quite a few changes here that are not actually related to the goal of using the VoxelShape system for visibility checks.

For example, removing final modifiers, changing constructor visibility and parameters, and even changing the tinted glass pieces to inherit directly from the transparent ones.

Could you please revert all those unrelated changes, so that the PR only migrates to the VoxelShape system without changing anything else?

@Fellteros
Copy link
Author

ofc

@Fellteros
Copy link
Author

Fellteros commented Jan 25, 2025

Though I don't quite understand why. Why don't you want to inherit from those blocks? Also having protected constructor in a final class is redundant. Why not just make it public? just curious

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (1)

50-58: Consider caching shape calculations for better performance.

The current implementation calculates outline shapes for every visibility check. While correct, this could be optimized by caching the shapes, especially since they're calculated using EmptyBlockView and don't depend on world state.

Example optimization:

+ private static final Map<BlockState, VoxelShape> SHAPE_CACHE = new ConcurrentHashMap<>();
+
+ private VoxelShape getCachedShape(BlockState state) {
+     return SHAPE_CACHE.computeIfAbsent(state,
+         s -> s.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN));
+ }
+
  protected boolean isInvisible(BlockState state, BlockState stateFrom,
      Direction direction)
  {
-     VoxelShape stateCullingShape =
-         state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN);
-     VoxelShape stateFromCullingShape =
-         stateFrom.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN);
+     VoxelShape stateCullingShape = getCachedShape(state);
+     VoxelShape stateFromCullingShape = getCachedShape(stateFrom);
      return VoxelShapes.isSideCovered(stateCullingShape,
          stateFromCullingShape, direction);
  }
src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (1)

10-10: Remove unused import.

The MoGlass import is no longer used after the constructor parameter was removed.

-import net.wurstclient.glass.MoGlass;
src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (1)

10-10: Remove unused import.

The MoGlass import is no longer used after the constructor parameter was removed.

-import net.wurstclient.glass.MoGlass;
src/main/java/net/wurstclient/glass/GlassStairsBlock.java (1)

Line range hint 27-29: Consider making the constructor public.

Since the class is no longer final and might be extended, consider making the constructor public to allow instantiation from other packages.

-protected GlassStairsBlock(Settings settings)
+public GlassStairsBlock(Settings settings)
src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (1)

10-10: Remove unused import.

The MoGlass import is no longer used after the constructor parameter was removed.

-import net.wurstclient.glass.MoGlass;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44ea779 and 0ee3389.

📒 Files selected for processing (9)
  • src/main/java/net/wurstclient/glass/GlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/GlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (2 hunks)
  • src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/net/wurstclient/glass/StainedGlassStairsBlock.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (13)
src/main/java/net/wurstclient/glass/TintedGlassStairsBlock.java (2)

19-22: LGTM! Necessary imports added for the new implementation.

The new imports support the refactored visibility logic and client-side annotations.


40-44: LGTM! Simplified visibility logic.

The refactored code improves maintainability by:

  1. Consolidating visibility checks into a single isInvisible method
  2. Maintaining compatibility with the parent class
src/main/java/net/wurstclient/glass/mixin/TintedGlassBlockMixin.java (1)

48-57: LGTM! Improved visibility check using geometric shapes.

The new isInvisible method using VoxelShapes.isSideCovered is a more robust approach as it checks the actual block shapes rather than relying on block-specific logic. This change:

  • Improves maintainability by centralizing the visibility logic
  • Makes the check more accurate by using geometric shapes
  • Allows for better extensibility through the protected visibility
src/main/java/net/wurstclient/glass/mixin/GlassBlockMixin.java (1)

52-61: LGTM! Consistent implementation with TintedGlassBlockMixin.

The implementation follows the same pattern as TintedGlassBlockMixin, which is good for maintainability and consistency across the codebase.

src/main/java/net/wurstclient/glass/GlassSlabBlock.java (1)

Line range hint 24-58: LGTM! Good refactoring for extensibility.

The changes improve the code in several ways:

  • Removing final allows for inheritance, which is useful for specialized glass types
  • The new isInvisible method follows the same pattern as the mixin classes
  • Special case for Blocks.GLASS is handled correctly
src/main/java/net/wurstclient/glass/GlassStairsBlock.java (1)

50-58: LGTM! Consistent implementation with other glass blocks.

The implementation follows the same pattern as other glass blocks, which is good for maintainability.

src/main/java/net/wurstclient/glass/mixin/StainedGlassBlockMixin.java (1)

61-70: LGTM! Consistent implementation with proper color handling.

The implementation:

  • Follows the same pattern as other glass blocks
  • Correctly handles color matching for stained glass variants
  • Uses the same robust geometric approach for visibility checks
src/main/java/net/wurstclient/glass/StainedGlassSlabBlock.java (3)

17-17: LGTM! Import addition is appropriate.

The addition of EmptyBlockView import is necessary for the new shape-based visibility calculations.


44-51: LGTM! Improved visibility check implementation.

The refactoring simplifies the logic by using a unified isInvisible method while maintaining proper type checking. This makes the code more maintainable and consistent.


55-64: Consider caching BlockPos.ORIGIN and verify shape-based visibility.

The implementation looks good, using VoxelShapes for accurate visibility checks. However:

  1. Consider caching BlockPos.ORIGIN as a static final field to avoid repeated references.
  2. Let's verify that the shape-based visibility checks work correctly for all block states.
✅ Verification successful

Shape-based visibility implementation verified and consistent

The implementation is correctly aligned with all other glass block variants in the codebase, including full blocks, slabs, and stairs, ensuring consistent behavior across different glass types.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of shape-based visibility checks to ensure consistency
rg -l "VoxelShapes.isSideCovered" .

# Look for potential test cases or related block implementations
ast-grep --pattern 'class $_ implements Stainable {
  $$$
  isSideInvisible($$$) {
    $$$
  }
}'

Length of output: 723

src/main/java/net/wurstclient/glass/TintedGlassSlabBlock.java (3)

20-23: LGTM: Required imports added for shape calculations and client-side annotations.


41-47: LGTM: Simplified visibility checks using consolidated helper method.

The refactoring improves maintainability by removing duplicate logic and consolidating visibility checks into a single isInvisible method.


51-59: Verify performance and thread safety of shape calculations.

The new implementation uses VoxelShapes.isSideCovered for visibility checks, which might have performance implications. Additionally, using EmptyBlockView.INSTANCE singleton warrants thread safety consideration.

Let's verify the usage of similar patterns in other Minecraft mods:

Also, please confirm:

  1. Are shape calculations cached to avoid performance overhead?
  2. Is EmptyBlockView.INSTANCE thread-safe in the context of client-side rendering?
✅ Verification successful

Implementation follows Minecraft's standard block culling pattern

The current implementation is both performant and thread-safe:

  • Uses Minecraft's official VoxelShapes.isSideCovered API for culling
  • EmptyBlockView.INSTANCE is a stateless singleton designed for this exact use case
  • Consistent with implementation across all glass block types in the mod
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar VoxelShape visibility check patterns in other mods
rg -U "VoxelShapes\.isSideCovered.*EmptyBlockView\.INSTANCE" \
  --type java \
  -A 2 -B 2

Length of output: 84


Script:

#!/bin/bash
# Search for VoxelShapes.isSideCovered usage
echo "=== VoxelShapes.isSideCovered usage ==="
rg "VoxelShapes\.isSideCovered" --type java -B 2 -A 2

echo -e "\n=== EmptyBlockView usage ==="
rg "EmptyBlockView" --type java -B 2 -A 2

echo -e "\n=== Block culling/visibility patterns ==="
rg "getOutlineShape.*BlockPos\.ORIGIN" --type java -B 2 -A 2

Length of output: 23226

Copy link
Member

@Alexander01998 Alexander01998 left a comment

Choose a reason for hiding this comment

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

Hey @Fellteros, thanks for reverting those other changes!

I'm currently working on automating my testing process for the visibility/transparency stuff. Once that's ready, I'll use it to check if the VoxelShape method does indeed work in all cases. Until then, please be patient as this PR will likely just sit here for a while.

As for why I asked you to revert the other changes:

First, I want to point out that PRs in general should be small and focused. They should change only what is necessary to get the desired result and otherwise follow the project's existing conventions rather than challenging them. Unrelated changes make the diff harder to read, which can obscure bugs, but more importantly they create pointless arguments about whether coding pattern X is better than Y, which can get your PR stalled for reasons that have nothing to do with the original PR objective.

Second, the GlassSlabBlock and GlassStairsBlock are intended to be only the clear glass variants of these blocks. They are not intended as an abstract parent class of all three variants (or as a non-abstract parent of some variants while also being the clear variant at the same time). This change would have changed the meaning of these classes in a way that could have all kinds of unintended downstream effects, for example, silently breaking other mods that use instanceof GlassStairsBlock to detect only clear glass stairs. (Notice how this has nothing to do with the VoxelShape system - better to cut this out than to make the PR depend on it.)

Hope that makes sense!

@Fellteros
Copy link
Author

ok, thanks for explanation

@Alexander01998 Alexander01998 added status:never-stale Prevents StaleBot from marking this item as stale. type:refactor Refactoring or cleanup of existing code. labels Jan 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:never-stale Prevents StaleBot from marking this item as stale. type:refactor Refactoring or cleanup of existing code.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants