Skip to content

Add a per-block request rate to the ME Level Maintainer - #470

Open
mak8427 wants to merge 5 commits into
GTNewHorizons:masterfrom
mak8427:level-maintainer-refresh-rate
Open

mak8427 wants to merge 5 commits into
GTNewHorizons:masterfrom
mak8427:level-maintainer-refresh-rate

Conversation

@mak8427

@mak8427 mak8427 commented Sep 18, 2026

Copy link
Copy Markdown

Summary

with this pr let you set the interval at which the maintainer re-checks its request list. It has a per maintainer setting: editable in the GUI in seconds (1-60s, 0 goes back to the server default), saved with the tile and carried by the memory card, mirroring the lite mode override next to it.
image
image

The tick manager only reads a tickable's request when it registers or is told to, so the new interval is pushed through ITickManager.updateTickRate to apply immediately rather than at the next chunk or grid rebuild.
Waila now shows the effective rate and the time left until the next check.
image

Checklist

  • I have tested this PR in DevEnv
  • I have tested this PR in Fullpack
  • This PR is in compliance with the GTNH AI Policy
  • This PR requires another PR in order to merge

@mak8427

mak8427 commented Sep 18, 2026

Copy link
Copy Markdown
Author

I hope this PR will also help players manage performance, as level maintainers can be noticeably performance-heavy.

I was also thinking about adding an option in the config to set the minimum and maximum refresh rates, so server owners can increase the minimum level if needed to make them more manageable performance-wise.

@mak8427

mak8427 commented Sep 18, 2026

Copy link
Copy Markdown
Author

With this PR, LevelMaintainer.minTick from the config is no longer being used, and I’d like a second opinion on how we should handle it.

The old global minTick / maxTick range of 0.25s–6s has temporarily been replaced with a per-block interval, hard-bounded to 1s–24h.

The main issue is if i load the old default minimum was too aggressive. minTick defaulted to 5 ticks, or 0.25 seconds. If we simply start loading that setting again when this reaches the main branch, existing servers may still have that old value in their config. That could cause LevelMaintainers to run every 0.25 seconds and create significant performance problems.

If we want to keep a configurable server-side minimum, the existing minTick key also cannot reliably get a new default. Forge keeps values that are already present in an existing config file, so changing the default would not affect existing servers.

One option would be to introduce a new key, for example:

minIntervalTicks = 120

Then any per-block interval below that value would be clamped to the configured minimum.

Another option would be to add a temporary clamp when loading the config. If an existing server still has one of the old, overly aggressive values, we could clamp it to the new minimum instead of allowing the old setting to carry over unchanged.

For now, I’m leaning toward adding the clamp and setting a safer new default, probably around 120 ticks (6 seconds). That should prevent existing configs from accidentally causing LevelMaintainers to run far too frequently while still leaving room to make the interval configurable.

Thoughts?

@mak8427

mak8427 commented Sep 18, 2026

Copy link
Copy Markdown
Author

Okay, I think I had a better idea.

I’ll leave the existing minTick behavior for the active state unchanged and add a separate config option for the idle refresh interval, with something like 6 seconds as the default.

That way, server owners can adjust how often an idle LevelMaintainer checks for updates. Once it becomes active, it will behave exactly as it does now and use the existing tick settings.

This should solve the performance issue without changing the current active-state behavior.

It would still require an additional config entry for the minimum idle interval, so server owners can adjust it depending on their performance requirements.

@mak8427
mak8427 force-pushed the level-maintainer-refresh-rate branch from 22b65bb to c3fd2d7 Compare September 18, 2026 17:36
The requester now re-checks its request list on one interval, in seconds, set per
block: how long it waits between checks, editable in the GUI, saved with the
tile and carried by the memory card next to the lite mode override. 0 follows
the server default, LevelMaintainer.maxTick (120 ticks = 6 seconds), which is
what the requester effectively ran at before.

Both ends of the registered TickingRequest are that interval, so the tick
manager cannot park on the midpoint of a range and the value the GUI shows is
the value the grid uses. updateTickRate is called on change so it applies
immediately instead of at the next grid or chunk rebuild, and the deadline
handed to Waila is re-based at the same time so the countdown cannot point at a
time that has already passed.

