-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix lag from copying players and make them noncopyable #26331
Fix lag from copying players and make them noncopyable #26331
Conversation
Move various things from player and Character to SkillLevelMap so that code doesn't need an entire player object to access this information.
To avoid copying players, we need to be able to query Character volume and weight under the assumption that the items carried have been altered in some way. This adds the functionality to do so.
inventory_multiselector required a player object to compute the volume and weight stats. This is not feasible without copying the player, which we don't want to do. Therefore, change the interface so that the virtual function is returning the stats themselves, rather than the player from which they should be extracted. This also removes the need for ugly mutable data members. Fixes CleverRaven#26147 (both drop and washing clothing lag should be gone, since we assume they arose from unnecessary player copies). This lays the foundation for a better clothes washing interface that lets you know how much soap and water is required, but that is not implemented yet.
Rather than working with a copy of the npc, we rather work with just a copy of their inventory.
Rather than copying the player, we simply copy their skills.
Copying players was the cause of late-game lag which was hard to notice in simple testing (CleverRaven#26147). By making them noncopyable we prevent accidental copies.
Old gcc versions don't like this initialization.
src/skill.cpp
Outdated
int SkillLevelMap::get_skill_level( const skill_id &ident, const item &context ) const | ||
{ | ||
auto id = context.is_null() ? ident : context.contextualize_skill( ident ); | ||
return get_skill_level( std::move( id ) ); |
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.
The move here is useless, the other get_skill_level
function takes its argument as reference anyway.
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. It's also harmless, and might help in the future if that function changes. But I can see that's unlikely, so happy to change if you wish.
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.
On reflection, it has a performance impact on debug builds, so I'll get rid of it.
src/character.h
Outdated
replace_inv( &r ) | ||
{} | ||
const std::map<const item *, int> *without_items = nullptr; | ||
const inventory *replace_inv = nullptr; |
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.
Any reasons that this are pointers? It could be references, or optional<T>
if you want a "not used" value.
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.
They can't be references because they might not be present. And optional values are expensive to copy. I experimented with various ways of writing the code, and this seemed the cleanest.
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.
In that case, I recommend optional<std::reference_wrapper<T>>
. It
- indicates that the value is optional,
- that (if it exists) is a reference (never null),
- is fast to copy as
reference_wrapper
is basically just a pointer, - is easy to distinguish from a pointer (which may indicate ownership, or may require dynamic memory allocation, who knows).
src/character.h
Outdated
}; | ||
|
||
units::mass weight_carried_with_tweaks( item_tweaks ) const; | ||
units::volume volume_carried_with_tweaks( item_tweaks ) 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.
Any reason these functions take by value and not by const reference?
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.
No particular reason; would work fine either way.
Previous workaround broke things for clang, so add even more braces!
Replace with optional<reference_wrapper<T>>.
These objects are now more heavyweight, so it makes sense to pass by const reference rather than value.
Prior changes on this branch to the character creation process meant that the recipes reported during skill selection were unreliable and recipes learnt during skill selection would be retained in game. This is because calling player::knows_recipe updates the known recipe cache. Avoid doing that, and reproduce the previous behaviour for listed recipes.
Summary
SUMMARY: Bugfixes "Fix lag due to copying player objects"
Purpose of change
Issue #26147 described a few situations where lag was caused by copying
player
objects. We shouldn't ever be copying player objects. Some cases were fixed in #26272. This deals with the rest, and fixes #26147.Describe the solution
To ensure this problem is gone and cannot accidentally return, we make
player
noncopyable. That also requires thatnpc
be noncopyable. Since it was easy, I also madeCharacter
noncopyable.The remaining situations where such objects were being copied were:
To resolve all these required some changes:
SkillLevelMap
fromplayer
andCharacter
. So the new character screen can just copy that.As a side-effect, the wash clothes window no longer displays weight and volume at all (it served no purpose).
Describe alternatives you've considered
The messiest one to get working was the multidrop window. The way it handles item stacks is fairly weird. I suspect there's an opportunity there for further refactoring, but that would be a bigger change than seems sensible for this bugfix.
Additional context
This lays the groundwork for an improvement to the wash clothes window; it should be easy now to put water and soap requirements in the area where weight and volume previously were, but I don't want to do that on this PR.