-
-
Notifications
You must be signed in to change notification settings - Fork 538
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## About The Pull Request I'm not calling them biddle verbs. Basically, adds verbs to a queue so that they won't be run if the MC is overtiming since by default BYOND will just run them even if it means the MC won't get much/any time. tl;dr possible lag/overtime reduction from verbs like input Ports: tgstation/tgstation#65589 tgstation/tgstation#68990 tgstation/tgstation#70647 tgstation/tgstation#71520 ## Why It's Good For The Game Overtime bad ## Changelog :cl: /:cl: <!-- Both :cl:'s are required for the changelog to work! You can put your name to the right of the first :cl: if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. --> --------- Co-authored-by: KylerAce <[email protected]> Co-authored-by: Kyle Spier-Swenson <[email protected]> Co-authored-by: LemonInTheDark <[email protected]> Co-authored-by: Mothblocks <[email protected]>
- Loading branch information
1 parent
f0890b6
commit 4e0ccdc
Showing
29 changed files
with
450 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The byond tick proceeds as follows: | ||
1. procs sleeping via walk() are resumed (i dont know why these are first) | ||
|
||
2. normal sleeping procs are resumed, in the order they went to sleep in the first place, this is where the MC wakes up and processes subsystems. a consequence of this is that the MC almost never resumes before other sleeping procs, because it only goes to sleep for 1 tick 99% of the time, and 99% of procs either go to sleep for less time than the MC (which guarantees that they entered the sleep queue earlier when its time to wake up) and/or were called synchronously from the MC's execution, almost all of the time the MC is the last sleeping proc to resume in any given tick. This is good because it means the MC can account for the cost of previous resuming procs in the tick, and minimizes overtime. | ||
|
||
3. control is passed to byond after all of our code's procs stop execution for this tick | ||
|
||
4. a few small things happen in byond internals | ||
|
||
5. SendMaps is called for this tick, which processes the game state for all clients connected to the game and handles sending them changes | ||
in appearances within their view range. This is expensive and takes up a significant portion of our tick, about 0.45% per connected player | ||
as of 3/20/2022. meaning that with 50 players, 22.5% of our tick is being used up by just SendMaps, after all of our code has stopped executing. Thats only the average across all rounds, for most highpop rounds it can look like 0.6% of the tick per player, which is 30% for 50 players. | ||
|
||
6. After SendMaps ends, client verbs sent to the server are executed, and its the last major step before the next tick begins. | ||
During the course of the tick, a client can send a command to the server saying that they have executed any verb. The actual code defined | ||
for that /verb/name() proc isnt executed until this point, and the way the MC is designed makes this especially likely to make verbs | ||
"overrun" the bounds of the tick they executed in, stopping the other tick from starting and thus delaying the MC firing in that tick. | ||
|
||
The master controller can derive how much of the tick was used in: procs executing before it woke up (because of world.tick_usage), and SendMaps (because of world.map_cpu, since this is a running average you cant derive the tick spent on maptick on any particular tick). It cannot derive how much of the tick was used for sleeping procs resuming after the MC ran, or for verbs executing after SendMaps. | ||
|
||
It is for these reasons why you should heavily limit processing done in verbs, while procs resuming after the MC are rare, verbs are not, and are much more likely to cause overtime since theyre literally at the end of the tick. If you make a verb, try to offload any expensive work to the beginning of the next tick via a verb management subsystem. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
///if the running average click latency is above this amount then clicks will never queue and will execute immediately | ||
#define MAXIMUM_CLICK_LATENCY (0.5 DECISECONDS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* verb queuing thresholds. remember that since verbs execute after SendMaps the player wont see the effects of the verbs on the game world | ||
* until SendMaps executes next tick, and then when that later update reaches them. thus most player input has a minimum latency of world.tick_lag + player ping. | ||
* however thats only for the visual effect of player input, when a verb processes the actual latency of game state changes or semantic latency is effectively 1/2 player ping, | ||
* unless that verb is queued for the next tick in which case its some number probably smaller than world.tick_lag. | ||
* so some verbs that represent player input are important enough that we only introduce semantic latency if we absolutely need to. | ||
* its for this reason why player clicks are handled in SSinput before even movement - semantic latency could cause someone to move out of range | ||
* when the verb finally processes but it was in range if the verb had processed immediately and overtimed. | ||
*/ | ||
|
||
///queuing tick_usage threshold for verbs that are high enough priority that they only queue if the server is overtiming. | ||
///ONLY use for critical verbs | ||
#define VERB_OVERTIME_QUEUE_THRESHOLD 100 | ||
///queuing tick_usage threshold for verbs that need lower latency more than most verbs. | ||
#define VERB_HIGH_PRIORITY_QUEUE_THRESHOLD 95 | ||
///default queuing tick_usage threshold for most verbs which can allow a small amount of latency to be processed in the next tick | ||
#define VERB_DEFAULT_QUEUE_THRESHOLD 85 | ||
|
||
///attempt to queue this verb process if the server is overloaded. evaluates to FALSE if queuing isnt necessary or if it failed. | ||
///_verification_args... are only necessary if the verb_manager subsystem youre using checks them in can_queue_verb() | ||
///if you put anything in _verification_args that ISNT explicitely put in the can_queue_verb() override of the subsystem youre using, | ||
///it will runtime. | ||
#define TRY_QUEUE_VERB(_verb_callback, _tick_check, _subsystem_to_use, _verification_args...) (_queue_verb(_verb_callback, _tick_check, _subsystem_to_use, _verification_args)) | ||
///queue wrapper for TRY_QUEUE_VERB() when you want to call the proc if the server isnt overloaded enough to queue | ||
#define QUEUE_OR_CALL_VERB(_verb_callback, _tick_check, _subsystem_to_use, _verification_args...) \ | ||
if(!TRY_QUEUE_VERB(_verb_callback, _tick_check, _subsystem_to_use, _verification_args)) {\ | ||
_verb_callback:InvokeAsync() \ | ||
}; | ||
|
||
//goes straight to SSverb_manager with default tick threshold | ||
#define DEFAULT_TRY_QUEUE_VERB(_verb_callback, _verification_args...) (TRY_QUEUE_VERB(_verb_callback, VERB_DEFAULT_QUEUE_THRESHOLD, null, _verification_args)) | ||
#define DEFAULT_QUEUE_OR_CALL_VERB(_verb_callback, _verification_args...) QUEUE_OR_CALL_VERB(_verb_callback, VERB_DEFAULT_QUEUE_THRESHOLD, null, _verification_args) | ||
|
||
//default tick threshold but nondefault subsystem | ||
#define TRY_QUEUE_VERB_FOR(_verb_callback, _subsystem_to_use, _verification_args...) (TRY_QUEUE_VERB(_verb_callback, VERB_DEFAULT_QUEUE_THRESHOLD, _subsystem_to_use, _verification_args)) | ||
#define QUEUE_OR_CALL_VERB_FOR(_verb_callback, _subsystem_to_use, _verification_args...) QUEUE_OR_CALL_VERB(_verb_callback, VERB_DEFAULT_QUEUE_THRESHOLD, _subsystem_to_use, _verification_args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,5 @@ | ||
SUBSYSTEM_DEF(speech_controller) | ||
/// verb_manager subsystem just for handling say's | ||
VERB_MANAGER_SUBSYSTEM_DEF(speech_controller) | ||
name = "Speech Controller" | ||
wait = 1 | ||
flags = SS_TICKER | ||
priority = FIRE_PRIORITY_SPEECH_CONTROLLER//has to be high priority, second in priority ONLY to SSinput | ||
init_order = INIT_ORDER_SPEECH_CONTROLLER | ||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY | ||
|
||
///used so that an admin can force all speech verbs to execute immediately instead of queueing | ||
var/FOR_ADMINS_IF_BROKE_immediately_execute_all_speech = FALSE | ||
|
||
///list of the form: list(client mob, message that mob is queued to say, other say arguments (if any)). | ||
///this is our process queue, processed every tick. | ||
var/list/queued_says_to_execute = list() | ||
|
||
///queues mob_to_queue into our process list so they say(message) near the start of the next tick | ||
/datum/controller/subsystem/speech_controller/proc/queue_say_for_mob(mob/mob_to_queue, message, message_type) | ||
|
||
if(!TICK_CHECK || FOR_ADMINS_IF_BROKE_immediately_execute_all_speech) | ||
process_single_say(mob_to_queue, message, message_type) | ||
return TRUE | ||
|
||
queued_says_to_execute += list(list(mob_to_queue, message, message_type)) | ||
|
||
return TRUE | ||
|
||
/datum/controller/subsystem/speech_controller/fire(resumed) | ||
|
||
/// cache for sanic speed (lists are references anyways) | ||
var/list/says_to_process = queued_says_to_execute.Copy() | ||
queued_says_to_execute.Cut()//we should be going through the entire list every single iteration | ||
|
||
for(var/list/say_to_process as anything in says_to_process) | ||
|
||
var/mob/mob_to_speak = say_to_process[MOB_INDEX]//index 1 is the mob, 2 is the message, 3 is the message category | ||
var/message = say_to_process[MESSAGE_INDEX] | ||
var/message_category = say_to_process[CATEGORY_INDEX] | ||
|
||
process_single_say(mob_to_speak, message, message_category) | ||
|
||
///used in fire() to process a single mobs message through the relevant proc. | ||
///only exists so that sleeps in the message pipeline dont cause the whole queue to wait | ||
/datum/controller/subsystem/speech_controller/proc/process_single_say(mob/mob_to_speak, message, message_category) | ||
set waitfor = FALSE | ||
|
||
switch(message_category) | ||
if(SPEECH_CONTROLLER_QUEUE_SAY_VERB) | ||
mob_to_speak.say(message) | ||
|
||
if(SPEECH_CONTROLLER_QUEUE_WHISPER_VERB) | ||
mob_to_speak.whisper(message) | ||
|
||
if(SPEECH_CONTROLLER_QUEUE_EMOTE_VERB) | ||
mob_to_speak.emote("me",1,message,TRUE) |
Oops, something went wrong.