minRefreshTicks/maxRefreshTicks bound what a player may set (default 1 second to
24 hours, hard bounded to the same range) and every value - player set, config
default, loaded from NBT or applied from a card - goes through one clamp that
also snaps it to whole seconds, so the seconds shown in the GUI and in Waila are
exactly the interval used. The old minTick key is no longer read and is gone.

Waila shows the interval in effect and the time left until the next check, the
countdown only once the tile has actually been ticked.

In the GUI the field sits under the request rows on the field's own background
with a white value: an untouched Enter no longer pins the default in as an
override, an edit survives the one-per-second refresh while it is being typed
and is discarded when focus leaves, and the field is re-seeded from the block
after a resize.

Old worlds load unchanged, a card from a build without the key resets the rate to
the config default, and client and server must ship together: the packet gained a
field and the action enum gained a member.
@mak8427
mak8427 force-pushed the level-maintainer-refresh-rate branch from c3fd2d7 to 1142e9e Compare September 18, 2026 17:38
@freemanovec

Copy link
Copy Markdown

As someone who's been troubled with Level Maintainer performance impact often enough already, I like this idea.

My take on the config: I'd keep using the existing minTick as the floor for the GUI field rather than adding minRefreshTicks, and give the default check rate its own new key instead of reusing maxTick, since changing what maxTick means could confuse server owners reading their configs.

I would like this to go a bit further, and add an exponential (configurable) backoff per-row, when a craft fails. Currently, when a craft request fails it gets retried (a full new recipe calculation) on the next LM wake, indefinitely. This backoff would lift a ton of load IMHO when there's a borked craft lying around, and it would mitigate the issue where somebody might lower the wake rate and keep failing crafts in order to degrade server performance.
Let me know if that's something I could implement on top of this PR ^^

@mak8427

mak8427 commented Sep 29, 2026

Copy link
Copy Markdown
Author

That makes sense but I wonder if it would be retro compatible with previous settings form 2.8.x

@mak8427

mak8427 commented Sep 29, 2026

Copy link
Copy Markdown
Author

As someone who's been troubled with Level Maintainer performance impact often enough already, I like this idea.

My take on the config: I'd keep using the existing minTick as the floor for the GUI field rather than adding minRefreshTicks, and give the default check rate its own new key instead of reusing maxTick, since changing what maxTick means could confuse server owners reading their configs.

I would like this to go a bit further, and add an exponential (configurable) backoff per-row, when a craft fails. Currently, when a craft request fails it gets retried (a full new recipe calculation) on the next LM wake, indefinitely. This backoff would lift a ton of load IMHO when there's a borked craft lying around, and it would mitigate the issue where somebody might lower the wake rate and keep failing crafts in order to degrade server performance.

Let me know if that's something I could implement on top of this PR ^^

Yes sure go ahead do you have some GUI design in mind too?

@freemanovec

Copy link
Copy Markdown

Yes sure go ahead do you have some GUI design in mind too?

No, but I've had a patch live on my server since the performance issues that basically does what I described - with the backoff on failure. I'll open a separate MR for that, as these two shouldn't overlap (I think)

That makes sense but I wonder if it would be retro compatible with previous settings form 2.8.x

Re-reading the diff, I'm not quite sure anymore how beneficial letting the player override the value would be. Isn't the same (or better?) behavior achievable with just increasing maxTick? AE2 should then automatically delay the LM ticks when it's not performing any operation. The WAILA addition would be nice tho

@mak8427

mak8427 commented Sep 29, 2026

Copy link
Copy Markdown
Author

Yes sure go ahead do you have some GUI design in mind too?

No, but I've had a patch live on my server since the performance issues that basically does what I described - with the backoff on failure. I'll open a separate MR for that, as these two shouldn't overlap (I think)

Yeah makes sense, better to keep the failure backoff separate.

That makes sense but I wonder if it would be retro compatible with previous settings form 2.8.x

Re-reading the diff, I'm not quite sure anymore how beneficial letting the player override the value would be. Isn't the same (or better?) behavior achievable with just increasing maxTick? AE2 should then automatically delay the LM ticks when it's not performing any operation. The WAILA addition would be nice tho

Yeah I checked the AE2 ticking logic again and you’re right about the min/max behavior.

What I would still keep from this PR is the per-LM player choice, but use that value only as the idle / maxTick value.

So instead of:

new TickingRequest(refresh, refresh, false, true)

it would be more like:

