Skip to content

Upmerge expressions in the Logic Programmer to 26.1 - #1781

Merged
rubensworks merged 2 commits into
master-26-ltsfrom
feature/expression-cards-26
Nov 8, 2026
Merged

rubensworks merged 2 commits into
master-26-ltsfrom
feature/expression-cards-26

Conversation

@rubensworks

@rubensworks rubensworks commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Merges master-1.21-lts into master-26-lts. This brings the Logic Programmer expressions of #1766 to Minecraft 26.1. It is a single merge commit.

Fixes beyond the port

The merge commit also contains three fixes I found while testing the port in a 26.1 client:

  • Invisible operator symbol in the Logic Programmer. This was broken on 26.1 before this merge. RenderPattern drew the symbol with color 0, which 26.1 treats as fully transparent. Now it uses ARGB.opaque(0). It hid the symbol of every operator, so the expression panel was hard to test without it.
  • The book crashing on the Expressions tutorial. See Book crash.
  • @OnlyIn on the expression action packet. NeoForge 26.1 shows a loading warning screen on every start for mods that use it. No other packet on 26.1 uses it, so it is removed.

Port decisions

  • Client split:
    • The client-only classes of the expression panel move to core.logicprogrammer.client: ExpressionTextField, ExpressionTextController, ExpressionStartPanel, ExpressionValueEditor, ExpressionParameterChips, ParameterChipLayout, ExpressionBreadcrumb, ExpressionPanelButtons, ExpressionPanelRendering and ButtonIcon.
    • OperatorLPElement reaches them through its OperatorLPElementClient.
    • The typed text field belongs to the screen instead of the container. The container reaches it through ScreenCallbackHandler, which gains onExpressionNavigated() and resetExpressionText().
    • The expression and reference placeholder facades get ExpressionVariableFacadeClient and ReferencePlaceholderVariableFacadeClient for their card model overlays.
  • Serialization:
    • Expression trees are written with ValueOutput and ValueInput, so the operators and values inside them are stored the same way as everywhere else on 26.1 (under v). The card NBT therefore differs from 1.21.
    • Classic operator cards also changed format in the 26.1 port (from operatorName to operator), and neither kind of card is converted. A 1.21 expression card in an upgraded world reads as an invalid card, and with the book crash fix it no longer crashes anything.
  • Text form: the new ExpressionValueTags maps serialized values to the tags written in expression text and back.
    • It leaves out the v wrapper.
    • It writes items as their stack, with ExtendedCount for counts above 99.
    • So the text is the same as on 1.21: @Item{id: "minecraft:diamond"}, @Fluid{amount: 1000, id: "water"}.
  • Rendering and input:
    • Rendering uses GuiGraphicsExtractor, with colors that carry alpha.
    • Popups and the dragged parameter chip use nextStratum() instead of z offsets.
    • Tooltips go through setTooltipForNextFrame.
    • Input uses MouseButtonEvent, KeyEvent, CharacterEvent and ContainerInput.
  • Access transformer: the wider screen changes imageWidth, which is final in vanilla. CyclopsCore's access transformer has the same public-f line, but it only applies at runtime. This mod's own accesstransformer.cfg needs it to compile.
  • Config: expressionMaxLength uses @ConfigurablePropertyCommon with ModConfigLocation.SERVER, like the other 26.1 properties.
  • Optional-returning NBT getters:
    • The container compared "set" with the Optional that getString now returns, so setting typed text silently did nothing. It now uses getStringOr.
    • The game tests caught this, including the advancement test.
    • The other getters in the new code were ported the same way.

Book crash

Opening pages 7 and 8 of the Expressions tutorial crashed the client. There were two causes:

  • The icons of the three expression advancements are expression cards, and their JSON was still in the 1.21 format ("op": "integrateddynamics:arithmetic_addition", "v": 3).
  • Reading the operator threw a NoSuchElementException that nothing caught, so drawing the icon crashed the render thread.

The fix:

  • The icons are converted to the 26.1 format.
  • An unreadable operator now makes the card invalid, as an unreadable value already did.

Two tests cover it:

  • TestExpressionTrees.testSerializationOperatorWithoutName (unit test).
  • GameTestsAdvancements.testAdvancementExpressionIcons, which checks that the three icons are valid expression cards. I ran it against the old icons, and it fails on its assertion.

Test changes

  • The expression unit tests now use JUnit 5, like the rest of the 26.1 suite.
  • On 26.1, unknown items, invalid entity UUIDs and non-compound lists can no longer be read at all. Their literal errors are now bad_value instead of value_changed, and an empty entity prints as @Entity{}.
  • ValueTypeAddonMocked writes its value under v, like real value types on 26.1.

Testing

  • ./gradlew build: all 1057 unit tests pass.
  • ./gradlew runGameTestServer: all 1096 game tests pass, including the expression integration tests.
  • Visual test in a 26.1.2 dev client with clientdevbridge:
    • Panel and start panel:
      • Opening and closing the panel.
      • Typing in the start panel switches the root operator.
      • + Parameter in the start panel gives x => , and typing x + 1 opens Arithmetic Addition with an x chip.
    • Editing:
      • Selecting a slot filters the operator list and opens the value editor.
      • Escape cancels the selection, and Escape in the text field doesn't close the screen.
      • Nesting by double-click, and the breadcrumbs.
      • Operator search.
      • Parameter chips: add, drag into a slot, rename, and the type cycler.
    • Text:
      • The completion popup and the signature popup.
      • The syntax error tooltip with its arrow, and type errors in the footer.
      • Format.
      • Undo and redo.
      • Copy, checked on the clipboard.
    • Items and fluids:
      • A typed @Item keeps its count: Diamond (5).
      • A water bucket placed in a fluid slot becomes @Fluid{amount:1000,id:"minecraft:water"}.
      • @Fluid{amount: 250, id: "lava"} shows as Lava (250 mB).
    • Cards:
      • The write gate shows its "Invalid expression: ..." line.
      • The card tooltip.
      • Loading a card back into the Logic Programmer.
      • A label from the Labeller used in apply(#double, 3).
    • Network:
      • A Display Panel shows 14 for 2 * 5 + 4.
      • Another shows [20] for a classic Filter card that takes an expression operator.
      • All three expression advancements are granted.
    • Copying:
      • In a crafting grid, the card left in the grid gets a new id, and both cards keep the expression and the label.
      • A Wrench paste of a Display Panel holding an expression card gives a new id and the same value, and the panel is not rotated.
    • Book:
      • The tutorial (8 pages, with the advancements and rewards) and the manual's Expressions section (8 pages).

Found, not fixed here

  • Book advancement tooltips: descriptions don't wrap, so long ones run off the screen. CyclopsCore's AdvancementButton puts the description on one line. Existing advancements are affected too, such as smart_pressure_plate with 238 characters (ours is 135), so this PR doesn't cause it. It would need a fix in CyclopsCore.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y

This allows constant values and multiple nested operators
to be combined within a single variable card, without having
to combine multiple variable cards.

These expressions can either be created through the existing
Logic Programmer interface, or they can be written in a text-form
for easy exchange across worlds and players.
@coveralls

coveralls commented Nov 8, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 58.584% (+0.3%) from 58.308% — feature/expression-cards-26 into master-26-lts

Ports the expressions of the Logic Programmer (#1766) to Minecraft 26.1:
- Client-only classes of the expression panel move to core.logicprogrammer.client,
  the typed text is kept by the screen instead of the container, and the card
  facades get client classes for their model overlays.
- Expression trees are written with ValueOutput and ValueInput, so values and
  operators in them are serialized as everywhere else in 26.1.
- Values written with their type in the text leave out the "v" field of the
  serialized value, and items are written as their stack, so the text form is
  the same as in 1.21, such as @item{id: "minecraft:diamond"}.
- Rendering uses GuiGraphicsExtractor, with strata instead of depth for the
  popups and dragged parameters, and input uses the new event types.
- Tests use JUnit 5 and the game test API of 26.1. Unknown items, invalid entity
  ids and non-compound lists can no longer be read at all, so their error is
  bad_value instead of value_changed, and an empty entity is written as @entity{}.
- The icons of the expression advancements are converted to the 26.1 format, and
  an unreadable operator makes an expression card invalid instead of throwing,
  which crashed the book on the Expressions tutorial.
- The expression action packet no longer uses @onlyin, which NeoForge 26.1 warns
  about on every start.

Also fixes the invisible operator symbol in the Logic Programmer, which was drawn
with color 0, fully transparent since 26.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
@rubensworks
rubensworks force-pushed the feature/expression-cards-26 branch from 69ea9cb to d48baaa Compare November 8, 2026 18:11
@rubensworks
rubensworks merged commit 01d2516 into master-26-lts Nov 8, 2026
10 checks passed
@rubensworks
rubensworks deleted the feature/expression-cards-26 branch November 8, 2026 18:17
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