Files
WarMechanic 6a89b524d5 Event Scheduler System (#2355)
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Excerpt

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

thou shall be thy axe that continues the work - to behead all
`EntityQueryEnumerator`s, and thus i require it. 'tis imperative i have
at thee and perform the duties. if the plague bearing shitcode is not
cut at thy roots, it shall only nest a deeper grave and spread locusts.

---

# Description

It has been a reoccuring theme that someone irresponsibly uses
`EntityQueryEnumerator` and then suddenly server performance is worse. A
lot of these cases involve using EQE to iterate a timer on a component,
to start or stop an effect after a delay. Rather than iterating `frames
* n` times per second FOR EVERY UNIQUE SYSTEM THAT DOES THIS (QUITE A
FEW), we instead iterate `frames` per second regardless of systems using
the Event Scheduler.

The Event Scheduler itself is a list of events that wait to be triggered
after a delay. Rather than iterating through all of them, they are
sorted in order of occurance using a PriorityQueue. I love priority
queues because they sort as you enqueue to them, and apparently the sort
complexity is logarithmic? But mostly because of the former.

I chose to write the scheduler the way I did because the choice to use
async seems too big for me alone. So this system is synchronous and
updates on game time.

This is mostly a practical optimisation. The code which I have written
is almost certainly not optimal, but the simple act of replacing EQE
delays will significantly improve server performance anyway. Rust
monsters feel free to rewrite the event scheduler to be more performant.

NOTE: For some reason PriorityQueue is banned on the client, and
configuring it requires editing a RobustToolbox file. So for now this
system is restricted to Content.Server until we start using our engine
fork.
---

# TODO

<!--
A list of everything you have to do before this PR is "complete"
You probably won't have to complete everything before merging but it's
good to leave future references
-->

- [x] Working queue of events ordered by execution times
- [x] A function to enqueue any-event-defined-ever into the scheduler
with a delay
- [x] Delay the event, and then fire it
- [x] Implement retroactive schedule cancelling

In a future PR:
- Add ```System.Collection.Generic.PriorityQueue`2``` to whitelist in
`RobustToolbox/Robust.Server/ContentPack/Sandbox.yml`
- - Shared files had to be relocated to server because they were banned
on the client and would cause an exception.
- Investigate insert performance as more systems are added to use the
EventScheduler
- - MLGTASTICa rose the idea of 'buckets' as an optimisation.
- - I theorise multiple priority queues for different types of events
might also work.

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>
<p>

Demonstration:
https://youtu.be/CGB6SDWGc-Q

Response to MLGTASTICa, stress testing:
https://youtu.be/30OA06Pzhtk

</p>
</details>

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->
- add: Added the EventScheduler system that lets you raise an event at a
certain time or after a delay without killing performance.
- fix: Optimised EMP by migrating from EQE to the new EventScheduler
system.
2025-07-12 03:02:21 +10:00
..