new TickingRequest(Config.levelMaintainerMinTicks, getRefreshTicks(), false, true)

This way if the player sets one LM to 60s, it only means that LM can go up to 60s while idle. As soon as alertDevice() is triggered AE2 can bring it back to minTick, so active work stays responsive.

So basically keep the player configurable idle cycle + WAILA, but don’t replace the adaptive min/max behavior.

@Elios5014

Copy link
Copy Markdown

hey, I designed something like this :

The idea is the following, being able to set the minium (check rate when you have materials and cpu to craft. and set maximum, which will define the waiting time before trying to craft again.

It is configurable for each line.

If you are interested I can show you what I did. It also fix the round robin between lines, as sometimes it was getting stuck on a given line instead of crafting other lines.

Capture d’écran 2026-10-01 à 23 29 54 Capture d’écran 2026-10-01 à 23 30 02 Capture d’écran 2026-10-01 à 23 32 54

@mak8427

mak8427 commented Oct 2, 2026 •

Copy link
Copy Markdown
Author

hey, I designed something like this :

The idea is the following, being able to set the minium (check rate when you have materials and cpu to craft. and set maximum, which will define the waiting time before trying to craft again.

It is configurable for each line.

If you are interested I can show you what I did. It also fix the round robin between lines, as sometimes it was getting stuck on a given line instead of crafting other lines.

Capture d’écran 2026-10-01 à 23 29 54 Capture d’écran 2026-10-01 à 23 30 02 Capture d’écran 2026-10-01 à 23 32 54

I like the the idea but maybe I will not put the UI element in that position also the meaning of those values are kinda confusing for the players displayed like that

@Elios5014

Copy link
Copy Markdown

hey, I designed something like this :
The idea is the following, being able to set the minium (check rate when you have materials and cpu to craft. and set maximum, which will define the waiting time before trying to craft again.
It is configurable for each line.
If you are interested I can show you what I did. It also fix the round robin between lines, as sometimes it was getting stuck on a given line instead of crafting other lines.
Capture d’écran 2026-10-01 à 23 29 54
Capture d’écran 2026-10-01 à 23 30 02
Capture d’écran 2026-10-01 à 23 32 54

I like the the idea but maybe I will not put the UI element in that position also the meaning of those values are kinda confusing for the players displayed like that

Here is a proposition :
image

The tooltip explain each values. I also changed some stuffs in the logic to avoid lag, I am currently testing it

@Kogepan229 Kogepan229 added the Ongoing Freeze - Do NOT Merge For PRs submitted close to a release that must wait for the next dev cycle to merge. label Oct 4, 2026
@mak8427

mak8427 commented Oct 6, 2026

Copy link
Copy Markdown
Author

I think we need separate limits for active and idle operation.

The existing minTick defaults to 5 ticks. Using that as the GUI lower bound would still permit very frequent idle checks.

Using maxTick as the upper bound would cause another problem. Its default is 120 ticks, which would prevent players from selecting intervals above 6 seconds.

My proposal is:

  • Keep minTick as the active minimum.
  • Keep maxTick as the default idle maximum for blocks without an override.
  • Add minIdleTicks as the minimum permitted idle setting, with a default of 120 ticks.
  • Let each block override its idle maximum above that minimum. A value of 0 restores the server default.

For example, a player could select 60 seconds for one LM. A 1 second input would become 6 seconds with the default server limit. AE2 would retain its adaptive behavior between minTick and the block’s effective idle maximum. The setting would remain per block.

This gives server owners control over frequent idle checks and lets players select longer intervals.
Would this address your concern about the config behavior?

@Kogepan229 @freemanovec

@lc-1337

lc-1337 commented Oct 9, 2026

Copy link
Copy Markdown

make sure that tick rate apply correct, because getTickingRequest() happens... uh, sometimes

@mak8427

mak8427 commented Oct 10, 2026

Copy link
Copy Markdown
Author

make sure that tick rate apply correct, because getTickingRequest() happens... uh, sometimes

Yeah, that’s handled. Both GUI changes and memory-card uploads call updateTickRate(node) so AE2 reads the new limits immediately. We then call alertDevice(node) for a prompt check, so the change does not depend on a chunk reload or grid rebuild.

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

Labels

Ongoing Freeze - Do NOT Merge For PRs submitted close to a release that must wait for the next dev cycle to merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants