-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
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 .blend files with quotation marks in filename fail to import #94004
Fix .blend files with quotation marks in filename fail to import #94004
Conversation
63b5b00
to
36b6fe1
Compare
This comment was marked as resolved.
This comment was marked as resolved.
Note that the String class has |
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.
Changes make sense to me, and hopefully that covers all edge cases.
Thanks for the error handling code as a bonus, that's very nice :)
I just add a little problem with this PR on my computer. I had 2 versions on Godot running, one with this modification, and one without. If the version without did a blender import first, blender was started with the old script that returns 'cannot marshal None unless allow_none is enabled'. When I import a file in Godot with this PR included, the error is trapped and the importation fails. I had to close the old version before it works again. Both version use the same blender process with the old script. I think I should discard the error 'cannot marshal None unless allow_none is enabled' just to prevent issues for users using different versions of Godot? What do you think? |
Not sure if it's worth adding this check, for the rare case of having multiple Godot instances running and using Blender imports on both... I suppose if the check is simple enough to implement, documented with a comment as to why it's needed, it won't do any harm to add it. At least the error message is hard-coded on Python's side ^^ |
36b6fe1
to
8b5e65d
Compare
There you go, I just added a check to discard the error 'cannot marshal None unless allow_none is enabled'. |
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 was investigating #93997 and was digging through this code yesterday.
I wrote a similar thing but didn't finish it and saw yours today - demolke@7533dcd
@@ -268,7 +276,22 @@ Error EditorImportBlendRunner::do_import_rpc(const Dictionary &p_options) { | |||
} | |||
case HTTPClient::STATUS_BODY: { | |||
client->poll(); | |||
// Parse response here if needed. For now we can just ignore it. | |||
PackedByteArray response_chunk = client->read_response_body_chunk(); |
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 only works if the stream can be read in one go.
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.
Following your comments, I used an easier method to determine if the exportation is successful or not. I just look if the string 'BLENDER_GODOT_EXPORT_SUCCESSFUL' is present in the output. That way, I don't have to parse the XML and the success message should always be in the first chunk. Worst case scenario, the error message is incomplete which, I think it's not a big deal (we did not have any error message before).
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 you're introducing a string to look for, I think it would be safer to read the whole buffer. It's not too much extra work.
Something like
// Wait for response.
bool done = false;
PackedByteArray response;
while (!done) {
status = client->get_status();
switch (status) {
case HTTPClient::STATUS_REQUESTING: {
client->poll();
break;
}
case HTTPClient::STATUS_BODY: {
client->poll();
response.append_array(client->read_response_body_chunk());
break;
}
case HTTPClient::STATUS_CONNECTED: {
done = true;
break;
}
default: {
ERR_FAIL_V_MSG(ERR_CONNECTION_ERROR, vformat("Unexpected status during RPC response: %d", status));
}
}
}
String response_text = "No response from Blender.";
if (response.size() > 0) {
response_text = String::utf8((const char *)response_chunk.ptr(), response_chunk.size());
}
if (client->get_response_code() != HTTPClient::RESPONSE_OK) {
ERR_FAIL_V_MSG(ERR_QUERY_FAILED, vformat("Error received from Blender - status code: %s, error: %s", client->get_response_code(), response_text));
} else if (response_text.find("BLENDER_GODOT_EXPORT_SUCCESSFUL") < 0) {
// Previous versions of Godot used a Python script where the RPC function did not return
// a value, causing the error 'cannot marshal None unless allow_none is enabled'.
// If an older version of Godot is running and has started Blender with this script,
// we will receive the error, but there's a good chance that the import was successful.
// We are discarding this error to maintain backward compatibility and prevent situations
// where the user needs to close the older version of Godot or kill Blender.
if (response_text.find("cannot marshal None unless allow_none is enabled") < 0) {
ERR_FAIL_V_MSG(ERR_QUERY_FAILED, vformat("Blender exportation failed: %s", response_text));
}
}
return OK;
}
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.
Actually that's wrong, let me check what's the right way :)
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.
Updated the comment above
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 a lot for this code. I never worked with Godot HTTPClient before. It seems to work perfectly.
@@ -281,6 +304,75 @@ Error EditorImportBlendRunner::do_import_rpc(const Dictionary &p_options) { | |||
return OK; | |||
} | |||
|
|||
bool EditorImportBlendRunner::_parse_blender_http_response(const Vector<uint8_t> &p_response_data, String &r_error_message) { |
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 not return Error and then you can propagate the result out of do_import_rpc()
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.
Good point! But this method does not exists anymore.
if (parser->get_node_name() == "string") { | ||
parser->read(); | ||
r_error_message = parser->get_node_data().trim_suffix("\n"); | ||
// Previous versions of Godot used a Python script where the RPC function did not return |
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.
When I was looking at it, I thought I would just grab all the text elements and avoid the nesting/replicating the xml structure in code.
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.
Good idea, that made the code really simple and less prone to errors.
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.
8b5e65d
to
b16fe04
Compare
b16fe04
to
d244d6f
Compare
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
Thanks @Hilderin for the fix and everyone else for the in-depth review! |
The main problem was using
c_escape
for paths which add\
to'
resulting to an invalid path since\
is not an escape character in XML.Also, I added a parse and print for
faultString
if an error occur in Blender/Python.I also fixed the problem where every call returned 'cannot marshal None unless allow_none is enabled' because the Python method
export_gltf
did not return a value.Tested:
'
.'
(do_import_direct
).Example of the blender error message from RPC response: