-
Notifications
You must be signed in to change notification settings - Fork 2k
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 mob deserialization errors in the client #4743
Conversation
@Rogier-5 Could you find out, why dead infant still presented in that list? Obviously, its an another bug. Whenever infant dies, he should remove itself from parents set, isn't it? |
I suppose so. OTOH, often a child should not die to begin with, until the parent does... But that's probably a more complex thing to fix. Anyway, at this moment, the serialization code and the deserialization code do not match, which causes the deserialization errors. That is a problem, independently of what triggers it. So I agree that the trigger is most probably a bug too, and should be fixed, but that's another PR. |
@Rogier-5 May I ask you? I know that feeling when you want a fix and want it fast. Here is a code where "removed" objects removed: As you can see here, there is no sign of infants removing themselves from parents set. EDIT: Wait. What if player attached to entity and log off and then log in back? |
i think the problem is the message count sent by server, not the datas you fixed by adding empty message |
@nerzhul: indeed. However, to fix the message count, there are basically two options:
Both require additonal work on every serialization, while the problem itself is relatively rare, and the additional 4 bytes are negligible So I chose this option as the more efficient one. |
@Foghrye4: The non-existent-child problem has other associated questions that should be answered: why does the attached entity disappear while the parent still exists ? Was that intended ? Should attached entities stay with the parent in general, or only in specific cases ? Is it necessary to take special measures to avoid either the child or the parent disappearing while the other does not ? What happens when the child is in a block that is being deactivated while the parent is not ? And possibly more. |
Why not? For example player could be attached to cart. Whenever he log-off, cart still remain on place.
Indeed. Player will be sad, if he wouldn't find this cart.
I don't think its a good idea to reattach players on re-login automatically. We should leave that to modders.
No, by a reasons above.
Could not be happened. Child real coordinates are always same as parents. I tried to spawn two entities, attach to each other, kill one and move around a little. Cant repeat that bug. In what condition that bug happened? |
@Foghrye4: child objects are not just players on carts. Mobf uses them for health bars, and I've seen them used for other purposes as well. And I'm sure there are other uses as well that I can't imagine at this moment. Life bars for instance, should remain attached to the parent at all times...
And if it's a health bar ? Why does it disappear while the parent is still there ? Or why does the parent disappear while the health bar remains ? |
👍 Fixes a legitimate oversight in serialization code. |
5005fcc
to
75bcee3
Compare
@nerzhul: I changed the code to fix the message count if possible, by rewinding the stream and overwriting the expected count with the actual count if they differ. |
is this me or LuaEntitySAO and PlayerSAO serialization functions are very similar. @Rogier-5 do you think it's possible to factorize those functions ? We have a common class for this if needed, UnitSAO |
@nerzhul: they are almost identical indeed. Like some other PlayerSAO / LuaEntitySAO methods too... Too much copy/paste coding I suspect :-) What is the function of class UnitSAO BTW ? It is quite small. Can its functionality be folded into ServerActiveObject ? Or does it serve a purpose that does not allow this ? I suppose PlayerSAO & LuaEntitySAO could benefit from some refactoring. I'm willing to have a look at it, but it is a more complex operation, and it will probably require that some things be implemented differently (e.g. splitting methods, adding virtual functions, etc.) so that the code can be effectively shared. So I would propose not to delay this small/trivial fix just because the code it fixes could also be refactored. |
UnitSAO was very recent and used when refactoring PlayerSAO model to port some SAO values common between LuaEntitySAO & PlayerSAO. For this pr especially, if you have a nice way to factorize the client init data it's good, for other refactor, we will look at this later, not here :) |
75bcee3
to
e74b406
Compare
@nerzhul: I refactored the code, but one change required another, and quite a few things ended up being moved to UnitSAO... |
Very nice refactor, can you move the one line functions to class def instead of the cpp ? |
} | ||
|
||
|
||
std::string UnitSAO::getPropertyPacket() |
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 this possible to change this method call to void getPropertyPacket(std::string &result) ? This should avoid a useless copy
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 copy operation in getPropertyPacket should be optimized away by the compiler (return value optimization). Also, C++11 supports move semantics, and that should guarantee that the string is not copied while being returned.
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.
okay then, no problem. as you know we are not C++11 aware ATM
} | ||
|
||
|
||
ItemGroupList UnitSAO::getArmorGroups() |
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 function should be const
|
||
ItemGroupList UnitSAO::getArmorGroups() | ||
{ | ||
return m_armor_groups; |
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.
Do we modify this result in upper functions ? else could be nice to return const ItemGroupList &
} | ||
|
||
|
||
// Applies to LuaEntitySAO for protocol >= 14 |
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.
void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); | ||
void addAttachmentChild(int child_id); | ||
void removeAttachmentChild(int child_id); | ||
UNORDERED_SET<int> getAttachmentChildIds(); |
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.
if we didn't write on the result, could be very good to use const ref on the result
e74b406
to
9161d95
Compare
|
||
bool UnitSAO::isAttached() | ||
{ | ||
if(!m_attachment_parent_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.
missing space
if(!m_attachment_parent_id) | ||
return false; | ||
// Check if the parent still exists | ||
ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_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.
return m_env->getActiveObject(m_attachment_parent_id) != NULL;
void UnitSAO::sendMandatoryDataToClient() | ||
{ | ||
if(!m_properties_sent) | ||
{ |
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.
space + brace
os<<serializeString(""); // name | ||
writeU8(os, 0); // is_player | ||
writeU8(os, protocol_version < 14 ? 0 : 1); // version | ||
os<<serializeString(""); // 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.
spaces
void notifyObjectPropertiesModified() { m_properties_sent = false; } | ||
void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); | ||
void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) const; | ||
void setBonePosition(const std::string &bone, v3f position, v3f rotation) |
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.
const v3f &
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'll change it, but I doubt copying 4 fewer bytes (on a 64-bit platform) will have any measurable performance impact.
1de0cf4
to
38d29fd
Compare
OK. I reviewed the code I moved for coding style issues. As I made so many changes already, I could as well make some more. However, as they change existing code without a real need, I'm not sure they are desired. So they are in a separate commit:
I'll squash the two commits if the second commit is acceptable. |
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., | |||
#include "constants.h" | |||
#include "genericobject.h" | |||
#include "environment.h" | |||
#include "remoteplayer.h" |
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 include this in the header ?
Is this not possible to add just
class RemotePlayer;
and include the header in the CPP file ?
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.
m_player
is dereferenced in PlayerSAO::getDescription
...
Exact then the last change is okay for me, i need to re-do a global review |
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 is supposed to fix a bug, not refactor stuff. Please open a seperate PR for the refactor
@Rogier-5 did you thinks it's possible to have two commits on this good feature ? |
38d29fd
to
a1ecfb7
Compare
Moved the refactorization to a separate PR. See #4760 |
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 dislike seeking backwards in the stream, isn't it possible to count in advance?
@sfan5: I don't know if it makes a difference, but the seeking backwards is in the As the number of messages is not known in advance, there are basically three ways to avoid seeking:
With string copying being the only practical alternative, I judged that seeking was preferable to doing the additional string copy. |
I agree with @sfan5, seeking is bad. I'd prefer counting, like: int message_count = 6 + m_bone_position.size();
for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
ii != m_attachment_child_ids.end(); ++ii) {
if (ServerActiveObject *obj = m_env->getActiveObject(*ii))
message_count++;
}
} Plus I don't like the It requires no additional function on the base class or similar, or am I wrong? |
The problem was seen while using the mobf mod package. The problem happens when the server serializes entity attachments. Sometimes, such attachments no longer exist. The serialization code skips those. However, the total number of attachments was serialized earlier. Therefore the client expects more than it gets, and logs a serialization error.
a1ecfb7
to
05196e9
Compare
@est31: It is not known in advance how many objects are actually attached: some attachments in the list may not exist. So either it will have to estimated, and then the serialized data may have to contain a number of empty messages, or it will have to be counted in advance, requiring extra container iterating. With the refactor, part of the code (the part which is shared between LuaEntitySAO and PlayerSAO) will move to the common base class. The only maintainable way to obtain the number of messages that the base class will serialize is to call a method in the base class. Adding a magic number in a derived class representing the number of messages the base class will serialize is not an option of course. So, the simplest and most maintainable solution IMO is to serialize the messages to a string, and append that string to the rest of the serialized data after serializing the count. I pushed a new version that does that, and I also eliiminated most of the counting. |
Nice, great job @Rogier-5 |
The problem was seen while using the mobf mod package.
The problem happened when the server would serialize entity attachments.
Sometimes, such attachments no longer exist. The serialization code would
skip those. However, the total number of attachments was serialized
earlier. Therefore the client would expect more than it got, and log a
serialization error.