feat: Introduce an action system and command language #138

Open
Vectornaut wants to merge 26 commits from Vectornaut/dyna3:actions into main
Member

The branch to be merged introduces actions: trait objects representing things a user can do. It also introduces a command language for describing actions.

One purpose of the action system is to ensure consistency between different user affordances for doing the same thing. On the branch to be merged, as a proof of concept, the element and regulator creation buttons work by creating and executing actions, rather than by acting directly on the application state. A newly introduced keyboard shortcut system provides a different way to trigger each of these actions (addressing issue #132). You can also carry out actions by describing them in the command language and entering those descriptions into the newly introduced command prompt component (addressing issue #133).

The branch to be merged introduces [_actions_](wiki/Action-system): trait objects representing things a user can do. It also introduces a [_command language_](wiki/Command-language) for describing actions. One purpose of the action system is to ensure consistency between different user affordances for doing the same thing. On the branch to be merged, as a proof of concept, the element and regulator creation buttons work by creating and executing actions, rather than by acting directly on the application state. A newly introduced keyboard shortcut system provides a different way to trigger each of these actions (addressing issue #132). You can also carry out actions by describing them in the command language and entering those descriptions into the newly introduced _command prompt_ component (addressing issue #133).
In the process, revamp the element insertion system and turn elements'
identifiers and labels into signals.

This commit moves the responsibility for adding default regulators from
the assembly to the action system, but it the element creation buttons
still work directly on the assembly. This leads to a regression: the
element creation buttons leave out the default regulators.
Enable interface buttons to trigger actions rather than directly
manipulating the application state.

Reimplement the element and regulator creation buttons as action
buttons. This addresses the regression in the previous commit: the
element creation buttons now produce default regulators again.

This commit introduces a new regression: the button that creates an
inversive distance regulator no longer clears the selection. A reminder
to fix this is added as comment on the `ParametricAction` structure.
Unlike boxed actions, reference-counted actions can be cloned, so we can
use them as parameters for parametric actions.
This reverts `subject_array_from_vec` to its original form. We made it
more generic to work around difficulties with cloning boxed actions,
but we're not using boxed actions anymore.
Before this commit, the buttons and shortcuts for element creation used
parametric actions where the parameter was always `Ok(())`. With
constant actions, we can make those buttons and shortcuts simpler.
In the process, introduce a macro to streamline entity naming.
Since conditional actions are meant for repeated use, we rebrand them
as "actors".
We've gotten away with making `peek` return an owned value so far
because actions' values tend to be lightweight and clonable: often
reference-counted pointers. Conceptually, though, peeking should only
provide a reference, because we expect an action to hold onto its
precomputed value until it's executed.
Use this to implement the creation of half-curvature regulators.
In the process, unify regulator creation actions.
In particular, state the programmer-enforced assumptions that the action
system relies on.
Explain the proper use and spirit of actors
All checks were successful
/ test (pull_request) Successful in 3m49s
80d7cf733b
Move actors to their own module
Some checks failed
/ test (pull_request) Failing after 1m41s
dd05799e20
In the process, rename the module providing the `ActorButton` component
to match the component's current name.
Author
Member

@glen I've moved the Actor trait into its own module, as we discussed during the meeting, so the pull request is ready for review! I think this move is an all-around improvement: it reduces clutter in the action module, and it improves the organization of action and actor imports.

@glen I've moved the `Actor` trait into its own module, as we discussed during the meeting, so the pull request is ready for review! I think this move is an all-around improvement: it reduces clutter in the `action` module, and it improves the organization of action and actor imports.
Actually check in the new actor module
All checks were successful
/ test (pull_request) Successful in 3m42s
8c28db05d2
Clarify naming of descriptor declaration action
All checks were successful
/ test (pull_request) Successful in 3m55s
8e1d866d29
Vectornaut changed title from feat: Introduce an action system and command language to WIP: feat: Introduce an action system and command language 2026-03-26 04:58:03 +00:00
Author
Member

I just noticed that regulators created by descendant actions aren't actually being resolved correctly. The command

HalfCurvature Sphere @ 3

doesn't set the regulator of the newly created sphere, because we end up looking for the half-curvature regulator in a clone of the sphere creation action rather than in the original sphere creation action. The cloned sphere creation action creates a different sphere, with a different default half-curvature regulator.

I didn't notice this before because the sphere created by the cloned action has the same curvature has the sphere created by the original action. The command HalfCurvature Sphere therefore prints the expected measurement, even though it's not reading from the expected regulator.

I've put the pull request in draft mode while I work on a fix.

I just noticed that regulators created by descendant actions aren't actually being resolved correctly. The command ``` HalfCurvature Sphere @ 3 ``` doesn't set the regulator of the newly created sphere, because we end up looking for the half-curvature regulator in a clone of the sphere creation action rather than in the original sphere creation action. The cloned sphere creation action creates a different sphere, with a different default half-curvature regulator. I didn't notice this before because the sphere created by the cloned action has the same curvature has the sphere created by the original action. The command `HalfCurvature Sphere` therefore prints the expected measurement, even though it's not reading from the expected regulator. I've put the pull request in draft mode while I work on a fix.
Use the UnitAction type alias more
All checks were successful
/ test (pull_request) Successful in 3m45s
d316a8f2ed
Use the Self type more
All checks were successful
/ test (pull_request) Successful in 3m42s
50b96be285
Author
Member

I've drafted a fix for the bug discussed in this comment. The draft includes a strengthened descendant_regulator_resolution_test that's able to catch the bug, using the technique described in the comment.

The fix works by introducing an environment that keeps track of all the elements and regulators that can be referenced within a command. We carry the environment through the whole interpretation process, updating it whenever the thing we're interpreting creates a regulator.

The current implementation of the environment is a little rough around the edges. For example:

  • It provides parallel "recorded" versions of all the creation command constructors, which take a mutable reference to an environment and record all created regulators in it. This code is pretty repetitive.
  • It clones the elements_by_id index from the state into the environment for convenience, even though we could've saved a clone by passing the application state and using the elements_by_id index from there.

The implementation is decent enough, though, that I'd feel okay including it in this first pass. Shall I go ahead and push it to the branch to be merged, or would you like to discuss it at a check-in meeting first?

I've drafted a fix for the bug discussed in [this comment](pulls/138#issuecomment-3793). The draft includes a strengthened `descendant_regulator_resolution_test` that's able to catch the bug, using the technique described in the comment. The fix works by introducing an environment that keeps track of all the elements and regulators that can be referenced within a command. We carry the environment through the whole interpretation process, updating it whenever the thing we're interpreting creates a regulator. The current implementation of the environment is a little rough around the edges. For example: - It provides parallel "recorded" versions of all the creation command constructors, which take a mutable reference to an environment and record all created regulators in it. This code is pretty repetitive. - It clones the `elements_by_id` index from the state into the environment for convenience, even though we could've saved a clone by passing the application state and using the `elements_by_id` index from there. The implementation is decent enough, though, that I'd feel okay including it in this first pass. Shall I go ahead and push it to the branch to be merged, or would you like to discuss it at a check-in meeting first?
Owner

@Vectornaut wrote in #138 (comment):

Shall I go ahead and push it to the branch to be merged,

Yes, I am keen to move this along and in to the main branch. Thanks!

@Vectornaut wrote in https://code.studioinfinity.org/StudioInfinity/dyna3/pulls/138#issuecomment-3796: > Shall I go ahead and push it to the branch to be merged, Yes, I am keen to move this along and in to the main branch. Thanks!
This replaces the old system where regulator information was stored
throughout the action tree, and fixes a bug that the old system made
difficult to address. We modify `descendant_regulator_resolution_test`
to catch the bug.
Rename descriptor statements as declarations
All checks were successful
/ test (pull_request) Successful in 3m43s
4c9ddff53a
Vectornaut changed title from WIP: feat: Introduce an action system and command language to feat: Introduce an action system and command language 2026-03-31 07:40:24 +00:00
Author
Member

Okay, I've pushed the fix and reactivated the pull request! Commit 1405329 does a reorganization that I'm finding helpful for implementing the "details" or "attributes" syntax we'll use to avoid repetition in the engine block and elsewhere in our save files.

Okay, I've pushed the fix and reactivated the pull request! Commit 1405329 does a reorganization that I'm finding helpful for implementing the "details" or "attributes" syntax we'll use to avoid repetition in the engine block and elsewhere in our save files.
All checks were successful
/ test (pull_request) Successful in 3m43s
Required
Details
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u actions:Vectornaut-actions
git switch Vectornaut-actions
Sign in to join this conversation.
No description provided.