[8.0] Custom Graph Solution #1066

Merged
Rover656 merged 35 commits from feat/graph-library into dev/1.21.1 2025-05-11 22:42:28 +00:00
Rover656 commented 2025-05-10 00:24:18 +00:00 (Migrated from github.com)

Description

This implements a custom graph network system in EnderCore, which is then used and expanded by conduits to cache commonly queried information about networks for tickers.
This results in much simpler conduit tickers, and they should be much faster than previous versions as they do not need to filter nodes every tick.

Closes: #920

TODO

  • Fully evaluate conduit behaviour, with both in-game playtesting and potentially some game tests.
  • Explore partial cache updates rather than removing and re-adding a node. Deferred to "later"
  • Ensure Network tests are comprehensive enough
  • Implement proper context splitting
  • Javadocs
  • Ensure relevant TODOs have been addressed.

After this PR

After this PR, I'll revisit #922 and recreate the relevant bugfixes with our Network system.

Breaking Changes

Changes the save format of our conduit network saved data somewhat, it will not be possible to load a v7.1 world after loading v8 (this is acceptable as this was already the case).

Checklist

  • My code follows the style guidelines of this project (.editorconfig, most IDEs will use this for you).
  • I have made corresponding changes to the documentation.
  • My changes are ready for review from a contributor.

Summary by CodeRabbit

  • New Features

    • Introduced a new conduit network system with caching, chunk tracking, and context support for enhanced performance and reliability.
    • Added new interfaces and abstractions for conduit nodes and networks, improving modularity and extensibility.
    • Integrated automated game tests covering conduit network behaviour, merging, splitting, and item conduit transfer.
    • Added server-side test framework integration with command support for testing and debugging.
  • Refactor

    • Replaced legacy graph-based conduit system with a modern network-based architecture.
    • Updated all APIs and internal logic to use "insert/extract" terminology instead of "send/receive" for clarity.
    • Simplified and unified ticker logic across all conduit types using the new network API and connection abstractions.
    • Enhanced abstraction by replacing concrete classes with interfaces for nodes and networks.
    • Updated build scripts and workflows to improve testing, concurrency, and code style enforcement.
  • Bug Fixes

    • Corrected tooltip calculations and terminology to consistently use network tick rates.
    • Fixed transfer rate computations and refined context splitting logic to distribute state proportionally.
  • Chores

    • Removed deprecated and unused legacy code, interfaces, classes, and annotations.
    • Improved documentation and added package-level nullability annotations for better code safety.
  • Tests

    • Added comprehensive unit tests for network creation, merging, splitting, and edge indexing.
    • Added game tests for conduit placement, network merging/splitting, and item conduit transfer behaviours.
# Description This implements a custom graph network system in EnderCore, which is then used and expanded by conduits to cache commonly queried information about networks for tickers. This results in much simpler conduit tickers, and they should be much faster than previous versions as they do not need to filter nodes every tick. Closes: #920 <!-- If you're submitting a Draft PR, consider providing a TODO list using checkboxes --> # TODO - [x] Fully evaluate conduit behaviour, with both in-game playtesting and potentially some game tests. - [ ] ~~Explore partial cache updates rather than removing and re-adding a node.~~ Deferred to "later" - [x] Ensure `Network` tests are comprehensive enough - [x] Implement proper context splitting - [x] Javadocs - [x] Ensure relevant TODOs have been addressed. # After this PR After this PR, I'll revisit #922 and recreate the relevant bugfixes with our `Network` system. # Breaking Changes Changes the save format of our conduit network saved data somewhat, it will not be possible to load a v7.1 world after loading v8 (this is acceptable as this was already the case). <!-- For drafts, fill this in as you go; if you are leaving draft, make sure these are all complete. --> # Checklist - [ ] My code follows the style guidelines of this project (.editorconfig, most IDEs will use this for you). - [ ] I have made corresponding changes to the documentation. - [ ] My changes are ready for review from a contributor. <!-- Thanks to: https://embeddedartistry.com/blog/2017/08/04/a-github-pull-request-template-for-your-projects/ for the building blocks of this template --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new conduit network system with caching, chunk tracking, and context support for enhanced performance and reliability. - Added new interfaces and abstractions for conduit nodes and networks, improving modularity and extensibility. - Integrated automated game tests covering conduit network behaviour, merging, splitting, and item conduit transfer. - Added server-side test framework integration with command support for testing and debugging. - **Refactor** - Replaced legacy graph-based conduit system with a modern network-based architecture. - Updated all APIs and internal logic to use "insert/extract" terminology instead of "send/receive" for clarity. - Simplified and unified ticker logic across all conduit types using the new network API and connection abstractions. - Enhanced abstraction by replacing concrete classes with interfaces for nodes and networks. - Updated build scripts and workflows to improve testing, concurrency, and code style enforcement. - **Bug Fixes** - Corrected tooltip calculations and terminology to consistently use network tick rates. - Fixed transfer rate computations and refined context splitting logic to distribute state proportionally. - **Chores** - Removed deprecated and unused legacy code, interfaces, classes, and annotations. - Improved documentation and added package-level nullability annotations for better code safety. - **Tests** - Added comprehensive unit tests for network creation, merging, splitting, and edge indexing. - Added game tests for conduit placement, network merging/splitting, and item conduit transfer behaviours. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai[bot] commented 2025-05-10 00:24:25 +00:00 (Migrated from github.com)

Walkthrough

This update replaces the old graph-based conduit network system with a new, custom network and node implementation. It introduces new APIs (IConduitNetwork, IConduitNode), rewrites network persistence and ticking, and refactors all conduit types, tickers, and GUIs to use the new abstractions. Legacy graph classes and interfaces are removed, and comprehensive automated and game tests are added for validation.

Changes

Files / Groups Change Summary
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/*, .../network/ConduitNetwork.java, .../network/ConduitNode.java, .../network/ConduitNetworkSavedData.java, .../network/IConduitNetwork.java, .../network/ConduitBlockConnection.java, .../network/node/IConduitNode.java, .../network/node/NodeDataType.java, .../network/node/NodeDataAccessor.java, .../network/node/ConduitNode.java, .../network/ConduitNetworkContextType.java, .../network/ConduitNetworkContext.java Old graph-based network, node, and context interfaces/classes removed. New network and node system introduced with custom classes and interfaces, including cache management, context split/merge, and updated persistence.
enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java, .../bundle/ConduitBundle.java, .../connection/config/IOConnectionConfig.java, .../ticker/ConduitTicker.java, .../ticker/IOAwareConduitTicker.java, .../ticker/LoadedAwareConduitTicker.java API refactored to use new network and node interfaces, with old graph and node types removed. IO terminology updated from send/receive to insert/extract.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java, .../common/conduit/menu/ConduitMenu.java, .../common/conduit/ConduitBlockItem.java Bundle and menu logic refactored to use new node/network types and updated persistence.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/* All conduit types (energy, fluid, item, redstone, etc.) updated to use new network/node APIs, with connection config field/method renames and updated logic for connection, transfer, and tooltips.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/*Ticker.java All ticker classes rewritten to implement the new ConduitTicker interface and operate on IConduitNetwork, removing legacy connection logic.
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/*, .../client/model/conduit/bundle/ConduitConnectionRenderState.java GUI and rendering code updated for new connection config field names and logic.
enderio-conduits/src/main/java/com/enderio/conduits/server/tests/* New automated and game tests added for conduit network integrity, merging, splitting, and transfer behaviour, with helper classes for test setup and assertions.
enderio-conduits/build.gradle.kts, endercore/build.gradle.kts, enderio/build.gradle.kts, .github/workflows/verify.yml Build scripts and CI updated to remove graphlib dependency, add test framework, and introduce new test configurations and workflows.
endercore/src/main/java/com/enderio/core/common/graph/*, endercore/src/test/java/com/enderio/core/* New generic network/graph infrastructure and tests added, independent of the main conduit system.
enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java Minor: GUI code commented out to avoid server-side errors.
Many files across enderio-conduits Terminology updated from send/receive to insert/extract in all relevant config, GUI, and logic code.
Deprecated/legacy files Deprecated annotations updated to remove forRemoval = true; legacy code retained for compatibility but not for removal.

Sequence Diagram(s)

sequenceDiagram
    participant Player
    participant ConduitBundleBlockEntity
    participant ConduitNode
    participant ConduitNetwork
    participant ConduitNetworkSavedData

    Player->>ConduitBundleBlockEntity: Place conduit block
    ConduitBundleBlockEntity->>ConduitNode: Create/attach node
    ConduitNode->>ConduitNetworkSavedData: Claim node or create network
    ConduitNetworkSavedData->>ConduitNetwork: Add node, update caches
    ConduitNetwork->>ConduitNode: Set network reference
    Note over ConduitNetwork: On network mutation, caches rebuilt
    Player->>ConduitBundleBlockEntity: Remove conduit block
    ConduitBundleBlockEntity->>ConduitNode: Detach node
    ConduitNode->>ConduitNetwork: Remove node, possibly split network
    ConduitNetwork->>ConduitNetworkSavedData: Update on split/discard
sequenceDiagram
    participant ServerTick
    participant ConduitNetworkSavedData
    participant ConduitNetwork
    participant ConduitTicker

    ServerTick->>ConduitNetworkSavedData: On tick event
    ConduitNetworkSavedData->>ConduitNetwork: For each network, call beforeTicking()
    ConduitNetwork->>ConduitTicker: Call tick(level, conduit, network)
    ConduitTicker->>ConduitNetwork: Query connections, transfer resources

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Implement context splitting logic to prevent energy duplication when separating conduit networks (#920)
  • #1007: Both PRs concern conduit network persistence and loading, but this PR replaces the entire system that #1007 modifies.
  • #1025: This PR is a direct continuation and expansion of the original conduit block rewrite in #1025, sharing core architectural changes.

Poem

In tunnels deep where conduits wind,
The networks split, then merge in kind.
No more the graphs of old remain,
New nodes and links now rule the chain.
With tests and ticks, the rabbits cheer—
For bugs like duplication disappear!
🐇

Tip

️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @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.
<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- walkthrough_start --> ## Walkthrough This update replaces the old graph-based conduit network system with a new, custom network and node implementation. It introduces new APIs (`IConduitNetwork`, `IConduitNode`), rewrites network persistence and ticking, and refactors all conduit types, tickers, and GUIs to use the new abstractions. Legacy graph classes and interfaces are removed, and comprehensive automated and game tests are added for validation. ## Changes | Files / Groups | Change Summary | |-----------------------------------------------------------------------------------------------------------------|----------------| | `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/*`, `.../network/ConduitNetwork.java`, `.../network/ConduitNode.java`, `.../network/ConduitNetworkSavedData.java`, `.../network/IConduitNetwork.java`, `.../network/ConduitBlockConnection.java`, `.../network/node/IConduitNode.java`, `.../network/node/NodeDataType.java`, `.../network/node/NodeDataAccessor.java`, `.../network/node/ConduitNode.java`, `.../network/ConduitNetworkContextType.java`, `.../network/ConduitNetworkContext.java` | Old graph-based network, node, and context interfaces/classes removed. New network and node system introduced with custom classes and interfaces, including cache management, context split/merge, and updated persistence. | | `enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java`, `.../bundle/ConduitBundle.java`, `.../connection/config/IOConnectionConfig.java`, `.../ticker/ConduitTicker.java`, `.../ticker/IOAwareConduitTicker.java`, `.../ticker/LoadedAwareConduitTicker.java` | API refactored to use new network and node interfaces, with old graph and node types removed. IO terminology updated from send/receive to insert/extract. | | `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java`, `.../common/conduit/menu/ConduitMenu.java`, `.../common/conduit/ConduitBlockItem.java` | Bundle and menu logic refactored to use new node/network types and updated persistence. | | `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/*` | All conduit types (energy, fluid, item, redstone, etc.) updated to use new network/node APIs, with connection config field/method renames and updated logic for connection, transfer, and tooltips. | | `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/*Ticker.java` | All ticker classes rewritten to implement the new `ConduitTicker` interface and operate on `IConduitNetwork`, removing legacy connection logic. | | `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/*`, `.../client/model/conduit/bundle/ConduitConnectionRenderState.java` | GUI and rendering code updated for new connection config field names and logic. | | `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/*` | New automated and game tests added for conduit network integrity, merging, splitting, and transfer behaviour, with helper classes for test setup and assertions. | | `enderio-conduits/build.gradle.kts`, `endercore/build.gradle.kts`, `enderio/build.gradle.kts`, `.github/workflows/verify.yml` | Build scripts and CI updated to remove graphlib dependency, add test framework, and introduce new test configurations and workflows. | | `endercore/src/main/java/com/enderio/core/common/graph/*`, `endercore/src/test/java/com/enderio/core/*` | New generic network/graph infrastructure and tests added, independent of the main conduit system. | | `enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java` | Minor: GUI code commented out to avoid server-side errors. | | Many files across `enderio-conduits` | Terminology updated from send/receive to insert/extract in all relevant config, GUI, and logic code. | | Deprecated/legacy files | Deprecated annotations updated to remove `forRemoval = true`; legacy code retained for compatibility but not for removal. | ## Sequence Diagram(s) ```mermaid sequenceDiagram participant Player participant ConduitBundleBlockEntity participant ConduitNode participant ConduitNetwork participant ConduitNetworkSavedData Player->>ConduitBundleBlockEntity: Place conduit block ConduitBundleBlockEntity->>ConduitNode: Create/attach node ConduitNode->>ConduitNetworkSavedData: Claim node or create network ConduitNetworkSavedData->>ConduitNetwork: Add node, update caches ConduitNetwork->>ConduitNode: Set network reference Note over ConduitNetwork: On network mutation, caches rebuilt Player->>ConduitBundleBlockEntity: Remove conduit block ConduitBundleBlockEntity->>ConduitNode: Detach node ConduitNode->>ConduitNetwork: Remove node, possibly split network ConduitNetwork->>ConduitNetworkSavedData: Update on split/discard ``` ```mermaid sequenceDiagram participant ServerTick participant ConduitNetworkSavedData participant ConduitNetwork participant ConduitTicker ServerTick->>ConduitNetworkSavedData: On tick event ConduitNetworkSavedData->>ConduitNetwork: For each network, call beforeTicking() ConduitNetwork->>ConduitTicker: Call tick(level, conduit, network) ConduitTicker->>ConduitNetwork: Query connections, transfer resources ``` ## Assessment against linked issues | Objective (Issue #) | Addressed | Explanation | |------------------------------------------------------------------------------------------------------|-----------|-------------| | Implement context splitting logic to prevent energy duplication when separating conduit networks (#920) | ✅ | | ## Possibly related PRs - [#1007](https://github.com/Team-EnderIO/EnderIO/pull/1007): Both PRs concern conduit network persistence and loading, but this PR replaces the entire system that #1007 modifies. - [#1025](https://github.com/Team-EnderIO/EnderIO/pull/1025): This PR is a direct continuation and expansion of the original conduit block rewrite in #1025, sharing core architectural changes. ## Poem > In tunnels deep where conduits wind, > The networks split, then merge in kind. > No more the graphs of old remain, > New nodes and links now rule the chain. > With tests and ticks, the rabbits cheer— > For bugs like duplication disappear! > 🐇✨ <!-- walkthrough_end --> <!-- announcements_start --> > [!TIP] > <details> > <summary>⚡️ Faster reviews with caching</summary> > > - CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings. > > Enjoy the performance boost—your workflow just got faster. > > </details> <!-- announcements_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end --> <!-- finishing_touch_checkbox_start --> <details open="true"> <summary>✨ Finishing Touches</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. <details> <summary>❤️ Share</summary> - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) </details> <details> <summary>🪧 Tips</summary> ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Team-EnderIO/EnderIO&utm_content=1066): - 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.` ### Support Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions. 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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR. - `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR. - `@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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit. - Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements. </details> <!-- tips_end -->
github-advanced-security[bot] (Migrated from github.com) reviewed 2025-05-10 21:42:35 +00:00
@ -1,30 +1,52 @@
name: Verify Build
name: Game and Unit Tests
github-advanced-security[bot] (Migrated from github.com) commented 2025-05-10 21:42:35 +00:00

Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

Show more details

## Workflow does not contain permissions Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}} [Show more details](https://github.com/Team-EnderIO/EnderIO/security/code-scanning/179)
github-advanced-security[bot] (Migrated from github.com) reviewed 2025-05-10 22:49:10 +00:00
@ -1,30 +1,52 @@
name: Verify Build
name: Game and Unit Tests
github-advanced-security[bot] (Migrated from github.com) commented 2025-05-10 22:49:10 +00:00

Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

Show more details

## Workflow does not contain permissions Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}} [Show more details](https://github.com/Team-EnderIO/EnderIO/security/code-scanning/182)
Rover656 commented 2025-05-11 20:24:47 +00:00 (Migrated from github.com)

We don't have full conduit test coverage, but I think we're in a good enough position to consider this for merge now.

We don't have full conduit test coverage, but I think we're in a good enough position to consider this for merge now.
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 20:34:00 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 33

🔭 Outside diff range comments (7)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (1)

57-66: 💡 Verification agent

Verification inconclusive

Merging networks with incompatible locked fluids may silently corrupt state
If both networks have non-empty but different lockedFluid values, the current logic just keeps the caller’s value, discarding the other without warning. This could allow two incompatible fluid channels to merge invisibly. Consider:

  1. Validating that the two fluids match and throwing/logging when they don’t; or
  2. Choosing a deterministic resolution strategy and documenting it.

Handle conflicting locked fluids during network merge

The current mergeWith implementation silently retains the caller’s non‐empty lockedFluid and discards the other’s without warning whenever they differ. This can lead to inconsistent network state if two fluid channels with different locked fluids are merged.

Please update FluidConduitNetworkContext.mergeWith to one of the following:

  • Validate that both lockedFluid values are equal before merging, and throw or log an error if they differ.
  • Define and document a deterministic resolution strategy (for example, always pick the lexicographically lesser fluid or prefer the other network’s fluid).

Locations to address:

  • File: enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java
  • Method: mergeWith, lines 57–66

Example validation snippet:

if (!lockedFluid.equals(Fluids.EMPTY)
  && !other.lockedFluid.equals(Fluids.EMPTY)
  && !lockedFluid.equals(other.lockedFluid)) {
    throw new IllegalStateException(
      "Cannot merge networks with different locked fluids: "
      + lockedFluid + " vs " + other.lockedFluid);
}
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (1)

87-90: ⚠️ Potential issue

convertConnection ignores two of its parameters – potential logic defect.

redstoneControl and redstoneChannel are accepted but never used when constructing RedstoneConduitConnectionConfig. This means UI selections for redstone behaviour are silently discarded.

-        return new RedstoneConduitConnectionConfig(isInsert, inputChannel, isExtract, outputChannel, false);
+        return new RedstoneConduitConnectionConfig(
+                isInsert,
+                inputChannel,
+                isExtract,
+                outputChannel,
+                /* isSelfFeed */ false,
+                redstoneControl,
+                redstoneChannel);

(Adjust the constructor to match the actual field order/availability.)

Failing to persist those values will break redstone-controlled operation after a world reload or when opening the screen again.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

29-32: ⚠️ Potential issue

Division-by-zero risk in heat transfer calculation.

invConduction can be 0 if both handlers report zero inverse conduction, leading to an infinite temperature difference and NaN heat transfer.

-                double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature())
-                        / invConduction;
+                if (invConduction == 0) {
+                    continue; // nothing to conduct
+                }
+                double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature())
+                        / invConduction;

Add the guard (or clamp to a minimum epsilon) to prevent floating-point anomalies and potential downstream crashes.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (2)

133-143: ⚠️ Potential issue

Null-check missing when migrating legacy data

legacyData.get(side) may return null. Calling oldSideConfig.isRoundRobin etc. will then crash with an NPE during world upgrade.

-var oldSideConfig = legacyData.get(side);
-...
-connectionConfigSetter.accept(side,
-        currentConfig.withIsRoundRobin(oldSideConfig.isRoundRobin)
-                     .withIsSelfFeed(oldSideConfig.isSelfFeed)
-                     .withPriority(oldSideConfig.priority));
+var oldSideConfig = legacyData.get(side);
+if (oldSideConfig != null) {
+    connectionConfigSetter.accept(side,
+            currentConfig.withIsRoundRobin(oldSideConfig.isRoundRobin)
+                         .withIsSelfFeed(oldSideConfig.isSelfFeed)
+                         .withPriority(oldSideConfig.priority));
+}

Please add this guard before dereferencing.


183-190: ⚠️ Potential issue

Inconsistent API usage – receiveRedstoneChannel() call remains

The surrounding refactor switched to the extract terminology (extractRedstoneChannel() & friends), yet the call to:

tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.receiveRedstoneChannel()));

still references receiveRedstoneChannel().
This will fail to compile once the old accessor is removed, or worse, access the wrong channel if it still exists.

-tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.receiveRedstoneChannel()));
+tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.extractRedstoneChannel()));

Please update the accessor for consistency.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)

35-54: ⚠️ Potential issue

Total energy moved in one tick is unbounded by transferRate

Within the loop, every connection may receive up to transferRate, so if five inserts are processed the network could move 5 × transferRate in a single server tick.

Track remaining transferable energy instead:

-        int startingRotatingIndex = context.rotatingIndex();
+        int remaining = transferRate;
+        int startingRotatingIndex = context.rotatingIndex();

-        for (int i = startingRotatingIndex; i < startingRotatingIndex + inserts.size(); i++) {
+        for (int i = startingRotatingIndex; i < startingRotatingIndex + inserts.size() && remaining > 0; i++) {
             int insertIndex = i % inserts.size();
             var sendingConnection = inserts.get(insertIndex);

             IEnergyStorage insertHandler = sendingConnection.getSidedCapability(Capabilities.EnergyStorage.BLOCK);
             if (insertHandler == null || !insertHandler.canReceive()) {
                 continue;
             }

-            int energyToInsert = Math.min(transferRate, Math.max(context.energyStored(), 0));
+            int energyToInsert = Math.min(remaining, context.energyStored());
             int energyInserted = insertHandler.receiveEnergy(energyToInsert, false);
             context.setEnergyStored(context.energyStored() - energyInserted);
-            context.setRotatingIndex(insertIndex + 1);
+            context.setRotatingIndex(insertIndex + 1);
+            remaining -= energyInserted;
             if (context.energyStored() <= 0) {
                 break;
             }
         }
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (1)

60-67: ⚠️ Potential issue

reconnected() / disconnected() return null – guaranteed NPE downstream

Many callers expect these methods to return a non-null config (see other connection configs). Returning null will crash whenever a conduit is broken or re-placed.

Please provide proper implementations mirroring the logic used in ChemicalConduitConnectionConfig.

🧹 Nitpick comments (50)
.github/workflows/verify.yml (1)

42-42: Remove trailing whitespace

There's trailing whitespace on this line that should be removed.

-          distribution: 'temurin'
-          
+          distribution: 'temurin'
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 42-42: trailing spaces

(trailing-spaces)

enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)

17-17: Softened deprecation policy

Removing forRemoval = true from the deprecation annotations indicates a change in policy to maintain backwards compatibility longer than initially planned. This is a considerate approach for mod developers who might be using the deprecated registry.

However, consider adding Javadoc comments to clearly indicate the migration path from the deprecated registry to the new system, which would help developers transition their code more easily.

Also applies to: 32-32

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java (1)

133-135: Method name updated but tooltip key remains inconsistent.

While you've correctly updated the method call from graphTickRate() to networkTickRate() to align with the new conduit network system, the tooltip key ConduitLang.GRAPH_TICK_RATE_TOOLTIP still references the old "graph" terminology.

Consider updating the tooltip key to match the new terminology:

-               tooltipComponents.add(TooltipUtil.styledWithArgs(ConduitLang.GRAPH_TICK_RATE_TOOLTIP,
+               tooltipComponents.add(TooltipUtil.styledWithArgs(ConduitLang.NETWORK_TICK_RATE_TOOLTIP,
                        20 / conduit.value().networkTickRate()));

This would require updating the corresponding language key in the appropriate lang file as well.

enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (1)

61-69: Update documentation comments for consistency with code changes

The method documentation still refers to "isSend" and "isReceive" but the code has been updated to use "isInsert" and "isExtract". For complete consistency, consider updating these comments as well.

/**
- * Unless you changed the order of the titles, this should edit isSend.
+ * Unless you changed the order of the titles, this should edit isInsert.
 */
protected abstract U setLeftEnabled(U config, boolean isEnabled);

/**
- * Unless you changed the order of the titles, this should edit isReceive.
+ * Unless you changed the order of the titles, this should edit isExtract.
 */
protected abstract U setRightEnabled(U config, boolean isEnabled);
endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java (2)

5-6: Generic bounds could be simplified to lower boiler-plate

TNode already extends INetworkNode<TNet, TNode>, so repeating TNet’s own bound (extends Network<TNet, TNode>) inside it is redundant noise for the caller and makes the type declaration intimidating.
Unless you truly need the recursive double bind, consider:

-public interface INetworkNode<TNet extends Network<TNet, TNode>, TNode extends INetworkNode<TNet, TNode>> {
+public interface INetworkNode<TNet extends Network<?, ?>, TNode extends INetworkNode<TNet, TNode>> {

or even extracting a separate N extends Network<?,?> alias. It keeps the self-type property while reducing the mental load.


10-16: Null-safety annotations missing on getNetwork

getNetwork() promises to return a non-null value when isValid() is true, yet this isn’t expressed. Add @NotNull (JetBrains) or Jakarta/JSR-305 equivalents so static analysers can help catching misuse.

- TNet getNetwork();
+ @NotNull TNet getNetwork();
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (2)

14-18: CODEC should be final to prevent accidental reassignment

Static mutable fields that represent constants are prone to unexpected mutations (especially through mixins or reflective test code). Mark it final like your other context classes do:

-    public static MapCodec<ChemicalConduitNetworkContext> CODEC = RecordCodecBuilder.mapCodec(instance -> instance
+    public static final MapCodec<ChemicalConduitNetworkContext> CODEC = RecordCodecBuilder.mapCodec(instance -> instance

The same applies to TYPE just below for consistency.


66-70: Unused parameters in split – either document or suppress

selfNetwork and allNetworks are currently ignored. That’s absolutely fine (no split-specific state), but the intent should be explicit to silence IDE warnings:

-public ChemicalConduitNetworkContext split(IConduitNetwork selfNetwork,
-        Set<? extends IConduitNetwork> allNetworks) {
+@SuppressWarnings("unused") // Context split does not depend on network size
+public ChemicalConduitNetworkContext split(IConduitNetwork selfNetwork,
+        Set<? extends IConduitNetwork> allNetworks) {

Alternatively rename them to _unusedSelf / _unusedAll if you follow that convention.

endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)

10-10: Consider adding more documentation for type parameters.

While the class has good overall documentation, consider expanding on what the type parameters represent, especially how the recursive type parameter N extends INetworkNode<BasicNetwork<N>, N> works.

- /**
-  * A basic network implementation with no special context.
-  * @param <N> The node type.
-  */
+ /**
+  * A basic network implementation with no special context.
+  * @param <N> The node type that implements INetworkNode with this network type.
+  *            The recursive type parameter enables nodes to reference their containing network type.
+  */
enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (2)

22-30: Make the framework instance a final field or local variable only
MutableTestFramework framework is created and immediately used but never retained. If another subsystem needs to query the framework later (e.g., from other event-bus callbacks), you’ll have no reference to it. Either:

  1. Store it in a private final field, or
  2. If it truly is fire-and-forget, declare it as a local final variable so the intent is obvious.
-        final MutableTestFramework framework = FrameworkConfiguration
+        final var framework = FrameworkConfiguration

35-36: Name collision risk for the root tests command
Several mods expose a root-level tests command. Collisions cause one command to shadow the other, depending on load order. Consider namespacing it (enderio_tests) or placing it under your mod’s root literal (/enderio tests …).

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (1)

15-20: Declare constants final to guarantee immutability
CODEC is mutable at runtime. Mark it public static final (likewise for TYPE) to prevent accidental reassignment and aid inlining during optimisation.

-    public static MapCodec<FluidConduitNetworkContext> CODEC =
+    public static final MapCodec<FluidConduitNetworkContext> CODEC =
endercore/src/test/java/com/enderio/core/TestNode.java (3)

13-16: Avoid eager network creation in tests to reflect real usage
Creating a brand-new BasicNetwork in every default constructor means a lone node is always part of a network, which might mask bugs where nodes start unassigned. Consider leaving network as null by default and providing a helper in tests that explicitly wires nodes.


17-21: Constructor duplication – forward to the primary constructor
TestNode(boolean) manually repeats the network-creation logic. Delegate to the no-arg constructor to keep behaviour in one place:

-        if (shouldCreateNetwork) {
-            network = new BasicNetwork<>(this);
-        }
+        if (shouldCreateNetwork) {
+            new TestNode(); // or `this();`
+        }

24-26: isValid should consult network state, not just nullity
If the network later becomes invalidated/removed, network != null will still return true. Expose a BasicNetwork#isValid() (or similar) and delegate:

return network != null && network.isValid();
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (2)

52-54: Use of “insertColour” looks correct – just ensure language keys are updated.

The renaming from send/receive to insert/extract is consistent here. Verify that the associated localisation keys (ConduitLang.CONDUIT_CHANNEL) still make sense in-game (e.g., “Insert Channel” rather than “Send Channel”) so players are not shown legacy wording.


86-99: Minor optimisation: avoid repeated config look-ups inside lambdas.

Every frame the lambdas passed to addColorPicker / addRedstoneControlPicker capture dataAccess and call getConnectionConfig() again. The extra GC pressure is negligible, but you could slightly reduce allocations by storing the config once before registering the widgets, in line with the other GUI screen implementations.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (1)

66-74: Consider implementing planned tests

The TODOs outline important test cases that should be implemented to ensure robust network behavior, particularly around redstone signalling and transfer resumption after network breaks.

Would you like help implementing these additional test cases? I can draft test methods for redstone signalling and transfer behavior verification.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (2)

81-102: Consider early-returning when the node already owns a ready main grid node

Inside onCreated, the mainNode.isReady() guard is checked only after a potential (re-)initialisation of the node data.
If the conduit is re-placed in the same position (e.g. via schematic/structure loading) initMainNode() could be invoked even though the underlying IManagedGridNode is already ready, leading to an unnecessary (and fairly expensive) wrapAsHolder & createManagedNode chain.

A tiny rearrangement avoids that overhead:

-        var data = node.getOrCreateNodeData(MEConduitNodeData.TYPE);
-        if (data.getMainNode() == null) {
-            initMainNode(level, data);
-        }
-
-        IManagedGridNode mainNode = data.getMainNode();
-        if (mainNode.isReady()) {
-            return;
-        }
+        var data = node.getOrCreateNodeData(MEConduitNodeData.TYPE);
+        IManagedGridNode mainNode = data.getMainNode();
+        if (mainNode == null) {
+            initMainNode(level, data);
+            mainNode = data.getMainNode();
+        }
+        if (mainNode.isReady()) {
+            return;
+        }

This is optional, but avoids a handful of allocations during chunk re-loads.


152-160: Minor: context parameter is never inspected

proxyCapability keeps the tContext parameter but doesn’t use it.
Not an error, yet a quick // ignored comment (or removal if the interface allows) would make the intent explicit and silence IDE warnings.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)

44-46: Declare CODEC as static final to follow immutable-constant conventions

CODEC is a pure value object that should never be reassigned at runtime.
Making it static final prevents accidental re-assignment and communicates intent more clearly.

-    public static Codec<ConduitNetworkSavedData> CODEC = ConduitNetwork.CODEC.listOf()
+    public static final Codec<ConduitNetworkSavedData> CODEC = ConduitNetwork.CODEC.listOf()
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)

45-47: Redundant Math.max call

context.energyStored() is guaranteed to be > 0 thanks to the early return above; the extra clamp adds noise and a tiny overhead.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (1)

91-102: Suppress or handle unchecked cast in proxyCapability

The down-cast to (TCapability) data.container is unchecked and will generate warnings.
Either add @SuppressWarnings("unchecked") locally or use capability.cast(data.container) (NeoForge helper) to avoid raw casts.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (2)

42-50: Variable / comment naming is now misleading

fluidConduit and the surrounding comment still reference “fluid” even though the class works with chemicals. This can hinder future searches and comprehension.

-        if (dataAccess.conduit() instanceof ChemicalConduit fluidConduit && !fluidConduit.isMultiChemical()) {
+        if (dataAccess.conduit() instanceof ChemicalConduit chemicalConduit && !chemicalConduit.isMultiChemical()) {

and update the leading comment to “Locked chemical widget”.


90-96: Avoid repeating the same expensive lookup every frame

dataAccess.getConnectionConfig() is fetched twice per frame inside addPreRenderAction and the red-stone control picker supplier.
Caching the value inside a local variable makes the intent clearer and prevents any accidental divergence if the underlying call ever acquires locks or performs NBT reads.

-        screen.addPreRenderAction(() -> redstoneChannelWidget.visible = dataAccess.getConnectionConfig()
-                .extractRedstoneControl()
-                .isRedstoneSensitive());
+        screen.addPreRenderAction(() -> {
+            var cfg = dataAccess.getConnectionConfig();
+            redstoneChannelWidget.visible = cfg.extractRedstoneControl().isRedstoneSensitive();
+        });
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (2)

24-28: Out-of-date inline comment

The colour picker now edits the extract channel, but the comment still says “Send channel”. Please update to avoid confusion.


35-39: Ditto for the right-hand channel

Rename the comment to “Insert channel” so it matches the renamed API.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)

52-54: Minor performance tweak

network.insertConnectionsFrom(extractConnection) is recomputed for every slot.
Fetching it once per extract connection and re-using the list avoids repeated cache look-ups.


112-120: Unused TODO

// TODO: is this necessary? was left above isEmpty.
Either remove the helper entirely (per previous comment) or replace the TODO with a concise explanation of why the helper is required.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java (1)

66-69: Stale TODO and outdated wording

The comment still reads // TODO: sendRedstoneControl, referencing the pre-refactor terminology.
Replace with something like // TODO: honour insertRedstoneControl (not implemented yet) or drop the line if no action is required.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1)

73-74: Unnecessary generic on setNodeData

<T extends NodeData> void setNodeData(@Nullable T data);

The generic parameter is unused in the method signature and offers no type safety (callers still pass a concrete NodeData). Dropping the <T …> avoids noise:

-    <T extends NodeData> void setNodeData(@Nullable T data);
+    void setNodeData(@Nullable NodeData data);
enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (1)

79-88: Javadoc still speaks “sending/receiving” after the insert/extract rename

The comments for insertConnections*/extractConnections* refer to “sending” and “receiving” which were replaced by “insert” and “extract”. This may confuse future contributors and modders reading the API.

Please update the phrasing to:

  • “all the insert-connections”
  • “all the extract-connections”

to remain consistent with the rest of the refactor.

Also applies to: 110-122

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)

63-66: Redundant null-check on context

network.getOrCreateContext(...) is guaranteed to return a non-null instance by contract.
The subsequent context != null guard can therefore be removed, which will slightly reduce byte-code and avoid misleading future readers.

-        if (!conduit.isMultiFluid()) {
-            if (context != null && !context.lockedFluid().equals(context.lastLockedFluid())) {
+        if (!conduit.isMultiFluid()
+                && !context.lockedFluid().equals(context.lastLockedFluid())) {

24-26: Guard against zero-rate conduits

When transferRatePerTick() or networkTickRate() returns 0 the loops below still iterate, performing capability look-ups each tick for no possible transfer.
An early return improves clarity and avoids needless work.

        final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+       if (fluidRate <= 0) {
+           return;
+       }
endercore/src/test/java/com/enderio/core/NetworkTests.java (2)

167-178: Logical expression is correct but hard to read – add parentheses for clarity

Although operator precedence makes the expression behave as intended, explicit parentheses around the second && term improve readability and prevent accidental mistakes during future edits.

-                if ((pair.getFirst() == indexA && pair.getSecond() == indexB)
-                        || pair.getFirst() == indexB && pair.getSecond() == indexA) {
+                if ((pair.getFirst() == indexA && pair.getSecond() == indexB)
+                        || (pair.getFirst() == indexB && pair.getSecond() == indexA)) {

23-30: Consider asserting node1’s network identity after connect

After network.connect(node1, node2, …) both nodes should still belong to the same network instance.
Adding Assertions.assertSame(network, node1.getNetwork()) would protect against unintended internal re-allocation.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (2)

50-53: Hard-coded placement face may fail in edge cases

The helper always supplies Direction.DOWN for the hit-result, which places a conduit as if the player clicked the top of the block below.
When tests attempt to place conduits on vertical faces or ceilings this will mis-behave.
Pass the desired face as a parameter (defaulting to Direction.UP) or compute it from the target position to make the utility more flexible.


60-68: Triple-nested loops could be replaced with BlockPos.betweenClosed

For clarity and marginal performance gains:

BlockPos.betweenClosed(startX, startY, startZ, endX, endY, endZ)
        .forEach(pos -> setBlock(pos, blockState));

avoids manual index management.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (1)

18-54: Declare static codec/constants as final

CODEC, STREAM_CODEC and TYPE should be static final to prevent accidental reassignment and help the JVM inline them.

-public static MapCodec<ChemicalConduitConnectionConfig> CODEC =
+public static final MapCodec<ChemicalConduitConnectionConfig> CODEC =

(same for the other two constants)

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (2)

24-31: Minor: inconsistent field name

For consistency with other configs and future data migration tools, prefer "extract_color" over "extract_redstone_channel" for the colour that is not a redstone channel.


91-105: Parameter shadowing makes the ‘with…’ helpers error-prone

Using the same identifier names (isInsert, isExtract) for both the record components and method parameters invites accidental mix-ups (see the previous PR that prompted this refactor). Consider prefixing parameters with new or using this. to make the intent explicit.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java (1)

18-54: Make codec/constants final

As above: mark CODEC, STREAM_CODEC, TYPE as static final.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)

24-31: Duplicate capability lookup – micro-optimisation

extractHandler is fetched here and again inside doChemicalTransfer, incurring two capability queries each tick per extract face.

Pass the handler as a parameter or cache it in the connection to avoid redundant calls.


65-121: Variable naming confusion (receiverHandler is actually the source)

Inside doChemicalTransfer the variable receiverHandler represents the extract side (the source).
This is counter-intuitive and likely to mislead future maintainers.

Consider renaming to sourceHandler and destinationHandler for clarity.

enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (2)

60-64: Update Javadoc to match the renamed method

The Javadoc still refers to “conduit graph ticks”, but the method has been renamed to networkTickRate(). This can cause confusion when reading the generated documentation.

- * @return the number of ticks that should pass before the conduit graph ticks.
+ * @return the number of ticks that should pass before the conduit network ticks.

141-145: Consider making the comparison deterministic for equal distances

compareNodes returns 0 when two connections have the same Manhattan distance to the reference node.
If this comparator is later used in a TreeSet/sorted() pipeline, equality will suppress one of the entries and may give non-deterministic ordering across JVMs.

Add a secondary comparison (e.g., by direction, block pos, or hashCode()) to guarantee strict ordering.

endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)

283-306: Potential network corruption when merging

When merging another network, nodes are added before edges, but the other graph’s edges are copied without clearing any self-loops.
If the other network contained a self-loop (which your graph forbids), putEdge will silently ignore it, but you lose the opportunity to warn developers.

Consider validating edges:

for (var edge : other.graph.edges()) {
    Preconditions.checkArgument(edge.nodeU() != edge.nodeV(), "Self-loops are not allowed.");
    graph.putEdge(edge);
}
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)

100-108: attach() never clears the shouldRebuildCache flag

attach() notifies the network via onNodeLoaded() but does not explicitly reset any pending rebuild flags.
If the node was created while the chunk was unloaded, the network flags a rebuild; once everything attaches, the cache is still rebuilt even though individual incremental updates would suffice.

Minor performance nit – resetting the flag inside onNodeLoaded() (or here) would avoid unnecessary cache churn when many chunks load simultaneously.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)

404-424: Sorting hot-path allocates comparators each tick

sortConnectionLists() re-sorts many lists every time haveConnectionsChanged is true, creating a new lambda comparator (ref, a, b -> …) for each call.

Cache the comparator per conduit instance (or make Conduit#compareNodes implement Comparator<ConduitBlockConnection> itself) to avoid this repeated allocation in the common ticking path.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)

1277-1286: Side-effects in setRemoved() can emit block updates after the BE is detached

setRemoved() calls setFacadeProvider(ItemStack.EMPTY), which ultimately triggers bundleChanged() and sends block updates while the block entity is in the process of being destroyed.
Depending on Forge event order this may re-enter removed objects or update neighbours that no longer exist.

It is safer to manipulate raw fields here (skip bundleChanged()), or move façade drop/clearing logic to onRemove() in the block class.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6e0501bf and 7d1bf03e24a43c72c05ab8410df14e022d055b46.

📒 Files selected for processing (100)
  • .github/workflows/verify.yml (1 hunks)
  • endercore/build.gradle.kts (2 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/Network.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/package-info.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/NetworkTests.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/TestNode.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (4 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (5 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (7 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (3 hunks)
  • enderio-conduits/build.gradle.kts (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (9 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (27 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (5 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java (6 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (7 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java (1 hunks)
  • enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java (1 hunks)
  • enderio/build.gradle.kts (1 hunks)
💤 Files with no reviewable changes (13)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java
🧰 Additional context used
🧬 Code Graph Analysis (15)
enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (2)
enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java (1)
  • NodeData (80-86)
enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)
  • EnderIOConduitsRegistries (12-54)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (3)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (1)
  • Deprecated (20-93)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1)
  • Deprecated (21-153)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1)
  • Deprecated (20-97)
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (1)
enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)
  • EIOLang (15-330)
enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1)
enderio-base/src/main/java/com/enderio/base/api/EnderIO.java (1)
  • EnderIO (5-11)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java (1)
  • EnergyConduitNetworkContext (11-72)
enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)
  • EnderIOConduitsRegistries (12-54)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1)
enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)
  • EIOLang (15-330)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1)
  • ConduitLang (9-109)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (1)
enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)
  • EIOLang (15-330)
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (1)
enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)
  • EIOLang (15-330)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (1)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (1)
  • RSConduitNodeData (22-142)
enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (1)
enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)
  • EIOLang (15-330)
endercore/src/test/java/com/enderio/core/NetworkTests.java (1)
endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)
  • BasicNetwork (10-32)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (1)
  • ChemicalConduitNetworkContext (12-76)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)
  • ConduitNode (25-299)
🪛 YAMLlint (1.35.1)
.github/workflows/verify.yml

[error] 42-42: trailing spaces

(trailing-spaces)

🔇 Additional comments (116)
enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java (2)

8-11: Clean implementation of persistence check

The addition of the isPersistent() method provides a clear way to determine if a node data type should be persisted based on the presence of a codec. This is a good design decision that makes the API more intuitive to use.


13-16: Good error handling for codec access

The codecOrThrow() method ensures that attempting to access a non-persistent node data's codec will fail with a clear error message rather than a null pointer exception. Using Guava's Preconditions for validation is appropriate here.

.github/workflows/verify.yml (3)

3-4: Security improvement by limiting permissions

Adding the permissions block with contents: read properly addresses the security concern flagged by the code scanning bot, following the principle of least privilege.


7-20: More granular workflow triggers

The refined triggers with specific event types and path filtering will reduce unnecessary CI runs, improving efficiency. Adding push triggers for dev branches ensures changes are verified before they can impact other developers.


47-54: Improved test granularity

Breaking down the build verification into separate steps (style check, game tests, unit tests) is an excellent improvement that provides clearer feedback on what might be failing during CI.

endercore/src/main/java/com/enderio/core/common/graph/package-info.java (1)

4-4: Appropriate package relocation

Moving the graph functionality from conduits-specific package to the core module improves code reusability and aligns with the architectural changes described in the PR objectives. This makes the graph system available for other components beyond conduits.

enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java (1)

3-3: Updated deprecation strategy appropriately

Good change to the deprecation annotation. Removing the forRemoval = true attribute while keeping the since = "8.0.0" version marker aligns with the PR's approach of introducing a new custom graph network system while maintaining backwards compatibility during transition.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java (1)

10-10: Consistent deprecation approach for legacy components

Good update to the deprecation annotation, removing the forRemoval = true attribute while maintaining the version marker. This change follows the same approach as other legacy components in this PR, allowing for a smoother transition to the new network system.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java (1)

1-4: Good addition of nullability annotations

These package-level annotations establish that parameters and return values within this package are non-null by default, which promotes null safety and reduces the risk of NullPointerExceptions. This is particularly important for network-related code that serves as a critical foundation for the conduit system.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (2)

20-20: Consistent deprecation approach

Appropriate update to the deprecation annotation, removing the forRemoval = true attribute while maintaining the version marker. This follows the same pattern as other legacy components in this PR.


68-71: Future refactoring noted in TODO

The TODO comment mentions a pending change to use records instead of classes. Consider addressing this in a follow-up PR to complete the modernisation of these data structures.

enderio/build.gradle.kts (1)

105-108: Adding game test server configuration - good addition for testing conduit networks.

This change adds a new run configuration for game tests, which aligns with the PR's goal of comprehensive evaluation through both in-game playtesting and automated testing.

enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java (1)

27-27: Updated Javadoc to reference the new interface-based node abstraction.

The Javadoc now correctly references IConduitNode interface instead of the concrete ConduitNode implementation, which aligns with the architectural shift to decoupled, interface-based abstractions.

enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java (1)

138-138: Terminology update in commented code: receive → extract.

The terminology change from receiveRedstoneChannel() to extractRedstoneChannel() aligns with the broader refactoring across the codebase where "send"/"receive" terms are being replaced with "insert"/"extract" for consistency. While this is in commented code, it's good practice to maintain the updated terminology.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1)

20-20: Softening deprecation stance on legacy class.

The forRemoval = true attribute has been removed from the @Deprecated annotation, indicating this legacy class will remain in the codebase indefinitely. This aligns with other legacy components being preserved for backward compatibility during the transition to the new network system.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1)

21-21: Softening deprecation stance on legacy class.

The forRemoval = true attribute has been removed from the @Deprecated annotation, indicating this legacy class will remain in the codebase indefinitely. This makes sense given that the class implements a conversion to the new node data format (via the toNodeData() method), serving as a bridge between old and new systems.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java (1)

19-19: Softening deprecation stance on legacy enum.

The forRemoval = true attribute has been removed from the @Deprecated annotation, indicating this legacy enum will remain in the codebase indefinitely. This is part of a consistent pattern with other legacy components and suggests a more cautious approach to backward compatibility during the transition to the new network system.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java (1)

23-23: Softened deprecation approach makes sense for legacy transition

The removal of forRemoval = true while keeping the since = "8.0.0" deprecation notice indicates a more cautious approach to phasing out legacy code. This is appropriate given the scope of the refactoring, as it provides more time for dependent code to migrate to the new network system while clearly signaling the deprecated status.

enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java (1)

34-34: Method name change aligns with new terminology standards

The change from canSend to canInsert maintains the same logical function while adopting the more consistent "insert/extract" terminology now used throughout the codebase. This improves code clarity by using terms that better reflect the directional flow of resources in the conduit system.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java (1)

5-5: Consistent deprecation approach for legacy interfaces

The removal of forRemoval = true while maintaining the since = "8.0.0" deprecation is consistent with the approach taken for other legacy components. This measured transition strategy allows for proper migration to the new network system without immediately breaking dependent code.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java (1)

8-8: Consistent deprecation strategy applied to data types

The deprecation annotation change follows the same pattern as other legacy components, providing a clear transition path while maintaining compatibility during migration to the new network system. This is especially important for data types that may be referenced by persistent storage or serialization formats.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (2)

10-10: JavaDoc change aligns with the interface-based architecture

Updated JavaDoc correctly references IConduitNode interface instead of the concrete ConduitNode class, which aligns with the broader refactoring towards more abstract interfaces in the codebase.


15-15: Changed to fail-fast approach with codecOrThrow

The change from NodeDataType::codec to NodeDataType::codecOrThrow indicates a design shift toward failing fast when codecs are missing rather than handling optional/nullable results. This is appropriate for required codecs that should always be present, improving error detection.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java (1)

177-179: New handler method for connection config updates

This new method provides a dedicated entry point for handling connection configuration updates from network packets, which improves code organization by separating packet handling from the actual configuration update logic.

enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java (2)

9-9: Updated import for IConduitNode interface

The import is correctly updated to reference the interface rather than the concrete implementation class, supporting the architectural changes in this PR.


62-62: Return type changed to interface for better abstraction

The method signature now returns the IConduitNode interface instead of the concrete ConduitNode class. This improves abstraction and decoupling, allowing more flexibility in implementation details while maintaining the contract. This is a good design practice for APIs.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java (1)

1-4: Added package-level nullability annotations for test code

These annotations establish default nullability contracts for the test package, improving static analysis and null safety:

  • @ParametersAreNonnullByDefault ensures all parameters are non-null by default
  • @MethodsReturnNonnullByDefault ensures all methods return non-null values by default

This is good practice for ensuring null safety in test code and makes explicit when null values are expected or permitted.

enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java (1)

13-13: Encapsulation improvement using handler method

This change enhances encapsulation by replacing a direct setter call with a handler method that takes the entire packet. This approach allows for more complex processing, validation, or side-effects when updating connection configurations, which aligns well with the new network system.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java (1)

1-4: Good practice with nullability annotations

Adding these nullability annotations to the test package is excellent practice. It helps catch potential null pointer exceptions at compile time rather than runtime, which will improve code quality and reduce bugs in the test suite.

endercore/build.gradle.kts (1)

21-42: Well-configured test infrastructure

The addition of JUnit 5 testing dependencies and proper configuration of the test task is excellent. This setup will support the comprehensive testing needed for the new graph network system mentioned in the PR description.

enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (2)

53-55: Consistent terminology change from 'send' to 'insert'

The change from isSend() to isInsert() improves terminology consistency in the codebase. This aligns with the constants leftTitle and rightTitle which already use "insert" terminology.


57-59: Consistent terminology change from 'receive' to 'extract'

The change from isReceive() to isExtract() improves terminology consistency in the codebase. This aligns with the constants leftTitle and rightTitle which already use "extract" terminology.

enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (3)

58-58: Consistent terminology update from "Graph Ticks" to "Network Ticks"

The terminology change aligns with the new custom graph network system implementation mentioned in the PR objectives.


64-64: Updated tooltip to use consistent network terminology

The fluid rate tooltip now references "network tick" instead of "graph tick", maintaining terminology consistency across the codebase.


72-72: Updated item rate tooltip to use consistent network terminology

Similar to the fluid rate tooltip, this update ensures consistent terminology by using "network tick" instead of "graph tick".

enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java (1)

26-29: Method calls updated to use new insert/extract terminology

The connection configuration accessors have been updated from send/receive to insert/extract terminology to align with changes across the conduit system. This maintains functional equivalence while using the new API naming conventions.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java (4)

461-461: Updated redstone signal check to use new insert terminology

Changed from canSend to canInsert to align with the unified terminology in the new network system.


497-497: Updated redstone signal condition to use insert terminology

Consistently applied terminology change from send to insert for the redstone signal condition check.


521-521: Updated output signal colour method to use insert terminology

Changed from sendColor() to insertColor() to align with the new network API naming.


524-524: Updated signal context method to use insert terminology

Final update to use insertColor() instead of sendColor() for signal context handling.

enderio-conduits/build.gradle.kts (1)

44-47: Added Neo test framework dependency

Added a dependency on the Neo test framework with transitive dependencies disabled. This supports the implementation of comprehensive game tests for the new conduit network system mentioned in the PR objectives.

enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java (1)

4-4: Improved API design with interface-based approach

The interface has been updated to use IConduitNetwork instead of a concrete implementation, which is a good architectural improvement. This abstraction allows for better modularity and flexibility in the conduit system.

The method signature has also been simplified from tickGraph to tick with fewer parameters, making the API cleaner and more focused.

Also applies to: 8-8

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java (2)

3-3: Good use of abstraction with interface types

Using the IConduitNode interface instead of a concrete implementation improves design flexibility and follows interface-based programming principles.

Also applies to: 6-6


49-49: Improved network querying approach

Using the cached nodeCount() method instead of directly accessing the node collection size is better for performance, which aligns with the PR objective of improving conduit ticker performance by caching network information.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java (1)

8-8: Better deprecation strategy for smoother transition

Removing the forRemoval = true attribute from the @Deprecated annotation is a good decision as it provides better backward compatibility for mods that might still be using the legacy API while transitioning to the new network system.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java (5)

3-5: Updated imports for new functionality

Good addition of Guava's Preconditions for validation and switching to MapCodec for better structured data serialization.


8-13: Added helpful Javadoc documentation

The addition of detailed Javadoc comments describing the record's parameters improves code readability and helps API users understand the purpose of each parameter.


13-13: Improved serialization with MapCodec

Using MapCodec<T> instead of Codec<T> is a good upgrade for structured data serialization, which will help with the persistence of the new network system.


15-17: Useful helper method for persistence checking

The addition of the isPersistent() method provides a clean way to check if the context needs to be saved, which is essential for network context management.


19-22: Added safe codec access method

The codecOrThrow() method with Preconditions validation provides safer access to the codec, preventing null pointer exceptions and giving better error messages when issues occur.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (3)

3-3: Updated import to use the interface instead of concrete class.

The import has been updated to use IConduitNode interface rather than the concrete ConduitNode implementation, aligning with the new network abstraction architecture of the system.


23-23: Updated NodeDataType constructor to use null codec.

The TYPE field is now initialized with a null codec parameter instead of CODEC. This change is part of the broader refactoring to support nullable codec handling in the NodeDataType record.


36-36: Method signature updated to use interface type.

Method parameter type changed from concrete ConduitNode to IConduitNode interface. This improves flexibility and allows for different implementations of the conduit node interface to be used.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (3)

1-1: Moved class to legacy package.

The class has been moved from the graph package to the legacy package, correctly indicating its status as part of the old implementation being phased out.


18-18: Modified deprecation annotation.

The @Deprecated annotation has been updated to remove the forRemoval = true attribute. This indicates the class is still needed for compatibility but is no longer targeted for immediate removal.

The removal of the forRemoval = true attribute suggests this class will be maintained longer than originally planned, likely to support backward compatibility.


1-89:

Verification successful

Removed STREAM_CODEC field and related functionality.

The STREAM_CODEC field appears to have been removed from this class. Examining the relevant code snippets from other legacy conduit data classes, this change is consistent with the broader refactoring effort to replace the old serialization mechanism.


🏁 Script executed:

#!/bin/bash
# Verify that STREAM_CODEC is consistently removed from legacy classes
grep -r "STREAM_CODEC" . --include="*.java" | grep -v "legacy"

Length of output: 59405


🏁 Script executed:

#!/bin/bash
# Verify that no STREAM_CODEC declarations exist in the conduit legacy package
find . -type f -path "*/conduit/legacy/*.java" -name "*.java" | xargs grep -n "STREAM_CODEC" || echo "No STREAM_CODEC declarations in legacy package."

Length of output: 3649


Removed STREAM_CODEC field and related functionality.

  • ConduitDataContainer no longer declares a STREAM_CODEC, as intended.
  • Other legacy conduit data classes (e.g. ConnectionState, DynamicConnectionState, LegacyFluidConduitData, LegacyItemConduitData, LegacyRedstoneConduitData, StaticConnectionStates) still use STREAM_CODEC, confirming this change is specific to the container and not part of a blanket removal.
    No further action required.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java (2)

83-86: Interface method updated from copy() to split().

The method has been updated from copy() to split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks), aligning with the new ConduitNetworkContext interface requirements. This change supports the new network splitting mechanism in the refactored conduit system.


88-90: Original copy() method preserved as a private helper.

The original copy() implementation has been preserved as a private helper method. This is a good practice as it maintains the core functionality while adapting to the new interface requirements.

endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)

1-33: Well-structured implementation of the new network system.

This new class provides a clean, basic implementation of the abstract Network class for nodes implementing INetworkNode. The implementation includes:

  1. Multiple constructors for different initialization scenarios
  2. Proper override of the createEmpty method
  3. Clear type parameters and inheritance

This implementation serves as a good foundation for the custom graph solution.

enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (1)

34-38:

Incorrect review comment

Prefer the injected eventBus over the global bus to avoid double registration
You already receive the module-scoped IEventBus via the constructor. Using NeoForge.EVENT_BUS as well risks registering the same listener twice if another entry-point repeats this call, and makes unit tests harder because they can’t substitute the global bus.

-        NeoForge.EVENT_BUS.addListener((final RegisterCommandsEvent event) -> {
+        eventBus.addListener((final RegisterCommandsEvent event) -> {

I’ll verify that EnderIOConduitsServer indeed receives and stores a module-scoped IEventBus before recommending switching to it.


🏁 Script executed:

#!/bin/bash
# Find the class definition and its constructor
rg --context 3 "class EnderIOConduitsServer" -n enderio-conduits
rg --context 5 "public EnderIOConduitsServer" -n enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java
# Check for the eventBus field
rg -n "eventBus" -n enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java

Length of output: 2056


Ignore the injected bus suggestion for command registration

The injected eventBus is the FML mod‐scoped bus, which does not fire RegisterCommandsEvent. That event is only dispatched on NeoForge.EVENT_BUS (the global bus), so switching to eventBus.addListener(…) would break your commands. You should keep using NeoForge.EVENT_BUS.addListener(…) here.

Likely an incorrect or invalid review comment.

enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (2)

107-108: Left-hand enable toggle migrated correctly.
The new call to withIsInsert matches the terminology shift and keeps behaviour unchanged.


112-113: Right-hand enable toggle migrated correctly.
Likewise, withIsExtract is the correct counterpart.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (1)

42-44: Terminology update acknowledged.
networkTickRate replaces graphTickRate cleanly; no further action needed.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java (2)

35-38: Good abstraction with clear null-safety.

The generic return type and nullable annotation on getSidedCapability make intent explicit and avoid unchecked casts. No issues spotted.


63-66: Type-safety guarantees are excellent.

Throwing when a mismatched ConnectionConfigType is requested prevents subtle runtime errors. Nice touch.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

15-26: Efficient iteration and capability access.
The early-continue pattern avoids unnecessary inner-loop work when a capability is absent. Looks good.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java (1)

1-56: Well-structured test helper that enhances conduit testing capabilities

This new helper class provides valuable methods for testing item conduit functionality:

  • Inserts items into containers with proper slot handling
  • Asserts exact item counts with detailed error messages
  • Extends the base helper with item-specific testing capabilities

The implementation is robust with appropriate error handling and clear exception messages. This will significantly improve the test coverage for item conduits.

enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (5)

20-20: Terminology update from "receive" to "extract"

The change aligns with the broader effort to use more intuitive terminology across the conduit system.


24-25: Consistent terminology update for redstone control

Changed from receiveRedstoneControl() to extractRedstoneControl(), maintaining consistency with the new terminology.


28-29: Unified terminology for redstone control picker

Both the getter and setter now use the "extract" terminology, creating a more consistent API.


36-36: Updated connection enabled setter

Changed from withIsSend to withIsInsert to match the new terminology.


41-41: Updated right connection enabled setter

Changed from withIsReceive to withIsExtract for consistent terminology.

enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java (6)

10-13: Updated documentation and method name to use "insert" terminology

This change improves clarity by using more appropriate terminology for describing the direction of resource flow.


14-18: Updated documentation and method name to use "extract" terminology

Consistent terminology update that better describes the action of taking resources from a connected block.


19-26: Improved documentation for insert color channel

Documentation now more clearly explains the purpose of the insert color channel for separation in the ticker, along with the method name change.


27-34: Consistent terminology update for extract color channel

Updated method name and documentation to maintain consistency across the API.


35-41: Updated default methods to use insert/extract terminology

Both method names and their implementations have been consistently updated to use the new terminology.


43-46: Updated isConnected() implementation to use new method names

The implementation now correctly uses the renamed methods while maintaining the same functionality.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java (7)

14-15: Updated record fields to use insert/extract terminology

The record's fields have been renamed to maintain consistency with the interface changes, using more intuitive terminology.


20-28: Updated codec field names to match the new terminology

The codec field mappings have been consistently updated to use the new field names, ensuring proper serialization/deserialization.


29-34: Updated stream codec to use new method references

All method references in the stream codec definition have been updated to use the new terminology.


39-48: Updated reconnected() and disconnected() implementations

Both methods now use the new field names while maintaining the same functionality.


50-58: Updated withIsInsert and withInsertColor methods

These methods replace the previous withIsSend and withSendColor methods, maintaining the same functionality with more intuitive naming.


60-68: Updated withIsExtract and withExtractColor methods

These methods replace the previous withIsReceive and withReceiveColor methods while maintaining the same functionality.


70-73: Updated withIsStrongOutputSignal implementation

The method implementation now uses the new field names while maintaining the same functionality.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (1)

35-37: Signal reset relies on hidden nextTick() semantics

context.setSignal(channel, signal) is only invoked when signal > 0.
If a previously powered channel now reads 0, its entry is never updated here.
The correctness therefore hinges on context.nextTick() resetting all channel values to 0. If that assumption changes, the network will erroneously stay powered.

Please confirm that nextTick() indeed clears the per-channel map; otherwise explicit else { context.setSignal(channel, 0); } should be added.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (1)

71-80: Priority comparison looks good

The custom compareNodes implementation correctly orders higher-priority connections first and falls back to the default comparator when equal — clear and concise.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1)

16-29: Redstone widgets updated correctly

The switch from receive to extract terminology is implemented consistently across the colour picker, visibility predicate and control picker. No further issues spotted.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (5)

7-11: Documentation is well-structured and clear

The JavaDoc nicely describes the purpose of this interface and clarifies that it represents additional information stored on a conduit network that isn't synced to clients.


13-14: Good use of registry-based codec system

The implementation of GENERIC_CODEC using the registry system is a clean approach for handling serialization of different context types.


16-21: Clear documentation for mergeWith

The JavaDoc for mergeWith now clearly indicates that it may return either a new instance or the same instance if no merging is needed.


24-31: Improved context splitting design

Replacing the ambiguous copy() method with a more specific split() method and providing clear documentation about the requirement for returning a new instance is a good design choice. This makes the lifecycle of network contexts more explicit.


33-36: Type method facilitates serialization

Adding the type() method is a good design choice as it enables the registry-based serialization approach and makes type comparisons more straightforward.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java (4)

97-97: Good abstraction with interface instead of concrete class

Changing the parameter type from concrete ConduitNode to interface IConduitNode improves abstraction and flexibility.


145-146: Improved legacy data copy mechanism

Adding BiConsumer<Direction, ConnectionConfig> provides a flexible way to set connection configurations during legacy data migration, improving modularity.


191-191: Consistent use of interface abstraction

Method signatures consistently updated to use IConduitNode interface instead of concrete implementation class.

Also applies to: 197-197


229-229: Updated tooltip calculation

Changed graphTickRate() to networkTickRate() to align with the new network terminology, which is consistent with the updated network system.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (2)

14-38: Well-structured test for network splitting

The test effectively validates that conduits form a single network when placed in a row and that breaking a conduit splits the network. The use of sequence-based testing with clear steps makes the test easy to understand.


40-64: Good test case for network merging

This test properly validates that separate conduit networks merge when connected by a new conduit. The test setup is clear and the assertions verify the expected behavior.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (1)

13-13: Interface import improves decoupling – good move

Switching from the concrete ConduitNode class to the IConduitNode interface reduces coupling and will make testing and future extensions easier.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (2)

48-50: Method name reflects new terminology – nice catch

networkTickRate() fits the new network vocabulary and keeps parity with other conduit types.


134-137: Capability invalidation on removal looks correct

Calling level.invalidateCapabilities(pos) guarantees that adjacent blocks re-query capabilities after the conduit is gone.

enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (2)

56-60: Terminology update applied correctly

Insert-side colour picker now uses insertColor() / withInsertColor() – keeps UI & config consistent.


111-117: setRightEnabled toggle aligned with new API – good
Right-hand side now toggles isExtract, mirroring the backend rename.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)

18-19: Possible double-count when multiplying by networkTickRate()

transferRate is already defined as “per tick” on the conduit.
Multiplying by networkTickRate() means the total energy moved over one logical conduit-tick becomes rate * networkTickRate() * connectionsProcessed, which can exceed the intended throughput.

Confirm design intent; if the goal is per conduit tick, simply use conduit.transferRatePerTick() here.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (1)

105-112: Semantics now consistent – good change

Switching the enabled-state helpers to withIsInsert/Extract correctly matches the new terminology.

enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (1)

47-65: Behaviour matches the new API – looks good

Getter / setter rewiring from send/receive to insert/extract is correct and consistent.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1)

38-40: Clarify null-safety contract of getNetwork()

getNetwork() is not annotated @Nullable, implying it must never return null.
If nodes can temporarily be orphaned (e.g., during chunk unload or split/merge operations) please annotate accordingly or document the guarantee so callers do not need extra null-checks.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (1)

68-72: TODO left behind – insert redstone control ignored

canInsert blindly returns isInsert(), so redstone-sensitive inserts can never be disabled by a signal. Either implement the control (mirroring canExtract) or drop the unused fields from the record.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (7)

8-10: Import updates align with the network API refactoring

The imports have been updated to use the new network API interfaces. Changing from concrete implementation types to interfaces (particularly IConduitNode) follows good design principles and enables greater flexibility in the conduit system.


37-37: Transfer rate type change supports larger throughput

Changing the transferRatePerTick field type from int to long allows for much larger transfer rates, which is beneficial for high-throughput chemical transport systems in modded environments.


49-49: Codec update matches field type change

The codec has been properly updated to use Codec.LONG to match the field type change, ensuring consistent serialization/deserialization of conduit data.


99-99: Updated parameter type to use interface instead of implementation

The method signature has been updated to use the new IConduitNode interface instead of a concrete implementation, which is consistent with the network API refactoring.


141-142: Enhanced connection configuration handling

The method signature changes:

  1. Use IConduitNode interface instead of concrete implementation
  2. Replace Consumer<Direction> with BiConsumer<Direction, ConnectionConfig> to allow setting connection configuration directly

This provides more flexibility and integrates better with the new network system.


187-192: Updated method signatures for GUI and world data

Both methods have been updated to use the IConduitNode interface instead of concrete implementations, maintaining consistency across the codebase with the new network abstraction.


224-224: Updated tick rate calculation for tooltip

The raw transfer rate calculation now correctly uses networkTickRate() instead of the deprecated graphTickRate(), aligning with the new network system.

**Actionable comments posted: 33** <details> <summary>🔭 Outside diff range comments (7)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (1)</summary><blockquote> `57-66`: _💡 Verification agent_ <details> <summary>❓ Verification inconclusive</summary> **Merging networks with incompatible locked fluids may silently corrupt state** If both networks have non-empty but *different* `lockedFluid` values, the current logic just keeps the caller’s value, discarding the other without warning. This could allow two incompatible fluid channels to merge invisibly. Consider: 1. Validating that the two fluids match and throwing/logging when they don’t; or 2. Choosing a deterministic resolution strategy and documenting it. --- </details> **Handle conflicting locked fluids during network merge** The current mergeWith implementation silently retains the caller’s non‐empty lockedFluid and discards the other’s without warning whenever they differ. This can lead to inconsistent network state if two fluid channels with different locked fluids are merged. Please update FluidConduitNetworkContext.mergeWith to one of the following: - Validate that both lockedFluid values are equal before merging, and throw or log an error if they differ. - Define and document a deterministic resolution strategy (for example, always pick the lexicographically lesser fluid or prefer the other network’s fluid). Locations to address: - File: enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java - Method: mergeWith, lines 57–66 Example validation snippet: ```java if (!lockedFluid.equals(Fluids.EMPTY) && !other.lockedFluid.equals(Fluids.EMPTY) && !lockedFluid.equals(other.lockedFluid)) { throw new IllegalStateException( "Cannot merge networks with different locked fluids: " + lockedFluid + " vs " + other.lockedFluid); } ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (1)</summary><blockquote> `87-90`: _⚠️ Potential issue_ **`convertConnection` ignores two of its parameters – potential logic defect.** `redstoneControl` and `redstoneChannel` are accepted but never used when constructing `RedstoneConduitConnectionConfig`. This means UI selections for redstone behaviour are silently discarded. ```diff - return new RedstoneConduitConnectionConfig(isInsert, inputChannel, isExtract, outputChannel, false); + return new RedstoneConduitConnectionConfig( + isInsert, + inputChannel, + isExtract, + outputChannel, + /* isSelfFeed */ false, + redstoneControl, + redstoneChannel); ``` (Adjust the constructor to match the actual field order/availability.) Failing to persist those values will break redstone-controlled operation after a world reload or when opening the screen again. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary><blockquote> `29-32`: _⚠️ Potential issue_ **Division-by-zero risk in heat transfer calculation.** `invConduction` can be `0` if both handlers report zero inverse conduction, leading to an infinite temperature difference and `NaN` heat transfer. ```diff - double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature()) - / invConduction; + if (invConduction == 0) { + continue; // nothing to conduct + } + double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature()) + / invConduction; ``` Add the guard (or clamp to a minimum epsilon) to prevent floating-point anomalies and potential downstream crashes. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (2)</summary><blockquote> `133-143`: _⚠️ Potential issue_ **Null-check missing when migrating legacy data** `legacyData.get(side)` may return `null`. Calling `oldSideConfig.isRoundRobin` etc. will then crash with an NPE during world upgrade. ```diff -var oldSideConfig = legacyData.get(side); -... -connectionConfigSetter.accept(side, - currentConfig.withIsRoundRobin(oldSideConfig.isRoundRobin) - .withIsSelfFeed(oldSideConfig.isSelfFeed) - .withPriority(oldSideConfig.priority)); +var oldSideConfig = legacyData.get(side); +if (oldSideConfig != null) { + connectionConfigSetter.accept(side, + currentConfig.withIsRoundRobin(oldSideConfig.isRoundRobin) + .withIsSelfFeed(oldSideConfig.isSelfFeed) + .withPriority(oldSideConfig.priority)); +} ``` Please add this guard before dereferencing. --- `183-190`: _⚠️ Potential issue_ **Inconsistent API usage – `receiveRedstoneChannel()` call remains** The surrounding refactor switched to the *extract* terminology (`extractRedstoneChannel()` & friends), yet the call to: ```java tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.receiveRedstoneChannel())); ``` still references `receiveRedstoneChannel()`. This will fail to compile once the old accessor is removed, or worse, access the wrong channel if it still exists. ```diff -tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.receiveRedstoneChannel())); +tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.extractRedstoneChannel())); ``` Please update the accessor for consistency. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)</summary><blockquote> `35-54`: _⚠️ Potential issue_ **Total energy moved in one tick is unbounded by `transferRate`** Within the loop, every connection may receive up to `transferRate`, so if five inserts are processed the network could move `5 × transferRate` in a single server tick. Track remaining transferable energy instead: ```diff - int startingRotatingIndex = context.rotatingIndex(); + int remaining = transferRate; + int startingRotatingIndex = context.rotatingIndex(); - for (int i = startingRotatingIndex; i < startingRotatingIndex + inserts.size(); i++) { + for (int i = startingRotatingIndex; i < startingRotatingIndex + inserts.size() && remaining > 0; i++) { int insertIndex = i % inserts.size(); var sendingConnection = inserts.get(insertIndex); IEnergyStorage insertHandler = sendingConnection.getSidedCapability(Capabilities.EnergyStorage.BLOCK); if (insertHandler == null || !insertHandler.canReceive()) { continue; } - int energyToInsert = Math.min(transferRate, Math.max(context.energyStored(), 0)); + int energyToInsert = Math.min(remaining, context.energyStored()); int energyInserted = insertHandler.receiveEnergy(energyToInsert, false); context.setEnergyStored(context.energyStored() - energyInserted); - context.setRotatingIndex(insertIndex + 1); + context.setRotatingIndex(insertIndex + 1); + remaining -= energyInserted; if (context.energyStored() <= 0) { break; } } ``` </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (1)</summary><blockquote> `60-67`: _⚠️ Potential issue_ **`reconnected()` / `disconnected()` return `null` – guaranteed NPE downstream** Many callers expect these methods to return a non-null config (see other connection configs). Returning `null` will crash whenever a conduit is broken or re-placed. Please provide proper implementations mirroring the logic used in `ChemicalConduitConnectionConfig`. </blockquote></details> </blockquote></details> <details> <summary>🧹 Nitpick comments (50)</summary><blockquote> <details> <summary>.github/workflows/verify.yml (1)</summary><blockquote> `42-42`: **Remove trailing whitespace** There's trailing whitespace on this line that should be removed. ```diff - distribution: 'temurin' - + distribution: 'temurin' ``` <details> <summary>🧰 Tools</summary> <details> <summary>🪛 YAMLlint (1.35.1)</summary> [error] 42-42: trailing spaces (trailing-spaces) </details> </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)</summary><blockquote> `17-17`: **Softened deprecation policy** Removing `forRemoval = true` from the deprecation annotations indicates a change in policy to maintain backwards compatibility longer than initially planned. This is a considerate approach for mod developers who might be using the deprecated registry. However, consider adding Javadoc comments to clearly indicate the migration path from the deprecated registry to the new system, which would help developers transition their code more easily. Also applies to: 32-32 </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java (1)</summary><blockquote> `133-135`: **Method name updated but tooltip key remains inconsistent.** While you've correctly updated the method call from `graphTickRate()` to `networkTickRate()` to align with the new conduit network system, the tooltip key `ConduitLang.GRAPH_TICK_RATE_TOOLTIP` still references the old "graph" terminology. Consider updating the tooltip key to match the new terminology: ```diff - tooltipComponents.add(TooltipUtil.styledWithArgs(ConduitLang.GRAPH_TICK_RATE_TOOLTIP, + tooltipComponents.add(TooltipUtil.styledWithArgs(ConduitLang.NETWORK_TICK_RATE_TOOLTIP, 20 / conduit.value().networkTickRate())); ``` This would require updating the corresponding language key in the appropriate lang file as well. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (1)</summary><blockquote> `61-69`: **Update documentation comments for consistency with code changes** The method documentation still refers to "isSend" and "isReceive" but the code has been updated to use "isInsert" and "isExtract". For complete consistency, consider updating these comments as well. ```diff /** - * Unless you changed the order of the titles, this should edit isSend. + * Unless you changed the order of the titles, this should edit isInsert. */ protected abstract U setLeftEnabled(U config, boolean isEnabled); /** - * Unless you changed the order of the titles, this should edit isReceive. + * Unless you changed the order of the titles, this should edit isExtract. */ protected abstract U setRightEnabled(U config, boolean isEnabled); ``` </blockquote></details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java (2)</summary><blockquote> `5-6`: **Generic bounds could be simplified to lower boiler-plate** `TNode` already extends `INetworkNode<TNet, TNode>`, so repeating `TNet`’s own bound (`extends Network<TNet, TNode>`) inside it is redundant noise for the caller and makes the type declaration intimidating. Unless you truly need the recursive double bind, consider: ```diff -public interface INetworkNode<TNet extends Network<TNet, TNode>, TNode extends INetworkNode<TNet, TNode>> { +public interface INetworkNode<TNet extends Network<?, ?>, TNode extends INetworkNode<TNet, TNode>> { ``` or even extracting a separate `N extends Network<?,?>` alias. It keeps the self-type property while reducing the mental load. --- `10-16`: **Null-safety annotations missing on `getNetwork`** `getNetwork()` promises to return a non-null value when `isValid()` is true, yet this isn’t expressed. Add `@NotNull` (JetBrains) or Jakarta/JSR-305 equivalents so static analysers can help catching misuse. ```diff - TNet getNetwork(); + @NotNull TNet getNetwork(); ``` </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (2)</summary><blockquote> `14-18`: **`CODEC` should be `final` to prevent accidental reassignment** Static mutable fields that represent constants are prone to unexpected mutations (especially through mixins or reflective test code). Mark it `final` like your other context classes do: ```diff - public static MapCodec<ChemicalConduitNetworkContext> CODEC = RecordCodecBuilder.mapCodec(instance -> instance + public static final MapCodec<ChemicalConduitNetworkContext> CODEC = RecordCodecBuilder.mapCodec(instance -> instance ``` The same applies to `TYPE` just below for consistency. --- `66-70`: **Unused parameters in `split` – either document or suppress** `selfNetwork` and `allNetworks` are currently ignored. That’s absolutely fine (no split-specific state), but the intent should be explicit to silence IDE warnings: ```diff -public ChemicalConduitNetworkContext split(IConduitNetwork selfNetwork, - Set<? extends IConduitNetwork> allNetworks) { +@SuppressWarnings("unused") // Context split does not depend on network size +public ChemicalConduitNetworkContext split(IConduitNetwork selfNetwork, + Set<? extends IConduitNetwork> allNetworks) { ``` Alternatively rename them to `_unusedSelf` / `_unusedAll` if you follow that convention. </blockquote></details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)</summary><blockquote> `10-10`: **Consider adding more documentation for type parameters.** While the class has good overall documentation, consider expanding on what the type parameters represent, especially how the recursive type parameter `N extends INetworkNode<BasicNetwork<N>, N>` works. ```diff - /** - * A basic network implementation with no special context. - * @param <N> The node type. - */ + /** + * A basic network implementation with no special context. + * @param <N> The node type that implements INetworkNode with this network type. + * The recursive type parameter enables nodes to reference their containing network type. + */ ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (2)</summary><blockquote> `22-30`: **Make the framework instance a `final` field or local variable only** `MutableTestFramework framework` is created and immediately used but never retained. If another subsystem needs to query the framework later (e.g., from other event-bus callbacks), you’ll have no reference to it. Either: 1. Store it in a `private final` field, or 2. If it truly is fire-and-forget, declare it as a local `final` variable so the intent is obvious. ```diff - final MutableTestFramework framework = FrameworkConfiguration + final var framework = FrameworkConfiguration ``` --- `35-36`: **Name collision risk for the root `tests` command** Several mods expose a root-level `tests` command. Collisions cause one command to shadow the other, depending on load order. Consider namespacing it (`enderio_tests`) or placing it under your mod’s root literal (`/enderio tests …`). </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (1)</summary><blockquote> `15-20`: **Declare constants `final` to guarantee immutability** `CODEC` is mutable at runtime. Mark it `public static final` (likewise for `TYPE`) to prevent accidental reassignment and aid inlining during optimisation. ```diff - public static MapCodec<FluidConduitNetworkContext> CODEC = + public static final MapCodec<FluidConduitNetworkContext> CODEC = ``` </blockquote></details> <details> <summary>endercore/src/test/java/com/enderio/core/TestNode.java (3)</summary><blockquote> `13-16`: **Avoid eager network creation in tests to reflect real usage** Creating a brand-new `BasicNetwork` in every default constructor means a lone node is *always* part of a network, which might mask bugs where nodes start unassigned. Consider leaving `network` as `null` by default and providing a helper in tests that explicitly wires nodes. --- `17-21`: **Constructor duplication – forward to the primary constructor** `TestNode(boolean)` manually repeats the network-creation logic. Delegate to the no-arg constructor to keep behaviour in one place: ```diff - if (shouldCreateNetwork) { - network = new BasicNetwork<>(this); - } + if (shouldCreateNetwork) { + new TestNode(); // or `this();` + } ``` --- `24-26`: **`isValid` should consult network state, not just nullity** If the network later becomes invalidated/removed, `network != null` will still return `true`. Expose a `BasicNetwork#isValid()` (or similar) and delegate: ```java return network != null && network.isValid(); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (2)</summary><blockquote> `52-54`: **Use of “insertColour” looks correct – just ensure language keys are updated.** The renaming from `send/receive` to `insert/extract` is consistent here. Verify that the associated localisation keys (`ConduitLang.CONDUIT_CHANNEL`) still make sense in-game (e.g., “Insert Channel” rather than “Send Channel”) so players are not shown legacy wording. --- `86-99`: **Minor optimisation: avoid repeated config look-ups inside lambdas.** Every frame the lambdas passed to `addColorPicker` / `addRedstoneControlPicker` capture `dataAccess` and call `getConnectionConfig()` again. The extra GC pressure is negligible, but you could slightly reduce allocations by storing the config once before registering the widgets, in line with the other GUI screen implementations. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (1)</summary><blockquote> `66-74`: **Consider implementing planned tests** The TODOs outline important test cases that should be implemented to ensure robust network behavior, particularly around redstone signalling and transfer resumption after network breaks. Would you like help implementing these additional test cases? I can draft test methods for redstone signalling and transfer behavior verification. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (2)</summary><blockquote> `81-102`: **Consider early-returning when the node already owns a ready main grid node** Inside `onCreated`, the `mainNode.isReady()` guard is checked only after a potential (re-)initialisation of the node data. If the conduit is *re-placed* in the same position (e.g. via schematic/structure loading) `initMainNode()` could be invoked even though the underlying `IManagedGridNode` is already ready, leading to an unnecessary (and fairly expensive) `wrapAsHolder` & `createManagedNode` chain. A tiny rearrangement avoids that overhead: ```diff - var data = node.getOrCreateNodeData(MEConduitNodeData.TYPE); - if (data.getMainNode() == null) { - initMainNode(level, data); - } - - IManagedGridNode mainNode = data.getMainNode(); - if (mainNode.isReady()) { - return; - } + var data = node.getOrCreateNodeData(MEConduitNodeData.TYPE); + IManagedGridNode mainNode = data.getMainNode(); + if (mainNode == null) { + initMainNode(level, data); + mainNode = data.getMainNode(); + } + if (mainNode.isReady()) { + return; + } ``` This is optional, but avoids a handful of allocations during chunk re-loads. --- `152-160`: **Minor: context parameter is never inspected** `proxyCapability` keeps the `tContext` parameter but doesn’t use it. Not an error, yet a quick `// ignored` comment (or removal if the interface allows) would make the intent explicit and silence IDE warnings. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)</summary><blockquote> `44-46`: **Declare `CODEC` as `static final` to follow immutable-constant conventions** `CODEC` is a pure value object that should never be reassigned at runtime. Making it `static final` prevents accidental re-assignment and communicates intent more clearly. ```diff - public static Codec<ConduitNetworkSavedData> CODEC = ConduitNetwork.CODEC.listOf() + public static final Codec<ConduitNetworkSavedData> CODEC = ConduitNetwork.CODEC.listOf() ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)</summary><blockquote> `45-47`: **Redundant `Math.max` call** `context.energyStored()` is guaranteed to be `> 0` thanks to the early return above; the extra clamp adds noise and a tiny overhead. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (1)</summary><blockquote> `91-102`: **Suppress or handle unchecked cast in `proxyCapability`** The down-cast to `(TCapability) data.container` is unchecked and will generate warnings. Either add `@SuppressWarnings("unchecked")` locally or use `capability.cast(data.container)` (NeoForge helper) to avoid raw casts. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (2)</summary><blockquote> `42-50`: **Variable / comment naming is now misleading** `fluidConduit` and the surrounding comment still reference “fluid” even though the class works with chemicals. This can hinder future searches and comprehension. ```diff - if (dataAccess.conduit() instanceof ChemicalConduit fluidConduit && !fluidConduit.isMultiChemical()) { + if (dataAccess.conduit() instanceof ChemicalConduit chemicalConduit && !chemicalConduit.isMultiChemical()) { ``` and update the leading comment to “Locked chemical widget”. --- `90-96`: **Avoid repeating the same expensive lookup every frame** `dataAccess.getConnectionConfig()` is fetched twice per frame inside `addPreRenderAction` and the red-stone control picker supplier. Caching the value inside a local variable makes the intent clearer and prevents any accidental divergence if the underlying call ever acquires locks or performs NBT reads. ```diff - screen.addPreRenderAction(() -> redstoneChannelWidget.visible = dataAccess.getConnectionConfig() - .extractRedstoneControl() - .isRedstoneSensitive()); + screen.addPreRenderAction(() -> { + var cfg = dataAccess.getConnectionConfig(); + redstoneChannelWidget.visible = cfg.extractRedstoneControl().isRedstoneSensitive(); + }); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (2)</summary><blockquote> `24-28`: **Out-of-date inline comment** The colour picker now edits the *extract* channel, but the comment still says “Send channel”. Please update to avoid confusion. --- `35-39`: **Ditto for the right-hand channel** Rename the comment to “Insert channel” so it matches the renamed API. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)</summary><blockquote> `52-54`: **Minor performance tweak** `network.insertConnectionsFrom(extractConnection)` is recomputed for every slot. Fetching it once per extract connection and re-using the list avoids repeated cache look-ups. --- `112-120`: **Unused TODO** `// TODO: is this necessary?` was left above `isEmpty`. Either remove the helper entirely (per previous comment) or replace the TODO with a concise explanation of why the helper is required. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java (1)</summary><blockquote> `66-69`: **Stale TODO and outdated wording** The comment still reads `// TODO: sendRedstoneControl`, referencing the pre-refactor terminology. Replace with something like `// TODO: honour insertRedstoneControl (not implemented yet)` or drop the line if no action is required. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1)</summary><blockquote> `73-74`: **Unnecessary generic on `setNodeData`** `<T extends NodeData> void setNodeData(@Nullable T data);` The generic parameter is unused in the method signature and offers no type safety (callers still pass a concrete `NodeData`). Dropping the `<T …>` avoids noise: ```diff - <T extends NodeData> void setNodeData(@Nullable T data); + void setNodeData(@Nullable NodeData data); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (1)</summary><blockquote> `79-88`: **Javadoc still speaks “sending/receiving” after the insert/extract rename** The comments for `insertConnections*`/`extractConnections*` refer to “sending” and “receiving” which were replaced by “insert” and “extract”. This may confuse future contributors and modders reading the API. Please update the phrasing to: * “all the insert-connections” * “all the extract-connections” to remain consistent with the rest of the refactor. Also applies to: 110-122 </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)</summary><blockquote> `63-66`: **Redundant null-check on `context`** `network.getOrCreateContext(...)` is guaranteed to return a non-null instance by contract. The subsequent `context != null` guard can therefore be removed, which will slightly reduce byte-code and avoid misleading future readers. ```diff - if (!conduit.isMultiFluid()) { - if (context != null && !context.lockedFluid().equals(context.lastLockedFluid())) { + if (!conduit.isMultiFluid() + && !context.lockedFluid().equals(context.lastLockedFluid())) { ``` --- `24-26`: **Guard against zero-rate conduits** When `transferRatePerTick()` or `networkTickRate()` returns 0 the loops below still iterate, performing capability look-ups each tick for no possible transfer. An early return improves clarity and avoids needless work. ```diff final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + if (fluidRate <= 0) { + return; + } ``` </blockquote></details> <details> <summary>endercore/src/test/java/com/enderio/core/NetworkTests.java (2)</summary><blockquote> `167-178`: **Logical expression is correct but hard to read – add parentheses for clarity** Although operator precedence makes the expression behave as intended, explicit parentheses around the second `&&` term improve readability and prevent accidental mistakes during future edits. ```diff - if ((pair.getFirst() == indexA && pair.getSecond() == indexB) - || pair.getFirst() == indexB && pair.getSecond() == indexA) { + if ((pair.getFirst() == indexA && pair.getSecond() == indexB) + || (pair.getFirst() == indexB && pair.getSecond() == indexA)) { ``` --- `23-30`: **Consider asserting `node1`’s network identity after connect** After `network.connect(node1, node2, …)` both nodes should still belong to the same `network` instance. Adding `Assertions.assertSame(network, node1.getNetwork())` would protect against unintended internal re-allocation. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (2)</summary><blockquote> `50-53`: **Hard-coded placement face may fail in edge cases** The helper always supplies `Direction.DOWN` for the hit-result, which places a conduit as if the player clicked the *top* of the block below. When tests attempt to place conduits on vertical faces or ceilings this will mis-behave. Pass the desired face as a parameter (defaulting to `Direction.UP`) or compute it from the target position to make the utility more flexible. --- `60-68`: **Triple-nested loops could be replaced with `BlockPos.betweenClosed`** For clarity and marginal performance gains: ```java BlockPos.betweenClosed(startX, startY, startZ, endX, endY, endZ) .forEach(pos -> setBlock(pos, blockState)); ``` avoids manual index management. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (1)</summary><blockquote> `18-54`: **Declare static codec/constants as `final`** `CODEC`, `STREAM_CODEC` and `TYPE` should be `static final` to prevent accidental reassignment and help the JVM inline them. ```diff -public static MapCodec<ChemicalConduitConnectionConfig> CODEC = +public static final MapCodec<ChemicalConduitConnectionConfig> CODEC = ``` (same for the other two constants) </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (2)</summary><blockquote> `24-31`: **Minor: inconsistent field name** For consistency with other configs and future data migration tools, prefer `"extract_color"` over `"extract_redstone_channel"` for the colour that is *not* a redstone channel. --- `91-105`: **Parameter shadowing makes the ‘with…’ helpers error-prone** Using the same identifier names (`isInsert`, `isExtract`) for both the record components and method parameters invites accidental mix-ups (see the previous PR that prompted this refactor). Consider prefixing parameters with `new` or using `this.` to make the intent explicit. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java (1)</summary><blockquote> `18-54`: **Make codec/constants `final`** As above: mark `CODEC`, `STREAM_CODEC`, `TYPE` as `static final`. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)</summary><blockquote> `24-31`: **Duplicate capability lookup – micro-optimisation** `extractHandler` is fetched here and again inside `doChemicalTransfer`, incurring two capability queries each tick per extract face. Pass the handler as a parameter or cache it in the connection to avoid redundant calls. --- `65-121`: **Variable naming confusion (`receiverHandler` is actually the *source*)** Inside `doChemicalTransfer` the variable `receiverHandler` represents the *extract* side (the source). This is counter-intuitive and likely to mislead future maintainers. Consider renaming to `sourceHandler` and `destinationHandler` for clarity. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (2)</summary><blockquote> `60-64`: **Update Javadoc to match the renamed method** The Javadoc still refers to “conduit graph ticks”, but the method has been renamed to `networkTickRate()`. This can cause confusion when reading the generated documentation. ```diff - * @return the number of ticks that should pass before the conduit graph ticks. + * @return the number of ticks that should pass before the conduit network ticks. ``` --- `141-145`: **Consider making the comparison deterministic for equal distances** `compareNodes` returns `0` when two connections have the same Manhattan distance to the reference node. If this comparator is later used in a `TreeSet`/`sorted()` pipeline, equality will suppress one of the entries and may give non-deterministic ordering across JVMs. Add a secondary comparison (e.g., by direction, block pos, or `hashCode()`) to guarantee strict ordering. </blockquote></details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)</summary><blockquote> `283-306`: **Potential network corruption when merging** When merging another network, nodes are added *before* edges, but the other graph’s edges are copied without clearing any self-loops. If the other network contained a self-loop (which your graph forbids), `putEdge` will silently ignore it, but you lose the opportunity to warn developers. Consider validating edges: ```java for (var edge : other.graph.edges()) { Preconditions.checkArgument(edge.nodeU() != edge.nodeV(), "Self-loops are not allowed."); graph.putEdge(edge); } ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary><blockquote> `100-108`: **`attach()` never clears the `shouldRebuildCache` flag** `attach()` notifies the network via `onNodeLoaded()` but does not explicitly reset any pending rebuild flags. If the node was created while the chunk was unloaded, the network flags a rebuild; once everything attaches, the cache is still rebuilt even though individual incremental updates would suffice. Minor performance nit – resetting the flag inside `onNodeLoaded()` (or here) would avoid unnecessary cache churn when many chunks load simultaneously. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> `404-424`: **Sorting hot-path allocates comparators each tick** `sortConnectionLists()` re-sorts many lists every time `haveConnectionsChanged` is true, creating a new lambda comparator (`ref, a, b -> …`) for each call. Cache the comparator per conduit instance (or make `Conduit#compareNodes` implement `Comparator<ConduitBlockConnection>` itself) to avoid this repeated allocation in the common ticking path. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)</summary><blockquote> `1277-1286`: **Side-effects in `setRemoved()` can emit block updates after the BE is detached** `setRemoved()` calls `setFacadeProvider(ItemStack.EMPTY)`, which ultimately triggers `bundleChanged()` and sends block updates while the block entity is in the process of being destroyed. Depending on Forge event order this may re-enter removed objects or update neighbours that no longer exist. It is safer to manipulate raw fields here (skip `bundleChanged()`), or move façade drop/clearing logic to `onRemove()` in the block class. </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 4e6e0501bf7ab7faa75304ff1cb6956cd85f1873 and 7d1bf03e24a43c72c05ab8410df14e022d055b46. </details> <details> <summary>📒 Files selected for processing (100)</summary> * `.github/workflows/verify.yml` (1 hunks) * `endercore/build.gradle.kts` (2 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/Network.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/package-info.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/NetworkTests.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/TestNode.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java` (4 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java` (5 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java` (7 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java` (3 hunks) * `enderio-conduits/build.gradle.kts` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java` (9 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java` (27 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java` (5 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java` (6 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java` (7 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java` (1 hunks) * `enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java` (1 hunks) * `enderio/build.gradle.kts` (1 hunks) </details> <details> <summary>💤 Files with no reviewable changes (13)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🧬 Code Graph Analysis (15)</summary> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (2)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java (1)</summary> * `NodeData` (80-86) </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)</summary> * `EnderIOConduitsRegistries` (12-54) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (3)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (1)</summary> * `Deprecated` (20-93) </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1)</summary> * `Deprecated` (21-153) </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1)</summary> * `Deprecated` (20-97) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)</summary> * `EIOLang` (15-330) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/api/EnderIO.java (1)</summary> * `EnderIO` (5-11) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java (1)</summary> * `EnergyConduitNetworkContext` (11-72) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)</summary> * `EnderIOConduitsRegistries` (12-54) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)</summary> * `EIOLang` (15-330) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1)</summary> * `ConduitLang` (9-109) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)</summary> * `EIOLang` (15-330) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)</summary> * `EIOLang` (15-330) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (1)</summary><blockquote> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (1)</summary> * `RSConduitNodeData` (22-142) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (1)</summary><blockquote> <details> <summary>enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java (1)</summary> * `EIOLang` (15-330) </details> </blockquote></details> <details> <summary>endercore/src/test/java/com/enderio/core/NetworkTests.java (1)</summary><blockquote> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)</summary> * `BasicNetwork` (10-32) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)</summary><blockquote> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (1)</summary> * `ChemicalConduitNetworkContext` (12-76) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary> * `ConduitNode` (25-299) </details> </blockquote></details> </details><details> <summary>🪛 YAMLlint (1.35.1)</summary> <details> <summary>.github/workflows/verify.yml</summary> [error] 42-42: trailing spaces (trailing-spaces) </details> </details> </details> <details> <summary>🔇 Additional comments (116)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java (2)</summary> `8-11`: **Clean implementation of persistence check** The addition of the `isPersistent()` method provides a clear way to determine if a node data type should be persisted based on the presence of a codec. This is a good design decision that makes the API more intuitive to use. --- `13-16`: **Good error handling for codec access** The `codecOrThrow()` method ensures that attempting to access a non-persistent node data's codec will fail with a clear error message rather than a null pointer exception. Using Guava's Preconditions for validation is appropriate here. </details> <details> <summary>.github/workflows/verify.yml (3)</summary> `3-4`: **Security improvement by limiting permissions** Adding the permissions block with `contents: read` properly addresses the security concern flagged by the code scanning bot, following the principle of least privilege. --- `7-20`: **More granular workflow triggers** The refined triggers with specific event types and path filtering will reduce unnecessary CI runs, improving efficiency. Adding push triggers for dev branches ensures changes are verified before they can impact other developers. --- `47-54`: **Improved test granularity** Breaking down the build verification into separate steps (style check, game tests, unit tests) is an excellent improvement that provides clearer feedback on what might be failing during CI. </details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/package-info.java (1)</summary> `4-4`: **Appropriate package relocation** Moving the graph functionality from conduits-specific package to the core module improves code reusability and aligns with the architectural changes described in the PR objectives. This makes the graph system available for other components beyond conduits. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java (1)</summary> `3-3`: **Updated deprecation strategy appropriately** Good change to the deprecation annotation. Removing the `forRemoval = true` attribute while keeping the `since = "8.0.0"` version marker aligns with the PR's approach of introducing a new custom graph network system while maintaining backwards compatibility during transition. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java (1)</summary> `10-10`: **Consistent deprecation approach for legacy components** Good update to the deprecation annotation, removing the `forRemoval = true` attribute while maintaining the version marker. This change follows the same approach as other legacy components in this PR, allowing for a smoother transition to the new network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java (1)</summary> `1-4`: **Good addition of nullability annotations** These package-level annotations establish that parameters and return values within this package are non-null by default, which promotes null safety and reduces the risk of NullPointerExceptions. This is particularly important for network-related code that serves as a critical foundation for the conduit system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (2)</summary> `20-20`: **Consistent deprecation approach** Appropriate update to the deprecation annotation, removing the `forRemoval = true` attribute while maintaining the version marker. This follows the same pattern as other legacy components in this PR. --- `68-71`: **Future refactoring noted in TODO** The TODO comment mentions a pending change to use records instead of classes. Consider addressing this in a follow-up PR to complete the modernisation of these data structures. </details> <details> <summary>enderio/build.gradle.kts (1)</summary> `105-108`: **Adding game test server configuration - good addition for testing conduit networks.** This change adds a new run configuration for game tests, which aligns with the PR's goal of comprehensive evaluation through both in-game playtesting and automated testing. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java (1)</summary> `27-27`: **Updated Javadoc to reference the new interface-based node abstraction.** The Javadoc now correctly references `IConduitNode` interface instead of the concrete `ConduitNode` implementation, which aligns with the architectural shift to decoupled, interface-based abstractions. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java (1)</summary> `138-138`: **Terminology update in commented code: receive → extract.** The terminology change from `receiveRedstoneChannel()` to `extractRedstoneChannel()` aligns with the broader refactoring across the codebase where "send"/"receive" terms are being replaced with "insert"/"extract" for consistency. While this is in commented code, it's good practice to maintain the updated terminology. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1)</summary> `20-20`: **Softening deprecation stance on legacy class.** The `forRemoval = true` attribute has been removed from the `@Deprecated` annotation, indicating this legacy class will remain in the codebase indefinitely. This aligns with other legacy components being preserved for backward compatibility during the transition to the new network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1)</summary> `21-21`: **Softening deprecation stance on legacy class.** The `forRemoval = true` attribute has been removed from the `@Deprecated` annotation, indicating this legacy class will remain in the codebase indefinitely. This makes sense given that the class implements a conversion to the new node data format (via the `toNodeData()` method), serving as a bridge between old and new systems. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java (1)</summary> `19-19`: **Softening deprecation stance on legacy enum.** The `forRemoval = true` attribute has been removed from the `@Deprecated` annotation, indicating this legacy enum will remain in the codebase indefinitely. This is part of a consistent pattern with other legacy components and suggests a more cautious approach to backward compatibility during the transition to the new network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java (1)</summary> `23-23`: **Softened deprecation approach makes sense for legacy transition** The removal of `forRemoval = true` while keeping the `since = "8.0.0"` deprecation notice indicates a more cautious approach to phasing out legacy code. This is appropriate given the scope of the refactoring, as it provides more time for dependent code to migrate to the new network system while clearly signaling the deprecated status. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java (1)</summary> `34-34`: **Method name change aligns with new terminology standards** The change from `canSend` to `canInsert` maintains the same logical function while adopting the more consistent "insert/extract" terminology now used throughout the codebase. This improves code clarity by using terms that better reflect the directional flow of resources in the conduit system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java (1)</summary> `5-5`: **Consistent deprecation approach for legacy interfaces** The removal of `forRemoval = true` while maintaining the `since = "8.0.0"` deprecation is consistent with the approach taken for other legacy components. This measured transition strategy allows for proper migration to the new network system without immediately breaking dependent code. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java (1)</summary> `8-8`: **Consistent deprecation strategy applied to data types** The deprecation annotation change follows the same pattern as other legacy components, providing a clear transition path while maintaining compatibility during migration to the new network system. This is especially important for data types that may be referenced by persistent storage or serialization formats. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (2)</summary> `10-10`: **JavaDoc change aligns with the interface-based architecture** Updated JavaDoc correctly references `IConduitNode` interface instead of the concrete `ConduitNode` class, which aligns with the broader refactoring towards more abstract interfaces in the codebase. --- `15-15`: **Changed to fail-fast approach with codecOrThrow** The change from `NodeDataType::codec` to `NodeDataType::codecOrThrow` indicates a design shift toward failing fast when codecs are missing rather than handling optional/nullable results. This is appropriate for required codecs that should always be present, improving error detection. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java (1)</summary> `177-179`: **New handler method for connection config updates** This new method provides a dedicated entry point for handling connection configuration updates from network packets, which improves code organization by separating packet handling from the actual configuration update logic. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java (2)</summary> `9-9`: **Updated import for IConduitNode interface** The import is correctly updated to reference the interface rather than the concrete implementation class, supporting the architectural changes in this PR. --- `62-62`: **Return type changed to interface for better abstraction** The method signature now returns the `IConduitNode` interface instead of the concrete `ConduitNode` class. This improves abstraction and decoupling, allowing more flexibility in implementation details while maintaining the contract. This is a good design practice for APIs. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java (1)</summary> `1-4`: **Added package-level nullability annotations for test code** These annotations establish default nullability contracts for the test package, improving static analysis and null safety: - `@ParametersAreNonnullByDefault` ensures all parameters are non-null by default - `@MethodsReturnNonnullByDefault` ensures all methods return non-null values by default This is good practice for ensuring null safety in test code and makes explicit when null values are expected or permitted. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java (1)</summary> `13-13`: **Encapsulation improvement using handler method** This change enhances encapsulation by replacing a direct setter call with a handler method that takes the entire packet. This approach allows for more complex processing, validation, or side-effects when updating connection configurations, which aligns well with the new network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java (1)</summary> `1-4`: **Good practice with nullability annotations** Adding these nullability annotations to the test package is excellent practice. It helps catch potential null pointer exceptions at compile time rather than runtime, which will improve code quality and reduce bugs in the test suite. </details> <details> <summary>endercore/build.gradle.kts (1)</summary> `21-42`: **Well-configured test infrastructure** The addition of JUnit 5 testing dependencies and proper configuration of the test task is excellent. This setup will support the comprehensive testing needed for the new graph network system mentioned in the PR description. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (2)</summary> `53-55`: **Consistent terminology change from 'send' to 'insert'** The change from `isSend()` to `isInsert()` improves terminology consistency in the codebase. This aligns with the constants `leftTitle` and `rightTitle` which already use "insert" terminology. --- `57-59`: **Consistent terminology change from 'receive' to 'extract'** The change from `isReceive()` to `isExtract()` improves terminology consistency in the codebase. This aligns with the constants `leftTitle` and `rightTitle` which already use "extract" terminology. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (3)</summary> `58-58`: **Consistent terminology update from "Graph Ticks" to "Network Ticks"** The terminology change aligns with the new custom graph network system implementation mentioned in the PR objectives. --- `64-64`: **Updated tooltip to use consistent network terminology** The fluid rate tooltip now references "network tick" instead of "graph tick", maintaining terminology consistency across the codebase. --- `72-72`: **Updated item rate tooltip to use consistent network terminology** Similar to the fluid rate tooltip, this update ensures consistent terminology by using "network tick" instead of "graph tick". </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java (1)</summary> `26-29`: **Method calls updated to use new insert/extract terminology** The connection configuration accessors have been updated from send/receive to insert/extract terminology to align with changes across the conduit system. This maintains functional equivalence while using the new API naming conventions. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java (4)</summary> `461-461`: **Updated redstone signal check to use new insert terminology** Changed from `canSend` to `canInsert` to align with the unified terminology in the new network system. --- `497-497`: **Updated redstone signal condition to use insert terminology** Consistently applied terminology change from send to insert for the redstone signal condition check. --- `521-521`: **Updated output signal colour method to use insert terminology** Changed from `sendColor()` to `insertColor()` to align with the new network API naming. --- `524-524`: **Updated signal context method to use insert terminology** Final update to use `insertColor()` instead of `sendColor()` for signal context handling. </details> <details> <summary>enderio-conduits/build.gradle.kts (1)</summary> `44-47`: **Added Neo test framework dependency** Added a dependency on the Neo test framework with transitive dependencies disabled. This supports the implementation of comprehensive game tests for the new conduit network system mentioned in the PR objectives. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java (1)</summary> `4-4`: **Improved API design with interface-based approach** The interface has been updated to use `IConduitNetwork` instead of a concrete implementation, which is a good architectural improvement. This abstraction allows for better modularity and flexibility in the conduit system. The method signature has also been simplified from `tickGraph` to `tick` with fewer parameters, making the API cleaner and more focused. Also applies to: 8-8 </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java (2)</summary> `3-3`: **Good use of abstraction with interface types** Using the `IConduitNode` interface instead of a concrete implementation improves design flexibility and follows interface-based programming principles. Also applies to: 6-6 --- `49-49`: **Improved network querying approach** Using the cached `nodeCount()` method instead of directly accessing the node collection size is better for performance, which aligns with the PR objective of improving conduit ticker performance by caching network information. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java (1)</summary> `8-8`: **Better deprecation strategy for smoother transition** Removing the `forRemoval = true` attribute from the `@Deprecated` annotation is a good decision as it provides better backward compatibility for mods that might still be using the legacy API while transitioning to the new network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java (5)</summary> `3-5`: **Updated imports for new functionality** Good addition of Guava's Preconditions for validation and switching to MapCodec for better structured data serialization. --- `8-13`: **Added helpful Javadoc documentation** The addition of detailed Javadoc comments describing the record's parameters improves code readability and helps API users understand the purpose of each parameter. --- `13-13`: **Improved serialization with MapCodec** Using `MapCodec<T>` instead of `Codec<T>` is a good upgrade for structured data serialization, which will help with the persistence of the new network system. --- `15-17`: **Useful helper method for persistence checking** The addition of the `isPersistent()` method provides a clean way to check if the context needs to be saved, which is essential for network context management. --- `19-22`: **Added safe codec access method** The `codecOrThrow()` method with Preconditions validation provides safer access to the codec, preventing null pointer exceptions and giving better error messages when issues occur. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (3)</summary> `3-3`: **Updated import to use the interface instead of concrete class.** The import has been updated to use `IConduitNode` interface rather than the concrete `ConduitNode` implementation, aligning with the new network abstraction architecture of the system. --- `23-23`: **Updated NodeDataType constructor to use null codec.** The `TYPE` field is now initialized with a `null` codec parameter instead of `CODEC`. This change is part of the broader refactoring to support nullable codec handling in the `NodeDataType` record. --- `36-36`: **Method signature updated to use interface type.** Method parameter type changed from concrete `ConduitNode` to `IConduitNode` interface. This improves flexibility and allows for different implementations of the conduit node interface to be used. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (3)</summary> `1-1`: **Moved class to legacy package.** The class has been moved from the `graph` package to the `legacy` package, correctly indicating its status as part of the old implementation being phased out. --- `18-18`: **Modified deprecation annotation.** The `@Deprecated` annotation has been updated to remove the `forRemoval = true` attribute. This indicates the class is still needed for compatibility but is no longer targeted for immediate removal. The removal of the `forRemoval = true` attribute suggests this class will be maintained longer than originally planned, likely to support backward compatibility. --- `1-89`: <details> <summary>✅ Verification successful</summary> **Removed STREAM_CODEC field and related functionality.** The `STREAM_CODEC` field appears to have been removed from this class. Examining the relevant code snippets from other legacy conduit data classes, this change is consistent with the broader refactoring effort to replace the old serialization mechanism. --- 🏁 Script executed: ```shell #!/bin/bash # Verify that STREAM_CODEC is consistently removed from legacy classes grep -r "STREAM_CODEC" . --include="*.java" | grep -v "legacy" ``` Length of output: 59405 --- 🏁 Script executed: ```shell #!/bin/bash # Verify that no STREAM_CODEC declarations exist in the conduit legacy package find . -type f -path "*/conduit/legacy/*.java" -name "*.java" | xargs grep -n "STREAM_CODEC" || echo "No STREAM_CODEC declarations in legacy package." ``` Length of output: 3649 --- </details> **Removed STREAM_CODEC field and related functionality.** - ConduitDataContainer no longer declares a `STREAM_CODEC`, as intended. - Other legacy conduit data classes (e.g. ConnectionState, DynamicConnectionState, LegacyFluidConduitData, LegacyItemConduitData, LegacyRedstoneConduitData, StaticConnectionStates) still use `STREAM_CODEC`, confirming this change is specific to the container and not part of a blanket removal. No further action required. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java (2)</summary> `83-86`: **Interface method updated from copy() to split().** The method has been updated from `copy()` to `split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks)`, aligning with the new `ConduitNetworkContext` interface requirements. This change supports the new network splitting mechanism in the refactored conduit system. --- `88-90`: **Original copy() method preserved as a private helper.** The original `copy()` implementation has been preserved as a private helper method. This is a good practice as it maintains the core functionality while adapting to the new interface requirements. </details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1)</summary> `1-33`: **Well-structured implementation of the new network system.** This new class provides a clean, basic implementation of the abstract `Network` class for nodes implementing `INetworkNode`. The implementation includes: 1. Multiple constructors for different initialization scenarios 2. Proper override of the `createEmpty` method 3. Clear type parameters and inheritance This implementation serves as a good foundation for the custom graph solution. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (1)</summary> `34-38`: <details> <summary>❌ Incorrect review comment</summary> **Prefer the injected `eventBus` over the global bus to avoid double registration** You already receive the module-scoped `IEventBus` via the constructor. Using `NeoForge.EVENT_BUS` as well risks registering the same listener twice if another entry-point repeats this call, and makes unit tests harder because they can’t substitute the global bus. ```diff - NeoForge.EVENT_BUS.addListener((final RegisterCommandsEvent event) -> { + eventBus.addListener((final RegisterCommandsEvent event) -> { ``` --- I’ll verify that `EnderIOConduitsServer` indeed receives and stores a module-scoped `IEventBus` before recommending switching to it. --- 🏁 Script executed: ```shell #!/bin/bash # Find the class definition and its constructor rg --context 3 "class EnderIOConduitsServer" -n enderio-conduits rg --context 5 "public EnderIOConduitsServer" -n enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java # Check for the eventBus field rg -n "eventBus" -n enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java ``` Length of output: 2056 --- </details> **Ignore the injected bus suggestion for command registration** The injected `eventBus` is the FML mod‐scoped bus, which does *not* fire `RegisterCommandsEvent`. That event is only dispatched on `NeoForge.EVENT_BUS` (the global bus), so switching to `eventBus.addListener(…)` would break your commands. You should keep using `NeoForge.EVENT_BUS.addListener(…)` here. > Likely an incorrect or invalid review comment. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (2)</summary> `107-108`: **Left-hand enable toggle migrated correctly.** The new call to `withIsInsert` matches the terminology shift and keeps behaviour unchanged. --- `112-113`: **Right-hand enable toggle migrated correctly.** Likewise, `withIsExtract` is the correct counterpart. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (1)</summary> `42-44`: **Terminology update acknowledged.** `networkTickRate` replaces `graphTickRate` cleanly; no further action needed. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java (2)</summary> `35-38`: **Good abstraction with clear null-safety.** The generic return type and nullable annotation on `getSidedCapability` make intent explicit and avoid unchecked casts. No issues spotted. --- `63-66`: **Type-safety guarantees are excellent.** Throwing when a mismatched `ConnectionConfigType` is requested prevents subtle runtime errors. Nice touch. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary> `15-26`: **Efficient iteration and capability access.** The early-continue pattern avoids unnecessary inner-loop work when a capability is absent. Looks good. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java (1)</summary> `1-56`: **Well-structured test helper that enhances conduit testing capabilities** This new helper class provides valuable methods for testing item conduit functionality: - Inserts items into containers with proper slot handling - Asserts exact item counts with detailed error messages - Extends the base helper with item-specific testing capabilities The implementation is robust with appropriate error handling and clear exception messages. This will significantly improve the test coverage for item conduits. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (5)</summary> `20-20`: **Terminology update from "receive" to "extract"** The change aligns with the broader effort to use more intuitive terminology across the conduit system. --- `24-25`: **Consistent terminology update for redstone control** Changed from `receiveRedstoneControl()` to `extractRedstoneControl()`, maintaining consistency with the new terminology. --- `28-29`: **Unified terminology for redstone control picker** Both the getter and setter now use the "extract" terminology, creating a more consistent API. --- `36-36`: **Updated connection enabled setter** Changed from `withIsSend` to `withIsInsert` to match the new terminology. --- `41-41`: **Updated right connection enabled setter** Changed from `withIsReceive` to `withIsExtract` for consistent terminology. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java (6)</summary> `10-13`: **Updated documentation and method name to use "insert" terminology** This change improves clarity by using more appropriate terminology for describing the direction of resource flow. --- `14-18`: **Updated documentation and method name to use "extract" terminology** Consistent terminology update that better describes the action of taking resources from a connected block. --- `19-26`: **Improved documentation for insert color channel** Documentation now more clearly explains the purpose of the insert color channel for separation in the ticker, along with the method name change. --- `27-34`: **Consistent terminology update for extract color channel** Updated method name and documentation to maintain consistency across the API. --- `35-41`: **Updated default methods to use insert/extract terminology** Both method names and their implementations have been consistently updated to use the new terminology. --- `43-46`: **Updated isConnected() implementation to use new method names** The implementation now correctly uses the renamed methods while maintaining the same functionality. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java (7)</summary> `14-15`: **Updated record fields to use insert/extract terminology** The record's fields have been renamed to maintain consistency with the interface changes, using more intuitive terminology. --- `20-28`: **Updated codec field names to match the new terminology** The codec field mappings have been consistently updated to use the new field names, ensuring proper serialization/deserialization. --- `29-34`: **Updated stream codec to use new method references** All method references in the stream codec definition have been updated to use the new terminology. --- `39-48`: **Updated reconnected() and disconnected() implementations** Both methods now use the new field names while maintaining the same functionality. --- `50-58`: **Updated withIsInsert and withInsertColor methods** These methods replace the previous withIsSend and withSendColor methods, maintaining the same functionality with more intuitive naming. --- `60-68`: **Updated withIsExtract and withExtractColor methods** These methods replace the previous withIsReceive and withReceiveColor methods while maintaining the same functionality. --- `70-73`: **Updated withIsStrongOutputSignal implementation** The method implementation now uses the new field names while maintaining the same functionality. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (1)</summary> `35-37`: **Signal reset relies on hidden `nextTick()` semantics** `context.setSignal(channel, signal)` is only invoked when `signal > 0`. If a previously powered channel now reads `0`, its entry is *never* updated here. The correctness therefore hinges on `context.nextTick()` resetting all channel values to `0`. If that assumption changes, the network will erroneously stay powered. Please confirm that `nextTick()` indeed clears the per-channel map; otherwise explicit `else { context.setSignal(channel, 0); }` should be added. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (1)</summary> `71-80`: **Priority comparison looks good** The custom `compareNodes` implementation correctly orders higher-priority connections first and falls back to the default comparator when equal — clear and concise. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1)</summary> `16-29`: **Redstone widgets updated correctly** The switch from *receive* to *extract* terminology is implemented consistently across the colour picker, visibility predicate and control picker. No further issues spotted. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (5)</summary> `7-11`: **Documentation is well-structured and clear** The JavaDoc nicely describes the purpose of this interface and clarifies that it represents additional information stored on a conduit network that isn't synced to clients. --- `13-14`: **Good use of registry-based codec system** The implementation of `GENERIC_CODEC` using the registry system is a clean approach for handling serialization of different context types. --- `16-21`: **Clear documentation for mergeWith** The JavaDoc for `mergeWith` now clearly indicates that it may return either a new instance or the same instance if no merging is needed. --- `24-31`: **Improved context splitting design** Replacing the ambiguous `copy()` method with a more specific `split()` method and providing clear documentation about the requirement for returning a new instance is a good design choice. This makes the lifecycle of network contexts more explicit. --- `33-36`: **Type method facilitates serialization** Adding the `type()` method is a good design choice as it enables the registry-based serialization approach and makes type comparisons more straightforward. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java (4)</summary> `97-97`: **Good abstraction with interface instead of concrete class** Changing the parameter type from concrete `ConduitNode` to interface `IConduitNode` improves abstraction and flexibility. --- `145-146`: **Improved legacy data copy mechanism** Adding `BiConsumer<Direction, ConnectionConfig>` provides a flexible way to set connection configurations during legacy data migration, improving modularity. --- `191-191`: **Consistent use of interface abstraction** Method signatures consistently updated to use `IConduitNode` interface instead of concrete implementation class. Also applies to: 197-197 --- `229-229`: **Updated tooltip calculation** Changed `graphTickRate()` to `networkTickRate()` to align with the new network terminology, which is consistent with the updated network system. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (2)</summary> `14-38`: **Well-structured test for network splitting** The test effectively validates that conduits form a single network when placed in a row and that breaking a conduit splits the network. The use of sequence-based testing with clear steps makes the test easy to understand. --- `40-64`: **Good test case for network merging** This test properly validates that separate conduit networks merge when connected by a new conduit. The test setup is clear and the assertions verify the expected behavior. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (1)</summary> `13-13`: **Interface import improves decoupling – good move** Switching from the concrete `ConduitNode` class to the `IConduitNode` interface reduces coupling and will make testing and future extensions easier. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (2)</summary> `48-50`: **Method name reflects new terminology – nice catch** `networkTickRate()` fits the new *network* vocabulary and keeps parity with other conduit types. --- `134-137`: **Capability invalidation on removal looks correct** Calling `level.invalidateCapabilities(pos)` guarantees that adjacent blocks re-query capabilities after the conduit is gone. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (2)</summary> `56-60`: **Terminology update applied correctly** Insert-side colour picker now uses `insertColor()` / `withInsertColor()` – keeps UI & config consistent. --- `111-117`: **`setRightEnabled` toggle aligned with new API – good** Right-hand side now toggles `isExtract`, mirroring the backend rename. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (1)</summary> `18-19`: **Possible double-count when multiplying by `networkTickRate()`** `transferRate` is already defined as “per tick” on the conduit. Multiplying by `networkTickRate()` means the total energy moved over one logical conduit-tick becomes `rate * networkTickRate() * connectionsProcessed`, which can exceed the intended throughput. Confirm design intent; if the goal is *per conduit tick*, simply use `conduit.transferRatePerTick()` here. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (1)</summary> `105-112`: **Semantics now consistent – good change** Switching the enabled-state helpers to `withIsInsert/Extract` correctly matches the new terminology. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (1)</summary> `47-65`: **Behaviour matches the new API – looks good** Getter / setter rewiring from `send/receive` to `insert/extract` is correct and consistent. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1)</summary> `38-40`: **Clarify null-safety contract of `getNetwork()`** `getNetwork()` is not annotated `@Nullable`, implying it must never return `null`. If nodes can temporarily be orphaned (e.g., during chunk unload or split/merge operations) please annotate accordingly or document the guarantee so callers do not need extra null-checks. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (1)</summary> `68-72`: **TODO left behind – insert redstone control ignored** `canInsert` blindly returns `isInsert()`, so redstone-sensitive inserts can never be disabled by a signal. Either implement the control (mirroring `canExtract`) or drop the unused fields from the record. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (7)</summary> `8-10`: **Import updates align with the network API refactoring** The imports have been updated to use the new network API interfaces. Changing from concrete implementation types to interfaces (particularly `IConduitNode`) follows good design principles and enables greater flexibility in the conduit system. --- `37-37`: **Transfer rate type change supports larger throughput** Changing the `transferRatePerTick` field type from `int` to `long` allows for much larger transfer rates, which is beneficial for high-throughput chemical transport systems in modded environments. --- `49-49`: **Codec update matches field type change** The codec has been properly updated to use `Codec.LONG` to match the field type change, ensuring consistent serialization/deserialization of conduit data. --- `99-99`: **Updated parameter type to use interface instead of implementation** The method signature has been updated to use the new `IConduitNode` interface instead of a concrete implementation, which is consistent with the network API refactoring. --- `141-142`: **Enhanced connection configuration handling** The method signature changes: 1. Use `IConduitNode` interface instead of concrete implementation 2. Replace `Consumer<Direction>` with `BiConsumer<Direction, ConnectionConfig>` to allow setting connection configuration directly This provides more flexibility and integrates better with the new network system. --- `187-192`: **Updated method signatures for GUI and world data** Both methods have been updated to use the `IConduitNode` interface instead of concrete implementations, maintaining consistency across the codebase with the new network abstraction. --- `224-224`: **Updated tick rate calculation for tooltip** The raw transfer rate calculation now correctly uses `networkTickRate()` instead of the deprecated `graphTickRate()`, aligning with the new network system. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -0,0 +19,4 @@
* Do not call this method directly.
* @param network The network this node is a member of, or null if this node is being invalidated.
*/
void setNetwork(@Nullable TNet network);
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

🛠️ Refactor suggestion

setNetwork is public – contradicts the “do not call directly” contract

Because interface methods are implicitly public, nothing prevents third-party code (or even your own conduit logic) from calling setNetwork and breaking network integrity.

Two possible mitigations:

-void setNetwork(@Nullable TNet network);
+@ApiStatus.Internal // or another internal marker you already use
+void setNetwork(@Nullable TNet network);

or move the write-side contract into a separate package-private interface that only the network implementation sees.

Either will make misuse harder and signal intent clearly in IDEs.

📝 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.

     * Do not call this method directly.
     * @param network The network this node is a member of, or null if this node is being invalidated.
     */
    @ApiStatus.Internal // or another internal marker you already use
    void setNetwork(@Nullable TNet network);
_🛠️ Refactor suggestion_ **`setNetwork` is public – contradicts the “do not call directly” contract** Because interface methods are implicitly `public`, nothing prevents third-party code (or even your own conduit logic) from calling `setNetwork` and breaking network integrity. Two possible mitigations: ```diff -void setNetwork(@Nullable TNet network); +@ApiStatus.Internal // or another internal marker you already use +void setNetwork(@Nullable TNet network); ``` or move the write-side contract into a separate package-private interface that only the network implementation sees. Either will make misuse harder and signal intent clearly in IDEs. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion * Do not call this method directly. * @param network The network this node is a member of, or null if this node is being invalidated. */ @ApiStatus.Internal // or another internal marker you already use void setNetwork(@Nullable TNet network); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +63,4 @@
var node = nodes.getFirst();
graph.addNode(node);
node.setNetwork(self());
onNodeAdded(node);
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

🛠️ Refactor suggestion

List#getFirst() requires Java 21

List#getFirst() was introduced in Java 21, but the NeoForge toolchain targets Java 17.
Using it will break compilation for anyone not on the latest toolchain.

-            var node = nodes.getFirst();
+            var node = nodes.get(0);

Perform the same replacement at line 350.

📝 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.

        if (nodes.size() == 1) {
            Preconditions.checkArgument(edges.isEmpty(), "A single node cannot have any edges.");
            var node = nodes.get(0);
            graph.addNode(node);
            node.setNetwork(self());
            onNodeAdded(node);
_🛠️ Refactor suggestion_ **`List#getFirst()` requires Java 21** `List#getFirst()` was introduced in Java 21, but the NeoForge toolchain targets Java 17. Using it will break compilation for anyone not on the latest toolchain. ```diff - var node = nodes.getFirst(); + var node = nodes.get(0); ``` Perform the same replacement at line 350. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion if (nodes.size() == 1) { Preconditions.checkArgument(edges.isEmpty(), "A single node cannot have any edges."); var node = nodes.get(0); graph.addNode(node); node.setNetwork(self()); onNodeAdded(node); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -15,38 +15,39 @@ import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

⚠️ Potential issue

STREAM_CODEC uses the same getter ordering issue

The byte-buffer codec must match the MapCodec, otherwise network syncs between server and client will desynchronise.

-        DyeColor.STREAM_CODEC,
-        ChemicalConduitConnectionConfig::extractColor,
+        DyeColor.STREAM_CODEC,
+        ChemicalConduitConnectionConfig::extractColor,

(only correct if you rename the field in the previous comment).

Committable suggestion skipped: line range outside the PR's diff.

_⚠️ Potential issue_ **STREAM_CODEC uses the same getter ordering issue** The byte-buffer codec must match the `MapCodec`, otherwise network syncs between server and client will desynchronise. ```diff - DyeColor.STREAM_CODEC, - ChemicalConduitConnectionConfig::extractColor, + DyeColor.STREAM_CODEC, + ChemicalConduitConnectionConfig::extractColor, ``` (only correct if you rename the field in the previous comment). > Committable suggestion skipped: line range outside the PR's diff. <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

⚠️ Potential issue

Mismatch between serialised field name and getter – will break world-load compatibility

extractColor is written under the key "extract_channel", but the getter supplied is extractColor().
When the config is (de)serialised the colour will silently end up in the wrong JSON/NBT field, causing old saves to fail to load once the key is corrected, or – worse – produce silent data loss.

-                    DyeColor.CODEC.fieldOf("extract_channel").forGetter(ChemicalConduitConnectionConfig::extractColor),
+                    DyeColor.CODEC.fieldOf("extract_color").forGetter(ChemicalConduitConnectionConfig::extractColor),
📝 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.

    public static MapCodec<ChemicalConduitConnectionConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance
            .group(Codec.BOOL.fieldOf("is_insert").forGetter(ChemicalConduitConnectionConfig::isInsert),
                    DyeColor.CODEC.fieldOf("insert_color").forGetter(ChemicalConduitConnectionConfig::insertColor),
                    Codec.BOOL.fieldOf("is_extract").forGetter(ChemicalConduitConnectionConfig::isExtract),
-                   DyeColor.CODEC.fieldOf("extract_channel").forGetter(ChemicalConduitConnectionConfig::extractColor),
+                   DyeColor.CODEC.fieldOf("extract_color").forGetter(ChemicalConduitConnectionConfig::extractColor),
                    RedstoneControl.CODEC.fieldOf("extract_redstone_control")
                            .forGetter(ChemicalConduitConnectionConfig::extractRedstoneControl),
                    DyeColor.CODEC.fieldOf("extract_redstone_channel")
                            .forGetter(ChemicalConduitConnectionConfig::extractRedstoneChannel))
            .apply(instance, ChemicalConduitConnectionConfig::new));
_⚠️ Potential issue_ **Mismatch between serialised field name and getter – will break world-load compatibility** `extractColor` is written under the key `"extract_channel"`, but the getter supplied is `extractColor()`. When the config is (de)serialised the colour will silently end up in the wrong JSON/NBT field, causing old saves to fail to load once the key is corrected, or – worse – produce silent data loss. ```diff - DyeColor.CODEC.fieldOf("extract_channel").forGetter(ChemicalConduitConnectionConfig::extractColor), + DyeColor.CODEC.fieldOf("extract_color").forGetter(ChemicalConduitConnectionConfig::extractColor), ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion public static MapCodec<ChemicalConduitConnectionConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance .group(Codec.BOOL.fieldOf("is_insert").forGetter(ChemicalConduitConnectionConfig::isInsert), DyeColor.CODEC.fieldOf("insert_color").forGetter(ChemicalConduitConnectionConfig::insertColor), Codec.BOOL.fieldOf("is_extract").forGetter(ChemicalConduitConnectionConfig::isExtract), - DyeColor.CODEC.fieldOf("extract_channel").forGetter(ChemicalConduitConnectionConfig::extractColor), + DyeColor.CODEC.fieldOf("extract_color").forGetter(ChemicalConduitConnectionConfig::extractColor), RedstoneControl.CODEC.fieldOf("extract_redstone_control") .forGetter(ChemicalConduitConnectionConfig::extractRedstoneControl), DyeColor.CODEC.fieldOf("extract_redstone_channel") .forGetter(ChemicalConduitConnectionConfig::extractRedstoneChannel)) .apply(instance, ChemicalConduitConnectionConfig::new)); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

🛠️ Refactor suggestion

reconnected()/disconnected() override the user’s settings

Both methods hard-code isInsert/isExtract to DEFAULT (or false), discarding the state that existed before the block was broken and re-placed. Players will be surprised to find their carefully set IO-modes reset.

-        return new ChemicalConduitConnectionConfig(DEFAULT.isInsert, insertColor, DEFAULT.isExtract, extractColor,
+        return new ChemicalConduitConnectionConfig(isInsert, insertColor, isExtract, extractColor,

Apply the same idea to disconnected().

_🛠️ Refactor suggestion_ **`reconnected()`/`disconnected()` override the user’s settings** Both methods hard-code `isInsert`/`isExtract` to `DEFAULT` (or `false`), discarding the state that existed before the block was broken and re-placed. Players will be surprised to find their carefully set IO-modes reset. ```diff - return new ChemicalConduitConnectionConfig(DEFAULT.isInsert, insertColor, DEFAULT.isExtract, extractColor, + return new ChemicalConduitConnectionConfig(isInsert, insertColor, isExtract, extractColor, ``` Apply the same idea to `disconnected()`. <!-- This is an auto-generated reply by CodeRabbit -->
@ -24,0 +31,4 @@
if (extractHandler == null) {
continue;
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

⚠️ Potential issue

Integer overflow when computing transferRate

transferRatePerTick() and networkTickRate() are ints.
int * int overflows before the value is widened to long, producing negative or wrapped results for large buffers.

-        final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate();
📝 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.

        final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate();
        var context = network.getOrCreateContext(ChemicalConduitNetworkContext.TYPE);
_⚠️ Potential issue_ **Integer overflow when computing `transferRate`** `transferRatePerTick()` and `networkTickRate()` are `int`s. `int * int` overflows before the value is widened to `long`, producing negative or wrapped results for large buffers. ```diff - final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate(); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate(); var context = network.getOrCreateContext(ChemicalConduitNetworkContext.TYPE); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -91,28 +91,28 @@ public class ItemConduitScreenType extends IOConduitScreenType<ItemConduitConnec
// Redstone control
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

⚠️ Potential issue

GUI still fetches receiveRedstoneChannel for extract side – causes wrong colour to display

The redstone channel widget for the extract panel reads the receive value, so the picker can display (and later serialise) the wrong colour.

-                () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(),
+                () -> dataAccess.getConnectionConfig().extractRedstoneChannel(),

Make the same change wherever the supplier lambda is used across screen types.

_⚠️ Potential issue_ **GUI still fetches `receiveRedstoneChannel` for extract side – causes wrong colour to display** The redstone channel widget for the *extract* panel reads the *receive* value, so the picker can display (and later serialise) the wrong colour. ```diff - () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + () -> dataAccess.getConnectionConfig().extractRedstoneChannel(), ``` Make the same change wherever the supplier lambda is used across screen types. <!-- This is an auto-generated comment by CodeRabbit -->
@ -1036,6 +968,93 @@ public final class ConduitBundleBlockEntity extends EnderBlockEntity
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

⚠️ Potential issue

Null-check order causes unavoidable NullPointerException

if (level == null && !level.isClientSide()) {  }

When level is null the right-hand side of the && is still evaluated, dereferencing level and crashing.
Swap the order or split the condition:

-        if (level == null && !level.isClientSide()) {
+        if (level != null && !level.isClientSide()) {
_⚠️ Potential issue_ **Null-check order causes unavoidable `NullPointerException`** ```java if (level == null && !level.isClientSide()) { … } ``` When `level` is `null` the right-hand side of the `&&` is still evaluated, dereferencing `level` and crashing. Swap the order or split the condition: ```diff - if (level == null && !level.isClientSide()) { + if (level != null && !level.isClientSide()) { ``` <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

🛠️ Refactor suggestion

Neighbour-capability caches never cleared

NeighboringCapabilityCaches survive the full lifetime of the bundle and are not invalidated on chunk unload, neighbour removal, or conduit removal. This can retain stale BlockCapabilityCache instances referencing unloaded chunks, blocking garbage collection and potentially leaking memory over long sessions (e.g. on large servers).

Provide an explicit clear()/invalidate() path and invoke it from:

  • removeConduit
  • onChunkUnloaded
  • any place where neighbour blocks change (e.g. updateConnections)
_🛠️ Refactor suggestion_ **Neighbour-capability caches never cleared** `NeighboringCapabilityCaches` survive the full lifetime of the bundle and are not invalidated on chunk unload, neighbour removal, or conduit removal. This can retain stale `BlockCapabilityCache` instances referencing unloaded chunks, blocking garbage collection and potentially leaking memory over long sessions (e.g. on large servers). Provide an explicit `clear()`/`invalidate()` path and invoke it from: * `removeConduit` * `onChunkUnloaded` * any place where neighbour blocks change (e.g. `updateConnections`) <!-- This is an auto-generated reply by CodeRabbit -->
@ -0,0 +1,580 @@
package com.enderio.conduits.common.conduit.network;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

🛠️ Refactor suggestion

Connection-list duplication & ordering costs

addLoadedNode() pushes every new ConduitBlockConnection into all existing accessibleBlockConnectionsMap value lists.
For large dense networks this becomes O(n²) memory and time, and duplicates are possible when two nodes share multiple endpoints.

A Set (LinkedHashSet to preserve order) eliminates duplicates and drops complexity to O(n) for insertion, at the cost of marginally slower sorting later.

_🛠️ Refactor suggestion_ **Connection-list duplication & ordering costs** `addLoadedNode()` pushes every new `ConduitBlockConnection` into *all* existing `accessibleBlockConnectionsMap` value lists. For large dense networks this becomes O(n²) memory and time, and duplicates are possible when two nodes share multiple endpoints. A `Set` (LinkedHashSet to preserve order) eliminates duplicates and drops complexity to O(n) for insertion, at the cost of marginally slower sorting later. <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

⚠️ Potential issue

onChunkCoverageChanged can be null – defensively check before accept()

addNodeToPositionMaps() invokes onChunkCoverageChanged.accept(this) without verifying that the consumer has been registered.
On servers that never wire up the callback this leads to an immediate NullPointerException once the first network spreads over more than one chunk.

-        if (!isRebuild && isNewChunk) {
-            onChunkCoverageChanged.accept(this);
+        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
+            onChunkCoverageChanged.accept(this);
         }
📝 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.

    private void addNodeToPositionMaps(ConduitNode node, boolean isRebuild) {
        // Put nodes into the position map.
        long chunk = ChunkPos.asLong(node.pos());
        boolean isNewChunk = !nodesByChunkPos.containsKey(chunk);
        nodesByChunkPos.put(chunk, node);

        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
            onChunkCoverageChanged.accept(this);
        }
    }
_⚠️ Potential issue_ **`onChunkCoverageChanged` can be null – defensively check before `accept()`** `addNodeToPositionMaps()` invokes `onChunkCoverageChanged.accept(this)` without verifying that the consumer has been registered. On servers that never wire up the callback this leads to an immediate `NullPointerException` once the first network spreads over more than one chunk. ```diff - if (!isRebuild && isNewChunk) { - onChunkCoverageChanged.accept(this); + if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { + onChunkCoverageChanged.accept(this); } ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion private void addNodeToPositionMaps(ConduitNode node, boolean isRebuild) { // Put nodes into the position map. long chunk = ChunkPos.asLong(node.pos()); boolean isNewChunk = !nodesByChunkPos.containsKey(chunk); nodesByChunkPos.put(chunk, node); if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { onChunkCoverageChanged.accept(this); } } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +1,341 @@
package com.enderio.conduits.common.conduit.network;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

⚠️ Potential issue

Removing empty networks here breaks internal chunk-tracking maps

networks.values().removeIf(network -> network.isValid() && network.isEmpty()); only removes the reference from networks.
networksByChunk and chunksByNetwork keep dangling entries, so later look-ups (e.g., in onChunkTicketLevelUpdated) will still hand out a discarded network, leading to NPEs or logic errors.

Prefer using existing disposal logic so all maps stay in sync:

-        networks.values().removeIf(network -> network.isValid() && network.isEmpty());
+        // Delegate to onNetworkDiscarded so auxiliary maps are cleaned as well.
+        networks.values().stream()
+                .filter(n -> n.isValid() && n.isEmpty())
+                .toList() // avoid CME
+                .forEach(this::onNetworkDiscarded);
📝 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.

    private void tick(ServerLevel serverLevel) {
        // Only remove empty graphs here
        // Delegate to onNetworkDiscarded so auxiliary maps are cleaned as well.
        networks.values().stream()
                .filter(n -> n.isValid() && n.isEmpty())
                .toList() // avoid CME
                .forEach(this::onNetworkDiscarded);
_⚠️ Potential issue_ **Removing empty networks here breaks internal chunk-tracking maps** `networks.values().removeIf(network -> network.isValid() && network.isEmpty());` only removes the reference from `networks`. `networksByChunk` and `chunksByNetwork` keep dangling entries, so later look-ups (e.g., in `onChunkTicketLevelUpdated`) will still hand out a discarded network, leading to NPEs or logic errors. Prefer using existing disposal logic so all maps stay in sync: ```diff - networks.values().removeIf(network -> network.isValid() && network.isEmpty()); + // Delegate to onNetworkDiscarded so auxiliary maps are cleaned as well. + networks.values().stream() + .filter(n -> n.isValid() && n.isEmpty()) + .toList() // avoid CME + .forEach(this::onNetworkDiscarded); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion private void tick(ServerLevel serverLevel) { // Only remove empty graphs here // Delegate to onNetworkDiscarded so auxiliary maps are cleaned as well. networks.values().stream() .filter(n -> n.isValid() && n.isEmpty()) .toList() // avoid CME .forEach(this::onNetworkDiscarded); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +166,4 @@
public boolean isDirty() {
// Always save networks when the opportunity arises
return true;
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

🛠️ Refactor suggestion

isDirty() returning true every tick will spam world-saving I/O

Always returning true causes the Minecraft data storage layer to write this file to disk on every save cycle (normally each tick).
Large worlds with many conduits will suffer significant, needless disk I/O and TPS loss.

Introduce an internal dirty flag and call setDirty() (inherited) whenever networks are added/removed or their data actually mutates.

_🛠️ Refactor suggestion_ **`isDirty()` returning `true` every tick will spam world-saving I/O** Always returning `true` causes the Minecraft data storage layer to write this file to disk on **every** save cycle (normally each tick). Large worlds with many conduits will suffer significant, needless disk I/O and TPS loss. Introduce an internal `dirty` flag and call `setDirty()` (inherited) whenever networks are added/removed or their data actually mutates. <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +1,302 @@
package com.enderio.conduits.common.conduit.network;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

🛠️ Refactor suggestion

markDirty() throws on perfectly legitimate calls while the chunk is still loading

markDirty() calls ensureValid() which in turn checks isLoaded().
During world-load (e.g. when the bundle is constructed but the chunk is not yet ticking) any innocent attempt to mark the node dirty will blow up with an IllegalStateException. Nothing in the Javadoc warns callers about this pre-condition.

Consider relaxing the contract so that marking dirty is always safe (no-op when not loaded), or – at the very least – document the restriction prominently and keep the public API exception-free.

_🛠️ Refactor suggestion_ **`markDirty()` throws on perfectly legitimate calls while the chunk is still loading** `markDirty()` calls `ensureValid()` which in turn checks `isLoaded()`. During world-load (e.g. when the bundle is constructed but the chunk is not yet ticking) any innocent attempt to mark the node dirty will blow up with an `IllegalStateException`. Nothing in the Javadoc warns callers about this pre-condition. Consider relaxing the contract so that marking dirty is always safe (no-op when not loaded), or – at the very least – document the restriction prominently and keep the *public* API exception-free. <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +298,4 @@
(side, config) -> conduitBundle.setConnectionConfig(conduit, side, config));
legacyDataContainer = null;
}
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

⚠️ Potential issue

Potential NPE in tryCopyLegacyData() when conduitBundle is still null

copyLegacyData() invokes conduitBundle.setConnectionConfig(...), but conduitBundle is only set once attach() has run.
If setNetwork() is called before attach() (which happens when the node is merged into an existing graph during deserialisation) the dereference will crash.

Guard with a null-check or call this helper from attach() instead, once the bundle reference is guaranteed.

-        if (network != null && legacyDataContainer != null && isLoaded()) {
+        if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) {
_⚠️ Potential issue_ **Potential NPE in `tryCopyLegacyData()` when `conduitBundle` is still `null`** `copyLegacyData()` invokes `conduitBundle.setConnectionConfig(...)`, but `conduitBundle` is only set once `attach()` has run. If `setNetwork()` is called *before* `attach()` (which happens when the node is merged into an existing graph during deserialisation) the dereference will crash. Guard with a null-check or call this helper from `attach()` instead, once the bundle reference is guaranteed. ```diff - if (network != null && legacyDataContainer != null && isLoaded()) { + if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) { ``` <!-- This is an auto-generated comment by CodeRabbit -->
@ -102,18 +101,17 @@ public record EnergyConduit(ResourceLocation texture, Component description, int
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

💡 Verification agent

🧩 Analysis chain

Redstone channel accessor mismatch will block extraction

proxyCapability now checks extraction settings but still queries config.receiveRedstoneChannel().
When a player configures an extract side and assigns an extract redstone channel, that channel will be ignored – the conduit falls back to the default and may stay disabled.

-                    boolean hasRedstone = node.hasRedstoneSignal(config.receiveRedstoneChannel());
+                    boolean hasRedstone = node.hasRedstoneSignal(config.extractRedstoneChannel());

Please update the getter to extractRedstoneChannel() (and any related references) to ensure the redstone gate works as intended.


🏁 Script executed:

#!/bin/bash
# Find any remaining mismatches where extract-side logic still calls receiveRedstoneChannel().
rg --line-number --context 2 $'\\.receiveRedstoneChannel\\(' | head

Length of output: 1714


Update extract-side redstone channel getters

The extract logic still reads from receiveRedstoneChannel(), so any configured extract channel will be ignored at runtime and in the GUI. Please update all of the following to use extractRedstoneChannel() instead:

  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (around line 111)
  • boolean hasRedstone = node.hasRedstoneSignal(config.receiveRedstoneChannel());
    
  • boolean hasRedstone = node.hasRedstoneSignal(config.extractRedstoneChannel());
    
    
    
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (line 19)
  • () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(),
    
  • () -> dataAccess.getConnectionConfig().extractRedstoneChannel(),
    
    
    
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (line 94)
  • () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(),
    
  • () -> dataAccess.getConnectionConfig().extractRedstoneChannel(),
    
    
    

This ensures the redstone gate for extraction respects the player-selected channel.

_💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Redstone channel accessor mismatch will block extraction** `proxyCapability` now checks extraction settings but still queries `config.receiveRedstoneChannel()`. When a player configures an *extract* side and assigns an *extract redstone channel*, that channel will be ignored – the conduit falls back to the default and may stay disabled. ```diff - boolean hasRedstone = node.hasRedstoneSignal(config.receiveRedstoneChannel()); + boolean hasRedstone = node.hasRedstoneSignal(config.extractRedstoneChannel()); ``` Please update the getter to `extractRedstoneChannel()` (and any related references) to ensure the redstone gate works as intended. --- 🏁 Script executed: ```shell #!/bin/bash # Find any remaining mismatches where extract-side logic still calls receiveRedstoneChannel(). rg --line-number --context 2 $'\\.receiveRedstoneChannel\\(' | head ``` Length of output: 1714 --- </details> **Update extract-side redstone channel getters** The extract logic still reads from `receiveRedstoneChannel()`, so any configured extract channel will be ignored at runtime and in the GUI. Please update all of the following to use `extractRedstoneChannel()` instead: - **enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java** (around line 111) ```diff - boolean hasRedstone = node.hasRedstoneSignal(config.receiveRedstoneChannel()); + boolean hasRedstone = node.hasRedstoneSignal(config.extractRedstoneChannel()); ``` - **enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java** (line 19) ```diff - () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + () -> dataAccess.getConnectionConfig().extractRedstoneChannel(), ``` - **enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java** (line 94) ```diff - () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + () -> dataAccess.getConnectionConfig().extractRedstoneChannel(), ``` This ensures the redstone gate for extraction respects the player-selected channel. <!-- This is an auto-generated comment by CodeRabbit -->
@ -15,93 +15,94 @@ import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

🛠️ Refactor suggestion

STREAM_CODEC uses the same inconsistent getter

Same mismatch as above – replace the getter with the correctly named one to stay in sync with the record fields.

_🛠️ Refactor suggestion_ **STREAM_CODEC uses the same inconsistent getter** Same mismatch as above – replace the getter with the correctly named one to stay in sync with the record fields. <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

⚠️ Potential issue

Compilation error: direct field access on a record

DEFAULT.isInsert and DEFAULT.isExtract access the synthetic fields, which are private in a record.
Use the accessor methods instead.

-        return new EnergyConduitConnectionConfig(DEFAULT.isInsert, DEFAULT.isExtract, extractRedstoneControl,
+        return new EnergyConduitConnectionConfig(DEFAULT.isInsert(), DEFAULT.isExtract(), extractRedstoneControl,
                 receiveRedstoneChannel);
📝 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.

    @Override
    public ConnectionConfig reconnected() {
        return new EnergyConduitConnectionConfig(DEFAULT.isInsert(), DEFAULT.isExtract(), extractRedstoneControl,
                receiveRedstoneChannel);
    }
_⚠️ Potential issue_ **Compilation error: direct field access on a record** `DEFAULT.isInsert` and `DEFAULT.isExtract` access the synthetic fields, which are `private` in a record. Use the accessor methods instead. ```diff - return new EnergyConduitConnectionConfig(DEFAULT.isInsert, DEFAULT.isExtract, extractRedstoneControl, + return new EnergyConduitConnectionConfig(DEFAULT.isInsert(), DEFAULT.isExtract(), extractRedstoneControl, receiveRedstoneChannel); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion @Override public ConnectionConfig reconnected() { return new EnergyConduitConnectionConfig(DEFAULT.isInsert(), DEFAULT.isExtract(), extractRedstoneControl, receiveRedstoneChannel); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

🛠️ Refactor suggestion

Missing accessor calls in withIsExtract

For consistency and to avoid future access-level issues, prefer the accessor methods.

-return new EnergyConduitConnectionConfig(isInsert, isExtract, extractRedstoneControl, receiveRedstoneChannel);
+return new EnergyConduitConnectionConfig(isInsert(), isExtract, extractRedstoneControl, receiveRedstoneChannel());
📝 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.

    public EnergyConduitConnectionConfig withIsExtract(boolean isExtract) {
        return new EnergyConduitConnectionConfig(isInsert(), isExtract, extractRedstoneControl, receiveRedstoneChannel());
    }
_🛠️ Refactor suggestion_ **Missing accessor calls in `withIsExtract`** For consistency and to avoid future access-level issues, prefer the accessor methods. ```diff -return new EnergyConduitConnectionConfig(isInsert, isExtract, extractRedstoneControl, receiveRedstoneChannel); +return new EnergyConduitConnectionConfig(isInsert(), isExtract, extractRedstoneControl, receiveRedstoneChannel()); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion public EnergyConduitConnectionConfig withIsExtract(boolean isExtract) { return new EnergyConduitConnectionConfig(isInsert(), isExtract, extractRedstoneControl, receiveRedstoneChannel()); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

⚠️ Potential issue

Codec field names and getters are inconsistent

The codec keys were changed to the new “insert/extract” terminology, but the getter for the channel still points to receiveRedstoneChannel, which is now semantically “extract”.
Rename the record component and getter to extractRedstoneChannel (or adjust the codec mapping) to avoid confusion and future maintenance errors.

-                    DyeColor.CODEC.fieldOf("extract_redstone_channel")
-                            .forGetter(EnergyConduitConnectionConfig::receiveRedstoneChannel))
+                    DyeColor.CODEC.fieldOf("extract_redstone_channel")
+                            .forGetter(EnergyConduitConnectionConfig::extractRedstoneChannel))
📝 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.

public static MapCodec<EnergyConduitConnectionConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance
        .group(Codec.BOOL.fieldOf("is_insert").forGetter(EnergyConduitConnectionConfig::isInsert),
               Codec.BOOL.fieldOf("is_extract").forGetter(EnergyConduitConnectionConfig::isExtract),
               RedstoneControl.CODEC.fieldOf("extract_redstone_control")
                       .forGetter(EnergyConduitConnectionConfig::extractRedstoneControl),
               DyeColor.CODEC.fieldOf("extract_redstone_channel")
                       .forGetter(EnergyConduitConnectionConfig::extractRedstoneChannel))
        .apply(instance, EnergyConduitConnectionConfig::new));
_⚠️ Potential issue_ **Codec field names and getters are inconsistent** The codec keys were changed to the new “insert/extract” terminology, but the getter for the channel still points to `receiveRedstoneChannel`, which is now semantically “extract”. Rename the record component and getter to `extractRedstoneChannel` (or adjust the codec mapping) to avoid confusion and future maintenance errors. ```diff - DyeColor.CODEC.fieldOf("extract_redstone_channel") - .forGetter(EnergyConduitConnectionConfig::receiveRedstoneChannel)) + DyeColor.CODEC.fieldOf("extract_redstone_channel") + .forGetter(EnergyConduitConnectionConfig::extractRedstoneChannel)) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion public static MapCodec<EnergyConduitConnectionConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance .group(Codec.BOOL.fieldOf("is_insert").forGetter(EnergyConduitConnectionConfig::isInsert), Codec.BOOL.fieldOf("is_extract").forGetter(EnergyConduitConnectionConfig::isExtract), RedstoneControl.CODEC.fieldOf("extract_redstone_control") .forGetter(EnergyConduitConnectionConfig::extractRedstoneControl), DyeColor.CODEC.fieldOf("extract_redstone_channel") .forGetter(EnergyConduitConnectionConfig::extractRedstoneChannel)) .apply(instance, EnergyConduitConnectionConfig::new)); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:59 +00:00

⚠️ Potential issue

withIsInsert flips the wrong value

The parameter is named isExtract, and the new instance places it into the insert slot, effectively swapping the two flags.

-public EnergyConduitConnectionConfig withIsInsert(boolean isExtract) {
-    return new EnergyConduitConnectionConfig(isExtract, this.isExtract, extractRedstoneControl,
-            receiveRedstoneChannel);
+public EnergyConduitConnectionConfig withIsInsert(boolean isInsert) {
+    return new EnergyConduitConnectionConfig(isInsert, this.isExtract(), extractRedstoneControl,
+            receiveRedstoneChannel());
}

Committable suggestion skipped: line range outside the PR's diff.

_⚠️ Potential issue_ **`withIsInsert` flips the wrong value** The parameter is named `isExtract`, and the new instance places it into the *insert* slot, effectively swapping the two flags. ```diff -public EnergyConduitConnectionConfig withIsInsert(boolean isExtract) { - return new EnergyConduitConnectionConfig(isExtract, this.isExtract, extractRedstoneControl, - receiveRedstoneChannel); +public EnergyConduitConnectionConfig withIsInsert(boolean isInsert) { + return new EnergyConduitConnectionConfig(isInsert, this.isExtract(), extractRedstoneControl, + receiveRedstoneChannel()); } ``` > Committable suggestion skipped: line range outside the PR's diff. <!-- This is an auto-generated comment by CodeRabbit -->
@ -55,8 +58,17 @@ public class EnergyConduitNetworkContext implements ConduitNetworkContext<Energy
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

⚠️ Potential issue

Potential divide-by-zero and energy loss during split

allNetworks.stream()...reduce(0, Integer::sum) may return 0 when the list is empty (or all networks report 0 nodes), which leads to a division by zero and Infinity proportion, corrupting stored energy.
Secondly, Math.floor on each network can drop residual joules, making the sum of children < parent.

-public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks) {
-    // Split stored energy based on the network size difference.
-    float proportion = selfNetwork.nodeCount()
-            / (float) allNetworks.stream().map(IConduitNetwork::nodeCount).reduce(0, Integer::sum);
-    return new EnergyConduitNetworkContext((int) Math.floor(proportion * energyStored));
+public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork,
+        Set<? extends IConduitNetwork> allNetworks) {
+    int totalNodes = allNetworks.stream().mapToInt(IConduitNetwork::nodeCount).sum();
+    if (totalNodes == 0) { // Fallback – shouldn’t happen, but avoids NaN/∞
+        return new EnergyConduitNetworkContext(energyStored);
+    }
+    int allocated = (int) ((long) energyStored * selfNetwork.nodeCount() / totalNodes); // avoid rounding loss
+    return new EnergyConduitNetworkContext(allocated);
 }

This guards against edge cases and conserves total energy across all split networks.

📝 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.

    public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork,
            Set<? extends IConduitNetwork> allNetworks) {
        int totalNodes = allNetworks.stream().mapToInt(IConduitNetwork::nodeCount).sum();
        if (totalNodes == 0) { // Fallback – shouldn’t happen, but avoids NaN/∞
            return new EnergyConduitNetworkContext(energyStored);
        }
        int allocated = (int) ((long) energyStored * selfNetwork.nodeCount() / totalNodes); // avoid rounding loss
        return new EnergyConduitNetworkContext(allocated);
    }
_⚠️ Potential issue_ **Potential divide-by-zero and energy loss during split** `allNetworks.stream()...reduce(0, Integer::sum)` may return `0` when the list is empty (or all networks report `0` nodes), which leads to a division by zero and `Infinity` proportion, corrupting stored energy. Secondly, `Math.floor` on each network can drop residual joules, making the sum of children `<` parent. ```diff -public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks) { - // Split stored energy based on the network size difference. - float proportion = selfNetwork.nodeCount() - / (float) allNetworks.stream().map(IConduitNetwork::nodeCount).reduce(0, Integer::sum); - return new EnergyConduitNetworkContext((int) Math.floor(proportion * energyStored)); +public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork, + Set<? extends IConduitNetwork> allNetworks) { + int totalNodes = allNetworks.stream().mapToInt(IConduitNetwork::nodeCount).sum(); + if (totalNodes == 0) { // Fallback – shouldn’t happen, but avoids NaN/∞ + return new EnergyConduitNetworkContext(energyStored); + } + int allocated = (int) ((long) energyStored * selfNetwork.nodeCount() / totalNodes); // avoid rounding loss + return new EnergyConduitNetworkContext(allocated); } ``` This guards against edge cases and conserves total energy across all split networks. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion public EnergyConduitNetworkContext split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks) { int totalNodes = allNetworks.stream().mapToInt(IConduitNetwork::nodeCount).sum(); if (totalNodes == 0) { // Fallback – shouldn’t happen, but avoids NaN/∞ return new EnergyConduitNetworkContext(energyStored); } int allocated = (int) ((long) energyStored * selfNetwork.nodeCount() / totalNodes); // avoid rounding loss return new EnergyConduitNetworkContext(allocated); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -15,38 +15,38 @@ import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

🛠️ Refactor suggestion

Same key mismatch as the chemical config

extractColor is stored under "extract_channel" – please rename to "extract_color" to avoid corrupting saved data.

_🛠️ Refactor suggestion_ **Same key mismatch as the chemical config** `extractColor` is stored under `"extract_channel"` – please rename to `"extract_color"` to avoid corrupting saved data. <!-- This is an auto-generated comment by CodeRabbit -->
@ -55,72 +55,72 @@ public record FluidConduitConnectionConfig(boolean isSend, DyeColor sendColor, b
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

🛠️ Refactor suggestion

reconnected()/disconnected() overwrite previous IO flags

Same issue as in ChemicalConduitConnectionConfig – the player’s insert/extract settings are lost.

_🛠️ Refactor suggestion_ **`reconnected()`/`disconnected()` overwrite previous IO flags** Same issue as in ChemicalConduitConnectionConfig – the player’s insert/extract settings are lost. <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

⚠️ Potential issue

split loses lastLockedFluid history
When a network is split, you create a new context with only lockedFluid, resetting lastLockedFluid to EMPTY. Any ticker logic relying on lastLockedFluid to mark nodes dirty will miss the change. Should the split copy both fields?

-        return new FluidConduitNetworkContext(lockedFluid);
+        return new FluidConduitNetworkContext(lockedFluid, lastLockedFluid);
📝 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.

    public FluidConduitNetworkContext split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks) {
        return new FluidConduitNetworkContext(lockedFluid, lastLockedFluid);
    }
_⚠️ Potential issue_ **`split` loses `lastLockedFluid` history** When a network is split, you create a new context with only `lockedFluid`, resetting `lastLockedFluid` to `EMPTY`. Any ticker logic relying on `lastLockedFluid` to mark nodes dirty will miss the change. Should the split copy both fields? ```diff - return new FluidConduitNetworkContext(lockedFluid); + return new FluidConduitNetworkContext(lockedFluid, lastLockedFluid); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion public FluidConduitNetworkContext split(IConduitNetwork selfNetwork, Set<? extends IConduitNetwork> allNetworks) { return new FluidConduitNetworkContext(lockedFluid, lastLockedFluid); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -1,16 +1,12 @@
package com.enderio.conduits.common.conduit.type.fluid;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:58 +00:00

⚠️ Potential issue

Incorrect filter slot used for inserts – may bypass insert-side filters

insertConnection.inventory().getStackInSlot(FluidConduit.EXTRACT_FILTER_SLOT) queries the extract filter slot while validating an insert action.
If INSERT_FILTER_SLOT exists (as it does for the other conduit types) this line will silently ignore player-configured insert filters and could allow unwanted fluids into inventories.

-            var insertFilter = insertConnection.inventory()
-                    .getStackInSlot(FluidConduit.EXTRACT_FILTER_SLOT)
+            var insertFilter = insertConnection.inventory()
+                    .getStackInSlot(FluidConduit.INSERT_FILTER_SLOT)
📝 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.

            var insertFilter = insertConnection.inventory()
                    .getStackInSlot(FluidConduit.INSERT_FILTER_SLOT)
                    .getCapability(EIOCapabilities.FLUID_FILTER);
_⚠️ Potential issue_ **Incorrect filter slot used for inserts – may bypass insert-side filters** `insertConnection.inventory().getStackInSlot(FluidConduit.EXTRACT_FILTER_SLOT)` queries the *extract* filter slot while validating an *insert* action. If `INSERT_FILTER_SLOT` exists (as it does for the other conduit types) this line will silently ignore player-configured insert filters and could allow unwanted fluids into inventories. ```diff - var insertFilter = insertConnection.inventory() - .getStackInSlot(FluidConduit.EXTRACT_FILTER_SLOT) + var insertFilter = insertConnection.inventory() + .getStackInSlot(FluidConduit.INSERT_FILTER_SLOT) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion var insertFilter = insertConnection.inventory() .getStackInSlot(FluidConduit.INSERT_FILTER_SLOT) .getCapability(EIOCapabilities.FLUID_FILTER); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -16,33 +16,34 @@ import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

⚠️ Potential issue

Inconsistent “receive” vs “extract” nomenclature breaks serialisation and API coherence

The field is still called receiveRedstoneChannel, yet every other identifier was migrated to the new extract* terminology.
Record parameter 6, the corresponding accessor, the codec key ("extract_redstone_channel"), the StreamCodec getter, and the withExtractRedstoneChannel builder now reference different names.
• Aside from the mental overhead, this will desynchronise data between server ↔ client and disk ↔ RAM because codecs write "extract_redstone_channel" but read via receiveRedstoneChannel().

-        DyeColor extractColor, RedstoneControl extractRedstoneControl, DyeColor receiveRedstoneChannel,
+        DyeColor extractColor, RedstoneControl extractRedstoneControl, DyeColor extractRedstoneChannel,

Apply the rename consistently:

  1. Record parameter & accessor → extractRedstoneChannel.
  2. All constructor invocations (DEFAULT, builder methods, etc.) → pass the renamed variable.
  3. Codec/StreamCodec getters → ItemConduitConnectionConfig::extractRedstoneChannel.
  4. Remove the obsolete accessor.

Failing to do this will silently corrupt saved worlds.

Also applies to: 28-36, 41-48, 118-121

_⚠️ Potential issue_ **Inconsistent “receive” vs “extract” nomenclature breaks serialisation and API coherence** The field is still called `receiveRedstoneChannel`, yet every other identifier was migrated to the new `extract*` terminology. • `Record` parameter 6, the corresponding accessor, the codec key (`"extract_redstone_channel"`), the `StreamCodec` getter, and the `withExtractRedstoneChannel` builder now reference different names. • Aside from the mental overhead, this will desynchronise data between server ↔ client and disk ↔ RAM because codecs write `"extract_redstone_channel"` but read via `receiveRedstoneChannel()`. ```diff - DyeColor extractColor, RedstoneControl extractRedstoneControl, DyeColor receiveRedstoneChannel, + DyeColor extractColor, RedstoneControl extractRedstoneControl, DyeColor extractRedstoneChannel, ``` Apply the rename consistently: 1. Record parameter & accessor → `extractRedstoneChannel`. 2. All constructor invocations (`DEFAULT`, builder methods, etc.) → pass the renamed variable. 3. Codec/StreamCodec getters → `ItemConduitConnectionConfig::extractRedstoneChannel`. 4. Remove the obsolete accessor. Failing to do this will silently corrupt saved worlds. Also applies to: 28-36, 41-48, 118-121 <!-- This is an auto-generated comment by CodeRabbit -->
@ -1,114 +1,110 @@
package com.enderio.conduits.common.conduit.type.item;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

🛠️ Refactor suggestion

Round-robin exit condition may skip remaining items

isEmpty(extractHandler, i + 1) ignores the current slot (i).
If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed.

A simpler, safer exit check is to rely solely on the transfer-rate cap:

-if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+if (extracted >= speed) {

If you still want the early-out optimisation, scan from slot 0 so the current slot is included.

📝 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.

                        if (successfullyInserted > 0) {
                            extracted += successfullyInserted;
                            extractHandler.extractItem(i, successfullyInserted, false);
-                           if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+                           if (extracted >= speed) {
                                if (connectionConfig.isRoundRobin()) {
                                    nodeData.setIndex(extractConnection.connectionSide(), senderIndex + 1);
                                }
                                continue toNextExtract;
                            } else {
                                continue nextItem;
                            }
                        }
_🛠️ Refactor suggestion_ **Round-robin exit condition may skip remaining items** `isEmpty(extractHandler, i + 1)` ignores the current slot (`i`). If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed. A simpler, safer exit check is to rely solely on the transfer-rate cap: ```diff -if (extracted >= speed || isEmpty(extractHandler, i + 1)) { +if (extracted >= speed) { ``` If you still want the early-out optimisation, scan from slot 0 so the current slot is included. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion if (successfullyInserted > 0) { extracted += successfullyInserted; extractHandler.extractItem(i, successfullyInserted, false); - if (extracted >= speed || isEmpty(extractHandler, i + 1)) { + if (extracted >= speed) { if (connectionConfig.isRoundRobin()) { nodeData.setIndex(extractConnection.connectionSide(), senderIndex + 1); } continue toNextExtract; } else { continue nextItem; } } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -1,97 +1,61 @@
package com.enderio.conduits.common.conduit.type.redstone;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

🛠️ Refactor suggestion

Neighbour updates executed redundantly for every channel

network.insertConnections() is iterated for every channel whenever any channel’s signal changes, resulting in updateNeighborsAt being called channels × inserts times per tick.
On larger networks this scales poorly and causes needless block-update spam.

Suggestion: collect all unique insertConnections whose output has actually changed and update them once:

Set<ConduitBlockConnection> dirtyInserts = new ObjectOpenHashSet<>();
...
if (context.isNew() || changed) {
    dirtyInserts.add(insertConnection);
}
...
dirtyInserts.forEach(conn -> {
    level.updateNeighborsAt(conn.node().pos(), ConduitBlocks.CONDUIT.get());
    if (conn.connectionConfig(RedstoneConduitConnectionConfig.TYPE).isStrongOutputSignal()) {
        level.updateNeighborsAt(conn.connectedBlockPos(), ConduitBlocks.CONDUIT.get());
    }
});

This preserves behaviour while cutting per-tick work dramatically.

_🛠️ Refactor suggestion_ **Neighbour updates executed redundantly for every channel** `network.insertConnections()` is iterated for *every* channel whenever *any* channel’s signal changes, resulting in `updateNeighborsAt` being called `channels × inserts` times per tick. On larger networks this scales poorly and causes needless block-update spam. Suggestion: collect all unique `insertConnection`s whose output has actually changed and update them once: ```java Set<ConduitBlockConnection> dirtyInserts = new ObjectOpenHashSet<>(); ... if (context.isNew() || changed) { dirtyInserts.add(insertConnection); } ... dirtyInserts.forEach(conn -> { level.updateNeighborsAt(conn.node().pos(), ConduitBlocks.CONDUIT.get()); if (conn.connectionConfig(RedstoneConduitConnectionConfig.TYPE).isStrongOutputSignal()) { level.updateNeighborsAt(conn.connectedBlockPos(), ConduitBlocks.CONDUIT.get()); } }); ``` This preserves behaviour while cutting per-tick work dramatically. <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

⚠️ Potential issue

Potential NPE on missing inventory or capability

extractConnection.inventory() may legitimately return null (e.g., if the block entity was removed between ticks).
Accessing getStackInSlot(...).getCapability(...) without a preceding null-check will crash the server in that scenario.

-var redstoneExtractFilter = extractConnection.inventory()
-        .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
-        .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER);
+var inv = extractConnection.inventory();
+IRedstoneExtractFilter redstoneExtractFilter = null;
+if (inv != null) {
+    redstoneExtractFilter = inv
+            .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
+            .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER);
+}

Alternatively, make extractConnection.inventory() return Optional<IItemHandler> to enforce safe access throughout the codebase.

📝 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.

                var inv = extractConnection.inventory();
                IRedstoneExtractFilter redstoneExtractFilter = null;
                if (inv != null) {
                    redstoneExtractFilter = inv
                            .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
                            .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER);
                }

                if (redstoneExtractFilter != null) {
                    signal = redstoneExtractFilter.getInputSignal(level, extractConnection.connectedBlockPos(),
                            extractConnection.connectionSide());
                } else {
                    signal = level.getSignal(extractConnection.connectedBlockPos(), extractConnection.connectionSide());
                }
_⚠️ Potential issue_ **Potential NPE on missing inventory or capability** `extractConnection.inventory()` may legitimately return `null` (e.g., if the block entity was removed between ticks). Accessing `getStackInSlot(...).getCapability(...)` without a preceding null-check will crash the server in that scenario. ```diff -var redstoneExtractFilter = extractConnection.inventory() - .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) - .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER); +var inv = extractConnection.inventory(); +IRedstoneExtractFilter redstoneExtractFilter = null; +if (inv != null) { + redstoneExtractFilter = inv + .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) + .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER); +} ``` Alternatively, make `extractConnection.inventory()` return `Optional<IItemHandler>` to enforce safe access throughout the codebase. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion var inv = extractConnection.inventory(); IRedstoneExtractFilter redstoneExtractFilter = null; if (inv != null) { redstoneExtractFilter = inv .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER); } if (redstoneExtractFilter != null) { signal = redstoneExtractFilter.getInputSignal(level, extractConnection.connectedBlockPos(), extractConnection.connectionSide()); } else { signal = level.getSignal(extractConnection.connectedBlockPos(), extractConnection.connectionSide()); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +34,4 @@
.set(2, 0, 2, Blocks.CHEST.defaultBlockState())
);
// @formatter:on
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

⚠️ Potential issue

Template height too small for later y=1 edits

All three tests place or edit blocks at y = 1, but the registered template is only one block tall (height = 1).
StructureTemplateBuilder bounds the test area; attempting to touch y = 1 will therefore throw an out-of-bounds exception and fail the test before the logic runs.

-@RegisterStructureTemplate(THREE_CHESTS)
-public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3,
+@RegisterStructureTemplate(THREE_CHESTS)
+public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3,

Do the same for the ad-hoc template in itemConduitBasicTransfer.

📝 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.

    @RegisterStructureTemplate(THREE_CHESTS)
    public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3,
        builder -> builder
            // Sender
            .set(2, 0, 0, Blocks.CHEST.defaultBlockState())

            // Receivers
            .set(0, 0, 2, Blocks.CHEST.defaultBlockState())
            .set(2, 0, 2, Blocks.CHEST.defaultBlockState())
    );
    // @formatter:on
_⚠️ Potential issue_ **Template height too small for later y=1 edits** All three tests place or edit blocks at `y = 1`, but the registered template is only one block tall (`height = 1`). `StructureTemplateBuilder` bounds the test area; attempting to touch `y = 1` will therefore throw an out-of-bounds exception and fail the test before the logic runs. ```diff -@RegisterStructureTemplate(THREE_CHESTS) -public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3, +@RegisterStructureTemplate(THREE_CHESTS) +public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3, ``` Do the same for the ad-hoc template in `itemConduitBasicTransfer`. <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion @RegisterStructureTemplate(THREE_CHESTS) public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3, builder -> builder // Sender .set(2, 0, 0, Blocks.CHEST.defaultBlockState()) // Receivers .set(0, 0, 2, Blocks.CHEST.defaultBlockState()) .set(2, 0, 2, Blocks.CHEST.defaultBlockState()) ); // @formatter:on ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +107,4 @@
// Ensure the item moves to the closer chest
.thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, 1))
// Place an item into the first chest
.thenSucceed();
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:57 +00:00

🛠️ Refactor suggestion

Chest y-coordinate off by one

The template places all chests at y = 0, yet the assertions reference y = 1 (e.g., assertContainerHasExactly(2, 1, 2, …)).
With the corrected template height this will point at air blocks and the tests will still fail.

Update every assertion and helper call that targets a chest to use y = 0.

Also applies to: 141-144, 178-180

_🛠️ Refactor suggestion_ **Chest y-coordinate off by one** The template places all chests at `y = 0`, yet the assertions reference `y = 1` (e.g., `assertContainerHasExactly(2, 1, 2, …)`). With the corrected template height this will point at air blocks and the tests will still fail. Update every assertion and helper call that targets a chest to use `y = 0`. Also applies to: 141-144, 178-180 <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:33:56 +00:00

💡 Verification agent

🧩 Analysis chain

Good fix for server-side compatibility issue.

The commented-out client-side screen code correctly addresses the classloading errors on dedicated servers. This avoids attempting to load client-only classes in a server context.

Consider implementing a proper client-server communication pattern to replace this temporary solution:


🏁 Script executed:

#!/bin/bash
# Look for proper client-server communication patterns in the codebase

# Look for network packet implementations for opening screens
rg -A 5 "extends SimpleChannel|NetworkEvent.Context" --type java

Length of output: 66


🏁 Script executed:

#!/bin/bash
# Search for any existing network channel or screen-opening helpers in the codebase

# Look for SimpleChannel definitions
rg -n "SimpleChannel" --type java

# Look for Forge’s NetworkHooks usage (e.g. openGui/openScreen)
rg -n "NetworkHooks" --type java

Length of output: 272


Implement proper client–server communication to open the Enderface screen

I didn’t find any registered SimpleChannel or active NetworkHooks usage for opening GUIs (aside from the commented line in CapacitorBankBlock.java:95). To replace the temporary client-only call, please:

• Create and register a SimpleChannel (e.g. in com.enderio.machines.network) during mod setup using NetworkRegistry.newSimpleChannel.
• Define an OpenEnderfaceScreenPacket with encode/decode methods and a handler.
• From the server side (in EnderfaceTravelTarget), send this packet when the travel target is activated.
• In the client handler, call

Minecraft.getInstance().setScreen(new EnderfaceScreen(pos.immutable(), Minecraft.getInstance().level));

to open the GUI without causing class-load errors on dedicated servers.

This will safely bridge server logic to the client UI and fully resolve the compatibility issue.

_💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Good fix for server-side compatibility issue.** The commented-out client-side screen code correctly addresses the classloading errors on dedicated servers. This avoids attempting to load client-only classes in a server context. Consider implementing a proper client-server communication pattern to replace this temporary solution: --- 🏁 Script executed: ```shell #!/bin/bash # Look for proper client-server communication patterns in the codebase # Look for network packet implementations for opening screens rg -A 5 "extends SimpleChannel|NetworkEvent.Context" --type java ``` Length of output: 66 --- 🏁 Script executed: ```shell #!/bin/bash # Search for any existing network channel or screen-opening helpers in the codebase # Look for SimpleChannel definitions rg -n "SimpleChannel" --type java # Look for Forge’s NetworkHooks usage (e.g. openGui/openScreen) rg -n "NetworkHooks" --type java ``` Length of output: 272 --- </details> **Implement proper client–server communication to open the Enderface screen** I didn’t find any registered `SimpleChannel` or active `NetworkHooks` usage for opening GUIs (aside from the commented line in `CapacitorBankBlock.java:95`). To replace the temporary client-only call, please: • Create and register a `SimpleChannel` (e.g. in `com.enderio.machines.network`) during mod setup using `NetworkRegistry.newSimpleChannel`. • Define an `OpenEnderfaceScreenPacket` with encode/decode methods and a handler. • From the server side (in `EnderfaceTravelTarget`), send this packet when the travel target is activated. • In the client handler, call ```java Minecraft.getInstance().setScreen(new EnderfaceScreen(pos.immutable(), Minecraft.getInstance().level)); ``` to open the GUI without causing class-load errors on dedicated servers. This will safely bridge server logic to the client UI and fully resolve the compatibility issue. <!-- This is an auto-generated comment by CodeRabbit -->
Rover656 (Migrated from github.com) reviewed 2025-05-11 20:47:52 +00:00
Rover656 (Migrated from github.com) commented 2025-05-11 20:47:52 +00:00

This is intended - if both values flip to false it will disconnect. These methods are used to revive the config to a valid state.

This is intended - if both values flip to false it will disconnect. These methods are used to revive the config to a valid state.
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 20:48:21 +00:00
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:48:21 +00:00

@Rover656 I understand now - thanks for the clarification! The methods are serving their intended purpose by ensuring connections return to valid states rather than preserving previous configurations. Makes sense that disconnected connections would have both values set to false.


✏️ Learnings added
Learnt from: Rover656
PR: Team-EnderIO/EnderIO#1066
File: enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java:58-66
Timestamp: 2025-05-11T20:48:20.822Z
Learning: In ChemicalConduitConnectionConfig and similar connection configs, the `reconnected()` and `disconnected()` methods intentionally reset IO settings to default/disconnected values rather than preserving user settings, as they're meant to restore configs to valid states.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@Rover656 I understand now - thanks for the clarification! The methods are serving their intended purpose by ensuring connections return to valid states rather than preserving previous configurations. Makes sense that disconnected connections would have both values set to false. --- <details> <summary>✏️ Learnings added</summary> ``` Learnt from: Rover656 PR: Team-EnderIO/EnderIO#1066 File: enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java:58-66 Timestamp: 2025-05-11T20:48:20.822Z Learning: In ChemicalConduitConnectionConfig and similar connection configs, the `reconnected()` and `disconnected()` methods intentionally reset IO settings to default/disconnected values rather than preserving user settings, as they're meant to restore configs to valid states. ``` > Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings). </details> <!-- <agentComponentType>chat-answer</agentComponentType> --> <!-- <rawResChunk><answer>@Rover656 I understand now - thanks for the clarification! The methods are serving their intended purpose by ensuring connections return to valid states rather than preserving previous configurations. Makes sense that disconnected connections would have both values set to false. --- <details> <summary>✏️ Learnings added</summary> ``` Learnt from: Rover656 PR: Team-EnderIO/EnderIO#1066 File: enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java:58-66 Timestamp: 2025-05-11T20:48:20.822Z Learning: In ChemicalConduitConnectionConfig and similar connection configs, the `reconnected()` and `disconnected()` methods intentionally reset IO settings to default/disconnected values rather than preserving user settings, as they're meant to restore configs to valid states. ``` > Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings). </details> </answer></rawResChunk> --> <!-- This is an auto-generated reply by CodeRabbit -->
Rover656 (Migrated from github.com) reviewed 2025-05-11 20:53:24 +00:00
Rover656 (Migrated from github.com) commented 2025-05-11 20:53:23 +00:00

Almost everything you said here was wrong

Almost everything you said here was wrong
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 20:54:01 +00:00
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 20:54:01 +00:00

@Rover656 You're right. Looking more closely at the code, I can see the capability caches are already properly handled - they're removed when conduits are removed, and the entire entity (with all caches) is unloaded when chunks unload. Sorry about the inaccurate suggestion.

@Rover656 You're right. Looking more closely at the code, I can see the capability caches are already properly handled - they're removed when conduits are removed, and the entire entity (with all caches) is unloaded when chunks unload. Sorry about the inaccurate suggestion. <!-- <agentComponentType>chat-answer</agentComponentType> --> <!-- <rawResChunk><answer>@Rover656 You're right. Looking more closely at the code, I can see the capability caches are already properly handled - they're removed when conduits are removed, and the entire entity (with all caches) is unloaded when chunks unload. Sorry about the inaccurate suggestion.</answer></rawResChunk> --> <!-- This is an auto-generated reply by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 21:10:45 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 5

♻️ Duplicate comments (17)
endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)

61-66: Replace getFirst() with get(0)

The List#getFirst() method was introduced in Java 21, but the NeoForge toolchain targets Java 17. Using it will break compilation for anyone not on the latest toolchain.

-            var node = nodes.getFirst();
+            var node = nodes.get(0);
.github/workflows/verify.yml (1)

3-4: Improved security by explicitly limiting permissions

Good improvement to limit GITHUB_TOKEN permissions to read-only access for contents.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)

24-26: ⚠️ Potential issue

Potential NullPointerException when accessing inventory

The code doesn't check if extractConnection.inventory() returns null before accessing methods on it, which could cause a server crash if the inventory is missing.

Add a null check before accessing the inventory:

-var redstoneExtractFilter = extractConnection.inventory()
-        .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
-        .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER);
+var inventory = extractConnection.inventory();
+var redstoneExtractFilter = inventory != null
+        ? inventory.getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
+          .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER)
+        : null;

40-50: 🛠️ Refactor suggestion

Redundant neighbour updates for multiple channels

The current implementation updates neighbors for all insert connections in each channel whenever any signal changes. This could cause redundant updates for the same block positions when a node participates in multiple channels.

Collect all connections that need updates in a set and perform the updates once:

+Set<var> dirtyInsertConnections = new HashSet<>();
 for (var channel : network.allChannels()) {
     // ... [extract logic remains the same] ...

     // Fire block updates if the signal changed.
     if (context.isNew() || context.getSignal(channel) != context.getSignalLastTick(channel)) {
-        for (var insertConnection : network.insertConnections(channel)) {
-            level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get());
-
-            if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE)
-                    .isStrongOutputSignal()) {
-                level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get());
-            }
-        }
+        network.insertConnections(channel).forEach(dirtyInsertConnections::add);
     }
 }

+// Perform updates once for each unique connection
+dirtyInsertConnections.forEach(insertConnection -> {
+    level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get());
+
+    if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE)
+            .isStrongOutputSignal()) {
+        level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get());
+    }
+});

This preserves behaviour while reducing per-tick block updates significantly.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)

165-169: Fix the isDirty() method to avoid excessive disk I/O.

Always returning true causes Minecraft's data storage layer to write this file to disk on every save cycle (normally each tick). Large worlds with many conduits will suffer significant, needless disk I/O and TPS loss.

Introduce an internal dirty flag and call setDirty() (inherited) whenever networks are added/removed or their data actually mutates.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (8)

26-37: ⚠️ Potential issue

Template height too small for later y=1 edits.

All three tests place or edit blocks at y = 1, but the registered template is only one block tall (height = 1). StructureTemplateBuilder bounds the test area; attempting to touch y = 1 will therefore throw an out-of-bounds exception and fail the test before the logic runs.

-@RegisterStructureTemplate(THREE_CHESTS)
-public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3,
+@RegisterStructureTemplate(THREE_CHESTS)
+public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3,

107-110: ⚠️ Potential issue

Chest y-coordinate off by one.

The template places all chests at y = 0, yet the assertions reference y = 1 (e.g., assertContainerHasExactly(2, 1, 2, …)). With the corrected template height this will point at air blocks and the tests will still fail.

