Skip to content

Allow part configurations to be copied and pasted with the Wrench - #1721

Merged
rubensworks merged 13 commits into
master-1.21-ltsfrom
claude/wrench-part-config-copy-paste-7u5bus
Sep 10, 2026
Merged

Allow part configurations to be copied and pasted with the Wrench#1721
rubensworks merged 13 commits into
master-1.21-ltsfrom
claude/wrench-part-config-copy-paste-7u5bus

Conversation

@rubensworks

@rubensworks rubensworks commented Sep 5, 2026

Copy link
Copy Markdown
Member

Closes #859

What

The Wrench gains three configuration modes. Shift+right-clicking a part copies into the Wrench, right-clicking another part pastes.

Mode Copies Paste target
Copy All settings + aspects same part type only
Copy Settings update interval, priority, channel, target side, target offset, and the offset variables any part
Copy Aspect the active variable, the aspect properties, and the aspect setting variables any part

Every variable inventory belongs to exactly one section, so the offset variables travel with the settings and the active and aspect setting variables travel with the aspects. Copy All is exactly Copy Settings + Copy Aspect.

Only non-default values are copied. A setting left alone on the source part is not stored, so pasting never resets it on the target. Copying a part that is still entirely default says so instead of storing an empty configuration.

Blank Variable Cards are checked before anything is consumed. Each pasted card costs one blank Variable Card (skipped in creative). If there aren't enough, the settings still apply, the cards are skipped, and the player is told how many more are needed. The Wrench tooltip lists the requirement up front, scoped to what the current mode will actually paste:

Wrench
Mode: Copy All
Copied from: Redstone Writer
Includes: settings, aspects
Blank Variable Cards needed: 1

The active mode is visible on the item — a small coloured badge per mode in the corner of the Wrench texture, driven by a wrench_mode item property and one model override per mode.

Cards already in the target part are given back to the player (dropped if the inventory is full), and pasted cards always get a new variable id.

Implementation

  • core/part/PartConfigSection: PART_SETTINGS and ASPECT, plus forInventoryName which decides which section a variable inventory belongs to.
  • core/part/PartConfigSnapshot: versioned, codec-backed, stored as a CompoundTag in a new wrench_part_config data component. Every part setting is an Optional, so absent means "was default, don't touch".
  • core/helper/PartConfigHelpers: snapshot/apply, non-default filtering, card copying, blank consumption, inventory resolution.
  • IPartType#snapshotConfig / #applyConfig and IPartState#getDefaultUpdateInterval, all default methods so addons keep compiling.
  • The paste result reports what was actually applied; warnings (offset out of range, skipped cards) go to the chat, since together with the result line they do not fit on the action bar.

Tests

  • Unit tests for the snapshot codec roundtrip, the non-default part settings, and the inventory-to-section mapping.
  • 17 game tests in GameTestsWrenchConfig: full copy/paste, part type mismatch, each mode in isolation, the offset variables belonging to Settings and the active variable to Aspect, subset across part types, card ids and ejection, insufficient blanks, creative, out-of-range offset, copying a default part, pasting not resetting untouched settings, sneak-clicking not removing the part, and mode cycling.

./gradlew build passes and all 959 game tests pass.

Verified in a dev client

Driven with clientdevbridge-cli. Confirmed in-game: the six mode badges are distinct; each mode's tooltip lists only what that mode pastes; a part with a changed update interval and an active variable copies as partSettings:{updateInterval:40} under Copy Settings, as variableCards:[$active] under Copy Aspect, and as both under Copy All; a default part reports nothing to copy; sneak-clicking in a config mode copies without removing the part; the full mode refuses a different part type while the subset modes accept it; and with no blank cards the settings still apply with the warning legible in chat.

Three bugs were found and fixed this way: the subset modes fell through to the part-removal branch, the combined result message ran off both edges of the screen, and the counts read as "1 blank Variable Cards".

🤖 Generated with Claude Code

https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF

Adds a new CONFIG mode to the Wrench, in which shift+right-clicking a part
copies its configuration into the Wrench, and right-clicking another part of
the same type pastes it.

The configuration consists of three sections: the general part settings
(update interval, priority, channel, target side override and target offset),
the statically configured aspect properties, and all variable cards (the
active variable inventory, the aspect setting variables and the offset
variables).

Pasted variable cards get a new variable id, and each of them consumes one
blank Variable Card from the player's inventory, unless they are in creative
mode. If there are not enough blank cards, the settings are still applied and
the player is told how many more cards are needed. Cards that were already
present in the target part are given back to the player.

The Settings and Aspect Settings screens also get copy and paste buttons,
which only copy and paste what those screens show. In the Aspect Settings
screen, settings are matched by property type instead of by aspect, so
settings can be copied between different aspects that share a property.

Closes #859

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
@coveralls

coveralls commented Sep 5, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 48.022% (+1.1%) from 46.886% — claude/wrench-part-config-copy-paste-7u5bus into master-1.21-lts

Replaces the copy and paste buttons in the Settings and Aspect Settings
screens with separate Wrench modes, so that copying a subset is a property
of the tool rather than of the screen you happen to have open.

The Wrench now has four configuration modes: Copy Configuration takes the
whole configuration and only pastes onto a part of the same type, while
Copy Part Settings, Copy Aspect Settings and Copy Variable Cards each take
one section and can be pasted onto any part.

Copying now only stores values that differ from the defaults of a freshly
placed part, so pasting never resets a setting that was left alone on the
copied part. Copying a part that is still fully default reports that there
is nothing to copy instead of storing an empty configuration.

The Wrench tooltip lists what was copied and how many blank Variable Cards
pasting it needs, and the active mode is shown on the Wrench item through a
small coloured badge per mode.

The paste result is now reported as what was actually applied, with the
warnings sent to the chat rather than appended to the action bar line,
where they did not fit.

Closes #859

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
The configuration sections now match the two things a player configures on a
part, and each Wrench mode maps onto one of them:

* Copy All takes both, and still only pastes onto a part of the same type
* Copy Settings takes the update interval, priority, channel, target side and
  target offset, together with the offset variables
* Copy Aspect takes the active variable, the aspect properties and the aspect
  setting variables

Every variable inventory belongs to exactly one section, so the offset
variables travel with the settings and the active and aspect setting
variables travel with the aspects, rather than all of them forming a section
of their own. This replaces the former Copy Aspect Settings and Copy Variable
Cards modes, which split the same data along a line that did not match any
screen a player uses.

Counts in the messages and the tooltip moved to the end of their sentence, so
that they read correctly for a single card as well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
The active variable was already covered, but the aspect setting variables
and the offset variables travel through the same copy, so assert their ids
change too and that the cards they replace are given back to the player.

Both tests were checked against a deliberately broken copy that reuses the
original id, so that they can actually fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
The mode explanation moved behind the same shift that already reveals the
item info, so the resting tooltip is only the state that matters: the mode,
where the configuration came from, what it includes and how many blank
Variable Cards pasting it needs. The explanations themselves are shorter,
as they used to wrap over three lines.

The mode badges on the Wrench are now small glyphs instead of plain coloured
squares, so that they say what the mode does rather than having to be
memorised by colour: two linked positions for the offset modes, echoing the
Part Offsets button, two overlapping sheets for Copy All, a grid of fields
for Copy Settings, echoing the Part Settings button, and a round aspect icon
for Copy Aspect.

Also renames the copied section in the tooltip from "settings" to
"part settings".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
The two offset badges were flat blobs that only differed by a single
pixel, and their outlines vanished against the dark inventory slot.
They become move arrows for the saved offset, and an arrow pointing at
a face for the saved offset and side.

The copy-all badge no longer uses a generic copy glyph, but combines
the badges of the two modes it covers: the settings grid next to the
aspect orb.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
They become a cube linked to the cube at the offset position, and the
same with sides instead of cubes for the mode that also saves a side.
Both are outlined in black, so that they stay readable on the light
backgrounds that the item is drawn on outside of inventory slots.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
The offset badge becomes a cube with a line running out to the offset
position, and the badge of the mode that also saves a side keeps that
same cube, with the side next to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF

@rubensworks rubensworks left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also do these things:

  • Even when the default of offset mode is shown, it shows in the tooltip the copied target and "Included". This is not necessary; hide it for those modes.
  • Can you manually test using clientdevbridge that this copying also works correctly for Integrated Tunnels interfaces, exporters, and importers?
  • Don't modify the changelog yourself.

Comment thread src/main/java/org/cyclops/integrateddynamics/core/helper/PartConfigHelpers.java Outdated
Comment thread src/main/java/org/cyclops/integrateddynamics/core/part/PartConfigApplyResult.java Outdated
* An immutable snapshot of the configuration of a part, which can be pasted onto another part.
*
* Only things that a player can configure are stored,
* so no part id, max offset, enabled state, error messages or active aspect.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Regarding max offset, could we make it so that offset enhancements can also be copied? And then taken care of in the same way as variables; the player needs the correct amount of them to paste. It also needs to be shown in the tooltip.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 74649c1. The maximum offset is now part of PartSettings (so it belongs to the Copy Settings section), stored only when it is above the default of zero.

Pasting raises the target part to that maximum offset by consuming Offset Enhancements from the player, in the same shape as the variable cards: if the player is short, nothing is consumed and PartConfigApplyResult reports how much value is still needed, as a chat warning. A target that already has a higher maximum offset is left alone, and creative players do not pay. Applying it happens before the target offset, so the copied offset now lands instead of failing on a fresh part.

One thing worth flagging: enhancements cannot be split, so if the player only has enhancements that do not add up exactly (needing 8, holding a 4 and a 5), they spend 9 and the part gets 9. consumeOffsetEnhancements takes the smallest ones first to keep that waste as low as possible.

The tooltip shows Offset Enhancement value needed: N next to the blank card requirement, for the sections the current mode pastes. Covered by five new game tests (consuming, insufficient, creative, keeping a higher existing offset, and the Aspect mode not touching it) plus unit tests for the new snapshot field.


Generated by Claude Code

NeoForge.EVENT_BUS.addListener(this::onPlayerLoggedOut);
}

public void onClientSetup(FMLClientSetupEvent event) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Please move this to the ItemWrenchConfigClient. (if this class does not exist yet, make it. This pattern should exist for other configs that require client-only things)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 74649c1. There was no item-level client config in this repo yet, so I added ItemWrenchClientConfig, following the BlockCableClientConfig pattern: it extends CyclopsCore's ItemClientConfig and registers its own mod event bus listener. ClientProxy no longer knows about the wrench.

Two notes on how it is wired:

  • I named it ItemWrenchClientConfig rather than ItemWrenchConfigClient, to match BlockCableClientConfig and CyclopsCore's own ItemClientConfig/BlockClientConfig. Say the word if you prefer the other order.
  • This required moving ItemWrenchConfig from ItemConfig to ItemConfigCommon, as only the latter has the getItemClientConfig hook (the same split BlockCableConfig already uses). The other item configs in this repo still use ItemConfig; I left them alone. Since getItemClientConfig is not cached by the base class, the config caches its instance so the listener is only registered once.

Verified in a dev client that the mode still shows on the item.


Generated by Claude Code

Copy the offset enhancements of a part as well, consuming enhancements
from the player just like the variable cards do, and show what a paste
needs in the Wrench tooltip.

Drop the unused Wrench lookup that was left over from the gui buttons,
as the Wrench that a player acts with always comes from the interaction
itself. Move the wrench mode item property into an item client config,
and replace the lombok accessors of the apply result by plain ones.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
A configuration stays in the Wrench when switching modes, so the source
part and the included sections were also shown in the modes that do
nothing with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF

Copy link
Copy Markdown
Member Author

Sorry, I acted on your inline comments first and missed the three points in the review body. All three are handled now.

Tooltip in non-config modes (7dad1fb): the copied source and Includes lines are now gated on the mode actually pasting a configuration, so Default, Save Offset and Save Offset and Side only show the mode line. The configuration itself is still kept in the Wrench when switching modes.

Changelog (7dad1fb): reverted, CHANGELOG-1.21.1.md is back to what is on master-1.21-lts.

Integrated Tunnels, manually tested with clientdevbridge: I ran a dev client with IntegratedTunnels 1.21.1-1.11.1 in the mods folder and copied between pairs of parts, configuring each source through its own gui:

Part Configured on source Result on target
Item Exporter 15 ticks/operation, priority 2, Export All Items channel 7 all three pasted
Item Importer 19 ticks/operation, priority 4 both pasted
Item Interface 17 ticks/operation, priority 3, Interface Channel 5 ticks and priority pasted, Interface Channel not

Copy All correctly refused to paste an Item Interface configuration onto an Item Exporter, and the tooltip named the source part properly (Copied from: Item Exporter).

One gap worth deciding on: the Item Interface's Interface Channel (channelInterface) is Integrated Tunnels' own part state, not one of the settings this snapshot knows about, so it stays at its default on the target. The same holds for any other addon-specific part state. PartConfigSnapshot has a fixed set of fields, so Integrated Tunnels cannot add to it even though snapshotConfig/applyConfig are overridable per part type.

If you want that covered, the smallest change on this side is an extra CompoundTag in the snapshot plus a pair of overridable no-op hooks on IPartType for a part type to write and read its own state, which Integrated Tunnels would then implement for the interface channel. Say the word and I will add it here; I left it out for now since it is an API addition and the Integrated Tunnels half lives in another repo.


Generated by Claude Code

Part types, and the part types that addons add in particular, hold state
that a configuration snapshot can not know about. They can now store it
per section, read it back when pasting, tell the player what a paste
took, and state in the tooltip what a paste is going to need.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF

Copy link
Copy Markdown
Member Author

Added the extension point in c599bc6, kept generic so any addon part type can use it.

PartConfigSnapshot gains a Map<PartConfigSection, CompoundTag> extraData, an opaque bucket per section that a part type fills and reads itself, and IPartType gains three default methods:

CompoundTag snapshotConfigExtra(ValueDeseralizationContext ctx, S state, PartConfigSection section);

void applyConfigExtra(ValueDeseralizationContext ctx, PartTarget target, S state, PartConfigSection section,
                      PartConfigSnapshot snapshot, Player player, PartConfigApplyResult result);

List<Component> getConfigExtraRequirements(PartConfigSnapshot snapshot, PartConfigSection section);

Design points:

  • Per section, so the subset Wrench modes stay honest. The Interface Channel belongs to PART_SETTINGS, so Copy Aspect neither copies nor pastes it, and Copy Settings does.
  • The whole snapshot goes to applyConfigExtra, so an implementation can check snapshot.sourcePartType() before reading a tag. That matters because the subset modes paste across part types, and a tag written by one part type should not be read blindly by another.
  • Only called when there is something to do: applyConfigExtra is skipped for sections whose tag is empty, and an empty tag from snapshotConfigExtra is not stored at all, so it never inflates a snapshot or makes an otherwise default part look configured.
  • hasSection and isEmpty account for it, so a part whose only non-default state is addon state still copies, and "nothing to copy" stays accurate.
  • Reporting is available too: PartConfigApplyResult.addApplied(Component) joins the "Pasted: ..." line and addWarning(Component) goes to the chat warnings, so an implementation that has to consume something from the player can say so, in its own words. getConfigExtraRequirements puts the matching line in the Wrench tooltip, resolved on the part type the snapshot came from.

For the Item Interface, Integrated Tunnels would override the first two on its interface part types and move channelInterface through them.

Two game tests cover the plumbing, using a part type proxy that stores and reads back a value: one asserts the value survives a copy and paste and that the part type's own message reaches the player, the other asserts Copy Aspect neither stores nor pastes a PART_SETTINGS tag. I checked they bite by removing the applyConfigExtra call, which fails the first one with Expected The part type did not read its own state back to be 42, but was -1. Unit tests cover the codec round trip and the section accounting.


Generated by Claude Code

Widening it to public broke every part state outside of this mod that
overrides it, such as the ones in Integrated Tunnels, so the public
accessor that snapshots need is a separate method that delegates to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBAgcFp6jWcYRWz9N8h7AF
@rubensworks
rubensworks merged commit 16c31ef into master-1.21-lts Sep 10, 2026
7 checks passed
@rubensworks
rubensworks deleted the claude/wrench-part-config-copy-paste-7u5bus branch September 10, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants