-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Custom splits #397
Custom splits #397
Conversation
I'm not sure how to fix the unresolved external symbols. I don't have this issue in my environment on Windows |
This builds fine for me on Fedora 37. Probably weird stuff with MSVC.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed write-up! Yeah, I can see the appeal here, and it's great to see an implementation.
There are very likely entities and other events that runners would want to be able to split on, like maybe an NPC dying or something, that are not tracked yet within BXT. I guess we'll be adding patterns and offsets to cover those situations as we need them, gradually.
Yeah, that sounds like a good plan to me.
Custom splits make more sense when used with subsplits on LiveSplit, instead of normal splits.
I guess this isn't really a question about the functionality in this PR, but rather how to best use it with LiveSplit.
I believe ButtonUse would have to check if there's a master entity and if it's triggered, and also check the toggle state because it may be returning to the initial state instead of getting activated. These would conditionate if the button has to get activated or not, and right now we are not taking those into account. I don't think it's necessary for this PR, because it's not a big problem as it is now, at least it hasn't been in my testing, setting up splits on a lot of entities along the HL run. Oh yeah, another problem with this is that as a result, we may be splitting on multi_managers that would be called after the button finishes moving or after the activation delay is completed, which is not exactly what runners may want, since they probably want to track the time they press the button at, and that time +2s or whatever is the delay for that button. Muty says it doesn't really matter, but if we release this as is and then improve this behaviour on a next release, then they may easily gold the split corresponding to the entity with improved behaviour, and they would have a now fake gold in the split that follows this one.
Sure, if you and Muty tested it extensively and it works fine, then it's good. As for fake golds, hopefully that kind of "breaking" fix won't be a frequent occurrence and not a big bother to fix the splits manually on the runner's side.
The split times are output to console when the run ends (timer stops) are not sorted by arrival order. They show in the same order as they were added. It's not a big problem because you can just add them in the order you want them to appear, but if there's a map with different routes and you have 2 splits that can be hit in any order depending on the route you choose that run, then it would be pretty useful to have them sorted by arrival.
Yeah, doesn't sound like a blocker for this PR. Can be added later.
Add a splits history, that keeps track of all the split times/speeds/etc. during the current game session or even stores them in a file. So that you can throw this data on a spreadsheet or whatever, or just to add the capability of comparing against your best time/speed for a split.
Isn't this sort of stuff better delegated to LiveSplit or other timers? Not sure how much value there is to reimplementing parts of LiveSplit in BXT.
Should i add an explanation for the command/cvars, or is it enough with the code? you may want it for the wiki too, but i guess anything could change after the code review, so maybe it's better to document that later
I would appreciate if you added the documentation to the wiki page after the PR is merged. I think anyone can edit the wiki page?
Sorry about the CI, Microsoft's servers are acting up for the past few days for some reason (I'm sick of it too). But I see a bunch of new warnings, could you fix them please?
BunnymodXT/modules/HwDLL.cpp
Outdated
void HwDLL::TimerStart() | ||
{ | ||
if (!CustomHud::GetCountingTime()) | ||
HwDLL::GetInstance().Called_Timer = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HwDLL::GetInstance().Called_Timer = true; | |
HwDLL::GetInstance().Called_Timer = true; |
BunnymodXT/modules/HwDLL.cpp
Outdated
static void handler() | ||
{ | ||
if (Splits::splits.empty()) { | ||
HwDLL::GetInstance().ORIG_Con_Printf("You haven't placed any split triggers.\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HwDLL::GetInstance().ORIG_Con_Printf("You haven't placed any split triggers.\n"); | |
HwDLL::GetInstance().ORIG_Con_Printf("You haven't added any splits.\n"); |
Since there aren't only triggers?
BunnymodXT/modules/HwDLL.cpp
Outdated
|
||
struct HwDLL::Cmd_BXT_Split_On | ||
{ | ||
USAGE("Usage: bxt_split_on <target_name> [map_name]\n Tells BunnySplit to split when activating an entity by the specified name, and prints to console the current time.\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd like more descriptive names than "bxt_split_on" and "bxt_split_add". How about "bxt_split_on_entity" and "bxt_split_add_trigger"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about:
- bxt_split_on -> bxt_splits_add_entity
- bxt_splits_add -> bxt_splits_add_trigger (as you suggested, but in plural)
May be easier to remember and to navigate when using down arrow on the autocomplete dropdown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works yeah
BunnymodXT/modules/HwDLL.cpp
Outdated
static void handler(const char* id_or_name) | ||
{ | ||
// First try to find it by name, otherwise we'll try to find by id | ||
const auto itr = std::find_if(Splits::splits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird line break
BunnymodXT/modules/HwDLL.cpp
Outdated
// First try to find it by name, otherwise we'll try to find by id | ||
const auto itr = std::find_if(Splits::splits. | ||
begin(), Splits::splits.end(), | ||
[&id_or_name](const Splits::Split& s) { return std::string(id_or_name) == s.get_name(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allocates a new std::string every iteration. It should either do it once before the search or use something like strcmp
BunnymodXT/modules/ServerDLL.cpp
Outdated
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BunnymodXT/modules/ServerDLL.cpp
Outdated
// already have the button's master entity triggered and the button should already be enabled, so it's | ||
// better than the ButtonUse in that regard, but it doesn't get called for every button... | ||
|
||
// TODO: check for this button's availability, it may not be usable yet, like the button in the test chamber, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the comment above say that the button in this method is in fact usable already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the bottom comment is deprecated :/
BunnymodXT/patterns.hpp
Outdated
|
||
PATTERNS(CBaseDoor__DoorActivate, | ||
"HL-WON", | ||
"51 56 8B F1 57 8D BE ?? ?? ?? ??" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing ?? do not affect the matching
"51 56 8B F1 57 8D BE ?? ?? ?? ??" | |
"51 56 8B F1 57 8D BE" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, I just blindly copied from makesig, I should have been more careful
BunnymodXT/splits.cpp
Outdated
// because this is just a more specific kind of trigger | ||
} | ||
|
||
const std::string& Split::get_name() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any particular reason to add getters and setters for these rather than just working with fields directly? They all seem to just do what they say with no extra logic. The two ones in custom_triggers do have extra logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, my brain just had a java moment :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the PR marked as draft btw? Do you plan on giving it more testing before merging? I'm asking because I need to release a new BXT version very soon (ppl are waiting for nvidia crash fix), so if this PR needs more testing I won't wait for it.
BunnymodXT/modules/HwDLL.cpp
Outdated
|
||
Splits::splits.erase(Splits::splits.begin() + (idx - 1)); | ||
const auto it = std::find_if(Splits::splits.begin(), Splits::splits.end(), [&split](const Splits::Split& s) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this won't actually work if this split has an empty name, it's being deleted by id, and there's another split with an empty name before. Or if there are multiple splits with the same name. Maybe worth retaining the search as is then...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, i have reverted it
I marked it as a draft initially because i didn't know if you were gonna ask me to fix any of the points in the "Pending to resolve" part, or if you were gonna ask for some big change, so i thought there could still be some discussion to do. As i mentioned in the writeup, we think this is good enough to go, and we already gave it some testing in HL1 and HC. There could totally be cases that don't work outside these 2, but i believe the current functionality gives it enough flexibility to work around any issues that may arise, and in any case it should be better to go with the current implementation than delaying it further. I tested with the requested changes yesterday for a few hours, and it looks good. |
I'm not planning on adding anything else for this PR. If you have any more changes to request i'll work on them tomorrow |
Looks good, thanks a lot! |
Why?
I implemented BXT splits with the main goal of being able to use custom splits with Livesplit, that can rely on different entities being activated and not only on a volume that you can go through (like custom triggers or
trigger_*
entities). There are a few cases where we would need the more fine grained pace information that these custom splits can provide, and where map splits are just too verbose or even useless. There are some key moments in the run where runners analyze their pace and compare to other runs, but don't keep track of it through Livesplit because there's no autosplit there, and it would be very convenient that some events during the run could be used for autosplitting. Some examples of this are:And there are probably a few more that are common pace-checking moments among most runners, and that are not being tracked at least automatically (afaik no HL runner does manual splits or a mix of both, they mostly rely on map/chapter autosplitting). That is for the full run of HL. There are probably a lot more instances where you would want to autosplit in other mods.
Another important case where custom splits would be useful is on Individual Level runs, where runs are much more optimized than a full game run and having more fine grained information is essential. It's applicable to any HL IL nowadays, but the easiest example is Hazard Course. It has lots of stages or places where you would really want to keep track of your pace, because you're trying to save less than a couple seconds in a 2:30 minutes run and losing 400ms in the first couple stages (25s) would already mean a reset. It's inconvenient to wait until the next changelevel to know your pace (you may not remember the pace you had in your PB in all of these stages), because that's 1/3 into the run. Then some changelevels are placed in a way that don't really tell much about your pace in such optimized runs, because you may have entered the trigger with a good time, but bad speed, position and/or angles, due to being inconveniently placed in a corridor or something. It gives inconsistent or useless info about your pace, so in these cases you wish you could replace that split with another one that is more meaningful, harder to trick like a button press or some event that is followed by a cutscene or waiting time, where your speed/position when splitting doesn't really matter or it's gonna get reset anyways.
Lastly, it can be used for practicing microoptimizations in segments inside a map. So basically to break down a map into smaller parts and be able to track the pace in every segment. Thinking not only about campaign maps, but any movement map like the ones played in HLKZ. I have used these custom splits with one HLKZ map and it was pretty useful to optimize the run and use this information later to do actual runs on AG. I'm not familiar with TASes or segmenting, but maybe they can also use custom splits this way, since it's not only meant to work with Livesplit, but it can also tell you the pace ingame through the HUD or console.
So that's the purpose of bxt splits, but why create a whole set of new commands and not work with existing custom triggers and
bxt_fire_on_*
to make them able to split? I thought about this but discarded the idea mainly for a couple reasons:bxt_timer_
commands are also disallowed IIRC.Pending to resolve
There are some things that can be improved and other things that are not directly related to BXT, but are problems that arise because of it. I'm not sure if i want to resolve them in this PR or if it is good enough to start going. I have been reviewing most if not all of the functionality with Muty, and he thinks this is good to go and can be useful immediately after the next BXT release, and Kananga would like the Livesplit issue (mentioned below) to be resolved to start using custom splits, but that's unrelated to this PR and i'll try to resolve it soon;
ButtonUse
would have to check if there's a master entity and if it's triggered, and also check the toggle state because it may be returning to the initial state instead of getting activated. These would conditionate if the button has to get activated or not, and right now we are not taking those into account. I don't think it's necessary for this PR, because it's not a big problem as it is now, at least it hasn't been in my testing, setting up splits on a lot of entities along the HL run. Oh yeah, another problem with this is that as a result, we may be splitting onmulti_manager
s that would be called after the button finishes moving or after the activation delay is completed, which is not exactly what runners may want, since they probably want to track the time they press the button at, and that time +2s or whatever is the delay for that button. Muty says it doesn't really matter, but if we release this as is and then improve this behaviour on a next release, then they may easily gold the split corresponding to the entity with improved behaviour, and they would have a now fake gold in the split that follows this one.Ideas for future PRs related to the current one
I should probably create an issue for each these:
Should i add an explanation for the command/cvars, or is it enough with the code? you may want it for the wiki too, but i guess anything could change after the code review, so maybe it's better to document that later
Also i implemented most of this back in September, but i got very sick for a month and couldn't really work on anything, and then i got busy with other things, so i postponed this until now. Hopefully that's not a big problem when trying to answer questions about why i did this or that, but i may have forgotten the reasoning behind
About the code style, i just tried to imitate whatever was surrounding the new changes, and in the case of the Split class i tried to imitate the Trigger class. All of this results in having different code styles along the commit