Update this assertion to use y = 0:

-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, 1))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, 1))

141-144: ⚠️ Potential issue

Chest y-coordinate off by one.

Similar to the earlier assertion, this one also references y = 1 when the chest is at y = 0.

-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1))

178-180: ⚠️ Potential issue

Chest y-coordinate off by one.

The same issue occurs in this test's assertions as well.

-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, transferRate))
-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, transferRate))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, transferRate))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, transferRate))

65-65: ⚠️ Potential issue

Chest y-coordinate off by one.

The insertion to the container uses wrong y-coordinate here too.

-                    .thenExecute(() -> helper.insertIntoContainer(0, 1, 0, Items.DIRT, 1))
+                    .thenExecute(() -> helper.insertIntoContainer(0, 0, 0, Items.DIRT, 1))

41-43: ⚠️ Potential issue

Template height too small in ad-hoc template.

Like the main template, this ad-hoc template also needs its height increased to accommodate operations at y=1.

-test.registerGameTestTemplate(() -> StructureTemplateBuilder.withSize(1, 1, 3)
+test.registerGameTestTemplate(() -> StructureTemplateBuilder.withSize(1, 2, 3)

175-175: ⚠️ Potential issue

Chest y-coordinate off by one.

The insertion to the container uses wrong y-coordinate in this test as well.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, transferRate * 2))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, transferRate * 2))

140-140: ⚠️ Potential issue

Chest y-coordinate off by one.

The insertion to the container uses wrong y-coordinate in this test as well.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1))
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)

97-97: ⚠️ Potential issue

Round-robin exit condition may skip remaining items.

isEmpty(extractHandler, i + 1) ignores the current slot (i). If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed.

A simpler, safer exit check is to rely solely on the transfer-rate cap:

-if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+if (extracted >= speed) {

If you still want the early-out optimisation, scan from slot 0 so the current slot is included.


112-113: 🛠️ Refactor suggestion

Address the TODO about the isEmpty method.

The code contains a TODO questioning whether the isEmpty method is necessary. Given the issue identified in the round-robin exit condition, this is a valid concern.

There are two options:

  1. Remove the isEmpty check entirely and rely on the transfer rate limit
  2. Fix the isEmpty check to include the current slot in appropriate cases

If you're keeping the method, also consider changing the logic to:

private boolean isEmpty(IItemHandler itemHandler, int afterIndex) {
    // Check if there are any non-empty slots starting from afterIndex
-   for (var i = afterIndex; i < itemHandler.getSlots(); i++) {
+   // Start from the first slot to ensure we check the current slot too
+   for (var i = 0; i < itemHandler.getSlots(); i++) {
        if (!itemHandler.getStackInSlot(i).isEmpty()) {
            return false;
        }
    }
    return true;
}
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (2)

292-359: ⚠️ Potential issue

Fix potential performance bottleneck in connection list handling

The addLoadedNode() method has O(n²) memory and time complexity when adding connections to multiple lists. For large networks, this could lead to significant performance degradation.

I recommend using a LinkedHashSet instead of ArrayList to eliminate duplicates and reduce complexity to O(n):

-            accessibleBlockConnectionsMap.computeIfAbsent(connection,
-                    k -> new ArrayList<>(endpointConnections.values().size()));
+            accessibleBlockConnectionsMap.computeIfAbsent(connection,
+                    k -> new LinkedHashSet<>(endpointConnections.values().size()));

This maintains the order (needed for sorting later) while preventing duplicates when two nodes share multiple endpoints.


473-482: ⚠️ Potential issue

Add null check before invoking onChunkCoverageChanged

There's a potential NullPointerException if onChunkCoverageChanged is null when invoking accept().

-        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
+        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
             onChunkCoverageChanged.accept(this);
         }

Although it appears you've already added the null check, the comment in onChunkUnloaded() in the bundle class suggests this is happening on servers that never wire up the callback.

🧹 Nitpick comments (17)
endercore/src/main/java/com/enderio/core/common/graph/Network.java (6)

347-348: Address the TODO comment

There's a TODO comment about potentially reworking the code to use a new constructor. This should either be addressed before merging or documented as a future enhancement with a clear explanation of what needs to be done.


220-226: Add documentation to hook methods

The protected hook methods like onNodeAdded, onNodesConnected, etc. would benefit from JavaDoc comments explaining when they're called and how subclasses should use them.

+    /**
+     * Called when a node is added to the network.
+     * Subclasses can override this to perform additional actions.
+     * @param node The node that was added
+     */
     protected void onNodeAdded(TNode node) {
     }

+    /**
+     * Called when two nodes are connected in the network.
+     * Subclasses can override this to perform additional actions.
+     * @param node1 The first node in the connection
+     * @param node2 The second node in the connection
+     */
     protected void onNodesConnected(TNode node1, TNode node2) {
     }

307-310: Add documentation to onMerged method

The comment "This is where you'd merge any additional context, for example" would be more helpful as a proper JavaDoc comment explaining when this method is called and what subclasses should do.

+    /**
+     * Called when this network has merged with another network.
+     * Subclasses should override this to merge any additional context.
+     * @param other The other network that was merged into this one
+     */
     protected void onMerged(TNet other) {
-        // This is where you'd merge any additional context, for example.
     }

381-384: Add documentation to onGraphSplit method

Similar to the onMerged method, the comment about context splitting would be more helpful as a proper JavaDoc comment.

+    /**
+     * Called when this network has been split into multiple networks.
+     * Subclasses should override this to handle splitting any additional context.
+     * @param newGraphs The new networks created from the split
+     */
     protected void onGraphSplit(Set<TNet> newGraphs) {
-        // This is where context splitting should occur.
     }

436-440: Improve documentation for self() method

The comment "Generic fun and games :(" doesn't provide much information about why this method is needed. Consider adding a proper JavaDoc comment explaining its purpose.

-    // Generic fun and games :(
+    /**
+     * Returns this network cast to its concrete implementation type.
+     * This is needed to handle the recursive generic type parameter TNet.
+     * @return This network cast to its concrete implementation type
+     */
     @SuppressWarnings("unchecked")
     private TNet self() {
         return (TNet) this;
     }

283-306: Consider extracting helper methods from complex logic

The mergeIfDifferent method contains several logical steps. For better readability and maintainability, consider extracting some of these operations into helper methods with descriptive names.

     private void mergeIfDifferent(TNet other, @Nullable Consumer<TNet> onNetworkDiscard) {
         // Do not attempt to merge with ourself.
         if (other == this) {
             return;
         }

-        // Bring all nodes and edges into this network.
-        other.graph.nodes().forEach(graph::addNode);
-        other.graph.edges().forEach(graph::putEdge);
-
-        // Move nodes to this network.
-        for (var node : other.graph.nodes()) {
-            node.setNetwork(self());
-        }
+        transferNodesAndEdges(other);
+        updateNodeNetworkReferences(other);

         // Handle the merge.
         onMerged(other);

         // Mark the other network as discarded.
         other.isDiscarded = true;
         if (onNetworkDiscard != null) {
             onNetworkDiscard.accept(other);
         }
     }
+    
+    private void transferNodesAndEdges(TNet other) {
+        // Bring all nodes and edges into this network.
+        other.graph.nodes().forEach(graph::addNode);
+        other.graph.edges().forEach(graph::putEdge);
+    }
+    
+    private void updateNodeNetworkReferences(TNet other) {
+        // Move nodes to this network.
+        for (var node : other.graph.nodes()) {
+            node.setNetwork(self());
+        }
+    }
.github/workflows/verify.yml (1)

42-42: Remove trailing whitespace

Remove the trailing whitespace on this line.

-          distribution: 'temurin'
-          
+          distribution: 'temurin'
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 42-42: trailing spaces

(trailing-spaces)

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (2)

209-209: Consider adding better offset distribution for network ticking.

Currently, the offset for network ticking is based on the conduit ID modulo the tick rate. This could cause multiple conduits to tick simultaneously if they have the same ID modulo value, potentially causing tick spikes.

Consider implementing a more uniform distribution of ticks by using a hash of the network itself or assigning sequential offsets when networks are created:

-if (serverLevel.getGameTime() % conduitTickRate == conduitId % conduitTickRate) {
+// Use the network's hashCode as an offset to distribute ticks more evenly
+if (serverLevel.getGameTime() % conduitTickRate == Math.abs(network.hashCode()) % conduitTickRate) {

82-83: Consider more robust error handling for partial codec results.

The current code uses getPartialOrThrow() which can throw exceptions for any failure. For greater resilience, consider handling partial results to facilitate recovery from minor corruptions.

-return CODEC.parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), nbt.get(KEY_NEW_DATA))
-        .getPartialOrThrow();
+var result = CODEC.parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), nbt.get(KEY_NEW_DATA));
+if (result.error().isPresent()) {
+    LOGGER.error("Error parsing conduit network data: {}", result.error().get());
+}
+return result.getPartialOrThrow();
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1)

75-76: Replace Objects.requireNonNull with explicit null check.

Using Objects.requireNonNull could throw a NullPointerException if the extract handler is null, which might happen if the capability changed between the initial check and this method call.

-var extractHandler = Objects
-        .requireNonNull(extractConnection.getSidedCapability(Capabilities.FluidHandler.BLOCK));
+var extractHandler = extractConnection.getSidedCapability(Capabilities.FluidHandler.BLOCK);
+if (extractHandler == null) {
+    return maxTransfer; // Can't extract, so the full amount is still available
+}
enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (1)

47-54: Return-value of place is ignored – failed placements go unnoticed

BlockItem#place returns an InteractionResult that you can use to verify whether the conduit was actually placed (e.g. world is loaded, position is replaceable, player has permission, etc.).
Silently ignoring an unsuccessful result can leave the test-world in an unexpected state and make subsequent assertions fail for reasons that are hard to diagnose.

-            blockItem.place(new BlockPlaceContext(getLevel(), fakePlayer, InteractionHand.MAIN_HAND, conduitItem,
-                    new BlockHitResult(absolutePos.getCenter(), Direction.DOWN, absolutePos, false)));
+            var result = blockItem.place(new BlockPlaceContext(getLevel(), fakePlayer, InteractionHand.MAIN_HAND,
+                    conduitItem, new BlockHitResult(absolutePos.getCenter(), Direction.DOWN, absolutePos, false)));
+            if (!result.consumesAction()) {
+                throw new GameTestAssertException("Failed to place conduit at " + x + ", " + y + ", " + z
+                        + " – reason: " + result);
+            }
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)

19-20: Guard against long overflow in transferRate calculation

transferRatePerTick() returns long, but the multiplication by networkTickRate() can still overflow for very high custom rates (e.g. datapack-tuned conduits).

-        final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final long transferRate = Math.multiplyFull(conduit.transferRatePerTick(), conduit.networkTickRate());

Math.multiplyFull (Java 21) preserves the full 128-bit product and is cheap; if you need to stay on Java 17 use Math.multiplyExact with overflow handling.


55-62: Redundant null-check obscures intent

network.getOrCreateContext(...) is guaranteed non-null, therefore the explicit context != null check is dead code.

-        if (!conduit.isMultiChemical()) {
-            if (context != null && !context.lockedChemical().equals(context.lastLockedChemical())) {
+        if (!conduit.isMultiChemical()
+                && !context.lockedChemical().equals(context.lastLockedChemical())) {
enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (1)

59-65: Javadoc still references “graph” – should be “network”

The terminology was updated project-wide, but the comment above networkTickRate() still says “conduit graph ticks”.
Updating the wording prevents confusion for API users.

-     * @return the number of ticks that should pass before the conduit graph ticks.
+     * @return the number of ticks that should pass before the conduit <strong>network</strong> ticks.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)

255-276: Consider optimizing node update logic

The current node update logic removes and re-adds the node, which is a simple but potentially inefficient approach.

     public void onNodeUpdated(ConduitNode node) {
         Preconditions.checkArgument(node.isLoaded(), "Node is not loaded!");
 
         if (shouldRebuildCache) {
             return;
         }
 
         // If we've somehow missed this node, do the full works
         if (!loadedNodes.contains(node)) {
             addLoadedNode(node);
         } else {
-            // TODO: is it worth writing more code to do a partial rebuild?
-            removeLoadedNode(node);
-            addLoadedNode(node);
+            // Update only the affected connection entries without full removal/re-addition
+            updateNodeConnections(node);
         }
 
         // Update channel list
         updateChannelList();
 
         // Must re-sort in case ordering has changed.
         haveConnectionsChanged = true;
     }
+    
+    private void updateNodeConnections(ConduitNode node) {
+        // Remove old connections but keep node in loadedNodes
+        for (var connection : endpointConnections.get(node)) {
+            // Clean up connection mappings
+            /* Implementation details omitted for brevity */
+        }
+        
+        // Re-add connections
+        /* Implementation details omitted for brevity */
+    }

A targeted update that only modifies the affected connections would be more efficient for large networks with frequent updates.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (2)

766-775: Consider condensing connection status update logic

The connection status update logic has multiple branches with redundant network update notifications.

     public void setConnectionConfig(Holder<Conduit<?, ?>> conduit, Direction side, ConnectionConfig config) {
         if (config.type() != conduit.value().connectionConfigType()) {
             throw new IllegalArgumentException("Connection config is not the right type for this conduit.");
         }
 
         conduitConnections.get(conduit).setConfig(side, config);
+        ConnectionStatus newStatus = config.isConnected() ? 
+            ConnectionStatus.CONNECTED_BLOCK : ConnectionStatus.DISABLED;
+            
+        if (getConnectionStatus(conduit, side) != newStatus) {
+            setConnectionStatus(conduit, side, newStatus);
+        } else if (level != null && !level.isClientSide()) {
+            // Only fire config changed if connection status hasn't changed
+            getConduitNode(conduit).onConfigChanged();
+        }
-        if (config.isConnected() && getConnectionStatus(conduit, side) != ConnectionStatus.CONNECTED_BLOCK) {
-            setConnectionStatus(conduit, side, ConnectionStatus.CONNECTED_BLOCK);
-        } else if (!config.isConnected()) {
-            setConnectionStatus(conduit, side, ConnectionStatus.DISABLED);
-        } else {
-            // Fire on config changed manually if we've not changed any connections
-            if (level != null && !level.isClientSide()) {
-                getConduitNode(conduit).onConfigChanged();
-            }
-        }
 
         bundleChanged();
     }

This simplification makes the logic more straightforward while maintaining the same behavior.


660-668: Remove obsolete commented code

The commented-out code is now obsolete with the new network system. Since this PR implements a complete network system replacement, these comments are no longer needed.

-        // TODO: Do we need an equivalent to this?
-//        if (level instanceof ServerLevel serverLevel) {
-//            if (neighborBundle.hasConduitByType(conduit)) {
-//                Optional.of(neighborBundle.getConduitNode(conduit))
-//                        .map(ConduitGraphObject::getGraph)
-//                        .filter(Objects::nonNull)
-//                        .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel));
-//            }
-//        }

The old graph system's concept of "potential graphs" has been replaced by the new network system's node and network management.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d1bf03e24a43c72c05ab8410df14e022d055b46 and 48826cbada07dc827ec68d569cd98c7228eea868.

📒 Files selected for processing (100)
  • .github/workflows/verify.yml (1 hunks)
  • endercore/build.gradle.kts (2 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/Network.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/package-info.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/NetworkTests.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/TestNode.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (4 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (5 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (7 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (3 hunks)
  • enderio-conduits/build.gradle.kts (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (9 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (27 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (5 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java (6 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (6 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java (1 hunks)
  • enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java (1 hunks)
  • enderio/build.gradle.kts (1 hunks)
💤 Files with no reviewable changes (13)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java
Files skipped from review due to trivial changes (6)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java
  • enderio-conduits/build.gradle.kts
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java
🚧 Files skipped from review as they are similar to previous changes (67)
  • enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java
  • endercore/src/main/java/com/enderio/core/common/graph/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java
  • enderio/build.gradle.kts
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java
  • endercore/build.gradle.kts
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java
  • endercore/src/test/java/com/enderio/core/TestNode.java
  • endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java
  • endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java
  • endercore/src/test/java/com/enderio/core/NetworkTests.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java
🧰 Additional context used
🧬 Code Graph Analysis (3)
enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)
  • EnderIOConduitsRegistries (12-54)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (1)
  • ChemicalConduitNetworkContext (12-76)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)
  • ConduitNode (25-302)
🪛 YAMLlint (1.35.1)
.github/workflows/verify.yml

[error] 42-42: trailing spaces

(trailing-spaces)

Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (31)
endercore/src/main/java/com/enderio/core/common/graph/Network.java (3)

350-351: Replace getFirst() with get(0)

Similar to the earlier instance, List#getFirst() requires Java 21 while the NeoForge toolchain targets Java 17.

-            firstNode = remaining.iterator().next();
+            firstNode = remaining.iterator().next();

Note: This line already uses iterator().next() rather than getFirst(), so no change is needed. The comment in the PR about line 350 seems to be referring to a previous version of the code.


27-36: Well-designed generic network implementation

The design of this network class is well thought out. It uses bounded generic types to ensure type safety while providing a flexible foundation for different types of networks. The stable element ordering in the graph builder is especially important for ensuring consistent serialization behavior.


389-407: Robust serialization support

The serialization approach using Mojang's codec system is well implemented. By creating lists of nodes and indexed edges, you've ensured that the network structure can be properly serialized and deserialized while maintaining the relationships between nodes.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (2)

3-4: Clean refactoring to the new network API

The refactoring from the old graph-based approach to the new network system has simplified this ticker significantly. The implementation now uses clear network queries to iterate over connections and calculate heat transfers efficiently.

The heat transfer calculation logic itself looks correct, using temperature differences and combined inverse conduction to determine the transfer amount.

Also applies to: 9-9, 15-40


32-32: Address the TODO comment regarding ambient temperature

There's an unresolved TODO about possibly subtracting ambient temperature that should be addressed before finalising this PR.

Could you clarify whether ambient temperature subtraction should be implemented at this stage, or if it's planned for future work?

.github/workflows/verify.yml (2)

7-20: More targeted CI triggers improve efficiency

The refined trigger configuration (specific branches, event types, and path filters) will reduce unnecessary workflow runs, focusing only on Java file changes and relevant branch operations.


47-54: Improved test organization with separate steps

Splitting the build process into three distinct steps (code style, game tests, and unit tests) gives clearer feedback when issues occur and allows for easier debugging of specific failure types.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (3)

13-14: Well-implemented generic codec for serialization

This codec implementation efficiently handles serialization of all context types by dispatching through the registry.


24-31: Improved contract for network splitting

The replacement of copy() with split() provides a clearer contract for network partitioning. The documentation clearly states that new instances must be returned, preventing shared contexts between networks.


33-36: Type accessor supports type safety

The addition of the type() method ensures type safety during serialization and network operations.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)

14-18: Clean implementation of the new ticker interface

The implementation properly retrieves the network context and tracks the active state before ticking.


53-58: Efficient handling of node updates when active state changes

Good implementation for marking nodes as dirty only when necessary (new context or active state change), which avoids unnecessary block model updates.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)

179-184: Good implementation for removing empty networks.

The code properly avoids concurrent modification exceptions by using .toList() to create a copy before iteration, and correctly calls onNetworkDiscarded to ensure all related collections are updated.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (1)

184-184: Add tests for item filters.

The TODO comment indicates missing test coverage for item filters, which is an important feature of item conduits.

Consider implementing tests for the filter functionality to ensure comprehensive test coverage.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)

13-13: Implementation follows the SOLID principles.

The class now implements ConduitTicker<ItemConduit> directly rather than extending a more complex base class, following the Interface Segregation Principle.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (3)

107-109: Fixed the filter slot for inserts.

The code now correctly uses FluidConduit.INSERT_FILTER_SLOT for insert filters instead of the extract filter slot, fixing a potential issue where insert filters could be bypassed.


18-18: Implementation follows the SOLID principles.

Similar to the ItemConduitTicker, this class now implements ConduitTicker<FluidConduit> directly rather than extending a more complex base class, following the Interface Segregation Principle.


36-58: Fluid locking logic is well-implemented.

The code efficiently handles multi-fluid vs. single-fluid conduits by checking for locked fluids first and then scanning tanks to potentially lock a new fluid.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (5)

25-34: Well-designed network serialization with contextual preservation

The CODEC implementation correctly handles the persistent context of a network, wrapping it in an Optional based on the persistence flag, and ensuring that the network's underlying graph structure is properly encoded.


40-67: Comprehensive cache design for network operations

The caching system is well-structured with clear separation of concerns between different types of network data. This allows for efficient querying of network state during ticking operations:

  • Chunk-based node grouping facilitates chunk loading/unloading operations
  • Separate caches for loaded nodes, block endpoint connections
  • Specialized maps for insert/extract connections with channel filtering
  • Deferred cache rebuilding flags to optimize update timing

This design addresses the PR objective of simplifying conduit tickers by eliminating the need to filter nodes every tick.


216-231: Efficient deferred cache rebuilding pattern

The cache rebuilding strategy is well-implemented. By deferring cache rebuilds until they're actually needed (just before ticking), you reduce the performance impact of network mutations. This is especially important for large conduit networks which may undergo frequent changes.


532-547: Well-implemented network merge strategy

The merge operation correctly handles context merging, with proper null handling. The strategy of deferring cache rebuilding until necessary is also good for performance.


550-577: Effective context splitting with safeguards

The network split operation includes robust error checking to prevent the same context from being reused across multiple networks. The validation throws an appropriate exception with a clear error message that includes the conduit type name.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (8)

171-185: Successful null check correction

The condition level != null && !level.isClientSide() properly checks for null before attempting to access the level property, addressing the previously identified issue.


317-334: Well-implemented network disconnection logic

The implementation correctly:

  1. Retrieves nodes from both sides of the connection
  2. Verifies node validity
  3. Checks if they're in the same network
  4. Uses the disconnect method to sever the connection
  5. Registers the new network with the appropriate saved data

This approach ensures network integrity is maintained when connections are broken.


376-381: Network-aware capability proxying

The capability proxy mechanism has been updated to work with the new network system. It correctly verifies node validity before attempting to proxy the capability, preventing potential null reference issues.


879-891: Good network update notification

The onConnectionsUpdated method appropriately:

  1. Collects the connected sides
  2. Notifies the conduit about the connection changes
  3. Notifies the network that the node has been updated

This ensures all components stay in sync when connections change.


973-1005: Well-designed capability caching system

The NeighboringCapabilityCaches system is well-implemented with several advantages:

  1. Separate caches per conduit type allow cleanup when conduits are removed
  2. Support for both directional and void capabilities
  3. Lazy initialization of capability caches
  4. Clear organization with separate maps for different capability types

This implementation addresses the previous concerns about capability caches not being properly cleared.


1009-1052: Comprehensive redstone signal handling

The redstone signal handling implementation correctly:

  1. Caches the redstone signal state
  2. Updates all nodes when redstone changes
  3. Provides a query method that checks both direct signals and network-wide signals
  4. Handles colored redstone signals from the redstone conduit network

This addresses an important aspect of the conduit ticker performance optimization mentioned in the PR objectives.


1247-1287: Improved node lifecycle with chunk unload awareness

The addition of isChunkUnload flag and its use in setRemoved() is an important improvement:

  1. It prevents unnecessary conduit removal and facade dropping when chunks unload
  2. It ensures conduit nodes are properly returned to the saved data
  3. It correctly distinguishes between chunk unloads and actual block destruction

This prevents potential issues with item duplication or network corruption during chunk loading/unloading cycles.


1609-1643: Enhanced capability caching with support for void capabilities

The implementation of NeighboringCapabilityCaches now includes support for void capabilities alongside directional ones, which improves capability handling flexibility and covers more use cases.

**Actionable comments posted: 5** <details> <summary>♻️ Duplicate comments (17)</summary><blockquote> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)</summary><blockquote> `61-66`: **Replace `getFirst()` with `get(0)`** The `List#getFirst()` method was introduced in Java 21, but the NeoForge toolchain targets Java 17. Using it will break compilation for anyone not on the latest toolchain. ```diff - var node = nodes.getFirst(); + var node = nodes.get(0); ``` </blockquote></details> <details> <summary>.github/workflows/verify.yml (1)</summary><blockquote> `3-4`: **Improved security by explicitly limiting permissions** Good improvement to limit GITHUB_TOKEN permissions to read-only access for contents. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)</summary><blockquote> `24-26`: _⚠️ Potential issue_ **Potential NullPointerException when accessing inventory** The code doesn't check if `extractConnection.inventory()` returns null before accessing methods on it, which could cause a server crash if the inventory is missing. Add a null check before accessing the inventory: ```diff -var redstoneExtractFilter = extractConnection.inventory() - .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) - .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER); +var inventory = extractConnection.inventory(); +var redstoneExtractFilter = inventory != null + ? inventory.getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) + .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER) + : null; ``` --- `40-50`: _🛠️ Refactor suggestion_ **Redundant neighbour updates for multiple channels** The current implementation updates neighbors for all insert connections in each channel whenever any signal changes. This could cause redundant updates for the same block positions when a node participates in multiple channels. Collect all connections that need updates in a set and perform the updates once: ```diff +Set<var> dirtyInsertConnections = new HashSet<>(); for (var channel : network.allChannels()) { // ... [extract logic remains the same] ... // Fire block updates if the signal changed. if (context.isNew() || context.getSignal(channel) != context.getSignalLastTick(channel)) { - for (var insertConnection : network.insertConnections(channel)) { - level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get()); - - if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE) - .isStrongOutputSignal()) { - level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get()); - } - } + network.insertConnections(channel).forEach(dirtyInsertConnections::add); } } +// Perform updates once for each unique connection +dirtyInsertConnections.forEach(insertConnection -> { + level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get()); + + if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE) + .isStrongOutputSignal()) { + level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get()); + } +}); ``` This preserves behaviour while reducing per-tick block updates significantly. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)</summary><blockquote> `165-169`: **Fix the `isDirty()` method to avoid excessive disk I/O.** Always returning `true` causes Minecraft's data storage layer to write this file to disk on every save cycle (normally each tick). Large worlds with many conduits will suffer significant, needless disk I/O and TPS loss. Introduce an internal `dirty` flag and call `setDirty()` (inherited) whenever networks are added/removed or their data actually mutates. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (8)</summary><blockquote> `26-37`: _⚠️ Potential issue_ **Template height too small for later y=1 edits.** All three tests place or edit blocks at `y = 1`, but the registered template is only one block tall (`height = 1`). `StructureTemplateBuilder` bounds the test area; attempting to touch `y = 1` will therefore throw an out-of-bounds exception and fail the test before the logic runs. ```diff -@RegisterStructureTemplate(THREE_CHESTS) -public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3, +@RegisterStructureTemplate(THREE_CHESTS) +public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3, ``` --- `107-110`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** The template places all chests at `y = 0`, yet the assertions reference `y = 1` (e.g., `assertContainerHasExactly(2, 1, 2, …)`). With the corrected template height this will point at air blocks and the tests will still fail. Update this assertion to use `y = 0`: ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, 1)) ``` --- `141-144`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** Similar to the earlier assertion, this one also references `y = 1` when the chest is at `y = 0`. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1)) ``` --- `178-180`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** The same issue occurs in this test's assertions as well. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, transferRate)) - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, transferRate)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, transferRate)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, transferRate)) ``` --- `65-65`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** The insertion to the container uses wrong y-coordinate here too. ```diff - .thenExecute(() -> helper.insertIntoContainer(0, 1, 0, Items.DIRT, 1)) + .thenExecute(() -> helper.insertIntoContainer(0, 0, 0, Items.DIRT, 1)) ``` --- `41-43`: _⚠️ Potential issue_ **Template height too small in ad-hoc template.** Like the main template, this ad-hoc template also needs its height increased to accommodate operations at y=1. ```diff -test.registerGameTestTemplate(() -> StructureTemplateBuilder.withSize(1, 1, 3) +test.registerGameTestTemplate(() -> StructureTemplateBuilder.withSize(1, 2, 3) ``` --- `175-175`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** The insertion to the container uses wrong y-coordinate in this test as well. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, transferRate * 2)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, transferRate * 2)) ``` --- `140-140`: _⚠️ Potential issue_ **Chest y-coordinate off by one.** The insertion to the container uses wrong y-coordinate in this test as well. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1)) ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)</summary><blockquote> `97-97`: _⚠️ Potential issue_ **Round-robin exit condition may skip remaining items.** `isEmpty(extractHandler, i + 1)` ignores the current slot (`i`). If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed. A simpler, safer exit check is to rely solely on the transfer-rate cap: ```diff -if (extracted >= speed || isEmpty(extractHandler, i + 1)) { +if (extracted >= speed) { ``` If you still want the early-out optimisation, scan from slot 0 so the current slot is included. --- `112-113`: _🛠️ Refactor suggestion_ **Address the TODO about the isEmpty method.** The code contains a TODO questioning whether the `isEmpty` method is necessary. Given the issue identified in the round-robin exit condition, this is a valid concern. There are two options: 1. Remove the `isEmpty` check entirely and rely on the transfer rate limit 2. Fix the `isEmpty` check to include the current slot in appropriate cases If you're keeping the method, also consider changing the logic to: ```diff private boolean isEmpty(IItemHandler itemHandler, int afterIndex) { // Check if there are any non-empty slots starting from afterIndex - for (var i = afterIndex; i < itemHandler.getSlots(); i++) { + // Start from the first slot to ensure we check the current slot too + for (var i = 0; i < itemHandler.getSlots(); i++) { if (!itemHandler.getStackInSlot(i).isEmpty()) { return false; } } return true; } ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (2)</summary><blockquote> `292-359`: _⚠️ Potential issue_ **Fix potential performance bottleneck in connection list handling** The `addLoadedNode()` method has O(n²) memory and time complexity when adding connections to multiple lists. For large networks, this could lead to significant performance degradation. I recommend using a `LinkedHashSet` instead of `ArrayList` to eliminate duplicates and reduce complexity to O(n): ```diff - accessibleBlockConnectionsMap.computeIfAbsent(connection, - k -> new ArrayList<>(endpointConnections.values().size())); + accessibleBlockConnectionsMap.computeIfAbsent(connection, + k -> new LinkedHashSet<>(endpointConnections.values().size())); ``` This maintains the order (needed for sorting later) while preventing duplicates when two nodes share multiple endpoints. --- `473-482`: _⚠️ Potential issue_ **Add null check before invoking onChunkCoverageChanged** There's a potential `NullPointerException` if `onChunkCoverageChanged` is null when invoking `accept()`. ```diff - if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { + if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { onChunkCoverageChanged.accept(this); } ``` Although it appears you've already added the null check, the comment in `onChunkUnloaded()` in the bundle class suggests this is happening on servers that never wire up the callback. </blockquote></details> </blockquote></details> <details> <summary>🧹 Nitpick comments (17)</summary><blockquote> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (6)</summary><blockquote> `347-348`: **Address the TODO comment** There's a TODO comment about potentially reworking the code to use a new constructor. This should either be addressed before merging or documented as a future enhancement with a clear explanation of what needs to be done. --- `220-226`: **Add documentation to hook methods** The protected hook methods like `onNodeAdded`, `onNodesConnected`, etc. would benefit from JavaDoc comments explaining when they're called and how subclasses should use them. ```diff + /** + * Called when a node is added to the network. + * Subclasses can override this to perform additional actions. + * @param node The node that was added + */ protected void onNodeAdded(TNode node) { } + /** + * Called when two nodes are connected in the network. + * Subclasses can override this to perform additional actions. + * @param node1 The first node in the connection + * @param node2 The second node in the connection + */ protected void onNodesConnected(TNode node1, TNode node2) { } ``` --- `307-310`: **Add documentation to onMerged method** The comment "This is where you'd merge any additional context, for example" would be more helpful as a proper JavaDoc comment explaining when this method is called and what subclasses should do. ```diff + /** + * Called when this network has merged with another network. + * Subclasses should override this to merge any additional context. + * @param other The other network that was merged into this one + */ protected void onMerged(TNet other) { - // This is where you'd merge any additional context, for example. } ``` --- `381-384`: **Add documentation to onGraphSplit method** Similar to the `onMerged` method, the comment about context splitting would be more helpful as a proper JavaDoc comment. ```diff + /** + * Called when this network has been split into multiple networks. + * Subclasses should override this to handle splitting any additional context. + * @param newGraphs The new networks created from the split + */ protected void onGraphSplit(Set<TNet> newGraphs) { - // This is where context splitting should occur. } ``` --- `436-440`: **Improve documentation for self() method** The comment "Generic fun and games :(" doesn't provide much information about why this method is needed. Consider adding a proper JavaDoc comment explaining its purpose. ```diff - // Generic fun and games :( + /** + * Returns this network cast to its concrete implementation type. + * This is needed to handle the recursive generic type parameter TNet. + * @return This network cast to its concrete implementation type + */ @SuppressWarnings("unchecked") private TNet self() { return (TNet) this; } ``` --- `283-306`: **Consider extracting helper methods from complex logic** The `mergeIfDifferent` method contains several logical steps. For better readability and maintainability, consider extracting some of these operations into helper methods with descriptive names. ```diff private void mergeIfDifferent(TNet other, @Nullable Consumer<TNet> onNetworkDiscard) { // Do not attempt to merge with ourself. if (other == this) { return; } - // Bring all nodes and edges into this network. - other.graph.nodes().forEach(graph::addNode); - other.graph.edges().forEach(graph::putEdge); - - // Move nodes to this network. - for (var node : other.graph.nodes()) { - node.setNetwork(self()); - } + transferNodesAndEdges(other); + updateNodeNetworkReferences(other); // Handle the merge. onMerged(other); // Mark the other network as discarded. other.isDiscarded = true; if (onNetworkDiscard != null) { onNetworkDiscard.accept(other); } } + + private void transferNodesAndEdges(TNet other) { + // Bring all nodes and edges into this network. + other.graph.nodes().forEach(graph::addNode); + other.graph.edges().forEach(graph::putEdge); + } + + private void updateNodeNetworkReferences(TNet other) { + // Move nodes to this network. + for (var node : other.graph.nodes()) { + node.setNetwork(self()); + } + } ``` </blockquote></details> <details> <summary>.github/workflows/verify.yml (1)</summary><blockquote> `42-42`: **Remove trailing whitespace** Remove the trailing whitespace on this line. ```diff - distribution: 'temurin' - + distribution: 'temurin' ``` <details> <summary>🧰 Tools</summary> <details> <summary>🪛 YAMLlint (1.35.1)</summary> [error] 42-42: trailing spaces (trailing-spaces) </details> </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (2)</summary><blockquote> `209-209`: **Consider adding better offset distribution for network ticking.** Currently, the offset for network ticking is based on the conduit ID modulo the tick rate. This could cause multiple conduits to tick simultaneously if they have the same ID modulo value, potentially causing tick spikes. Consider implementing a more uniform distribution of ticks by using a hash of the network itself or assigning sequential offsets when networks are created: ```diff -if (serverLevel.getGameTime() % conduitTickRate == conduitId % conduitTickRate) { +// Use the network's hashCode as an offset to distribute ticks more evenly +if (serverLevel.getGameTime() % conduitTickRate == Math.abs(network.hashCode()) % conduitTickRate) { ``` --- `82-83`: **Consider more robust error handling for partial codec results.** The current code uses `getPartialOrThrow()` which can throw exceptions for any failure. For greater resilience, consider handling partial results to facilitate recovery from minor corruptions. ```diff -return CODEC.parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), nbt.get(KEY_NEW_DATA)) - .getPartialOrThrow(); +var result = CODEC.parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), nbt.get(KEY_NEW_DATA)); +if (result.error().isPresent()) { + LOGGER.error("Error parsing conduit network data: {}", result.error().get()); +} +return result.getPartialOrThrow(); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1)</summary><blockquote> `75-76`: **Replace Objects.requireNonNull with explicit null check.** Using `Objects.requireNonNull` could throw a `NullPointerException` if the extract handler is null, which might happen if the capability changed between the initial check and this method call. ```diff -var extractHandler = Objects - .requireNonNull(extractConnection.getSidedCapability(Capabilities.FluidHandler.BLOCK)); +var extractHandler = extractConnection.getSidedCapability(Capabilities.FluidHandler.BLOCK); +if (extractHandler == null) { + return maxTransfer; // Can't extract, so the full amount is still available +} ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (1)</summary><blockquote> `47-54`: **Return-value of `place` is ignored – failed placements go unnoticed** `BlockItem#place` returns an `InteractionResult` that you can use to verify whether the conduit was actually placed (e.g. world is loaded, position is replaceable, player has permission, etc.). Silently ignoring an unsuccessful result can leave the test-world in an unexpected state and make subsequent assertions fail for reasons that are hard to diagnose. ```diff - blockItem.place(new BlockPlaceContext(getLevel(), fakePlayer, InteractionHand.MAIN_HAND, conduitItem, - new BlockHitResult(absolutePos.getCenter(), Direction.DOWN, absolutePos, false))); + var result = blockItem.place(new BlockPlaceContext(getLevel(), fakePlayer, InteractionHand.MAIN_HAND, + conduitItem, new BlockHitResult(absolutePos.getCenter(), Direction.DOWN, absolutePos, false))); + if (!result.consumesAction()) { + throw new GameTestAssertException("Failed to place conduit at " + x + ", " + y + ", " + z + + " – reason: " + result); + } ``` </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)</summary><blockquote> `19-20`: **Guard against long overflow in `transferRate` calculation** `transferRatePerTick()` returns `long`, but the multiplication by `networkTickRate()` can still overflow for very high custom rates (e.g. datapack-tuned conduits). ```diff - final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final long transferRate = Math.multiplyFull(conduit.transferRatePerTick(), conduit.networkTickRate()); ``` `Math.multiplyFull` (Java 21) preserves the full 128-bit product and is cheap; if you need to stay on Java 17 use `Math.multiplyExact` with overflow handling. --- `55-62`: **Redundant null-check obscures intent** `network.getOrCreateContext(...)` is guaranteed non-null, therefore the explicit `context != null` check is dead code. ```diff - if (!conduit.isMultiChemical()) { - if (context != null && !context.lockedChemical().equals(context.lastLockedChemical())) { + if (!conduit.isMultiChemical() + && !context.lockedChemical().equals(context.lastLockedChemical())) { ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (1)</summary><blockquote> `59-65`: **Javadoc still references “graph” – should be “network”** The terminology was updated project-wide, but the comment above `networkTickRate()` still says *“conduit graph ticks”*. Updating the wording prevents confusion for API users. ```diff - * @return the number of ticks that should pass before the conduit graph ticks. + * @return the number of ticks that should pass before the conduit <strong>network</strong> ticks. ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> `255-276`: **Consider optimizing node update logic** The current node update logic removes and re-adds the node, which is a simple but potentially inefficient approach. ```diff public void onNodeUpdated(ConduitNode node) { Preconditions.checkArgument(node.isLoaded(), "Node is not loaded!"); if (shouldRebuildCache) { return; } // If we've somehow missed this node, do the full works if (!loadedNodes.contains(node)) { addLoadedNode(node); } else { - // TODO: is it worth writing more code to do a partial rebuild? - removeLoadedNode(node); - addLoadedNode(node); + // Update only the affected connection entries without full removal/re-addition + updateNodeConnections(node); } // Update channel list updateChannelList(); // Must re-sort in case ordering has changed. haveConnectionsChanged = true; } + + private void updateNodeConnections(ConduitNode node) { + // Remove old connections but keep node in loadedNodes + for (var connection : endpointConnections.get(node)) { + // Clean up connection mappings + /* Implementation details omitted for brevity */ + } + + // Re-add connections + /* Implementation details omitted for brevity */ + } ``` A targeted update that only modifies the affected connections would be more efficient for large networks with frequent updates. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (2)</summary><blockquote> `766-775`: **Consider condensing connection status update logic** The connection status update logic has multiple branches with redundant network update notifications. ```diff public void setConnectionConfig(Holder<Conduit<?, ?>> conduit, Direction side, ConnectionConfig config) { if (config.type() != conduit.value().connectionConfigType()) { throw new IllegalArgumentException("Connection config is not the right type for this conduit."); } conduitConnections.get(conduit).setConfig(side, config); + ConnectionStatus newStatus = config.isConnected() ? + ConnectionStatus.CONNECTED_BLOCK : ConnectionStatus.DISABLED; + + if (getConnectionStatus(conduit, side) != newStatus) { + setConnectionStatus(conduit, side, newStatus); + } else if (level != null && !level.isClientSide()) { + // Only fire config changed if connection status hasn't changed + getConduitNode(conduit).onConfigChanged(); + } - if (config.isConnected() && getConnectionStatus(conduit, side) != ConnectionStatus.CONNECTED_BLOCK) { - setConnectionStatus(conduit, side, ConnectionStatus.CONNECTED_BLOCK); - } else if (!config.isConnected()) { - setConnectionStatus(conduit, side, ConnectionStatus.DISABLED); - } else { - // Fire on config changed manually if we've not changed any connections - if (level != null && !level.isClientSide()) { - getConduitNode(conduit).onConfigChanged(); - } - } bundleChanged(); } ``` This simplification makes the logic more straightforward while maintaining the same behavior. --- `660-668`: **Remove obsolete commented code** The commented-out code is now obsolete with the new network system. Since this PR implements a complete network system replacement, these comments are no longer needed. ```diff - // TODO: Do we need an equivalent to this? -// if (level instanceof ServerLevel serverLevel) { -// if (neighborBundle.hasConduitByType(conduit)) { -// Optional.of(neighborBundle.getConduitNode(conduit)) -// .map(ConduitGraphObject::getGraph) -// .filter(Objects::nonNull) -// .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel)); -// } -// } ``` The old graph system's concept of "potential graphs" has been replaced by the new network system's node and network management. </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 7d1bf03e24a43c72c05ab8410df14e022d055b46 and 48826cbada07dc827ec68d569cd98c7228eea868. </details> <details> <summary>📒 Files selected for processing (100)</summary> * `.github/workflows/verify.yml` (1 hunks) * `endercore/build.gradle.kts` (2 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/Network.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/package-info.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/NetworkTests.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/TestNode.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java` (4 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java` (5 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java` (7 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java` (3 hunks) * `enderio-conduits/build.gradle.kts` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java` (9 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java` (27 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java` (5 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java` (6 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java` (6 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java` (1 hunks) * `enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java` (1 hunks) * `enderio/build.gradle.kts` (1 hunks) </details> <details> <summary>💤 Files with no reviewable changes (13)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java </details> <details> <summary>✅ Files skipped from review due to trivial changes (6)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java * enderio-conduits/build.gradle.kts * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (67)</summary> * enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java * endercore/src/main/java/com/enderio/core/common/graph/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java * enderio/build.gradle.kts * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java * enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java * enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java * enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java * enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java * endercore/build.gradle.kts * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java * endercore/src/test/java/com/enderio/core/TestNode.java * endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java * endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java * enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java * endercore/src/test/java/com/enderio/core/NetworkTests.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🧬 Code Graph Analysis (3)</summary> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (1)</summary> * `EnderIOConduitsRegistries` (12-54) </details> </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)</summary><blockquote> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (1)</summary> * `ChemicalConduitNetworkContext` (12-76) </details> </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary> * `ConduitNode` (25-302) </details> </blockquote></details> </details><details> <summary>🪛 YAMLlint (1.35.1)</summary> <details> <summary>.github/workflows/verify.yml</summary> [error] 42-42: trailing spaces (trailing-spaces) </details> </details> </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary> * GitHub Check: test </details> <details> <summary>🔇 Additional comments (31)</summary><blockquote> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (3)</summary> `350-351`: **Replace `getFirst()` with `get(0)`** Similar to the earlier instance, `List#getFirst()` requires Java 21 while the NeoForge toolchain targets Java 17. ```diff - firstNode = remaining.iterator().next(); + firstNode = remaining.iterator().next(); ``` Note: This line already uses `iterator().next()` rather than `getFirst()`, so no change is needed. The comment in the PR about line 350 seems to be referring to a previous version of the code. --- `27-36`: **Well-designed generic network implementation** The design of this network class is well thought out. It uses bounded generic types to ensure type safety while providing a flexible foundation for different types of networks. The stable element ordering in the graph builder is especially important for ensuring consistent serialization behavior. --- `389-407`: **Robust serialization support** The serialization approach using Mojang's codec system is well implemented. By creating lists of nodes and indexed edges, you've ensured that the network structure can be properly serialized and deserialized while maintaining the relationships between nodes. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (2)</summary> `3-4`: **Clean refactoring to the new network API** The refactoring from the old graph-based approach to the new network system has simplified this ticker significantly. The implementation now uses clear network queries to iterate over connections and calculate heat transfers efficiently. The heat transfer calculation logic itself looks correct, using temperature differences and combined inverse conduction to determine the transfer amount. Also applies to: 9-9, 15-40 --- `32-32`: **Address the TODO comment regarding ambient temperature** There's an unresolved TODO about possibly subtracting ambient temperature that should be addressed before finalising this PR. Could you clarify whether ambient temperature subtraction should be implemented at this stage, or if it's planned for future work? </details> <details> <summary>.github/workflows/verify.yml (2)</summary> `7-20`: **More targeted CI triggers improve efficiency** The refined trigger configuration (specific branches, event types, and path filters) will reduce unnecessary workflow runs, focusing only on Java file changes and relevant branch operations. --- `47-54`: **Improved test organization with separate steps** Splitting the build process into three distinct steps (code style, game tests, and unit tests) gives clearer feedback when issues occur and allows for easier debugging of specific failure types. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (3)</summary> `13-14`: **Well-implemented generic codec for serialization** This codec implementation efficiently handles serialization of all context types by dispatching through the registry. --- `24-31`: **Improved contract for network splitting** The replacement of `copy()` with `split()` provides a clearer contract for network partitioning. The documentation clearly states that new instances must be returned, preventing shared contexts between networks. --- `33-36`: **Type accessor supports type safety** The addition of the `type()` method ensures type safety during serialization and network operations. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)</summary> `14-18`: **Clean implementation of the new ticker interface** The implementation properly retrieves the network context and tracks the active state before ticking. --- `53-58`: **Efficient handling of node updates when active state changes** Good implementation for marking nodes as dirty only when necessary (new context or active state change), which avoids unnecessary block model updates. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1)</summary> `179-184`: **Good implementation for removing empty networks.** The code properly avoids concurrent modification exceptions by using `.toList()` to create a copy before iteration, and correctly calls `onNetworkDiscarded` to ensure all related collections are updated. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (1)</summary> `184-184`: **Add tests for item filters.** The TODO comment indicates missing test coverage for item filters, which is an important feature of item conduits. Consider implementing tests for the filter functionality to ensure comprehensive test coverage. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)</summary> `13-13`: **Implementation follows the SOLID principles.** The class now implements `ConduitTicker<ItemConduit>` directly rather than extending a more complex base class, following the Interface Segregation Principle. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (3)</summary> `107-109`: **Fixed the filter slot for inserts.** The code now correctly uses `FluidConduit.INSERT_FILTER_SLOT` for insert filters instead of the extract filter slot, fixing a potential issue where insert filters could be bypassed. --- `18-18`: **Implementation follows the SOLID principles.** Similar to the ItemConduitTicker, this class now implements `ConduitTicker<FluidConduit>` directly rather than extending a more complex base class, following the Interface Segregation Principle. --- `36-58`: **Fluid locking logic is well-implemented.** The code efficiently handles multi-fluid vs. single-fluid conduits by checking for locked fluids first and then scanning tanks to potentially lock a new fluid. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (5)</summary> `25-34`: **Well-designed network serialization with contextual preservation** The `CODEC` implementation correctly handles the persistent context of a network, wrapping it in an `Optional` based on the persistence flag, and ensuring that the network's underlying graph structure is properly encoded. --- `40-67`: **Comprehensive cache design for network operations** The caching system is well-structured with clear separation of concerns between different types of network data. This allows for efficient querying of network state during ticking operations: - Chunk-based node grouping facilitates chunk loading/unloading operations - Separate caches for loaded nodes, block endpoint connections - Specialized maps for insert/extract connections with channel filtering - Deferred cache rebuilding flags to optimize update timing This design addresses the PR objective of simplifying conduit tickers by eliminating the need to filter nodes every tick. --- `216-231`: **Efficient deferred cache rebuilding pattern** The cache rebuilding strategy is well-implemented. By deferring cache rebuilds until they're actually needed (just before ticking), you reduce the performance impact of network mutations. This is especially important for large conduit networks which may undergo frequent changes. --- `532-547`: **Well-implemented network merge strategy** The merge operation correctly handles context merging, with proper null handling. The strategy of deferring cache rebuilding until necessary is also good for performance. --- `550-577`: **Effective context splitting with safeguards** The network split operation includes robust error checking to prevent the same context from being reused across multiple networks. The validation throws an appropriate exception with a clear error message that includes the conduit type name. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (8)</summary> `171-185`: **Successful null check correction** The condition `level != null && !level.isClientSide()` properly checks for null before attempting to access the `level` property, addressing the previously identified issue. --- `317-334`: **Well-implemented network disconnection logic** The implementation correctly: 1. Retrieves nodes from both sides of the connection 2. Verifies node validity 3. Checks if they're in the same network 4. Uses the `disconnect` method to sever the connection 5. Registers the new network with the appropriate saved data This approach ensures network integrity is maintained when connections are broken. --- `376-381`: **Network-aware capability proxying** The capability proxy mechanism has been updated to work with the new network system. It correctly verifies node validity before attempting to proxy the capability, preventing potential null reference issues. --- `879-891`: **Good network update notification** The `onConnectionsUpdated` method appropriately: 1. Collects the connected sides 2. Notifies the conduit about the connection changes 3. Notifies the network that the node has been updated This ensures all components stay in sync when connections change. --- `973-1005`: **Well-designed capability caching system** The `NeighboringCapabilityCaches` system is well-implemented with several advantages: 1. Separate caches per conduit type allow cleanup when conduits are removed 2. Support for both directional and void capabilities 3. Lazy initialization of capability caches 4. Clear organization with separate maps for different capability types This implementation addresses the previous concerns about capability caches not being properly cleared. --- `1009-1052`: **Comprehensive redstone signal handling** The redstone signal handling implementation correctly: 1. Caches the redstone signal state 2. Updates all nodes when redstone changes 3. Provides a query method that checks both direct signals and network-wide signals 4. Handles colored redstone signals from the redstone conduit network This addresses an important aspect of the conduit ticker performance optimization mentioned in the PR objectives. --- `1247-1287`: **Improved node lifecycle with chunk unload awareness** The addition of `isChunkUnload` flag and its use in `setRemoved()` is an important improvement: 1. It prevents unnecessary conduit removal and facade dropping when chunks unload 2. It ensures conduit nodes are properly returned to the saved data 3. It correctly distinguishes between chunk unloads and actual block destruction This prevents potential issues with item duplication or network corruption during chunk loading/unloading cycles. --- `1609-1643`: **Enhanced capability caching with support for void capabilities** The implementation of `NeighboringCapabilityCaches` now includes support for void capabilities alongside directional ones, which improves capability handling flexibility and covers more use cases. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -0,0 +511,4 @@
if (shouldRebuildCache) {
return;
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:10:44 +00:00

💡 Verification agent

🧩 Analysis chain

Review null check and early return in onNodeAdded

The TODO comment suggests you need to review this behavior more thoroughly.

Consider refactoring to avoid the null check by ensuring the field is always initialized before this method is called, or document why this special case is needed.


🏁 Script executed:

#!/bin/bash
# Check for potential issues related to the nodesByChunkPos null state
rg -A 5 "nodesByChunkPos\s*==" enderio-conduits/
rg -A 5 "if\s*\(\s*nodesByChunkPos\s*==\s*null" enderio-conduits/

Length of output: 1506


I’d like to inspect how nodesByChunkPos is declared, initialised, and used before onNodeAdded. Let’s pull in the class header, constructor(s) and onNodeAdded signature:


🏁 Script executed:

#!/bin/bash
set -e
FILE=enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java

# Class declaration & field declaration
rg -n "class ConduitNetwork" -A3 $FILE
rg -n "nodesByChunkPos" -n $FILE

# Constructor(s)
rg -n "public ConduitNetwork" -A10 $FILE

# onNodeAdded signature & opening lines
rg -n "onNodeAdded" -A10 $FILE

Length of output: 2433


Refactor onNodeAdded guard to avoid relying on a null-check

The if (nodesByChunkPos == null) is only there to dodge an NPE when the superclass constructor calls your override before Java has run your field initialisers. Rather than leaving this as a “magic” null-check, either:

• Introduce an explicit “construction complete” flag:
– Declare private boolean constructed;
– In each constructor, after super(…) and once all fields are in place, set constructed = true;
– At the top of onNodeAdded, replace the null-check with if (!constructed) return;

• Or, better yet, remove the overridable call in the superclass constructor (if you can change Network), and invoke your cache-rebuild logic only once the network is fully built.

Either way, add a comment explaining that this guard exists because Java initialises instance fields only after the superclass constructor returns. This makes the intent explicit and removes the reliance on a null field.

Relevant location:
• enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java: lines 506–514

_💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Review null check and early return in onNodeAdded** The TODO comment suggests you need to review this behavior more thoroughly. Consider refactoring to avoid the null check by ensuring the field is always initialized before this method is called, or document why this special case is needed. --- 🏁 Script executed: ```shell #!/bin/bash # Check for potential issues related to the nodesByChunkPos null state rg -A 5 "nodesByChunkPos\s*==" enderio-conduits/ rg -A 5 "if\s*\(\s*nodesByChunkPos\s*==\s*null" enderio-conduits/ ``` Length of output: 1506 --- I’d like to inspect how nodesByChunkPos is declared, initialised, and used before onNodeAdded. Let’s pull in the class header, constructor(s) and onNodeAdded signature: --- 🏁 Script executed: ```shell #!/bin/bash set -e FILE=enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java # Class declaration & field declaration rg -n "class ConduitNetwork" -A3 $FILE rg -n "nodesByChunkPos" -n $FILE # Constructor(s) rg -n "public ConduitNetwork" -A10 $FILE # onNodeAdded signature & opening lines rg -n "onNodeAdded" -A10 $FILE ``` Length of output: 2433 --- </details> **Refactor onNodeAdded guard to avoid relying on a null-check** The `if (nodesByChunkPos == null)` is only there to dodge an NPE when the superclass constructor calls your override before Java has run your field initialisers. Rather than leaving this as a “magic” null-check, either: • Introduce an explicit “construction complete” flag: – Declare `private boolean constructed;` – In each constructor, after `super(…)` and once all fields are in place, set `constructed = true;` – At the top of `onNodeAdded`, replace the null-check with `if (!constructed) return;` • Or, better yet, remove the overridable call in the superclass constructor (if you can change `Network`), and invoke your cache-rebuild logic only once the network is fully built. Either way, add a comment explaining that this guard exists because Java initialises instance fields only *after* the superclass constructor returns. This makes the intent explicit and removes the reliance on a null field. Relevant location: • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java: lines 506–514 <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +126,4 @@
if (network != bundle.getConduitNode(conduit).getNetwork()) {
throw new GameTestAssertException("Conduit node networks differ at " + x + ", " + y + ", " + z);
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:10:44 +00:00

⚠️ Potential issue

Possible NullPointerException when a bundle does not contain the requested conduit

assertAllNetworksMatch assumes that every non-null bundle in the region owns the conduit type under test.
When the assumption is violated, bundle.getConduitNode(conduit) will throw, terminating the entire test with a NullPointerException instead of a descriptive assertion failure.

-                    if (!foundFirstNetwork) {
+                    if (!bundle.hasConduitStrict(conduit)) {
+                        throw new GameTestAssertException("Missing expected conduit " + conduit + " at "
+                                + x + ", " + y + ", " + z);
+                    }
+
+                    if (!foundFirstNetwork) {
📝 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.

                    var bundle = getConduitBundle(x, y, z, allowMissingBundles);
                    if (bundle == null) {
                        continue;
                    }

                    if (!bundle.hasConduitStrict(conduit)) {
                        throw new GameTestAssertException("Missing expected conduit " + conduit + " at "
                                + x + ", " + y + ", " + z);
                    }

                    if (!foundFirstNetwork) {
                        network = bundle.getConduitNode(conduit).getNetwork();
                        foundFirstNetwork = true;
                        continue;
                    }

                    if (network != bundle.getConduitNode(conduit).getNetwork()) {
                        throw new GameTestAssertException("Conduit node networks differ at " + x + ", " + y + ", " + z);
                    }
_⚠️ Potential issue_ **Possible `NullPointerException` when a bundle does not contain the requested conduit** `assertAllNetworksMatch` assumes that every non-null bundle in the region owns the conduit type under test. When the assumption is violated, `bundle.getConduitNode(conduit)` will throw, terminating the entire test with a `NullPointerException` instead of a descriptive assertion failure. ```diff - if (!foundFirstNetwork) { + if (!bundle.hasConduitStrict(conduit)) { + throw new GameTestAssertException("Missing expected conduit " + conduit + " at " + + x + ", " + y + ", " + z); + } + + if (!foundFirstNetwork) { ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion var bundle = getConduitBundle(x, y, z, allowMissingBundles); if (bundle == null) { continue; } if (!bundle.hasConduitStrict(conduit)) { throw new GameTestAssertException("Missing expected conduit " + conduit + " at " + x + ", " + y + ", " + z); } if (!foundFirstNetwork) { network = bundle.getConduitNode(conduit).getNetwork(); foundFirstNetwork = true; continue; } if (network != bundle.getConduitNode(conduit).getNetwork()) { throw new GameTestAssertException("Conduit node networks differ at " + x + ", " + y + ", " + z); } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +64,4 @@
// Put some dirt in the chest we'll extract from
.thenExecute(() -> helper.insertIntoContainer(0, 1, 0, Items.DIRT, 1))
// Ensure the item is still there
.thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 0, Items.DIRT, 1))
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:10:44 +00:00

⚠️ Potential issue

Chest y-coordinate off by one.

In itemConduitBasicTransfer test, the assertion for the source chest also uses incorrect y-coordinate.

-                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 0, Items.DIRT, 1))
+                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1))
📝 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.

                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1))
_⚠️ Potential issue_ **Chest y-coordinate off by one.** In `itemConduitBasicTransfer` test, the assertion for the source chest also uses incorrect y-coordinate. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 0, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1)) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1)) ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +72,4 @@
.withIsInsert(false)
.withExtractRedstoneControl(RedstoneControl.ALWAYS_ACTIVE)))
// Ensure the item moves
.thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1))
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:10:44 +00:00

⚠️ Potential issue

Chest y-coordinate off by one.

The insertion result assertion here also uses wrong y-coordinate.

-                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1))
+                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1))
📝 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.

                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1))
_⚠️ Potential issue_ **Chest y-coordinate off by one.** The insertion result assertion here also uses wrong y-coordinate. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1)) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1)) ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
@ -0,0 +103,4 @@
.withIsInsert(false)
.withExtractRedstoneControl(RedstoneControl.ALWAYS_ACTIVE)))
// Put some dirt in the chest we'll extract from
.thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1))
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:10:44 +00:00

⚠️ Potential issue

Chest y-coordinate off by one.

The insertion to the container uses wrong y-coordinate in this test as well.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1))
📝 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.

                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1))
_⚠️ Potential issue_ **Chest y-coordinate off by one.** The insertion to the container uses wrong y-coordinate in this test as well. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1)) ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1)) ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 21:47:57 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

🔭 Outside diff range comments (2)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)

115-124: 🛠️ Refactor suggestion

Consider resolving the TODO by removing the isEmpty method

The TODO comment correctly questions whether this method is necessary. As per the previous review comment, the exit condition can be simplified to only depend on the transfer rate limit:

-if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+if (extracted >= speed) {

If you take this approach, the isEmpty method becomes redundant and can be removed entirely, simplifying the code.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

37-37: 💡 Verification agent

🧩 Analysis chain

Consider addressing the ambient temperature TODO.

The TODO comment about subtracting ambient temperature has been carried over from the previous implementation. Consider implementing this feature or creating a separate issue to track it if it's important for accurate heat transfer calculations.


🌐 Web query:

How does Mekanism handle ambient temperature in heat transfer calculations?

💡 Result:

Mekanism, a popular Minecraft mod, handles ambient temperature as part of its heat transfer mechanics by considering it as the baseline or reference temperature in its calculations. Essentially, the ambient temperature is the temperature of the environment surrounding the heat-conducting entities (machines, pipes, etc.).

In Mekanism’s heat transfer system:

  1. Reference Point: Ambient temperature acts as the baseline or equilibrium temperature that heat transfer processes tend to move towards. When an object or machine is hotter than the ambient temperature, it will lose heat to its surroundings, and when it is cooler, it can gain heat from the environment.

  2. Heat Transfer Calculation: Heat transfer between blocks or entities is influenced by the difference between their current temperature and the ambient temperature. This difference drives the direction and amount of heat flow.

  3. Cooling and Heating: If a block or machine’s temperature differs from ambient temperature, the system calculates heat transfer with adjacent blocks or the environment, which can lead to cooling down (if hotter) or warming up (if cooler), trying to reach equilibrium with ambient.

  4. Modifiable Ambient Temperature: The ambient temperature can be affected by biome, altitude, or mod-specific conditions, so it is not necessarily a fixed value. For instance, hotter biomes may have higher ambient temperatures, influencing heat transfer dynamics differently than colder biomes.

Would you like specific details about the temperature values used or formulas for the heat transfer mechanics in Mekanism?


Address ambient temperature handling in heat transfer

The lingering // TODO subtract ambient? comment on line 37 of HeatTicker.java means we’re still not accounting for the environmental baseline when computing heat flow. Mekanism treats ambient temperature as the equilibrium point, so without subtracting it you’ll over- or under-estimate transfers in colder or hotter biomes.

Please either:

  • Implement ambient-temperature adjustment, e.g.
    double ambient = HeatAPI.getAmbientTemp(level, pos);
    and use (sourceTemp – ambient) / invConduction to drive your heat delta.
  • Or, if you’d prefer to tackle this later, open a dedicated issue to track adding ambient-temperature support so it isn’t forgotten.

File needing attention:

  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (line 37)
♻️ Duplicate comments (2)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)

100-100: Round-robin exit condition may skip remaining items

The current implementation uses isEmpty(extractHandler, i + 1) which ignores the current slot (i). If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed.

A simpler, safer exit check is to rely solely on the transfer-rate cap:

-if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+if (extracted >= speed) {

If you still want the early-out optimisation, scan from slot 0 so the current slot is included.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)

19-19: ⚠️ Potential issue

Integer overflow issue persists.

The integer overflow issue previously identified has not been addressed. When multiplying two integers, overflow can occur before the result is assigned to a long variable.

-        final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final long transferRate = (long)conduit.transferRatePerTick() * conduit.networkTickRate();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 48826cbada07dc827ec68d569cd98c7228eea868 and 69f36cb2807f8d026cbcaddd63a7e686a1832595.

📒 Files selected for processing (5)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java
Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: test
🔇 Additional comments (7)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)

17-113: Overall implementation provides a clean and efficient item transfer logic

The new implementation is well-structured and follows a logical flow:

  1. Iterates through network channels
  2. Processes extract connections and their corresponding insert connections
  3. Applies filters and respects configuration settings like round-robin and self-feeding
  4. Manages item transfer with proper accounting

This aligns well with the PR objective to simplify conduit tickers and improve performance by leveraging the new network abstraction to cache frequently queried information.


34-35:

Verification successful

Verify consistent naming for connection configuration

The PR summary mentions a shift in terminology from "send"/"receive" to "insert"/"extract". Ensure that the connectionConfig variable is properly retrieving the configuration with the updated naming convention throughout all conduit types.


🏁 Script executed:

#!/bin/bash
# Check for consistent naming in connection configurations across conduit types
rg "(send|receive|insert|extract)" --type java -g "*ConnectionConfig*.java" -A 1 -B 1

Length of output: 69619


Consistent insert/extract naming confirmed

All ConnectionConfig records (Item, Fluid, Energy, Redstone, etc.) now use isInsert/insertChannel and isExtract/extractChannel, and ItemConduitTicker correctly retrieves the configuration via extractConnection.connectionConfig(...). No further changes are required.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

9-46: Implementation looks clean and well-structured.

The refactoring from extending IOAwareConduitTicker to implementing ConduitTicker<HeatConduit> has significantly simplified the heat transfer logic. The code iterates through extract/insert connections efficiently using the new network abstraction.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)

110-112: Fixed filter slot selection issue.

The code now correctly uses FluidConduit.INSERT_FILTER_SLOT when checking insert filters, fixing the previously reported issue.


66-73: Network marking approach looks good.

The implementation for tracking locked fluid changes and marking nodes as dirty is efficient and only updates when necessary.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)

58-65: Network marking logic is well implemented.

The implementation for tracking locked chemical changes and marking nodes as dirty is efficient, only updating when necessary.


68-124: Chemical transfer logic is well structured.

The refactored chemical transfer method handles extraction, filtering, and insertion clearly and efficiently.

**Actionable comments posted: 1** <details> <summary>🔭 Outside diff range comments (2)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)</summary><blockquote> `115-124`: _🛠️ Refactor suggestion_ **Consider resolving the TODO by removing the isEmpty method** The TODO comment correctly questions whether this method is necessary. As per the previous review comment, the exit condition can be simplified to only depend on the transfer rate limit: ```diff -if (extracted >= speed || isEmpty(extractHandler, i + 1)) { +if (extracted >= speed) { ``` If you take this approach, the `isEmpty` method becomes redundant and can be removed entirely, simplifying the code. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary><blockquote> `37-37`: _💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Consider addressing the ambient temperature TODO.** The TODO comment about subtracting ambient temperature has been carried over from the previous implementation. Consider implementing this feature or creating a separate issue to track it if it's important for accurate heat transfer calculations. --- 🌐 Web query: ```web How does Mekanism handle ambient temperature in heat transfer calculations? ``` 💡 Result: Mekanism, a popular Minecraft mod, handles ambient temperature as part of its heat transfer mechanics by considering it as the baseline or reference temperature in its calculations. Essentially, the ambient temperature is the temperature of the environment surrounding the heat-conducting entities (machines, pipes, etc.). In Mekanism’s heat transfer system: 1. **Reference Point**: Ambient temperature acts as the baseline or equilibrium temperature that heat transfer processes tend to move towards. When an object or machine is hotter than the ambient temperature, it will lose heat to its surroundings, and when it is cooler, it can gain heat from the environment. 2. **Heat Transfer Calculation**: Heat transfer between blocks or entities is influenced by the difference between their current temperature and the ambient temperature. This difference drives the direction and amount of heat flow. 3. **Cooling and Heating**: If a block or machine’s temperature differs from ambient temperature, the system calculates heat transfer with adjacent blocks or the environment, which can lead to cooling down (if hotter) or warming up (if cooler), trying to reach equilibrium with ambient. 4. **Modifiable Ambient Temperature**: The ambient temperature can be affected by biome, altitude, or mod-specific conditions, so it is not necessarily a fixed value. For instance, hotter biomes may have higher ambient temperatures, influencing heat transfer dynamics differently than colder biomes. Would you like specific details about the temperature values used or formulas for the heat transfer mechanics in Mekanism? --- </details> **Address ambient temperature handling in heat transfer** The lingering `// TODO subtract ambient?` comment on line 37 of HeatTicker.java means we’re still not accounting for the environmental baseline when computing heat flow. Mekanism treats ambient temperature as the equilibrium point, so without subtracting it you’ll over- or under-estimate transfers in colder or hotter biomes. Please either: - Implement ambient-temperature adjustment, e.g. `double ambient = HeatAPI.getAmbientTemp(level, pos);` and use `(sourceTemp – ambient) / invConduction` to drive your heat delta. - Or, if you’d prefer to tackle this later, open a dedicated issue to track adding ambient-temperature support so it isn’t forgotten. File needing attention: - enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (line 37) </blockquote></details> </blockquote></details> <details> <summary>♻️ Duplicate comments (2)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)</summary><blockquote> `100-100`: **Round-robin exit condition may skip remaining items** The current implementation uses `isEmpty(extractHandler, i + 1)` which ignores the current slot (`i`). If only part of the stack in the current slot was moved (e.g., transfer rate not reached or destination full) the ticker will prematurely abandon this extract connection and leave items unprocessed. A simpler, safer exit check is to rely solely on the transfer-rate cap: ```diff -if (extracted >= speed || isEmpty(extractHandler, i + 1)) { +if (extracted >= speed) { ``` If you still want the early-out optimisation, scan from slot 0 so the current slot is included. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)</summary><blockquote> `19-19`: _⚠️ Potential issue_ **Integer overflow issue persists.** The integer overflow issue previously identified has not been addressed. When multiplying two integers, overflow can occur before the result is assigned to a long variable. ```diff - final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final long transferRate = (long)conduit.transferRatePerTick() * conduit.networkTickRate(); ``` </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 48826cbada07dc827ec68d569cd98c7228eea868 and 69f36cb2807f8d026cbcaddd63a7e686a1832595. </details> <details> <summary>📒 Files selected for processing (5)</summary> * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java` (1 hunks) </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary> * GitHub Check: Analyze (java-kotlin) * GitHub Check: test </details> <details> <summary>🔇 Additional comments (7)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (2)</summary> `17-113`: **Overall implementation provides a clean and efficient item transfer logic** The new implementation is well-structured and follows a logical flow: 1. Iterates through network channels 2. Processes extract connections and their corresponding insert connections 3. Applies filters and respects configuration settings like round-robin and self-feeding 4. Manages item transfer with proper accounting This aligns well with the PR objective to simplify conduit tickers and improve performance by leveraging the new network abstraction to cache frequently queried information. --- `34-35`: <details> <summary>✅ Verification successful</summary> **Verify consistent naming for connection configuration** The PR summary mentions a shift in terminology from "send"/"receive" to "insert"/"extract". Ensure that the `connectionConfig` variable is properly retrieving the configuration with the updated naming convention throughout all conduit types. --- 🏁 Script executed: ```shell #!/bin/bash # Check for consistent naming in connection configurations across conduit types rg "(send|receive|insert|extract)" --type java -g "*ConnectionConfig*.java" -A 1 -B 1 ``` Length of output: 69619 --- </details> **Consistent insert/extract naming confirmed** All ConnectionConfig records (Item, Fluid, Energy, Redstone, etc.) now use `isInsert`/`insertChannel` and `isExtract`/`extractChannel`, and `ItemConduitTicker` correctly retrieves the configuration via `extractConnection.connectionConfig(...)`. No further changes are required. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary> `9-46`: **Implementation looks clean and well-structured.** The refactoring from extending `IOAwareConduitTicker` to implementing `ConduitTicker<HeatConduit>` has significantly simplified the heat transfer logic. The code iterates through extract/insert connections efficiently using the new network abstraction. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)</summary> `110-112`: **Fixed filter slot selection issue.** The code now correctly uses `FluidConduit.INSERT_FILTER_SLOT` when checking insert filters, fixing the previously reported issue. --- `66-73`: **Network marking approach looks good.** The implementation for tracking locked fluid changes and marking nodes as dirty is efficient and only updates when necessary. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)</summary> `58-65`: **Network marking logic is well implemented.** The implementation for tracking locked chemical changes and marking nodes as dirty is efficient, only updating when necessary. --- `68-124`: **Chemical transfer logic is well structured.** The refactored chemical transfer method handles extraction, filtering, and insertion clearly and efficiently. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -29,0 +33,4 @@
IFluidHandler extractHandler = extractConnection.getSidedCapability(Capabilities.FluidHandler.BLOCK);
if (extractHandler == null) {
continue;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 21:47:56 +00:00

🛠️ Refactor suggestion

Check for potential integer overflow.

The multiplication of two integers without explicit type conversion could potentially overflow if both values are large.

-        final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate());
📝 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.

        final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate());
_🛠️ Refactor suggestion_ **Check for potential integer overflow.** The multiplication of two integers without explicit type conversion could potentially overflow if both values are large. ```diff - final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate()); ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate()); ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 22:28:31 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

🔭 Outside diff range comments (2)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)

962-963: 🛠️ Refactor suggestion

Missing network update after disconnection

When disconnecting a conduit, the code calls onConnectionsUpdated but doesn't trigger a network update like in other connection change paths.

 disconnect(conduit, side);
 onConnectionsUpdated(conduit);
+
+// If we're on the server and the conduit is still valid, update its network
+if (level != null && !level.isClientSide) {
+    var node = getConduitNode(conduit);
+    if (node.isValid()) {
+        node.getNetwork().onNodeUpdated(node);
+    }
+}
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

37-37: 💡 Verification agent

🧩 Analysis chain

Address TODO comment before final release.

There's a TODO comment about subtracting ambient temperature that should be addressed before the final release.


🏁 Script executed:

#!/bin/bash
# Check if ambient temperature handling is addressed in Mekanism's HeatAPI
rg -A 2 -B 2 "getAmbientTemp" --type java

Length of output: 936


Subtract ambient temperature in heat transfer calculation

The TODO in HeatTicker.java still needs addressing before release. You should subtract the ambient temperature (via HeatAPI.getAmbientTemp(level, pos)) when computing tempToTransfer prior to dividing by invConduction.

Locations to update:

  • File: src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java
  • Line ~37:
        double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature()
             HeatAPI.getAmbientTemp(level, pos))
            / invConduction;
    

Ensure the ambient subtraction is correctly integrated or remove the TODO if it’s no longer needed.

♻️ Duplicate comments (17)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)

24-27: ⚠️ Potential issue

Fix potential NPE with inventory access

The code doesn't check if extractConnection.inventory() returns null before accessing methods on it, which could cause a NullPointerException.

-var redstoneExtractFilter = extractConnection.inventory()
-        .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
-        .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER);
+var inventory = extractConnection.inventory();
+var redstoneExtractFilter = inventory != null ? 
+        inventory.getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT)
+        .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER) : null;

41-49: 🛠️ Refactor suggestion

Optimise neighbour updates

The current implementation updates neighbours for every insert connection whenever any signal changes, which is inefficient for large networks.

    public void tick(ServerLevel level, RedstoneConduit conduit, IConduitNetwork network) {
        var context = network.getOrCreateContext(RedstoneConduitNetworkContext.TYPE);
        boolean isActiveBeforeTick = context.isActive();
        context.nextTick();

+        // Track connections that need updates
+        java.util.Set<ConduitBlockConnection> dirtyInserts = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>();
+        
        for (var channel : network.allChannels()) {
            // Receive input signals.
            for (var extractConnection : network.extractConnections(channel)) {
                // ... existing signal extraction code ...
            }

            // Fire block updates if the signal changed.
            if (context.isNew() || context.getSignal(channel) != context.getSignalLastTick(channel)) {
                for (var insertConnection : network.insertConnections(channel)) {
-                    level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get());
-
-                    if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE)
-                            .isStrongOutputSignal()) {
-                        level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get());
-                    }
+                    dirtyInserts.add(insertConnection);
                }
            }
        }
+        
+        // Update all dirty connections once
+        for (var insertConnection : dirtyInserts) {
+            level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get());
+            
+            if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE)
+                    .isStrongOutputSignal()) {
+                level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get());
+            }
+        }
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)

100-105: ⚠️ Potential issue

Round-robin exit condition may skip remaining items

The exit condition isEmpty(extractHandler, i + 1) checks if slots after the current slot are empty, but this ignores any partial extraction from the current slot. If only part of the stack was transferred (due to transfer rate limit or destination being partially full), the remaining items in the current slot will be ignored.

Recommend simplifying to rely solely on the transfer rate cap:

-if (extracted >= speed || isEmpty(extractHandler, i + 1)) {
+if (extracted >= speed) {

If you still want the early-exit optimisation, consider scanning from slot 0 to ensure the current slot is included.

enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (6)

26-37: ⚠️ Potential issue

Template height too small for later y=1 edits

The template is registered with a height of only 1 block (height = 1), but the test attempts to place and interact with blocks at y = 1. This will cause the test to fail with out-of-bounds exceptions.

Update the template height:

-@RegisterStructureTemplate(THREE_CHESTS)
-public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3,
+@RegisterStructureTemplate(THREE_CHESTS)
+public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3,

Also apply this fix to the ad-hoc template in itemConduitBasicTransfer.


67-67: ⚠️ Potential issue

Chest y-coordinate off by one

All chests are placed at y = 0 according to the template, but the assertion references y = 1.

-                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 0, Items.DIRT, 1))
+                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1))

75-75: ⚠️ Potential issue

Chest y-coordinate off by one

The assertion is referencing a chest at y = 1, but chests are placed at y = 0 in the template.

-                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1))
+                    .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1))

106-110: ⚠️ Potential issue

Chest y-coordinate off by one

The insertion to the container and subsequent assertion use the wrong y-coordinate.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1))
-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, 1))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, 1))

140-144: ⚠️ Potential issue

Chest y-coordinate off by one

The insertion to the container and subsequent assertion use the wrong y-coordinate.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1))
-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1))

175-180: ⚠️ Potential issue

Chest y-coordinate off by one

The insertion to the container and the subsequent assertions use the wrong y-coordinate.

-                .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, transferRate * 2))
+                .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, transferRate * 2))
-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, transferRate))
-                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, transferRate))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, transferRate))
+                .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, transferRate))
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1)

24-24: Check for potential integer overflow

The multiplication of two integers without explicit type conversion could potentially overflow if both values are large.

Suggest using a cast to avoid potential overflow:

-        final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate());

Run the following script to check typical values:

#!/bin/bash
# Check typical values of transferRatePerTick and networkTickRate to assess overflow risk

rg -n "transferRatePerTick" --no-heading
rg -n "networkTickRate" --no-heading

# Look for field or method declarations defining these values
ast-grep --pattern "transferRatePerTick() { return $_; }"
ast-grep --pattern "networkTickRate() { return $_; }"
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)

291-301: ⚠️ Potential issue

Potential NPE in tryCopyLegacyData() when conduitBundle is null

The method uses conduitBundle without checking if it's null, which can happen if setNetwork() is called before attach().

-        if (network != null && legacyDataContainer != null && isLoaded()) {
+        if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) {
endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)

61-66: ⚠️ Potential issue

Replace List#getFirst() with List#get(0) for Java 17 compatibility

The getFirst() method requires Java 21, but NeoForge targets Java 17. This will break compilation for developers not using the latest toolchain.

-            var node = nodes.getFirst();
+            var node = nodes.get(0);
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (3)

292-315: 🛠️ Refactor suggestion

Connection-list duplication & ordering costs

The addLoadedNode() method pushes every new ConduitBlockConnection into all existing accessibleBlockConnectionsMap value lists. For large networks with many connections, this creates O(n²) time complexity and potential memory issues.

Consider using a Set (specifically LinkedHashSet to preserve insertion order) instead of ArrayList to eliminate duplicates and reduce the insertion complexity to O(n).


479-481: ⚠️ Potential issue

Protect against null onChunkCoverageChanged callback

The callback is accessed without checking if it's null, which can lead to a NullPointerException when the network spreads to a new chunk but no callback has been registered yet.

-        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
+        if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) {
             onChunkCoverageChanged.accept(this);
         }

505-514: 🛠️ Refactor suggestion

Refactor onNodeAdded guard to avoid relying on a null-check

The if (nodesByChunkPos == null) check is only there to avoid a NullPointerException when the superclass constructor calls your override before Java has initialized your fields.

Consider introducing an explicit construction flag:

// Add field
+ private boolean constructed;

// In constructors, after super() call
+ constructed = true;

// Replace in onNodeAdded
- if (nodesByChunkPos == null) {
+ if (!constructed) {
     return;
 }

Or better yet, modify the superclass to avoid calling overridable methods during construction.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)

171-171: ⚠️ Potential issue

Ensure level is non-null before checking isClientSide()

The current condition order could lead to a NullPointerException if level is null, as it would still try to evaluate level.isClientSide().

-        if (level != null && !level.isClientSide()) {
+        if (level != null && !level.isClientSide()) {

This is already correctly ordered, but I wanted to ensure you're aware this pattern is important to maintain throughout the codebase.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)

19-19: ⚠️ Potential issue

Fix integer overflow when computing transferRate

The multiplication of two int values can overflow before the result is widened to long, potentially producing negative or wrapped results for large buffers.

-        final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate();
+        final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate();
🧹 Nitpick comments (17)
.github/workflows/verify.yml (2)

43-43: Remove trailing whitespace

There's unnecessary whitespace at the end of line 43.

-          
+
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 43-43: trailing spaces

(trailing-spaces)


23-25: Consider adding test results reporting

While the split test commands are good, consider adding test result collection and reporting to make it easier to diagnose test failures.

  test:
    name: Run Tests
    timeout-minutes: 60
+    continue-on-error: true

And after the test steps:

- name: Publish Test Report
  uses: mikepenz/action-junit-report@v4
  if: success() || failure()
  with:
    report_paths: '**/build/test-results/test/TEST-*.xml'
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (1)

195-197: Consider adding a utility method for effective speed calculation

The effective speed calculation is duplicated in multiple places. Consider extracting it to a utility method.

+    /**
+     * Calculates the effective transfer rate per second.
+     * @return Items per second that can be transferred
+     */
+    public double getEffectiveTransferRate() {
+        return transferRatePerCycle() * (20.0 / networkTickRate());
+    }

    @Override
    public void addToTooltip(Item.TooltipContext pContext, Consumer<Component> pTooltipAdder,
            TooltipFlag pTooltipFlag) {
        String calculatedTransferLimitFormatted = String.format("%,d",
-                (int) Math.floor(transferRatePerCycle() * (20.0 / networkTickRate())));
+                (int) Math.floor(getEffectiveTransferRate()));
        // ...
    }
    
    @Override
    public int compareTo(@NotNull ItemConduit o) {
-        double selfEffectiveSpeed = transferRatePerCycle() * (20.0 / networkTickRate());
-        double otherEffectiveSpeed = o.transferRatePerCycle() * (20.0 / o.networkTickRate());
+        double selfEffectiveSpeed = getEffectiveTransferRate();
+        double otherEffectiveSpeed = o.getEffectiveTransferRate();
        // ...
    }
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)

115-116: Consider removing unnecessary isEmpty check

The method isEmpty has a TODO questioning its necessity, and it's only used once in the code. The early exit optimisation it enables could lead to overlooked items as mentioned in the previous comment, making it potentially problematic.

Consider removing this method entirely if you implement the suggested fix for the round-robin exit condition.

enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (8)

27-30: Consider removing experimental isEmpty method

The isEmpty() method is marked as experimental with a TODO comment indicating uncertainty about its usefulness for tickers. Since it's functionally equivalent to checking nodeCount() == 0, it adds little value.

If you do decide to keep it, consider documenting a specific use case or removing the experimental annotation once the API stabilizes.


36-39: Consider removing experimental contains method

Like isEmpty(), the contains() method is marked as experimental with the same TODO, suggesting it might not be necessary for tickers. Unless there's a specific use case where this query is more efficient than iterating through nodes(), it may be redundant.

Consider documenting specific usage scenarios or removing the experimental annotation if the method proves useful in real implementations.


86-87: Be consistent with terminology across the interface

The method insertConnections() is documented as returning "sending connections" rather than "insert connections", which doesn't match the method name and could cause confusion.

- * @return all the sending connections across all channels.
+ * @return all the insert connections across all channels.

93-94: Terminology mismatch in documentation

The documentation uses "sending connections" instead of "insert connections".

- * @return all the sending connections in the given {@code channel}.
+ * @return all the insert connections in the given {@code channel}.

102-103: Terminology mismatch in documentation

The documentation refers to "receiving connections" instead of "extract connections".

- * @return all the receiving connections that are accessible to the {@code insertConnection}, in the conduit's specified order.
+ * @return all the extract connections that are accessible to the {@code insertConnection}, in the conduit's specified order.

110-111: Terminology mismatch in documentation

The documentation refers to "receiving connections" instead of "extract connections".

- * @return all the receiving connections across all channels.
+ * @return all the extract connections across all channels.

119-120: Terminology mismatch in documentation

The documentation refers to "receiving connections" instead of "extract connections".

- * @return all the receiving connections in the given {@code channel}.
+ * @return all the extract connections in the given {@code channel}.

128-129: Terminology mismatch in documentation

The documentation refers to "sending connections" instead of "insert connections".

- * @return all the sending connections that are accessible to the {@code extractConnection}, in the conduit's specified order.
+ * @return all the insert connections that are accessible to the {@code extractConnection}, in the conduit's specified order.
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)

59-68: Consider adding validation for the conduit parameter

The constructor doesn't check if the conduit parameter is null, but later methods assume it's non-null.

    public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos) {
+        Objects.requireNonNull(conduit, "Conduit cannot be null");
        this(conduit, pos, (NodeData) null);
    }

    public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, @Nullable NodeData nodeData) {
+        Objects.requireNonNull(conduit, "Conduit cannot be null");
        this.pos = pos;
        this.nodeData = nodeData;
        this.network = new ConduitNetwork(conduit, this);
    }
endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)

338-380: Consider adding protection against network fragmentation

When splitting networks, if many disconnected sub-networks are created, the current implementation might lead to performance issues. Consider adding a limit or optimization for handling large network splits.

Add a check for the number of new networks being created and log a warning if it exceeds a threshold, as excessive fragmentation could hurt performance:

        // If any nodes remain, they are now disconnected - form graphs to replace them.
        Set<TNet> newGraphs = Sets.newHashSet();
+       int networkCount = 0;
+       final int MAX_NETWORK_SPLITS = 100; // Define a reasonable threshold
        while (!remaining.isEmpty()) {
+           networkCount++;
+           if (networkCount > MAX_NETWORK_SPLITS) {
+               LOGGER.warn("Network splitting resulted in {} sub-networks, which may indicate an issue", networkCount);
+           }
            // TODO: Potentially rework this to use the new constructor?
            var newGraph = createEmpty();
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)

266-276: Consider implementing partial cache rebuilds

The TODO comment acknowledges the inefficiency of the current approach. For large networks, removing and re-adding a node during updates is inefficient and could be optimized with incremental updates.

Consider a more granular approach where you only update the specific parts of the cache that are affected by the node update, rather than doing a full node removal and re-addition.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)

660-668: Commented-out legacy code should be removed

There's a block of commented-out legacy code related to ConduitSavedData. Since this PR is implementing a replacement for the old system, this commented code should be removed to avoid confusion.

-        // TODO: Do we need an equivalent to this?
-//        if (level instanceof ServerLevel serverLevel) {
-//            if (neighborBundle.hasConduitByType(conduit)) {
-//                Optional.of(neighborBundle.getConduitNode(conduit))
-//                        .map(ConduitGraphObject::getGraph)
-//                        .filter(Objects::nonNull)
-//                        .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel));
-//            }
-//        }
enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)

35-53: Consider adding comments to explain the locked chemical handling logic.

The locked chemical handling logic is important for understanding how the ticker behaves differently for multi-chemical vs single-chemical conduits. Adding explanatory comments would improve code readability.

         if (!context.lockedChemical().isEmptyType()) {
+            // If we have a locked chemical type, only transfer that specific chemical
             doChemicalTransfer(context.lockedChemical(), transferRate, extractConnection, insertConnections);
         } else {
             long remaining = transferRate;

+            // No locked chemical, try transferring from each tank until we reach the transfer limit
             for (int i = 0; i < extractHandler.getChemicalTanks() && remaining > 0; i++) {
                 if (extractHandler.getChemicalInTank(i).isEmpty()) {
                     continue;
                 }

                 Chemical chemical = extractHandler.getChemicalInTank(i).getChemical();
                 remaining = doChemicalTransfer(chemical, remaining, extractConnection, insertConnections);

+                // For non-multi-chemical conduits, lock to the first chemical type we successfully transfer
                 if (!conduit.isMultiChemical() && remaining < transferRate) {
                     context.setLockedChemical(chemical);
                     break;
                 }
             }
         }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 69f36cb2807f8d026cbcaddd63a7e686a1832595 and 1563c55d06.

📒 Files selected for processing (100)
  • .github/workflows/verify.yml (1 hunks)
  • endercore/build.gradle.kts (2 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/Network.java (1 hunks)
  • endercore/src/main/java/com/enderio/core/common/graph/package-info.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/NetworkTests.java (1 hunks)
  • endercore/src/test/java/com/enderio/core/TestNode.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java (4 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java (5 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java (7 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java (2 hunks)
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java (3 hunks)
  • enderio-conduits/build.gradle.kts (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (9 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java (3 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (27 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java (0 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java (5 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java (6 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (6 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java (4 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java (2 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java (1 hunks)
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java (1 hunks)
  • enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java (1 hunks)
  • enderio/build.gradle.kts (1 hunks)
💤 Files with no reviewable changes (13)
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java
Files skipped from review due to trivial changes (2)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java
🚧 Files skipped from review as they are similar to previous changes (71)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java
  • endercore/src/main/java/com/enderio/core/common/graph/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java
  • enderio/build.gradle.kts
  • enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java
  • endercore/build.gradle.kts
  • enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java
  • enderio-conduits/build.gradle.kts
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java
  • endercore/src/test/java/com/enderio/core/TestNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java
  • endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java
  • endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java
  • enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java
  • endercore/src/test/java/com/enderio/core/NetworkTests.java
  • enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)
  • ConduitNode (25-302)
🪛 YAMLlint (1.35.1)
.github/workflows/verify.yml

[error] 43-43: trailing spaces

(trailing-spaces)

Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run Tests
🔇 Additional comments (29)
.github/workflows/verify.yml (4)

1-5: Good security update with the permissions block

The addition of the permissions block with contents: read follows best practice and addresses the previous CodeQL security warnings.


7-20: Improved workflow trigger configuration

The workflow now triggers on both dev/* branches and has more specific PR event types, which will help avoid unnecessary test runs. The path filter for Java files is also a good addition to prevent running tests when only non-code files change.


28-31: Effective concurrency controls

The concurrency configuration prevents duplicate test runs and cancels in-progress runs when new commits are pushed, which optimises CI resource usage.


48-52: Split test execution improves visibility and reliability

Splitting the test execution into separate game tests and unit tests steps provides better visibility into test failures and aligns with the new testing structure in the codebase.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)

14-15: Clean use of network context

The implementation properly uses the network context system to track and manage redstone signals across ticks.


53-58: Efficient model updates when state changes

The code efficiently marks all nodes as dirty only when the active state changes, avoiding unnecessary updates.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (5)

39-40: Good terminology update for clarity

Renaming graphTickRate to networkTickRate improves clarity and aligns with the new network abstraction.


45-54: Clean codec implementation with backward compatibility

The codec implementation properly handles the renamed field and includes optionals to support the old conduit format, which is important for backward compatibility.


72-80: Effective priority-based node comparison

The added compareNodes method properly implements priority-based sorting with a fallback to the default comparison, which will improve item routing in complex networks.


126-143: Improved legacy data migration with external connection config setter

The updated copyLegacyData method now uses a BiConsumer for setting connection configs, which better separates concerns and improves testability.


177-191: Consistent terminology update for extraction/insertion

The rename from "receive"/"send" to "extract"/"insert" terminology improves consistency across the codebase.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)

110-112: Make sure the correct filter slot is used for inserts

The code now correctly uses FluidConduit.INSERT_FILTER_SLOT for insert filters.

This fixes a potential issue where insert-side filters could have been bypassed if FluidConduit.EXTRACT_FILTER_SLOT was incorrectly used here.


122-123: Verify FluidUtil transfer direction matches intended behavior

The fluid transfer is set up as FluidUtil.tryFluidTransfer(insertHandler, extractHandler, fluidToInsert, true) which is passing the extract handler as the source and the insert handler as the destination. Make sure this matches the intended direction of flow.

The parameters match the typical usage pattern of tryFluidTransfer(destination, source, resource, doTransfer), but double-check that this is the intended direction, especially when considering the variable names and network terminology.

enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (3)

133-146: Good addition of the compareNodes method for sorting connections

The new method provides a useful sorting mechanism based on Manhattan distance, which will help optimize network transfers.


258-283: LGTM! Good documentation improvement for legacy methods

The improved documentation clarifies the purpose of these legacy methods and their planned removal timeline.


63-65: Method renamed from graphTickRate to networkTickRate

The method has been renamed to align with the new network-based terminology, but the implementation remains unchanged.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)

25-41: Good implementation of serialization with backward compatibility

The class provides both legacy and new codecs, with a clear migration path for 1.22. The documentation about planned removal helps maintainers understand the timeline.

endercore/src/main/java/com/enderio/core/common/graph/Network.java (2)

26-40: Well-designed generic network system

The Network class provides a robust foundation for building network systems, with good support for stable element ordering, network merging/splitting, and serialization.


346-351: ⚠️ Potential issue

Replace List#getFirst() with List#get(0) in splitIfRequired method

Another instance of using getFirst() which requires Java 21.

-            firstNode = remaining.iterator().next();
+            firstNode = remaining.iterator().next();

No change needed here as it's already using iterator().next(), not getFirst().

Likely an incorrect or invalid review comment.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (3)

427-467: Good separation of cache rebuilding logic

The cache rebuild method is well-structured with clear steps for rebuilding all caches, adding nodes to position maps, updating channel sets, and sorting connections. The defensive check for the chunk coverage callback is also good practice.


469-471: Efficient use of conduit's custom sorting

The implementation leverages the conduit's own comparison logic for sorting connections, which allows for type-specific optimizations in connection handling.


549-577: Robust network split handling with context validation

The network split implementation includes proper validation to ensure that split contexts are not duplicated across networks, which prevents potential data corruption and inconsistency issues.

enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (4)

317-334: Enhanced network disconnect handling with clear error path

The implementation properly disconnects nodes from the network when a connection is disabled, including handling the case when networks need to be split. The code also includes a proper error path with a TODO comment for handling the case when nodes are invalid.


973-1052: Well-implemented redstone signal caching and query system

The redstone signal caching and query system is well-implemented, with proper neighbor update handling and efficient signal retrieval based on color channels. The code also includes good null checks and defensive coding practices.


1247-1287: Ensure clean-up is skipped during chunk unload

The code correctly uses an isChunkUnload flag to prevent removing conduits and facades when the block entity is unloaded due to a chunk unload. This is important for ensuring that the conduit network state is preserved when chunks are unloaded and later reloaded.


1610-1642: Improved capability caching with separated directional and void capabilities

The NeighboringCapabilityCaches implementation now properly separates directional and void capabilities, and uses efficient map-based caching to avoid redundant capability lookups. This should improve performance for capability-heavy operations.

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)

3-4: Implementation looks good - clean refactoring to the new network API.

The ticker has been successfully refactored to use the new network abstraction. The implementation follows a clean pattern of iterating over extract connections, finding matching insert connections, and transferring heat between them based on temperature differences and conduction values.

Also applies to: 9-9, 14-45

enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)

3-5: Clean implementation of chemical transfer logic using the new network API.

The ticker has been well refactored to use the new network abstraction. The implementation handles chemical transfer appropriately, respecting locked chemical types, multi-chemical settings, and transfer rates. The marking of nodes as dirty when the locked chemical changes is a good approach to ensure proper network updates.

Also applies to: 15-66


68-124: Good encapsulation of chemical transfer logic.

The doChemicalTransfer method nicely encapsulates the logic for transferring chemicals between extract and insert connections, handling filtering appropriately.

**Actionable comments posted: 1** <details> <summary>🔭 Outside diff range comments (2)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)</summary><blockquote> `962-963`: _🛠️ Refactor suggestion_ **Missing network update after disconnection** When disconnecting a conduit, the code calls `onConnectionsUpdated` but doesn't trigger a network update like in other connection change paths. ```diff disconnect(conduit, side); onConnectionsUpdated(conduit); + +// If we're on the server and the conduit is still valid, update its network +if (level != null && !level.isClientSide) { + var node = getConduitNode(conduit); + if (node.isValid()) { + node.getNetwork().onNodeUpdated(node); + } +} ``` </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary><blockquote> `37-37`: _💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Address TODO comment before final release.** There's a TODO comment about subtracting ambient temperature that should be addressed before the final release. --- 🏁 Script executed: ```shell #!/bin/bash # Check if ambient temperature handling is addressed in Mekanism's HeatAPI rg -A 2 -B 2 "getAmbientTemp" --type java ``` Length of output: 936 --- </details> **Subtract ambient temperature in heat transfer calculation** The TODO in `HeatTicker.java` still needs addressing before release. You should subtract the ambient temperature (via `HeatAPI.getAmbientTemp(level, pos)`) when computing `tempToTransfer` prior to dividing by `invConduction`. Locations to update: - File: `src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java` - Line ~37: ```java double tempToTransfer = (extractHandler.getTotalTemperature() - insertHandler.getTotalTemperature() – HeatAPI.getAmbientTemp(level, pos)) / invConduction; ``` Ensure the ambient subtraction is correctly integrated or remove the TODO if it’s no longer needed. </blockquote></details> </blockquote></details> <details> <summary>♻️ Duplicate comments (17)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)</summary><blockquote> `24-27`: _⚠️ Potential issue_ **Fix potential NPE with inventory access** The code doesn't check if `extractConnection.inventory()` returns null before accessing methods on it, which could cause a NullPointerException. ```diff -var redstoneExtractFilter = extractConnection.inventory() - .getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) - .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER); +var inventory = extractConnection.inventory(); +var redstoneExtractFilter = inventory != null ? + inventory.getStackInSlot(RedstoneConduit.EXTRACT_FILTER_SLOT) + .getCapability(ConduitCapabilities.REDSTONE_EXTRACT_FILTER) : null; ``` --- `41-49`: _🛠️ Refactor suggestion_ **Optimise neighbour updates** The current implementation updates neighbours for every insert connection whenever any signal changes, which is inefficient for large networks. ```diff public void tick(ServerLevel level, RedstoneConduit conduit, IConduitNetwork network) { var context = network.getOrCreateContext(RedstoneConduitNetworkContext.TYPE); boolean isActiveBeforeTick = context.isActive(); context.nextTick(); + // Track connections that need updates + java.util.Set<ConduitBlockConnection> dirtyInserts = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); + for (var channel : network.allChannels()) { // Receive input signals. for (var extractConnection : network.extractConnections(channel)) { // ... existing signal extraction code ... } // Fire block updates if the signal changed. if (context.isNew() || context.getSignal(channel) != context.getSignalLastTick(channel)) { for (var insertConnection : network.insertConnections(channel)) { - level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get()); - - if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE) - .isStrongOutputSignal()) { - level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get()); - } + dirtyInserts.add(insertConnection); } } } + + // Update all dirty connections once + for (var insertConnection : dirtyInserts) { + level.updateNeighborsAt(insertConnection.node().pos(), ConduitBlocks.CONDUIT.get()); + + if (insertConnection.connectionConfig(RedstoneConduitConnectionConfig.TYPE) + .isStrongOutputSignal()) { + level.updateNeighborsAt(insertConnection.connectedBlockPos(), ConduitBlocks.CONDUIT.get()); + } + } ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)</summary><blockquote> `100-105`: _⚠️ Potential issue_ **Round-robin exit condition may skip remaining items** The exit condition `isEmpty(extractHandler, i + 1)` checks if slots after the current slot are empty, but this ignores any partial extraction from the current slot. If only part of the stack was transferred (due to transfer rate limit or destination being partially full), the remaining items in the current slot will be ignored. Recommend simplifying to rely solely on the transfer rate cap: ```diff -if (extracted >= speed || isEmpty(extractHandler, i + 1)) { +if (extracted >= speed) { ``` If you still want the early-exit optimisation, consider scanning from slot 0 to ensure the current slot is included. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java (6)</summary><blockquote> `26-37`: _⚠️ Potential issue_ **Template height too small for later y=1 edits** The template is registered with a height of only 1 block (`height = 1`), but the test attempts to place and interact with blocks at `y = 1`. This will cause the test to fail with out-of-bounds exceptions. Update the template height: ```diff -@RegisterStructureTemplate(THREE_CHESTS) -public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 1, 3, +@RegisterStructureTemplate(THREE_CHESTS) +public static final Supplier<StructureTemplate> THREE_CHESTS_TEMPLATE = StructureTemplateBuilder.lazy(3, 2, 3, ``` Also apply this fix to the ad-hoc template in `itemConduitBasicTransfer`. --- `67-67`: _⚠️ Potential issue_ **Chest y-coordinate off by one** All chests are placed at `y = 0` according to the template, but the assertion references `y = 1`. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 0, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 0, Items.DIRT, 1)) ``` --- `75-75`: _⚠️ Potential issue_ **Chest y-coordinate off by one** The assertion is referencing a chest at `y = 1`, but chests are placed at `y = 0` in the template. ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1)) ``` --- `106-110`: _⚠️ Potential issue_ **Chest y-coordinate off by one** The insertion to the container and subsequent assertion use the wrong y-coordinate. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1)) ``` ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, 1)) ``` --- `140-144`: _⚠️ Potential issue_ **Chest y-coordinate off by one** The insertion to the container and subsequent assertion use the wrong y-coordinate. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, 1)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, 1)) ``` ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, 1)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, 1)) ``` --- `175-180`: _⚠️ Potential issue_ **Chest y-coordinate off by one** The insertion to the container and the subsequent assertions use the wrong y-coordinate. ```diff - .thenExecute(() -> helper.insertIntoContainer(2, 1, 0, Items.DIRT, transferRate * 2)) + .thenExecute(() -> helper.insertIntoContainer(2, 0, 0, Items.DIRT, transferRate * 2)) ``` ```diff - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 1, 2, Items.DIRT, transferRate)) - .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 1, 2, Items.DIRT, transferRate)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(2, 0, 2, Items.DIRT, transferRate)) + .thenExecuteAfter(tickRate, () -> helper.assertContainerHasExactly(0, 0, 2, Items.DIRT, transferRate)) ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (1)</summary><blockquote> `24-24`: **Check for potential integer overflow** The multiplication of two integers without explicit type conversion could potentially overflow if both values are large. Suggest using a cast to avoid potential overflow: ```diff - final int fluidRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final int fluidRate = (int)((long)conduit.transferRatePerTick() * conduit.networkTickRate()); ``` Run the following script to check typical values: ```shell #!/bin/bash # Check typical values of transferRatePerTick and networkTickRate to assess overflow risk rg -n "transferRatePerTick" --no-heading rg -n "networkTickRate" --no-heading # Look for field or method declarations defining these values ast-grep --pattern "transferRatePerTick() { return $_; }" ast-grep --pattern "networkTickRate() { return $_; }" ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary><blockquote> `291-301`: _⚠️ Potential issue_ **Potential NPE in tryCopyLegacyData() when conduitBundle is null** The method uses `conduitBundle` without checking if it's null, which can happen if `setNetwork()` is called before `attach()`. ```diff - if (network != null && legacyDataContainer != null && isLoaded()) { + if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) { ``` </blockquote></details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)</summary><blockquote> `61-66`: _⚠️ Potential issue_ **Replace List#getFirst() with List#get(0) for Java 17 compatibility** The `getFirst()` method requires Java 21, but NeoForge targets Java 17. This will break compilation for developers not using the latest toolchain. ```diff - var node = nodes.getFirst(); + var node = nodes.get(0); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (3)</summary><blockquote> `292-315`: _🛠️ Refactor suggestion_ **Connection-list duplication & ordering costs** The `addLoadedNode()` method pushes every new `ConduitBlockConnection` into *all* existing `accessibleBlockConnectionsMap` value lists. For large networks with many connections, this creates O(n²) time complexity and potential memory issues. Consider using a `Set` (specifically `LinkedHashSet` to preserve insertion order) instead of `ArrayList` to eliminate duplicates and reduce the insertion complexity to O(n). --- `479-481`: _⚠️ Potential issue_ **Protect against null onChunkCoverageChanged callback** The callback is accessed without checking if it's null, which can lead to a `NullPointerException` when the network spreads to a new chunk but no callback has been registered yet. ```diff - if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { + if (!isRebuild && isNewChunk && onChunkCoverageChanged != null) { onChunkCoverageChanged.accept(this); } ``` --- `505-514`: _🛠️ Refactor suggestion_ **Refactor onNodeAdded guard to avoid relying on a null-check** The `if (nodesByChunkPos == null)` check is only there to avoid a `NullPointerException` when the superclass constructor calls your override before Java has initialized your fields. Consider introducing an explicit construction flag: ```diff // Add field + private boolean constructed; // In constructors, after super() call + constructed = true; // Replace in onNodeAdded - if (nodesByChunkPos == null) { + if (!constructed) { return; } ``` Or better yet, modify the superclass to avoid calling overridable methods during construction. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)</summary><blockquote> `171-171`: _⚠️ Potential issue_ **Ensure level is non-null before checking isClientSide()** The current condition order could lead to a `NullPointerException` if level is null, as it would still try to evaluate `level.isClientSide()`. ```diff - if (level != null && !level.isClientSide()) { + if (level != null && !level.isClientSide()) { ``` This is already correctly ordered, but I wanted to ensure you're aware this pattern is important to maintain throughout the codebase. </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)</summary><blockquote> `19-19`: _⚠️ Potential issue_ **Fix integer overflow when computing `transferRate`** The multiplication of two `int` values can overflow before the result is widened to `long`, potentially producing negative or wrapped results for large buffers. ```diff - final long transferRate = conduit.transferRatePerTick() * conduit.networkTickRate(); + final long transferRate = (long) conduit.transferRatePerTick() * conduit.networkTickRate(); ``` </blockquote></details> </blockquote></details> <details> <summary>🧹 Nitpick comments (17)</summary><blockquote> <details> <summary>.github/workflows/verify.yml (2)</summary><blockquote> `43-43`: **Remove trailing whitespace** There's unnecessary whitespace at the end of line 43. ```diff - + ``` <details> <summary>🧰 Tools</summary> <details> <summary>🪛 YAMLlint (1.35.1)</summary> [error] 43-43: trailing spaces (trailing-spaces) </details> </details> --- `23-25`: **Consider adding test results reporting** While the split test commands are good, consider adding test result collection and reporting to make it easier to diagnose test failures. ```diff test: name: Run Tests timeout-minutes: 60 + continue-on-error: true ``` And after the test steps: ```shell - name: Publish Test Report uses: mikepenz/action-junit-report@v4 if: success() || failure() with: report_paths: '**/build/test-results/test/TEST-*.xml' ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (1)</summary><blockquote> `195-197`: **Consider adding a utility method for effective speed calculation** The effective speed calculation is duplicated in multiple places. Consider extracting it to a utility method. ```diff + /** + * Calculates the effective transfer rate per second. + * @return Items per second that can be transferred + */ + public double getEffectiveTransferRate() { + return transferRatePerCycle() * (20.0 / networkTickRate()); + } @Override public void addToTooltip(Item.TooltipContext pContext, Consumer<Component> pTooltipAdder, TooltipFlag pTooltipFlag) { String calculatedTransferLimitFormatted = String.format("%,d", - (int) Math.floor(transferRatePerCycle() * (20.0 / networkTickRate()))); + (int) Math.floor(getEffectiveTransferRate())); // ... } @Override public int compareTo(@NotNull ItemConduit o) { - double selfEffectiveSpeed = transferRatePerCycle() * (20.0 / networkTickRate()); - double otherEffectiveSpeed = o.transferRatePerCycle() * (20.0 / o.networkTickRate()); + double selfEffectiveSpeed = getEffectiveTransferRate(); + double otherEffectiveSpeed = o.getEffectiveTransferRate(); // ... } ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java (1)</summary><blockquote> `115-116`: **Consider removing unnecessary isEmpty check** The method `isEmpty` has a TODO questioning its necessity, and it's only used once in the code. The early exit optimisation it enables could lead to overlooked items as mentioned in the previous comment, making it potentially problematic. Consider removing this method entirely if you implement the suggested fix for the round-robin exit condition. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java (8)</summary><blockquote> `27-30`: **Consider removing experimental isEmpty method** The `isEmpty()` method is marked as experimental with a TODO comment indicating uncertainty about its usefulness for tickers. Since it's functionally equivalent to checking `nodeCount() == 0`, it adds little value. If you do decide to keep it, consider documenting a specific use case or removing the experimental annotation once the API stabilizes. --- `36-39`: **Consider removing experimental contains method** Like `isEmpty()`, the `contains()` method is marked as experimental with the same TODO, suggesting it might not be necessary for tickers. Unless there's a specific use case where this query is more efficient than iterating through `nodes()`, it may be redundant. Consider documenting specific usage scenarios or removing the experimental annotation if the method proves useful in real implementations. --- `86-87`: **Be consistent with terminology across the interface** The method `insertConnections()` is documented as returning "sending connections" rather than "insert connections", which doesn't match the method name and could cause confusion. ```diff - * @return all the sending connections across all channels. + * @return all the insert connections across all channels. ``` --- `93-94`: **Terminology mismatch in documentation** The documentation uses "sending connections" instead of "insert connections". ```diff - * @return all the sending connections in the given {@code channel}. + * @return all the insert connections in the given {@code channel}. ``` --- `102-103`: **Terminology mismatch in documentation** The documentation refers to "receiving connections" instead of "extract connections". ```diff - * @return all the receiving connections that are accessible to the {@code insertConnection}, in the conduit's specified order. + * @return all the extract connections that are accessible to the {@code insertConnection}, in the conduit's specified order. ``` --- `110-111`: **Terminology mismatch in documentation** The documentation refers to "receiving connections" instead of "extract connections". ```diff - * @return all the receiving connections across all channels. + * @return all the extract connections across all channels. ``` --- `119-120`: **Terminology mismatch in documentation** The documentation refers to "receiving connections" instead of "extract connections". ```diff - * @return all the receiving connections in the given {@code channel}. + * @return all the extract connections in the given {@code channel}. ``` --- `128-129`: **Terminology mismatch in documentation** The documentation refers to "sending connections" instead of "insert connections". ```diff - * @return all the sending connections that are accessible to the {@code extractConnection}, in the conduit's specified order. + * @return all the insert connections that are accessible to the {@code extractConnection}, in the conduit's specified order. ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary><blockquote> `59-68`: **Consider adding validation for the conduit parameter** The constructor doesn't check if the conduit parameter is null, but later methods assume it's non-null. ```diff public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos) { + Objects.requireNonNull(conduit, "Conduit cannot be null"); this(conduit, pos, (NodeData) null); } public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, @Nullable NodeData nodeData) { + Objects.requireNonNull(conduit, "Conduit cannot be null"); this.pos = pos; this.nodeData = nodeData; this.network = new ConduitNetwork(conduit, this); } ``` </blockquote></details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (1)</summary><blockquote> `338-380`: **Consider adding protection against network fragmentation** When splitting networks, if many disconnected sub-networks are created, the current implementation might lead to performance issues. Consider adding a limit or optimization for handling large network splits. Add a check for the number of new networks being created and log a warning if it exceeds a threshold, as excessive fragmentation could hurt performance: ```diff // If any nodes remain, they are now disconnected - form graphs to replace them. Set<TNet> newGraphs = Sets.newHashSet(); + int networkCount = 0; + final int MAX_NETWORK_SPLITS = 100; // Define a reasonable threshold while (!remaining.isEmpty()) { + networkCount++; + if (networkCount > MAX_NETWORK_SPLITS) { + LOGGER.warn("Network splitting resulted in {} sub-networks, which may indicate an issue", networkCount); + } // TODO: Potentially rework this to use the new constructor? var newGraph = createEmpty(); ``` </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> `266-276`: **Consider implementing partial cache rebuilds** The TODO comment acknowledges the inefficiency of the current approach. For large networks, removing and re-adding a node during updates is inefficient and could be optimized with incremental updates. Consider a more granular approach where you only update the specific parts of the cache that are affected by the node update, rather than doing a full node removal and re-addition. </blockquote></details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (1)</summary><blockquote> `660-668`: **Commented-out legacy code should be removed** There's a block of commented-out legacy code related to `ConduitSavedData`. Since this PR is implementing a replacement for the old system, this commented code should be removed to avoid confusion. ```diff - // TODO: Do we need an equivalent to this? -// if (level instanceof ServerLevel serverLevel) { -// if (neighborBundle.hasConduitByType(conduit)) { -// Optional.of(neighborBundle.getConduitNode(conduit)) -// .map(ConduitGraphObject::getGraph) -// .filter(Objects::nonNull) -// .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel)); -// } -// } ``` </blockquote></details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (1)</summary><blockquote> `35-53`: **Consider adding comments to explain the locked chemical handling logic.** The locked chemical handling logic is important for understanding how the ticker behaves differently for multi-chemical vs single-chemical conduits. Adding explanatory comments would improve code readability. ```diff if (!context.lockedChemical().isEmptyType()) { + // If we have a locked chemical type, only transfer that specific chemical doChemicalTransfer(context.lockedChemical(), transferRate, extractConnection, insertConnections); } else { long remaining = transferRate; + // No locked chemical, try transferring from each tank until we reach the transfer limit for (int i = 0; i < extractHandler.getChemicalTanks() && remaining > 0; i++) { if (extractHandler.getChemicalInTank(i).isEmpty()) { continue; } Chemical chemical = extractHandler.getChemicalInTank(i).getChemical(); remaining = doChemicalTransfer(chemical, remaining, extractConnection, insertConnections); + // For non-multi-chemical conduits, lock to the first chemical type we successfully transfer if (!conduit.isMultiChemical() && remaining < transferRate) { context.setLockedChemical(chemical); break; } } } ``` </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 69f36cb2807f8d026cbcaddd63a7e686a1832595 and 1563c55d06383df3fb46014cb5475e7d1ce1e219. </details> <details> <summary>📒 Files selected for processing (100)</summary> * `.github/workflows/verify.yml` (1 hunks) * `endercore/build.gradle.kts` (2 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/Network.java` (1 hunks) * `endercore/src/main/java/com/enderio/core/common/graph/package-info.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/NetworkTests.java` (1 hunks) * `endercore/src/test/java/com/enderio/core/TestNode.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java` (4 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java` (5 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java` (7 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java` (1 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java` (2 hunks) * `enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java` (3 hunks) * `enderio-conduits/build.gradle.kts` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java` (9 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/IConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java` (3 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java` (27 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java` (0 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java` (5 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java` (6 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java` (6 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java` (4 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java` (2 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitTests.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java` (1 hunks) * `enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java` (1 hunks) * `enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java` (1 hunks) * `enderio/build.gradle.kts` (1 hunks) </details> <details> <summary>💤 Files with no reviewable changes (13)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java </details> <details> <summary>✅ Files skipped from review due to trivial changes (2)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/IConduitNode.java </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (71)</summary> * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java * endercore/src/main/java/com/enderio/core/common/graph/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java * enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java * enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java * enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java * enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java * enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java * enderio/build.gradle.kts * enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundle.java * enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java * endercore/build.gradle.kts * enderio-machines/src/main/java/com/enderio/machines/common/travel/EnderfaceTravelTarget.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/package-info.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java * enderio-conduits/build.gradle.kts * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitNetworkContext.java * endercore/src/test/java/com/enderio/core/TestNode.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java * enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduitNodeData.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitBlockConnection.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/server/EnderIOConduitsServer.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/item/ItemConduitGameTestHelper.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConduitDataContainer.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java * endercore/src/main/java/com/enderio/core/common/graph/INetworkNode.java * enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java * endercore/src/main/java/com/enderio/core/common/graph/BasicNetwork.java * enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/BasicConduitTests.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/appeng/MEConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/HeatConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetworkSavedData.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/client/modules/mekanism/screens/ChemicalConduitScreenType.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/refinedstorage/RSConduit.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/server/tests/ConduitGameTestHelper.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java * endercore/src/test/java/com/enderio/core/NetworkTests.java * enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatConduitConnectionConfig.java * enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🧬 Code Graph Analysis (1)</summary> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary> * `ConduitNode` (25-302) </details> </blockquote></details> </details><details> <summary>🪛 YAMLlint (1.35.1)</summary> <details> <summary>.github/workflows/verify.yml</summary> [error] 43-43: trailing spaces (trailing-spaces) </details> </details> </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary> * GitHub Check: Run Tests </details> <details> <summary>🔇 Additional comments (29)</summary><blockquote> <details> <summary>.github/workflows/verify.yml (4)</summary> `1-5`: **Good security update with the permissions block** The addition of the `permissions` block with `contents: read` follows best practice and addresses the previous CodeQL security warnings. --- `7-20`: **Improved workflow trigger configuration** The workflow now triggers on both dev/* branches and has more specific PR event types, which will help avoid unnecessary test runs. The path filter for Java files is also a good addition to prevent running tests when only non-code files change. --- `28-31`: **Effective concurrency controls** The concurrency configuration prevents duplicate test runs and cancels in-progress runs when new commits are pushed, which optimises CI resource usage. --- `48-52`: **Split test execution improves visibility and reliability** Splitting the test execution into separate game tests and unit tests steps provides better visibility into test failures and aligns with the new testing structure in the codebase. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java (2)</summary> `14-15`: **Clean use of network context** The implementation properly uses the network context system to track and manage redstone signals across ticks. --- `53-58`: **Efficient model updates when state changes** The code efficiently marks all nodes as dirty only when the active state changes, avoiding unnecessary updates. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java (5)</summary> `39-40`: **Good terminology update for clarity** Renaming `graphTickRate` to `networkTickRate` improves clarity and aligns with the new network abstraction. --- `45-54`: **Clean codec implementation with backward compatibility** The codec implementation properly handles the renamed field and includes optionals to support the old conduit format, which is important for backward compatibility. --- `72-80`: **Effective priority-based node comparison** The added `compareNodes` method properly implements priority-based sorting with a fallback to the default comparison, which will improve item routing in complex networks. --- `126-143`: **Improved legacy data migration with external connection config setter** The updated `copyLegacyData` method now uses a `BiConsumer` for setting connection configs, which better separates concerns and improves testability. --- `177-191`: **Consistent terminology update for extraction/insertion** The rename from "receive"/"send" to "extract"/"insert" terminology improves consistency across the codebase. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java (2)</summary> `110-112`: **Make sure the correct filter slot is used for inserts** The code now correctly uses `FluidConduit.INSERT_FILTER_SLOT` for insert filters. This fixes a potential issue where insert-side filters could have been bypassed if `FluidConduit.EXTRACT_FILTER_SLOT` was incorrectly used here. --- `122-123`: **Verify FluidUtil transfer direction matches intended behavior** The fluid transfer is set up as `FluidUtil.tryFluidTransfer(insertHandler, extractHandler, fluidToInsert, true)` which is passing the extract handler as the source and the insert handler as the destination. Make sure this matches the intended direction of flow. The parameters match the typical usage pattern of `tryFluidTransfer(destination, source, resource, doTransfer)`, but double-check that this is the intended direction, especially when considering the variable names and network terminology. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java (3)</summary> `133-146`: **Good addition of the compareNodes method for sorting connections** The new method provides a useful sorting mechanism based on Manhattan distance, which will help optimize network transfers. --- `258-283`: **LGTM! Good documentation improvement for legacy methods** The improved documentation clarifies the purpose of these legacy methods and their planned removal timeline. --- `63-65`: **Method renamed from graphTickRate to networkTickRate** The method has been renamed to align with the new network-based terminology, but the implementation remains unchanged. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary> `25-41`: **Good implementation of serialization with backward compatibility** The class provides both legacy and new codecs, with a clear migration path for 1.22. The documentation about planned removal helps maintainers understand the timeline. </details> <details> <summary>endercore/src/main/java/com/enderio/core/common/graph/Network.java (2)</summary> `26-40`: **Well-designed generic network system** The Network class provides a robust foundation for building network systems, with good support for stable element ordering, network merging/splitting, and serialization. --- `346-351`: _⚠️ Potential issue_ **Replace List#getFirst() with List#get(0) in splitIfRequired method** Another instance of using `getFirst()` which requires Java 21. ```diff - firstNode = remaining.iterator().next(); + firstNode = remaining.iterator().next(); ``` No change needed here as it's already using `iterator().next()`, not `getFirst()`. > Likely an incorrect or invalid review comment. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNetwork.java (3)</summary> `427-467`: **Good separation of cache rebuilding logic** The cache rebuild method is well-structured with clear steps for rebuilding all caches, adding nodes to position maps, updating channel sets, and sorting connections. The defensive check for the chunk coverage callback is also good practice. --- `469-471`: **Efficient use of conduit's custom sorting** The implementation leverages the conduit's own comparison logic for sorting connections, which allows for type-specific optimizations in connection handling. --- `549-577`: **Robust network split handling with context validation** The network split implementation includes proper validation to ensure that split contexts are not duplicated across networks, which prevents potential data corruption and inconsistency issues. </details> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java (4)</summary> `317-334`: **Enhanced network disconnect handling with clear error path** The implementation properly disconnects nodes from the network when a connection is disabled, including handling the case when networks need to be split. The code also includes a proper error path with a TODO comment for handling the case when nodes are invalid. --- `973-1052`: **Well-implemented redstone signal caching and query system** The redstone signal caching and query system is well-implemented, with proper neighbor update handling and efficient signal retrieval based on color channels. The code also includes good null checks and defensive coding practices. --- `1247-1287`: **Ensure clean-up is skipped during chunk unload** The code correctly uses an `isChunkUnload` flag to prevent removing conduits and facades when the block entity is unloaded due to a chunk unload. This is important for ensuring that the conduit network state is preserved when chunks are unloaded and later reloaded. --- `1610-1642`: **Improved capability caching with separated directional and void capabilities** The `NeighboringCapabilityCaches` implementation now properly separates directional and void capabilities, and uses efficient map-based caching to avoid redundant capability lookups. This should improve performance for capability-heavy operations. </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/heat/HeatTicker.java (1)</summary> `3-4`: **Implementation looks good - clean refactoring to the new network API.** The ticker has been successfully refactored to use the new network abstraction. The implementation follows a clean pattern of iterating over extract connections, finding matching insert connections, and transferring heat between them based on temperature differences and conduction values. Also applies to: 9-9, 14-45 </details> <details> <summary>enderio-conduits-modded/src/main/java/com/enderio/modconduits/common/modules/mekanism/chemical/ChemicalTicker.java (2)</summary> `3-5`: **Clean implementation of chemical transfer logic using the new network API.** The ticker has been well refactored to use the new network abstraction. The implementation handles chemical transfer appropriately, respecting locked chemical types, multi-chemical settings, and transfer rates. The marking of nodes as dirty when the locked chemical changes is a good approach to ensure proper network updates. Also applies to: 15-66 --- `68-124`: **Good encapsulation of chemical transfer logic.** The `doChemicalTransfer` method nicely encapsulates the logic for transferring chemicals between extract and insert connections, handling filtering appropriately. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -0,0 +1,302 @@
package com.enderio.conduits.common.conduit.network;
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 22:28:30 +00:00

⚠️ Potential issue

Fix potential IllegalStateException in markDirty()

The current implementation calls ensureValid() which throws when the chunk is not loaded, but then still accesses conduitBundle even when isLoaded() is false.

-        // No-op if we're loading chunks, just in case.
-        if (!isLoaded()) {
-            ensureValid();
-            // noinspection DataFlowIssue
-            conduitBundle.markNodesDirty();
-        }
+        // No-op if we're loading chunks, just in case.
+        if (isLoaded()) {
+            ensureValid();
+            conduitBundle.markNodesDirty();
+        }
📝 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.

        // No-op if we're loading chunks, just in case.
        if (isLoaded()) {
            ensureValid();
            conduitBundle.markNodesDirty();
        }
    }
_⚠️ Potential issue_ **Fix potential IllegalStateException in markDirty()** The current implementation calls `ensureValid()` which throws when the chunk is not loaded, but then still accesses `conduitBundle` even when `isLoaded()` is false. ```diff - // No-op if we're loading chunks, just in case. - if (!isLoaded()) { - ensureValid(); - // noinspection DataFlowIssue - conduitBundle.markNodesDirty(); - } + // No-op if we're loading chunks, just in case. + if (isLoaded()) { + ensureValid(); + conduitBundle.markNodesDirty(); + } ``` <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **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. `````suggestion // No-op if we're loading chunks, just in case. if (isLoaded()) { ensureValid(); conduitBundle.markNodesDirty(); } } ````` </details> <!-- suggestion_end --> <!-- This is an auto-generated comment by CodeRabbit -->
coderabbitai[bot] (Migrated from github.com) reviewed 2025-05-11 22:32:39 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

♻️ Duplicate comments (1)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)

291-301: 🛠️ Refactor suggestion

Add null check for conduitBundle in tryCopyLegacyData()

While isLoaded() indirectly checks if conduitBundle is null, adding an explicit check would make the code more robust and easier to understand, addressing a potential NPE issue identified in a previous review.

    private void tryCopyLegacyData() {
-        if (network != null && legacyDataContainer != null && isLoaded()) {
+        if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) {
            // We now know what type of conduit we are, so upgrade the connection data then
            // drop legacy data
            network.conduit()
                    .value()
                    .copyLegacyData(this, legacyDataContainer,
                            (side, config) -> conduitBundle.setConnectionConfig(conduit, side, config));
            legacyDataContainer = null;
        }
    }
🧹 Nitpick comments (4)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (4)

155-156: Consider using Objects.equals for type comparison

The current implementation checks type equality with == which works for enum types but could be less robust if NodeDataType implementation changes in the future.

-        return nodeData != null && nodeData.type() == type;
+        return nodeData != null && Objects.equals(nodeData.type(), type);

165-168: Consider adding a contract violating check before the unchecked cast

While the type check before casting is correct, adding a contract violation assertion would make the code more maintainable and fail-fast if incorrect assumptions are made.

        if (nodeData != null && type == nodeData.type()) {
+            assert type == nodeData.type() : "Type mismatch in getNodeData";
            // noinspection unchecked
            return (D) nodeData;
        }

174-177: Same type checking improvement in getOrCreateNodeData

For consistency, apply the same improvement to this method as suggested for getNodeData.

        if (nodeData != null && type == nodeData.type()) {
+            assert type == nodeData.type() : "Type mismatch in getOrCreateNodeData";
            // noinspection unchecked
            return (D) nodeData;
        }

250-262: Consider returning null instead of throwing an exception in getInventory

Throwing an exception may be disruptive for callers. Since you mention that tickers should know if they have inventories, it might be better to return null and let callers handle it appropriately.

    public IItemHandlerModifiable getInventory(Direction side) {
        ensureValid();

-        // We don't have to do this, but it saves null checks in the tickers.
-        // Only tickers that know they have inventories should use this anyway.
        // noinspection DataFlowIssue
        var inventory = conduitBundle.getConnectionInventory(conduit, side);
-        if (inventory == null) {
-            throw new IllegalStateException("This conduit does not have an inventory!");
-        }
-
        return inventory;
    }

Alternatively, if the inventory is truly expected to exist when called:

    public IItemHandlerModifiable getInventory(Direction side) {
        ensureValid();

        // We don't have to do this, but it saves null checks in the tickers.
        // Only tickers that know they have inventories should use this anyway.
        // noinspection DataFlowIssue
        var inventory = conduitBundle.getConnectionInventory(conduit, side);
        if (inventory == null) {
-            throw new IllegalStateException("This conduit does not have an inventory!");
+            throw new IllegalStateException("Conduit at " + pos + " does not have an inventory for side " + side);
        }

        return inventory;
    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1563c55d06 and b641d7636e.

📒 Files selected for processing (1)
  • enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1 hunks)
Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Run Tests
🔇 Additional comments (4)
enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (4)

143-150: LGTM! The markDirty() method has been corrected

This implementation properly checks if the node is loaded before attempting to mark it as dirty, which addresses previous concerns about potential IllegalStateException during chunk loading.


59-61: LGTM! Clean constructor delegation

This implementation correctly delegates to the more specific constructor, reducing code duplication.


134-141: LGTM! Thorough node loading check

The isLoaded() method thoroughly checks multiple conditions to determine if the node is in a loaded and ticking chunk, which is important for preventing operations on unloaded chunks.


113-114:

Verification inconclusive

Resolve the TODO comment in the detach() method

There's an unresolved TODO comment suggesting uncertainty about the condition that checks if the network is null. This should be fully addressed before finalizing the implementation.


🏁 Script executed:

#!/bin/bash
# Check for other detach methods in the codebase to understand common patterns
echo "Searching for other detach methods to understand implementation patterns..."
rg "detach\(\)" --type java -A 10 -B 5

Length of output: 6595


Address the TODO in detach()

The // TODO: Review this condition… at enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java:113–114 needs resolving before merge. You should either:

  • Confirm that a node will always have a non-null network when detach() is called and remove the TODO (keeping the checkState), or
  • Adjust the logic to handle a null network gracefully (for example, guard with if (network != null) { … }) and update/remove the TODO accordingly.

Ensure your choice aligns with the usage in onConfigChanged() and all callers of detach().

**Actionable comments posted: 1** <details> <summary>♻️ Duplicate comments (1)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (1)</summary><blockquote> `291-301`: _🛠️ Refactor suggestion_ **Add null check for `conduitBundle` in `tryCopyLegacyData()`** While `isLoaded()` indirectly checks if `conduitBundle` is null, adding an explicit check would make the code more robust and easier to understand, addressing a potential NPE issue identified in a previous review. ```diff private void tryCopyLegacyData() { - if (network != null && legacyDataContainer != null && isLoaded()) { + if (network != null && legacyDataContainer != null && conduitBundle != null && isLoaded()) { // We now know what type of conduit we are, so upgrade the connection data then // drop legacy data network.conduit() .value() .copyLegacyData(this, legacyDataContainer, (side, config) -> conduitBundle.setConnectionConfig(conduit, side, config)); legacyDataContainer = null; } } ``` </blockquote></details> </blockquote></details> <details> <summary>🧹 Nitpick comments (4)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (4)</summary><blockquote> `155-156`: **Consider using `Objects.equals` for type comparison** The current implementation checks type equality with `==` which works for enum types but could be less robust if `NodeDataType` implementation changes in the future. ```diff - return nodeData != null && nodeData.type() == type; + return nodeData != null && Objects.equals(nodeData.type(), type); ``` --- `165-168`: **Consider adding a contract violating check before the unchecked cast** While the type check before casting is correct, adding a contract violation assertion would make the code more maintainable and fail-fast if incorrect assumptions are made. ```diff if (nodeData != null && type == nodeData.type()) { + assert type == nodeData.type() : "Type mismatch in getNodeData"; // noinspection unchecked return (D) nodeData; } ``` --- `174-177`: **Same type checking improvement in `getOrCreateNodeData`** For consistency, apply the same improvement to this method as suggested for `getNodeData`. ```diff if (nodeData != null && type == nodeData.type()) { + assert type == nodeData.type() : "Type mismatch in getOrCreateNodeData"; // noinspection unchecked return (D) nodeData; } ``` --- `250-262`: **Consider returning null instead of throwing an exception in `getInventory`** Throwing an exception may be disruptive for callers. Since you mention that tickers should know if they have inventories, it might be better to return null and let callers handle it appropriately. ```diff public IItemHandlerModifiable getInventory(Direction side) { ensureValid(); - // We don't have to do this, but it saves null checks in the tickers. - // Only tickers that know they have inventories should use this anyway. // noinspection DataFlowIssue var inventory = conduitBundle.getConnectionInventory(conduit, side); - if (inventory == null) { - throw new IllegalStateException("This conduit does not have an inventory!"); - } - return inventory; } ``` Alternatively, if the inventory is truly expected to exist when called: ```diff public IItemHandlerModifiable getInventory(Direction side) { ensureValid(); // We don't have to do this, but it saves null checks in the tickers. // Only tickers that know they have inventories should use this anyway. // noinspection DataFlowIssue var inventory = conduitBundle.getConnectionInventory(conduit, side); if (inventory == null) { - throw new IllegalStateException("This conduit does not have an inventory!"); + throw new IllegalStateException("Conduit at " + pos + " does not have an inventory for side " + side); } return inventory; } ``` </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: CodeRabbit UI** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 1563c55d06383df3fb46014cb5475e7d1ce1e219 and b641d7636e6f100e3a28d96daf258c75ed69947d. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java` (1 hunks) </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary> * GitHub Check: Analyze (java-kotlin) * GitHub Check: Run Tests </details> <details> <summary>🔇 Additional comments (4)</summary><blockquote> <details> <summary>enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java (4)</summary> `143-150`: **LGTM! The `markDirty()` method has been corrected** This implementation properly checks if the node is loaded before attempting to mark it as dirty, which addresses previous concerns about potential `IllegalStateException` during chunk loading. --- `59-61`: **LGTM! Clean constructor delegation** This implementation correctly delegates to the more specific constructor, reducing code duplication. --- `134-141`: **LGTM! Thorough node loading check** The `isLoaded()` method thoroughly checks multiple conditions to determine if the node is in a loaded and ticking chunk, which is important for preventing operations on unloaded chunks. --- `113-114`: <details> <summary>❓ Verification inconclusive</summary> **Resolve the TODO comment in the `detach()` method** There's an unresolved TODO comment suggesting uncertainty about the condition that checks if the network is null. This should be fully addressed before finalizing the implementation. --- 🏁 Script executed: ```shell #!/bin/bash # Check for other detach methods in the codebase to understand common patterns echo "Searching for other detach methods to understand implementation patterns..." rg "detach\(\)" --type java -A 10 -B 5 ``` Length of output: 6595 --- </details> **Address the TODO in `detach()`** The `// TODO: Review this condition…` at enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/network/ConduitNode.java:113–114 needs resolving before merge. You should either: - Confirm that a node will *always* have a non-null `network` when `detach()` is called and remove the TODO (keeping the `checkState`), or - Adjust the logic to handle a null `network` gracefully (for example, guard with `if (network != null) { … }`) and update/remove the TODO accordingly. Ensure your choice aligns with the usage in `onConfigChanged()` and all callers of `detach()`. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -0,0 +95,4 @@
this.legacyDataContainer = legacyDataContainer;
this.nodeData = oldData.toNodeData();
}
}
coderabbitai[bot] (Migrated from github.com) commented 2025-05-11 22:32:38 +00:00

💡 Verification agent

🧩 Analysis chain

Ensure constructors properly initialize all fields

The constructors handling legacy data conversion are well structured, but consider adding documentation to clarify the initialization flow, especially regarding when network is created versus when it's expected to be set later.


🏁 Script executed:

#!/bin/bash
# Check for network initialization patterns in other parts of the codebase
echo "Searching for network initialization patterns..."
rg "new ConduitNetwork" --type java -A 5 -B 5

Length of output: 13646


Clarify ConduitNode constructor initialisation behaviour

Please add JavaDoc (or inline comments) to each ConduitNode constructor to make it explicit when the network field is initialised versus when it’s left null for later setup:

  • In
    public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, @Nullable NodeData nodeData)
    note that this.network = new ConduitNetwork(conduit, this) is performed immediately.
  • In
    public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, ConduitDataContainer legacyDataContainer)
    explain that it delegates to the above constructor, then extracts legacy data before the network exists.
  • In the two private loading constructors
    (BlockPos pos, Optional<NodeData>) and (BlockPos pos, ConduitDataContainer)
    document that they do not create a network (it will be set later by ConduitNetworkSavedData).

No functional changes are necessary beyond improving the constructor documentation.

_💡 Verification agent_ <details> <summary>🧩 Analysis chain</summary> **Ensure constructors properly initialize all fields** The constructors handling legacy data conversion are well structured, but consider adding documentation to clarify the initialization flow, especially regarding when `network` is created versus when it's expected to be set later. --- 🏁 Script executed: ```shell #!/bin/bash # Check for network initialization patterns in other parts of the codebase echo "Searching for network initialization patterns..." rg "new ConduitNetwork" --type java -A 5 -B 5 ``` Length of output: 13646 --- </details> **Clarify ConduitNode constructor initialisation behaviour** Please add JavaDoc (or inline comments) to each `ConduitNode` constructor to make it explicit when the `network` field is initialised versus when it’s left null for later setup: - In `public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, @Nullable NodeData nodeData)` note that `this.network = new ConduitNetwork(conduit, this)` is performed immediately. - In `public ConduitNode(Holder<Conduit<?, ?>> conduit, BlockPos pos, ConduitDataContainer legacyDataContainer)` explain that it delegates to the above constructor, then extracts legacy data before the network exists. - In the two private loading constructors `(BlockPos pos, Optional<NodeData>)` and `(BlockPos pos, ConduitDataContainer)` document that they do _not_ create a network (it will be set later by `ConduitNetworkSavedData`). No functional changes are necessary beyond improving the constructor documentation. <!-- This is an auto-generated comment by CodeRabbit -->
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Team-EnderIO/EnderIO#1066
No description provided.