Skip to content

Environment Checks#1093

Open
Bloo-dev wants to merge 17 commits intoGamemode4Dev:update-26.1from
Bloo-dev:environment-checks
Open

Environment Checks#1093
Bloo-dev wants to merge 17 commits intoGamemode4Dev:update-26.1from
Bloo-dev:environment-checks

Conversation

@Bloo-dev
Copy link
Member

@Bloo-dev Bloo-dev commented Jan 28, 2025

This PR adresses #902.

Environment checks (echecks) are specified in the beet.yaml of a module, as a list under versioning. E.g.:

meta:
  gm4:
    versioning:
      required:
        gm4_metallurgy: 1.5.0
        lib_player_death: 1.3.0
      echecks: [gm4:echeck/score_on_non_player_entity]
      schedule_loops: [main]

Upon load, each module requests its environment checks to run. In the post_load stage, base (gm4) then calls the function tag #gm4:evaluate_echecks. This function tag is used to ensure versioning works for echecks defined in base or libraries.

New echecks may be defined by any module or library. To do so, simply write your echeck in .mcfunction files, keeping in mind, that you must ensure proper caller context (as and at) yourself. All echeck results are cached during a load, so each echeck will only run once. You do not need to implement caching yourself.
Once the .mcfunction code for your echecks is ready, add the #gm4:evaluate_echecks function tag to your namespace and point it to your echeck. Done.
You may now include your echeck in a module, a library, or base by adding it to the corresponding beet.yaml.

This PR includes the gm4:echeck/non_player_entity_has_score and gm4:echeck/non_player_entity_on_team checks. Other checks will have to be written in the future.

@Bloo-dev Bloo-dev added the tool This is related to our build pipeline label Jan 28, 2025
@Bloo-dev Bloo-dev marked this pull request as draft January 28, 2025 20:22
@Bloo-dev
Copy link
Member Author

Bloo-dev commented Feb 9, 2025

I have cleaned up the base python code to the point where I'm satisfied with functionality.

Here's what's left to do:

  • Decide: Should environment checks reside in function/environment_check/ during development, or should we place them in environment_check/ (same level as function/) and move all those files over at build time (may cause issues when someone names a folder in function/ the same as the folder we move the checks to.... Hence I don't really see a point in doing this)
  • Write the remaining checks. We can always add more checks later, I'd rather have this merged with some checks and add more later then have it sit around forever.

@Bloo-dev Bloo-dev marked this pull request as ready for review February 9, 2025 15:38
@Bloo-dev Bloo-dev marked this pull request as draft May 26, 2025 20:08
@Bloo-dev
Copy link
Member Author

After some more thinking we have come to the following solution, which splits load.mcfunction into 3 functions:

  • pre_load.mcfunction: Called by lantern load instead of load, starts environment checks (these may take longer than 1 tick). Then calls await_environment_checks.mcfunction.
  • await_environment_check_results.mcfunction: Checks for the results of the environment checks started by this module. Defers (self schedule 1t) further execution until all checks have returned a result. When this has happend, load.mcfunction is called.
  • load.mcfunction: Prints out error messages depending on the result of the environment checks. Then does all the version compat checking. Runs init if nothing fails

@runcows
Copy link
Contributor

runcows commented Aug 21, 2025

I believe this method would also require environment check clearing to move from post-load (where it is currently). I'm not entirely sure where it would go, but that's not exactly a huge issue

@Bloo-dev Bloo-dev changed the base branch from master to update-26.1 February 22, 2026 13:30
@Bloo-dev
Copy link
Member Author

Bloo-dev commented Feb 22, 2026

After some discussion we have decided to not make environment checks block the initialization of modules:

  • we can not completely prevent modules from becoming semi-active anyways (e.g. recipes will be available)
  • delaying load was deemed to be too risky and difficult

Instead, the new approach will print warnings to chat once all checks have completed. Below is an outline of how this new approach works:

Defining an EnvCheck

  1. Create .mcfunction files which run your check
  2. When the check finishes, add a result object to your EnvCheck's scoreboard entry in gm4:log.environment_check. If the check succeeded result:{passed:1, probable_cause:""}, if it failed: result:{passed:0, probable_cause: "<some text, e.g. Misode hates your code!>"} (passed:-1 signals that the test is still pending)
  3. Create #gm4:evaluate_environment_checks.json inside your datapack and populate it with the entry poitns (.mcfunction) of your EnvCheck

Using and EnvCheck

  1. Add environment_checks: [< list of namespace checks, e.g. gm4:score_on_non_player_entity>] under the versioning section in your beet.yaml
  2. Done

All environment check results are cached per-reload, you do not need to implement caching in your environment checks.

@Bloo-dev Bloo-dev marked this pull request as ready for review February 25, 2026 18:48
@Bloo-dev
Copy link
Member Author

This is now ready for review. We may want to work on the wording of a failed echeck readout in chat still.

Copy link
Contributor

Choose a reason for hiding this comment

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

We'll want to update the tag lists in this file to reflect changes in #1264

Copy link
Member

@misode misode left a comment

Choose a reason for hiding this comment

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

I'm not sure that I'm a big fan of using the term echeck in the code and file paths. I don't think environment_check is that long.

Right now the only example checks are pretty simple.

  • What about for example a check that checks the correct minecraft version (or pack format)?
  • Will we need separate environment check identifiers for each new version?
  • Do we have to keep all the old version checks in the base?
  • What happens when there are multiple GM4 base versions present, some of them having environment checks that the others don't have, would everything still work?

My worry is that with this much abstraction around the environment checks it will be difficult to add some of the more complex checks that require multiple ticks or rely on overlays/json resources.

@@ -0,0 +1,7 @@
# Tests if non-player entities can hold scores. We think spigot might have done this at some point, but are not sure.
Copy link
Member

Choose a reason for hiding this comment

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

After thinking about this again, we should probably just remove this check. I'll get annoying adding this to every module when it doesn't even matter in the current versions.

@@ -1,4 +1,6 @@
data merge storage gm4:log {queue:[],versions:[]}
data modify storage gm4:log echecks set value []
Copy link
Member

Choose a reason for hiding this comment

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

This command can be combined into the above command

execute unless data storage gm4:log echecks[{result:{passed:0}}] unless data storage gm4:log echecks[{result:{passed:-1}}] run return 0

# copy results into queue, KEEP VERSION IN echecks, SO WE CAN INSPECT IT FOR DEBUGGING
data modify storage gm4:log queue set from storage gm4:log echecks
Copy link
Member

Choose a reason for hiding this comment

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

This feels really weird and hacky. The NBT objects in echecks don't have the same shape as what is expected by queue. This could cause subtle bugs in the future.

This command also completely overwrites the queue field. That feels wrong and might cause problems. For example GM4 base log printing is already delayed to wait for at least one player. Environment checks could finish in between and cause the normal logs to be overwritten.

I would suggest adding a separate recursive function that iterates over the echecks list, or alternatively appending to the existing queue and making sure that it runs a second time if necessary.


# add environment check requests
echeck_requests: List[str] = []
for namespaced_echeck in opts.echecks:
Copy link
Member

Choose a reason for hiding this comment

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

Now that environment checks no longer block the module from loading, we should consider whether we really want these checks to be triggered from load, and not from init.

For example: shamir X has a very specific environment check Y. But shamir X requires that metallurgy is present. When it is not, shamir X will run load but not init because it is disabled. I don't think we want that very specific environment check to complain in that case because the module isn't active anyways.

This could also happen when two versions of a library are present and the one that is disabled has an environment check that the one that is active does not have.

Copy link
Member Author

Choose a reason for hiding this comment

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

That seems reasonable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tool This is related to our build pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants