Skip to content

doc: commit sprunk's resource excess guide#2600

Open
NortySpock wants to merge 2 commits into
beyond-all-reason:masterfrom
NortySpock:update-docs-with-excess-resource-sharing-example-from-sprunk
Open

doc: commit sprunk's resource excess guide#2600
NortySpock wants to merge 2 commits into
beyond-all-reason:masterfrom
NortySpock:update-docs-with-excess-resource-sharing-example-from-sprunk

Conversation

@NortySpock

@NortySpock NortySpock commented Oct 23, 2025

Copy link
Copy Markdown

work done

  • Cleaned up @sprunk 's description of resource excess from Beyond All Reason discord and put it in the docs.
  • Got the documentation to build

screenshot

image

AI / LLM usage statement:

I did not use an LLM in the creation of this PR.

@NortySpock NortySpock marked this pull request as ready for review October 23, 2025 03:03
@NortySpock NortySpock changed the title commit sprunk's resource excess guide doc: commit sprunk's resource excess guide Oct 23, 2025
Comment thread doc/site/content/docs/guides/resource-excess/_index.md Outdated
Comment on lines +36 to +49
### no excess gadget, sharing modrule = true (current state of affairs)

- you will receive the total excess 450 from frames 2 and 4, and have virtually 550/100
- then, anything above your red slider value of 75 is distributed to allies and you virtually have 75/100
- in case your allies are all above red slider, you may receive some metal back. this can put you at any value above 75
- the stored value is then capped at your max storage of 100.

### excess gadget, sharing modrule = true
- at the end of frame 2 you receive an event for 200 excess (a single event accumulated from that frame despite multiple individual chunks, and despite consumption attempts between them) and can consume it, or not
- at the end of frame 4 you receive an event for 250 excess and can consume it, or not
- you will receive either 0, 200, 250, or 450 excess depending on which of the two excess the gadget did not intercept, and virtually have either 100, 300, 350 or 550
- then anything above your red slider value of 75 is distributed to allies and you virtually have 75/100
- in case your allies are all above red slider, you may receive some metal back. this can put you at any value above 75
the stored value is then capped at your max storage of 100.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

terrible formatting and the explanation requires a lot of prior knowledge, who wrote this shit?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You wrote it, I only formatted it. I thought it was readable for anyone who had thought through excess handling on their own. How can we improve this?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I was being facetious, it would be good if somebody else than me reviewed this part.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I will try documenting + providing an exemple gadget.
Since i've already been building it

Comment thread doc/site/content/docs/guides/resource-excess/_index.md
@NortySpock NortySpock marked this pull request as draft October 23, 2025 11:44
@NortySpock NortySpock marked this pull request as ready for review October 28, 2025 00:27
@NortySpock

Copy link
Copy Markdown
Author

Updated per Sprunk's comments, ready for someone else to review, since Sprunk approved. Or if acceptable, this can be merged.

@NortySpock NortySpock force-pushed the update-docs-with-excess-resource-sharing-example-from-sprunk branch from f442b40 to aedb14d Compare October 28, 2025 00:32
@sprunk sprunk requested a review from badosu November 9, 2025 02:15
@badosu

badosu commented Dec 2, 2025

Copy link
Copy Markdown
Collaborator

Whats "excess gadget", whats "sharing modrules"?

Looks good but would be better to be more objective.

@Fx-Doo

Fx-Doo commented Dec 2, 2025

Copy link
Copy Markdown

I think there needs to be a clarification about "excess" being different from "overflow".
As in excess is just a part of what is overflowed each slow update, the "overflow" being made of the excess and whatever part is between %share and fullstorage.

I think this is important because TeamResourceExcess (and more recent ResourceExcess) 1) don't expose the sharing part and 2) aren't called when a team is over their share cursor but under their maxStorage. This is obvious but also a cause to not being able to just leak on ResourceExcess callin but rather just accumulate excess and process from gameframe(post) in case of taking the whole overflow process from game side.

As to how it works, i don't think using number really helps understand in this case.

I would just say, engine natively stacks frame excess over 30 frames to process them all at once during GameFramePost, for each team.
(Team)ResourceExcess is called every frame of excess, before it is added to the internal counter. Returning true will remove it from the accumulated excess during the last 30 frames, returning false will add it.

During slowUpdate(), resCurrent is incremented by the accumulated excess. The part that is over the share cursor is then natively shared among teammates.
NativeExcessSharing=false disables this native sharing system entirely.
After the native sharing system, resCurrent is clamped to storage again and everything that was still exceeding is accounted in the teamstats as truely excessed value.

NativeExcessSharing (could actually use another label to keep consistency with what it actually does such as NativeOverflowSharing) controls overflow altogether, ResourceExcess only controls the excess part of the overflow. So a combination of ResourceExcess returns true and nativeExcessSharing = true is an hybrid version that ignores excess but still allows to share over your share cursor.

While disabled NativeResourceExcess and still returning false in (Team)ResourceExcess allows to add excess to the counter and therefor account in teamstats without going through the Native overflowing process.

Edit:
There is also no clear mention of how native sharing adds your previously stacked excess to your storage via resDelayedShare.
As in if i excess 1k over the first 15 frames, then emptied my storage during the next 15, at the end of the 30th i will be at 1k res instead of 0.
Which becomes false if i dont accumulate from ResourceExcess and aim for a once per frame custom overflow handler.
That's more of a generality on overflow system but prolly worth documenting if people aim at rebuilding the system on game side.

@sprunk

sprunk commented Dec 2, 2025

Copy link
Copy Markdown
Collaborator

I think there needs to be a clarification about "excess" being different from "overflow".

Right, but the engine consistently calls all related phenomenons "excess", and doesn't call anything "overflow". AFAICT:

  • individual instances of resources being temporarily buffered due to current storage getting truncated to max storage are not called anything in the interface.
  • the internal resDelayedShare var, which is per-slowupdate and is currently an accumulator of those resources above, isn't called anything in the interface.
  • in Add gadget:ResourceExcess(excessTable) -> bool #2642, resDelayedShare becomes a metaaccumulator of the more granular per-frame accumulator (internally called resExcessThisFrame), which calls itself "excess", as in the callin is gadget:ResourceExcess().
  • the resources that are voided at the end of the slowupdate step when truncating current to max are called "excess" in statistics.
  • resource above the "red share slider" are also called "excess" implicitly by the system.nativeExcessSharing modrule.

So there's up to 3 different phenomenons here. #2642 isn't yet merged, and I don't think anyone other than you uses the new modrule yet, so we can still rename both. 2642 could become gadget:ResourceOverflow(), dunno how to call the modrule though.

Whats "excess gadget", whats "sharing modrules"?
Looks good but would be better to be more objective.

How about like this (taking 2642 into account, which the current text of the PR does anyway)?

Engine-based resource generation, within a frame

  • unit incomes generate individual transactions, and are never collected together. For example, if you have two +5 generators, two constructors reclaiming the same wreck for +2 each, and a unit consuming -8 upkeep, then it's five transactions of +5, +5, +2, +2, -8 respectively, not a single transaction of +6.
  • transaction order is unspecified. In the example above, maybe the order is +5, +5, +2, +2, -8 or maybe it is +2, -8, +5, +2, +5.
  • transactions are handled individually to completion as they come. If you have 10/100 stored resources then you will always fail to pay costs of -11, even if there's income transactions immediately after. If you've got 80/100 stored then you will always generate overflow on transactions of +21 or more, even if there's transactions to consume resources immediately after. In particular this means
  • overflow counter is an accumulator that is never directly used to pay costs. If you have 100/100 storage and get +10, -100, -5 then the first transaction generates 10 overflow, the second empties your stored to 0 while maintaining 10 overflow, and the third fails due to insufficient stored resources despite the 10 overflow.
  • Lua calls generate individual transactions, in the order they are called.
  • at the end of each frame, gadget:ResourceExcess is called with the value of the accumulator. Lua can return a boolean value that denotes whether it considers the overflow handled.
  • if not handled by Lua, the per-frame overflow accumulator is added to a per-slowupdate metaaccumulator. Either way it is then reset.

Resource handling at slowupdate

  • first, the overflow metaaccumulator is added to current stored resources disregarding the max storage cap. If you've got 80/100 stored and 40 overflow, then you've got 120/100 stored for the purpose of the following steps.
  • then, if system.nativeExcessSharing modrule is true, anything above the sharing level (aka the "red slider") is moved over into a common per-allyteam pool to be distributed. In particular, the slider is capped at 100% of max storage so will always attempt to redistribute resources above max storage, but they are not fundamentally treated any differently here.
  • lastly, current storage is truncated to max storage. This is the only stage at which resources are actually lost. These voided resources are what stats collection calls "excess", any shuffling that happens in the previous step is bookkept as sent/received.

@NortySpock

NortySpock commented Dec 2, 2025 via email

Copy link
Copy Markdown
Author

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.

4 participants